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 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 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 char *root_directory
;
649 struct dual_timestamp timestamp
;
654 static void machine_status_info_clear(MachineStatusInfo
*info
) {
660 free(info
->root_directory
);
666 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
667 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
668 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
674 fputs(strna(i
->name
), stdout
);
676 if (!sd_id128_is_null(i
->id
))
677 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
681 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
682 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
685 printf("\t Since: %s; %s\n", s2
, s1
);
687 printf("\t Since: %s\n", s2
);
690 _cleanup_free_
char *t
= NULL
;
692 printf("\t Leader: %u", (unsigned) i
->leader
);
694 get_process_comm(i
->leader
, &t
);
702 printf("\t Service: %s", i
->service
);
705 printf("; class %s", i
->class);
709 printf("\t Class: %s\n", i
->class);
711 if (i
->root_directory
)
712 printf("\t Root: %s\n", i
->root_directory
);
714 if (i
->n_netif
> 0) {
717 fputs("\t Iface:", stdout
);
719 for (c
= 0; c
< i
->n_netif
; c
++) {
720 char name
[IF_NAMESIZE
+1] = "";
722 if (if_indextoname(i
->netif
[c
], name
)) {
731 printf(" %i", i
->netif
[c
]);
737 if (print_addresses(bus
, i
->name
, ifi
,
740 ALL_IP_ADDRESSES
) > 0)
743 print_os_release(bus
, "GetMachineOSRelease", i
->name
, "\t OS: ");
745 print_uid_shift(bus
, i
->name
);
748 printf("\t Unit: %s\n", i
->unit
);
749 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
751 if (arg_transport
== BUS_TRANSPORT_LOCAL
)
753 show_journal_by_unit(
758 i
->timestamp
.monotonic
,
761 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
762 SD_JOURNAL_LOCAL_ONLY
,
768 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
769 MachineStatusInfo
*i
= userdata
;
774 assert_cc(sizeof(int32_t) == sizeof(int));
775 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
781 i
->n_netif
= l
/ sizeof(int32_t);
782 i
->netif
= memdup(v
, l
);
789 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
791 static const struct bus_properties_map map
[] = {
792 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
793 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
794 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
795 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
796 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
797 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
798 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
799 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
800 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
801 { "NetworkInterfaces", "ai", map_netif
, 0 },
805 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
806 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
814 r
= bus_map_all_properties(bus
,
815 "org.freedesktop.machine1",
821 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
827 print_machine_status_info(bus
, &info
);
832 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
844 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_value
, arg_all
);
846 log_error_errno(r
, "Could not get properties: %m");
851 static int show_machine(int argc
, char *argv
[], void *userdata
) {
853 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
854 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
855 bool properties
, new_line
= false;
856 sd_bus
*bus
= userdata
;
861 properties
= !strstr(argv
[0], "status");
863 pager_open(arg_no_pager
, false);
865 if (properties
&& argc
<= 1) {
867 /* If no argument is specified, inspect the manager
869 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
874 for (i
= 1; i
< argc
; i
++) {
875 const char *path
= NULL
;
877 r
= sd_bus_call_method(bus
,
878 "org.freedesktop.machine1",
879 "/org/freedesktop/machine1",
880 "org.freedesktop.machine1.Manager",
886 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
890 r
= sd_bus_message_read(reply
, "o", &path
);
892 return bus_log_parse_error(r
);
895 r
= show_machine_properties(bus
, path
, &new_line
);
897 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
903 static int print_image_hostname(sd_bus
*bus
, const char *name
) {
904 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
908 r
= sd_bus_call_method(
910 "org.freedesktop.machine1",
911 "/org/freedesktop/machine1",
912 "org.freedesktop.machine1.Manager",
914 NULL
, &reply
, "s", name
);
918 r
= sd_bus_message_read(reply
, "s", &hn
);
923 printf("\tHostname: %s\n", hn
);
928 static int print_image_machine_id(sd_bus
*bus
, const char *name
) {
929 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
930 sd_id128_t id
= SD_ID128_NULL
;
935 r
= sd_bus_call_method(
937 "org.freedesktop.machine1",
938 "/org/freedesktop/machine1",
939 "org.freedesktop.machine1.Manager",
941 NULL
, &reply
, "s", name
);
945 r
= sd_bus_message_read_array(reply
, 'y', &p
, &size
);
949 if (size
== sizeof(sd_id128_t
))
950 memcpy(&id
, p
, size
);
952 if (!sd_id128_is_null(id
))
953 printf(" Machine ID: " SD_ID128_FORMAT_STR
"\n", SD_ID128_FORMAT_VAL(id
));
958 static int print_image_machine_info(sd_bus
*bus
, const char *name
) {
959 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
962 r
= sd_bus_call_method(
964 "org.freedesktop.machine1",
965 "/org/freedesktop/machine1",
966 "org.freedesktop.machine1.Manager",
967 "GetImageMachineInfo",
968 NULL
, &reply
, "s", name
);
972 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
979 r
= sd_bus_message_read(reply
, "{ss}", &p
, &q
);
985 if (streq(p
, "DEPLOYMENT"))
986 printf(" Deployment: %s\n", q
);
989 r
= sd_bus_message_exit_container(reply
);
996 typedef struct ImageStatusInfo
{
1005 uint64_t usage_exclusive
;
1006 uint64_t limit_exclusive
;
1009 static void image_status_info_clear(ImageStatusInfo
*info
) {
1019 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
1020 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
1021 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
1022 char bs
[FORMAT_BYTES_MAX
], *s3
;
1023 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
1029 fputs(i
->name
, stdout
);
1034 printf("\t Type: %s\n", i
->type
);
1037 printf("\t Path: %s\n", i
->path
);
1039 (void) print_image_hostname(bus
, i
->name
);
1040 (void) print_image_machine_id(bus
, i
->name
);
1041 (void) print_image_machine_info(bus
, i
->name
);
1043 print_os_release(bus
, "GetImageOSRelease", i
->name
, "\t OS: ");
1045 printf("\t RO: %s%s%s\n",
1046 i
->read_only
? ansi_highlight_red() : "",
1047 i
->read_only
? "read-only" : "writable",
1048 i
->read_only
? ansi_normal() : "");
1050 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
1051 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
1053 printf("\t Created: %s; %s\n", s2
, s1
);
1055 printf("\t Created: %s\n", s2
);
1057 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
1058 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
1060 printf("\tModified: %s; %s\n", s2
, s1
);
1062 printf("\tModified: %s\n", s2
);
1064 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
1065 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
1067 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
1069 printf("\t Usage: %s\n", s3
);
1071 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
1072 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
1074 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
1076 printf("\t Limit: %s\n", s3
);
1079 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
1081 static const struct bus_properties_map map
[] = {
1082 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
1083 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
1084 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
1085 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
1086 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
1087 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
1088 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
1089 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
1090 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
1091 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
1095 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1096 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
1103 r
= bus_map_all_properties(bus
,
1104 "org.freedesktop.machine1",
1110 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
1116 print_image_status_info(bus
, &info
);
1121 typedef struct PoolStatusInfo
{
1127 static void pool_status_info_clear(PoolStatusInfo
*info
) {
1136 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
1137 char bs
[FORMAT_BYTES_MAX
], *s
;
1140 printf("\t Path: %s\n", i
->path
);
1142 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
1144 printf("\t Usage: %s\n", s
);
1146 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
1148 printf("\t Limit: %s\n", s
);
1151 static int show_pool_info(sd_bus
*bus
) {
1153 static const struct bus_properties_map map
[] = {
1154 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
1155 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
1156 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
1160 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
1161 .usage
= (uint64_t) -1,
1162 .limit
= (uint64_t) -1,
1165 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1170 r
= bus_map_all_properties(bus
,
1171 "org.freedesktop.machine1",
1172 "/org/freedesktop/machine1",
1177 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
1179 print_pool_status_info(bus
, &info
);
1185 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
1197 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_value
, arg_all
);
1199 log_error_errno(r
, "Could not get properties: %m");
1204 static int show_image(int argc
, char *argv
[], void *userdata
) {
1206 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1207 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1208 bool properties
, new_line
= false;
1209 sd_bus
*bus
= userdata
;
1214 properties
= !strstr(argv
[0], "status");
1216 pager_open(arg_no_pager
, false);
1220 /* If no argument is specified, inspect the manager
1224 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
1226 r
= show_pool_info(bus
);
1231 for (i
= 1; i
< argc
; i
++) {
1232 const char *path
= NULL
;
1234 r
= sd_bus_call_method(
1236 "org.freedesktop.machine1",
1237 "/org/freedesktop/machine1",
1238 "org.freedesktop.machine1.Manager",
1244 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
1248 r
= sd_bus_message_read(reply
, "o", &path
);
1250 return bus_log_parse_error(r
);
1253 r
= show_image_properties(bus
, path
, &new_line
);
1255 r
= show_image_info(bus
, path
, &new_line
);
1261 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1262 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1263 sd_bus
*bus
= userdata
;
1268 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1271 arg_kill_who
= "all";
1273 for (i
= 1; i
< argc
; i
++) {
1274 r
= sd_bus_call_method(
1276 "org.freedesktop.machine1",
1277 "/org/freedesktop/machine1",
1278 "org.freedesktop.machine1.Manager",
1282 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1284 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1292 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1293 arg_kill_who
= "leader";
1294 arg_signal
= SIGINT
; /* sysvinit + systemd */
1296 return kill_machine(argc
, argv
, userdata
);
1299 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1300 arg_kill_who
= "leader";
1301 arg_signal
= SIGRTMIN
+4; /* only systemd */
1303 return kill_machine(argc
, argv
, userdata
);
1306 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1307 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1308 sd_bus
*bus
= userdata
;
1313 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1315 for (i
= 1; i
< argc
; i
++) {
1316 r
= sd_bus_call_method(
1318 "org.freedesktop.machine1",
1319 "/org/freedesktop/machine1",
1320 "org.freedesktop.machine1.Manager",
1326 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1334 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1335 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1336 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1337 _cleanup_free_
char *abs_host_path
= NULL
;
1338 char *dest
, *host_path
, *container_path
;
1339 sd_bus
*bus
= userdata
;
1345 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1347 copy_from
= streq(argv
[0], "copy-from");
1348 dest
= argv
[3] ?: argv
[2];
1349 host_path
= copy_from
? dest
: argv
[2];
1350 container_path
= copy_from
? argv
[2] : dest
;
1352 if (!path_is_absolute(host_path
)) {
1353 r
= path_make_absolute_cwd(host_path
, &abs_host_path
);
1355 return log_error_errno(r
, "Failed to make path absolute: %m");
1357 host_path
= abs_host_path
;
1360 r
= sd_bus_message_new_method_call(
1363 "org.freedesktop.machine1",
1364 "/org/freedesktop/machine1",
1365 "org.freedesktop.machine1.Manager",
1366 copy_from
? "CopyFromMachine" : "CopyToMachine");
1368 return bus_log_create_error(r
);
1370 r
= sd_bus_message_append(
1374 copy_from
? container_path
: host_path
,
1375 copy_from
? host_path
: container_path
);
1377 return bus_log_create_error(r
);
1379 /* This is a slow operation, hence turn off any method call timeouts */
1380 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1382 return log_error_errno(r
, "Failed to copy: %s", bus_error_message(&error
, r
));
1387 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1388 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1389 sd_bus
*bus
= userdata
;
1394 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1396 r
= sd_bus_call_method(
1398 "org.freedesktop.machine1",
1399 "/org/freedesktop/machine1",
1400 "org.freedesktop.machine1.Manager",
1411 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1418 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1419 PTYForward
** forward
= (PTYForward
**) userdata
;
1426 /* If the forwarder is already initialized, tell it to
1427 * exit on the next vhangup(), so that we still flush
1428 * out what might be queued and exit then. */
1430 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1434 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1437 /* On error, or when the forwarder is not initialized yet, quit immediately */
1438 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1442 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, PTYForwardFlags flags
, const char *name
) {
1448 assert(master
>= 0);
1451 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1454 if (streq(name
, ".host"))
1455 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1457 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1460 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1461 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1463 r
= pty_forward_new(event
, master
, flags
, forward
);
1465 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1467 r
= sd_event_loop(event
);
1469 return log_error_errno(r
, "Failed to run event loop: %m");
1471 pty_forward_get_last_char(*forward
, &last_char
);
1474 (flags
& PTY_FORWARD_IGNORE_VHANGUP
) &&
1475 pty_forward_get_ignore_vhangup(*forward
) == 0;
1477 *forward
= pty_forward_free(*forward
);
1479 if (last_char
!= '\n')
1480 fputc('\n', stdout
);
1484 log_info("Machine %s terminated.", name
);
1485 else if (streq(name
, ".host"))
1486 log_info("Connection to the local host terminated.");
1488 log_info("Connection to machine %s terminated.", name
);
1491 sd_event_get_exit_code(event
, &ret
);
1495 static int parse_machine_uid(const char *spec
, const char **machine
, char **uid
) {
1497 * Whatever is specified in the spec takes priority over global arguments.
1500 const char *_machine
= NULL
;
1505 at
= strchr(spec
, '@');
1508 /* Do the same as ssh and refuse "@host". */
1512 _uid
= strndup(spec
, at
- spec
);
1519 if (arg_uid
&& !_uid
) {
1520 _uid
= strdup(arg_uid
);
1526 *machine
= isempty(_machine
) ? ".host" : _machine
;
1530 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1531 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1532 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1533 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1534 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot
= NULL
;
1535 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1537 sd_bus
*bus
= userdata
;
1538 const char *pty
, *match
, *machine
;
1542 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1543 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1547 if (!IN_SET(arg_transport
, BUS_TRANSPORT_LOCAL
, BUS_TRANSPORT_MACHINE
)) {
1548 log_error("Login only supported on local machines.");
1552 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1554 r
= sd_event_default(&event
);
1556 return log_error_errno(r
, "Failed to get event loop: %m");
1558 r
= sd_bus_attach_event(bus
, event
, 0);
1560 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1562 machine
= argc
< 2 || isempty(argv
[1]) ? ".host" : argv
[1];
1564 match
= strjoina("type='signal',"
1565 "sender='org.freedesktop.machine1',"
1566 "path='/org/freedesktop/machine1',",
1567 "interface='org.freedesktop.machine1.Manager',"
1568 "member='MachineRemoved',"
1569 "arg0='", machine
, "'");
1571 r
= sd_bus_add_match_async(bus
, &slot
, match
, on_machine_removed
, NULL
, &forward
);
1573 return log_error_errno(r
, "Failed to request machine removal match: %m");
1575 r
= sd_bus_call_method(
1577 "org.freedesktop.machine1",
1578 "/org/freedesktop/machine1",
1579 "org.freedesktop.machine1.Manager",
1585 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1589 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1591 return bus_log_parse_error(r
);
1593 return process_forward(event
, &forward
, master
, PTY_FORWARD_IGNORE_VHANGUP
, machine
);
1596 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1597 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
1598 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1599 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1600 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot
= NULL
;
1601 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1603 sd_bus
*bus
= userdata
;
1604 const char *pty
, *match
, *machine
, *path
;
1605 _cleanup_free_
char *uid
= NULL
;
1609 if (!IN_SET(arg_transport
, BUS_TRANSPORT_LOCAL
, BUS_TRANSPORT_MACHINE
)) {
1610 log_error("Shell only supported on local machines.");
1614 /* Pass $TERM to shell session, if not explicitly specified. */
1615 if (!strv_find_prefix(arg_setenv
, "TERM=")) {
1618 t
= strv_find_prefix(environ
, "TERM=");
1620 if (strv_extend(&arg_setenv
, t
) < 0)
1625 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1627 r
= sd_event_default(&event
);
1629 return log_error_errno(r
, "Failed to get event loop: %m");
1631 r
= sd_bus_attach_event(bus
, event
, 0);
1633 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1635 r
= parse_machine_uid(argc
>= 2 ? argv
[1] : NULL
, &machine
, &uid
);
1637 return log_error_errno(r
, "Failed to parse machine specification: %m");
1639 match
= strjoina("type='signal',"
1640 "sender='org.freedesktop.machine1',"
1641 "path='/org/freedesktop/machine1',",
1642 "interface='org.freedesktop.machine1.Manager',"
1643 "member='MachineRemoved',"
1644 "arg0='", machine
, "'");
1646 r
= sd_bus_add_match_async(bus
, &slot
, match
, on_machine_removed
, NULL
, &forward
);
1648 return log_error_errno(r
, "Failed to request machine removal match: %m");
1650 r
= sd_bus_message_new_method_call(
1653 "org.freedesktop.machine1",
1654 "/org/freedesktop/machine1",
1655 "org.freedesktop.machine1.Manager",
1656 "OpenMachineShell");
1658 return bus_log_create_error(r
);
1660 path
= argc
< 3 || isempty(argv
[2]) ? NULL
: argv
[2];
1662 r
= sd_bus_message_append(m
, "sss", machine
, uid
, path
);
1664 return bus_log_create_error(r
);
1666 r
= sd_bus_message_append_strv(m
, strv_length(argv
) <= 3 ? NULL
: argv
+ 2);
1668 return bus_log_create_error(r
);
1670 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1672 return bus_log_create_error(r
);
1674 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1676 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1680 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1682 return bus_log_parse_error(r
);
1684 return process_forward(event
, &forward
, master
, 0, machine
);
1687 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1688 sd_bus
*bus
= userdata
;
1693 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1695 for (i
= 1; i
< argc
; i
++) {
1696 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1697 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1699 r
= sd_bus_message_new_method_call(
1702 "org.freedesktop.machine1",
1703 "/org/freedesktop/machine1",
1704 "org.freedesktop.machine1.Manager",
1707 return bus_log_create_error(r
);
1709 r
= sd_bus_message_append(m
, "s", argv
[i
]);
1711 return bus_log_create_error(r
);
1713 /* This is a slow operation, hence turn off any method call timeouts */
1714 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1716 return log_error_errno(r
, "Could not remove image: %s", bus_error_message(&error
, r
));
1722 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1723 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1724 sd_bus
*bus
= userdata
;
1729 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1731 r
= sd_bus_call_method(
1733 "org.freedesktop.machine1",
1734 "/org/freedesktop/machine1",
1735 "org.freedesktop.machine1.Manager",
1739 "ss", argv
[1], argv
[2]);
1741 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1748 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1749 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1750 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1751 sd_bus
*bus
= userdata
;
1756 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1758 r
= sd_bus_message_new_method_call(
1761 "org.freedesktop.machine1",
1762 "/org/freedesktop/machine1",
1763 "org.freedesktop.machine1.Manager",
1766 return bus_log_create_error(r
);
1768 r
= sd_bus_message_append(m
, "ssb", argv
[1], argv
[2], arg_read_only
);
1770 return bus_log_create_error(r
);
1772 /* This is a slow operation, hence turn off any method call timeouts */
1773 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1775 return log_error_errno(r
, "Could not clone image: %s", bus_error_message(&error
, r
));
1780 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1781 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1782 sd_bus
*bus
= userdata
;
1788 b
= parse_boolean(argv
[2]);
1790 log_error("Failed to parse boolean argument: %s", argv
[2]);
1795 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1797 r
= sd_bus_call_method(
1799 "org.freedesktop.machine1",
1800 "/org/freedesktop/machine1",
1801 "org.freedesktop.machine1.Manager",
1802 "MarkImageReadOnly",
1807 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1814 static int image_exists(sd_bus
*bus
, const char *name
) {
1815 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1821 r
= sd_bus_call_method(
1823 "org.freedesktop.machine1",
1824 "/org/freedesktop/machine1",
1825 "org.freedesktop.machine1.Manager",
1831 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_IMAGE
))
1834 return log_error_errno(r
, "Failed to check whether image %s exists: %s", name
, bus_error_message(&error
, -r
));
1840 static int make_service_name(const char *name
, char **ret
) {
1846 if (!machine_name_is_valid(name
)) {
1847 log_error("Invalid machine name %s.", name
);
1851 r
= unit_name_build("systemd-nspawn", name
, ".service", ret
);
1853 return log_error_errno(r
, "Failed to build unit name: %m");
1858 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1859 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1860 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1861 sd_bus
*bus
= userdata
;
1866 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1868 r
= bus_wait_for_jobs_new(bus
, &w
);
1872 for (i
= 1; i
< argc
; i
++) {
1873 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1874 _cleanup_free_
char *unit
= NULL
;
1877 r
= make_service_name(argv
[i
], &unit
);
1881 r
= image_exists(bus
, argv
[i
]);
1885 log_error("Machine image '%s' does not exist.", argv
[1]);
1889 r
= sd_bus_call_method(
1891 "org.freedesktop.systemd1",
1892 "/org/freedesktop/systemd1",
1893 "org.freedesktop.systemd1.Manager",
1897 "ss", unit
, "fail");
1899 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1903 r
= sd_bus_message_read(reply
, "o", &object
);
1905 return bus_log_parse_error(r
);
1907 r
= bus_wait_for_jobs_add(w
, object
);
1912 r
= bus_wait_for_jobs(w
, arg_quiet
, NULL
);
1919 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1920 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1921 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1922 UnitFileChange
*changes
= NULL
;
1923 unsigned n_changes
= 0;
1924 int carries_install_info
= 0;
1925 const char *method
= NULL
;
1926 sd_bus
*bus
= userdata
;
1931 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1933 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1935 r
= sd_bus_message_new_method_call(
1938 "org.freedesktop.systemd1",
1939 "/org/freedesktop/systemd1",
1940 "org.freedesktop.systemd1.Manager",
1943 return bus_log_create_error(r
);
1945 r
= sd_bus_message_open_container(m
, 'a', "s");
1947 return bus_log_create_error(r
);
1949 for (i
= 1; i
< argc
; i
++) {
1950 _cleanup_free_
char *unit
= NULL
;
1952 r
= make_service_name(argv
[i
], &unit
);
1956 r
= image_exists(bus
, argv
[i
]);
1960 log_error("Machine image '%s' does not exist.", argv
[1]);
1964 r
= sd_bus_message_append(m
, "s", unit
);
1966 return bus_log_create_error(r
);
1969 r
= sd_bus_message_close_container(m
);
1971 return bus_log_create_error(r
);
1973 if (streq(argv
[0], "enable"))
1974 r
= sd_bus_message_append(m
, "bb", false, false);
1976 r
= sd_bus_message_append(m
, "b", false);
1978 return bus_log_create_error(r
);
1980 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1982 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1986 if (streq(argv
[0], "enable")) {
1987 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1989 return bus_log_parse_error(r
);
1992 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, &changes
, &n_changes
);
1996 r
= sd_bus_call_method(
1998 "org.freedesktop.systemd1",
1999 "/org/freedesktop/systemd1",
2000 "org.freedesktop.systemd1.Manager",
2006 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
2013 unit_file_changes_free(changes
, n_changes
);
2018 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
2019 const char **our_path
= userdata
, *line
;
2026 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
2028 bus_log_parse_error(r
);
2032 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
2035 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
2038 log_full(priority
, "%s", line
);
2042 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
2043 const char **our_path
= userdata
, *path
, *result
;
2050 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
2052 bus_log_parse_error(r
);
2056 if (!streq_ptr(*our_path
, path
))
2059 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
2063 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
2068 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
2070 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
2074 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
2075 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
2076 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2077 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2078 _cleanup_(sd_event_unrefp
) sd_event
* event
= NULL
;
2079 const char *path
= NULL
;
2086 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2088 r
= sd_event_default(&event
);
2090 return log_error_errno(r
, "Failed to get event loop: %m");
2092 r
= sd_bus_attach_event(bus
, event
, 0);
2094 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
2096 r
= sd_bus_match_signal_async(
2099 "org.freedesktop.import1",
2100 "/org/freedesktop/import1",
2101 "org.freedesktop.import1.Manager",
2103 match_transfer_removed
, NULL
, &path
);
2105 return log_error_errno(r
, "Failed to request match: %m");
2107 r
= sd_bus_match_signal_async(
2110 "org.freedesktop.import1",
2112 "org.freedesktop.import1.Transfer",
2114 match_log_message
, NULL
, &path
);
2116 return log_error_errno(r
, "Failed to request match: %m");
2118 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
2120 log_error("Failed to transfer image: %s", bus_error_message(&error
, -r
));
2124 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
2126 return bus_log_parse_error(r
);
2128 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
2131 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
2133 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
2134 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
2136 r
= sd_event_loop(event
);
2138 return log_error_errno(r
, "Failed to run event loop: %m");
2143 static int import_tar(int argc
, char *argv
[], void *userdata
) {
2144 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2145 _cleanup_free_
char *ll
= NULL
;
2146 _cleanup_close_
int fd
= -1;
2147 const char *local
= NULL
, *path
= NULL
;
2148 sd_bus
*bus
= userdata
;
2155 if (isempty(path
) || streq(path
, "-"))
2161 local
= basename(path
);
2162 if (isempty(local
) || streq(local
, "-"))
2166 log_error("Need either path or local name.");
2170 r
= tar_strip_suffixes(local
, &ll
);
2176 if (!machine_name_is_valid(local
)) {
2177 log_error("Local name %s is not a suitable machine name.", local
);
2182 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
2184 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2187 r
= sd_bus_message_new_method_call(
2190 "org.freedesktop.import1",
2191 "/org/freedesktop/import1",
2192 "org.freedesktop.import1.Manager",
2195 return bus_log_create_error(r
);
2197 r
= sd_bus_message_append(
2200 fd
>= 0 ? fd
: STDIN_FILENO
,
2205 return bus_log_create_error(r
);
2207 return transfer_image_common(bus
, m
);
2210 static int import_raw(int argc
, char *argv
[], void *userdata
) {
2211 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2212 _cleanup_free_
char *ll
= NULL
;
2213 _cleanup_close_
int fd
= -1;
2214 const char *local
= NULL
, *path
= NULL
;
2215 sd_bus
*bus
= userdata
;
2222 if (isempty(path
) || streq(path
, "-"))
2228 local
= basename(path
);
2229 if (isempty(local
) || streq(local
, "-"))
2233 log_error("Need either path or local name.");
2237 r
= raw_strip_suffixes(local
, &ll
);
2243 if (!machine_name_is_valid(local
)) {
2244 log_error("Local name %s is not a suitable machine name.", local
);
2249 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
2251 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2254 r
= sd_bus_message_new_method_call(
2257 "org.freedesktop.import1",
2258 "/org/freedesktop/import1",
2259 "org.freedesktop.import1.Manager",
2262 return bus_log_create_error(r
);
2264 r
= sd_bus_message_append(
2267 fd
>= 0 ? fd
: STDIN_FILENO
,
2272 return bus_log_create_error(r
);
2274 return transfer_image_common(bus
, m
);
2277 static void determine_compression_from_filename(const char *p
) {
2284 if (endswith(p
, ".xz"))
2286 else if (endswith(p
, ".gz"))
2287 arg_format
= "gzip";
2288 else if (endswith(p
, ".bz2"))
2289 arg_format
= "bzip2";
2292 static int export_tar(int argc
, char *argv
[], void *userdata
) {
2293 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2294 _cleanup_close_
int fd
= -1;
2295 const char *local
= NULL
, *path
= NULL
;
2296 sd_bus
*bus
= userdata
;
2302 if (!machine_name_is_valid(local
)) {
2303 log_error("Machine name %s is not valid.", local
);
2309 if (isempty(path
) || streq(path
, "-"))
2313 determine_compression_from_filename(path
);
2315 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2317 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2320 r
= sd_bus_message_new_method_call(
2323 "org.freedesktop.import1",
2324 "/org/freedesktop/import1",
2325 "org.freedesktop.import1.Manager",
2328 return bus_log_create_error(r
);
2330 r
= sd_bus_message_append(
2334 fd
>= 0 ? fd
: STDOUT_FILENO
,
2337 return bus_log_create_error(r
);
2339 return transfer_image_common(bus
, m
);
2342 static int export_raw(int argc
, char *argv
[], void *userdata
) {
2343 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2344 _cleanup_close_
int fd
= -1;
2345 const char *local
= NULL
, *path
= NULL
;
2346 sd_bus
*bus
= userdata
;
2352 if (!machine_name_is_valid(local
)) {
2353 log_error("Machine name %s is not valid.", local
);
2359 if (isempty(path
) || streq(path
, "-"))
2363 determine_compression_from_filename(path
);
2365 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2367 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2370 r
= sd_bus_message_new_method_call(
2373 "org.freedesktop.import1",
2374 "/org/freedesktop/import1",
2375 "org.freedesktop.import1.Manager",
2378 return bus_log_create_error(r
);
2380 r
= sd_bus_message_append(
2384 fd
>= 0 ? fd
: STDOUT_FILENO
,
2387 return bus_log_create_error(r
);
2389 return transfer_image_common(bus
, m
);
2392 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2393 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2394 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2395 const char *local
, *remote
;
2396 sd_bus
*bus
= userdata
;
2402 if (!http_url_is_valid(remote
)) {
2403 log_error("URL '%s' is not valid.", remote
);
2410 r
= import_url_last_component(remote
, &l
);
2412 return log_error_errno(r
, "Failed to get final component of URL: %m");
2417 if (isempty(local
) || streq(local
, "-"))
2421 r
= tar_strip_suffixes(local
, &ll
);
2427 if (!machine_name_is_valid(local
)) {
2428 log_error("Local name %s is not a suitable machine name.", local
);
2433 r
= sd_bus_message_new_method_call(
2436 "org.freedesktop.import1",
2437 "/org/freedesktop/import1",
2438 "org.freedesktop.import1.Manager",
2441 return bus_log_create_error(r
);
2443 r
= sd_bus_message_append(
2448 import_verify_to_string(arg_verify
),
2451 return bus_log_create_error(r
);
2453 return transfer_image_common(bus
, m
);
2456 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2457 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2458 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2459 const char *local
, *remote
;
2460 sd_bus
*bus
= userdata
;
2466 if (!http_url_is_valid(remote
)) {
2467 log_error("URL '%s' is not valid.", remote
);
2474 r
= import_url_last_component(remote
, &l
);
2476 return log_error_errno(r
, "Failed to get final component of URL: %m");
2481 if (isempty(local
) || streq(local
, "-"))
2485 r
= raw_strip_suffixes(local
, &ll
);
2491 if (!machine_name_is_valid(local
)) {
2492 log_error("Local name %s is not a suitable machine name.", local
);
2497 r
= sd_bus_message_new_method_call(
2500 "org.freedesktop.import1",
2501 "/org/freedesktop/import1",
2502 "org.freedesktop.import1.Manager",
2505 return bus_log_create_error(r
);
2507 r
= sd_bus_message_append(
2512 import_verify_to_string(arg_verify
),
2515 return bus_log_create_error(r
);
2517 return transfer_image_common(bus
, m
);
2520 typedef struct TransferInfo
{
2528 static int compare_transfer_info(const void *a
, const void *b
) {
2529 const TransferInfo
*x
= a
, *y
= b
;
2531 return strcmp(x
->local
, y
->local
);
2534 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2535 size_t max_type
= STRLEN("TYPE"), max_local
= STRLEN("LOCAL"), max_remote
= STRLEN("REMOTE");
2536 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2537 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2538 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2539 size_t n_transfers
= 0, n_allocated
= 0, j
;
2540 const char *type
, *remote
, *local
, *object
;
2541 sd_bus
*bus
= userdata
;
2542 uint32_t id
, max_id
= 0;
2546 pager_open(arg_no_pager
, false);
2548 r
= sd_bus_call_method(bus
,
2549 "org.freedesktop.import1",
2550 "/org/freedesktop/import1",
2551 "org.freedesktop.import1.Manager",
2557 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2561 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2563 return bus_log_parse_error(r
);
2565 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2568 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2571 transfers
[n_transfers
].id
= id
;
2572 transfers
[n_transfers
].type
= type
;
2573 transfers
[n_transfers
].remote
= remote
;
2574 transfers
[n_transfers
].local
= local
;
2575 transfers
[n_transfers
].progress
= progress
;
2595 return bus_log_parse_error(r
);
2597 r
= sd_bus_message_exit_container(reply
);
2599 return bus_log_parse_error(r
);
2601 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2603 if (arg_legend
&& n_transfers
> 0)
2604 printf("%-*s %-*s %-*s %-*s %-*s\n",
2605 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2607 (int) max_type
, "TYPE",
2608 (int) max_local
, "LOCAL",
2609 (int) max_remote
, "REMOTE");
2611 for (j
= 0; j
< n_transfers
; j
++)
2612 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2613 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2614 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2615 (int) max_type
, transfers
[j
].type
,
2616 (int) max_local
, transfers
[j
].local
,
2617 (int) max_remote
, transfers
[j
].remote
);
2620 if (n_transfers
> 0)
2621 printf("\n%zu transfers listed.\n", n_transfers
);
2623 printf("No transfers.\n");
2629 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2630 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2631 sd_bus
*bus
= userdata
;
2636 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2638 for (i
= 1; i
< argc
; i
++) {
2641 r
= safe_atou32(argv
[i
], &id
);
2643 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2645 r
= sd_bus_call_method(
2647 "org.freedesktop.import1",
2648 "/org/freedesktop/import1",
2649 "org.freedesktop.import1.Manager",
2655 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2663 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2664 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2665 sd_bus
*bus
= userdata
;
2669 if (STR_IN_SET(argv
[argc
-1], "-", "none", "infinity"))
2670 limit
= (uint64_t) -1;
2672 r
= parse_size(argv
[argc
-1], 1024, &limit
);
2674 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2678 /* With two arguments changes the quota limit of the
2679 * specified image */
2680 r
= sd_bus_call_method(
2682 "org.freedesktop.machine1",
2683 "/org/freedesktop/machine1",
2684 "org.freedesktop.machine1.Manager",
2688 "st", argv
[1], limit
);
2690 /* With one argument changes the pool quota limit */
2691 r
= sd_bus_call_method(
2693 "org.freedesktop.machine1",
2694 "/org/freedesktop/machine1",
2695 "org.freedesktop.machine1.Manager",
2702 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2709 static int clean_images(int argc
, char *argv
[], void *userdata
) {
2710 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2711 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2712 uint64_t usage
, total
= 0;
2713 char fb
[FORMAT_BYTES_MAX
];
2714 sd_bus
*bus
= userdata
;
2719 r
= sd_bus_message_new_method_call(
2722 "org.freedesktop.machine1",
2723 "/org/freedesktop/machine1",
2724 "org.freedesktop.machine1.Manager",
2727 return bus_log_create_error(r
);
2729 r
= sd_bus_message_append(m
, "s", arg_all
? "all" : "hidden");
2731 return bus_log_create_error(r
);
2733 /* This is a slow operation, hence permit a longer time for completion. */
2734 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, &reply
);
2736 return log_error_errno(r
, "Could not clean pool: %s", bus_error_message(&error
, r
));
2738 r
= sd_bus_message_enter_container(reply
, 'a', "(st)");
2740 return bus_log_parse_error(r
);
2742 while ((r
= sd_bus_message_read(reply
, "(st)", &name
, &usage
)) > 0) {
2743 log_info("Removed image '%s'. Freed exclusive disk space: %s",
2744 name
, format_bytes(fb
, sizeof(fb
), usage
));
2750 r
= sd_bus_message_exit_container(reply
);
2752 return bus_log_parse_error(r
);
2754 log_info("Removed %u images in total. Total freed exclusive disk space %s.",
2755 c
, format_bytes(fb
, sizeof(fb
), total
));
2760 static int help(int argc
, char *argv
[], void *userdata
) {
2761 pager_open(arg_no_pager
, false);
2763 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2764 "Send control commands to or query the virtual machine and container\n"
2765 "registration manager.\n\n"
2766 " -h --help Show this help\n"
2767 " --version Show package version\n"
2768 " --no-pager Do not pipe output into a pager\n"
2769 " --no-legend Do not show the headers and footers\n"
2770 " --no-ask-password Do not ask for system passwords\n"
2771 " -H --host=[USER@]HOST Operate on remote host\n"
2772 " -M --machine=CONTAINER Operate on local container\n"
2773 " -p --property=NAME Show only properties by this name\n"
2774 " -q --quiet Suppress output\n"
2775 " -a --all Show all properties, including empty ones\n"
2776 " --value When showing properties, only print the value\n"
2777 " -l --full Do not ellipsize output\n"
2778 " --kill-who=WHO Who to send signal to\n"
2779 " -s --signal=SIGNAL Which signal to send\n"
2780 " --uid=USER Specify user ID to invoke shell as\n"
2781 " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
2782 " --read-only Create read-only bind mount\n"
2783 " --mkdir Create directory before bind mounting, if missing\n"
2784 " -n --lines=INTEGER Number of journal entries to show\n"
2785 " --max-addresses=INTEGER Number of internet addresses to show at most\n"
2786 " -o --output=STRING Change journal output mode (short, short-precise,\n"
2787 " short-iso, short-iso-precise, short-full,\n"
2788 " short-monotonic, short-unix, verbose, export,\n"
2789 " json, json-pretty, json-sse, cat)\n"
2790 " --verify=MODE Verification mode for downloaded images (no,\n"
2791 " checksum, signature)\n"
2792 " --force Download image even if already exists\n\n"
2793 "Machine Commands:\n"
2794 " list List running VMs and containers\n"
2795 " status NAME... Show VM/container details\n"
2796 " show [NAME...] Show properties of one or more VMs/containers\n"
2797 " start NAME... Start container as a service\n"
2798 " login [NAME] Get a login prompt in a container or on the\n"
2800 " shell [[USER@]NAME [COMMAND...]]\n"
2801 " Invoke a shell (or other command) in a container\n"
2802 " or on the local host\n"
2803 " enable NAME... Enable automatic container start at boot\n"
2804 " disable NAME... Disable automatic container start at boot\n"
2805 " poweroff NAME... Power off one or more containers\n"
2806 " reboot NAME... Reboot one or more containers\n"
2807 " terminate NAME... Terminate one or more VMs/containers\n"
2808 " kill NAME... Send signal to processes of a VM/container\n"
2809 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2810 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2811 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2813 " list-images Show available container and VM images\n"
2814 " image-status [NAME...] Show image details\n"
2815 " show-image [NAME...] Show properties of image\n"
2816 " clone NAME NAME Clone an image\n"
2817 " rename NAME NAME Rename an image\n"
2818 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2819 " remove NAME... Remove an image\n"
2820 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
2821 " clean Remove hidden (or all) images\n\n"
2822 "Image Transfer Commands:\n"
2823 " pull-tar URL [NAME] Download a TAR container image\n"
2824 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2825 " import-tar FILE [NAME] Import a local TAR container image\n"
2826 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2827 " export-tar NAME [FILE] Export a TAR container image locally\n"
2828 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2829 " list-transfers Show list of downloads in progress\n"
2830 " cancel-transfer Cancel a download\n"
2831 , program_invocation_short_name
);
2836 static int parse_argv(int argc
, char *argv
[]) {
2839 ARG_VERSION
= 0x100,
2846 ARG_NO_ASK_PASSWORD
,
2854 static const struct option options
[] = {
2855 { "help", no_argument
, NULL
, 'h' },
2856 { "version", no_argument
, NULL
, ARG_VERSION
},
2857 { "property", required_argument
, NULL
, 'p' },
2858 { "all", no_argument
, NULL
, 'a' },
2859 { "value", no_argument
, NULL
, ARG_VALUE
},
2860 { "full", no_argument
, NULL
, 'l' },
2861 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2862 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2863 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2864 { "signal", required_argument
, NULL
, 's' },
2865 { "host", required_argument
, NULL
, 'H' },
2866 { "machine", required_argument
, NULL
, 'M' },
2867 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2868 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2869 { "quiet", no_argument
, NULL
, 'q' },
2870 { "lines", required_argument
, NULL
, 'n' },
2871 { "output", required_argument
, NULL
, 'o' },
2872 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2873 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2874 { "force", no_argument
, NULL
, ARG_FORCE
},
2875 { "format", required_argument
, NULL
, ARG_FORMAT
},
2876 { "uid", required_argument
, NULL
, ARG_UID
},
2877 { "setenv", required_argument
, NULL
, 'E' },
2878 { "max-addresses", required_argument
, NULL
, ARG_NUMBER_IPS
},
2882 bool reorder
= false;
2883 int c
, r
, shell
= -1;
2889 static const char option_string
[] = "-hp:als:H:M:qn:o:E:";
2891 c
= getopt_long(argc
, argv
, option_string
+ reorder
, options
, NULL
);
2897 case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
2898 * non-option argument was discovered. */
2902 /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2903 * for switches after the main verb. However, for "shell" we really don't want that, since we
2904 * want that switches specified after the machine name are passed to the program to execute,
2905 * and not processed by us. To make this possible, we'll first invoke getopt_long() with
2906 * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
2907 * non-option parameter. If it's the verb "shell" we remember its position and continue
2908 * processing options. In this case, as soon as we hit the next non-option argument we found
2909 * the machine name, and stop further processing. If the first non-option argument is any other
2910 * verb than "shell" we switch to normal reordering mode and continue processing arguments
2914 /* If we already found the "shell" verb on the command line, and now found the next
2915 * non-option argument, then this is the machine name and we should stop processing
2916 * further arguments. */
2917 optind
--; /* don't process this argument, go one step back */
2920 if (streq(optarg
, "shell"))
2921 /* Remember the position of the "shell" verb, and continue processing normally. */
2926 /* OK, this is some other verb. In this case, turn on reordering again, and continue
2927 * processing normally. */
2930 /* We changed the option string. getopt_long() only looks at it again if we invoke it
2931 * at least once with a reset option index. Hence, let's reset the option index here,
2932 * then invoke getopt_long() again (ignoring what it has to say, after all we most
2933 * likely already processed it), and the bump the option index so that we read the
2934 * intended argument again. */
2935 saved_optind
= optind
;
2937 (void) getopt_long(argc
, argv
, option_string
+ reorder
, options
, NULL
);
2938 optind
= saved_optind
- 1; /* go one step back, process this argument again */
2944 return help(0, NULL
, NULL
);
2950 r
= strv_extend(&arg_property
, optarg
);
2954 /* If the user asked for a particular
2955 * property, show it to him, even if it is
2973 if (safe_atou(optarg
, &arg_lines
) < 0) {
2974 log_error("Failed to parse lines '%s'", optarg
);
2980 arg_output
= output_mode_from_string(optarg
);
2981 if (arg_output
< 0) {
2982 log_error("Unknown output '%s'.", optarg
);
2988 arg_no_pager
= true;
2996 arg_kill_who
= optarg
;
3000 arg_signal
= signal_from_string_try_harder(optarg
);
3001 if (arg_signal
< 0) {
3002 log_error("Failed to parse signal string %s.", optarg
);
3007 case ARG_NO_ASK_PASSWORD
:
3008 arg_ask_password
= false;
3012 arg_transport
= BUS_TRANSPORT_REMOTE
;
3017 arg_transport
= BUS_TRANSPORT_MACHINE
;
3022 arg_read_only
= true;
3034 arg_verify
= import_verify_from_string(optarg
);
3035 if (arg_verify
< 0) {
3036 log_error("Failed to parse --verify= setting: %s", optarg
);
3046 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
3047 log_error("Unknown format: %s", optarg
);
3051 arg_format
= optarg
;
3059 if (!env_assignment_is_valid(optarg
)) {
3060 log_error("Environment assignment invalid: %s", optarg
);
3064 r
= strv_extend(&arg_setenv
, optarg
);
3069 case ARG_NUMBER_IPS
:
3070 if (streq(optarg
, "all"))
3071 arg_addrs
= ALL_IP_ADDRESSES
;
3072 else if (safe_atoi(optarg
, &arg_addrs
) < 0) {
3073 log_error("Invalid number of IPs");
3075 } else if (arg_addrs
< 0) {
3076 log_error("Number of IPs cannot be negative");
3085 assert_not_reached("Unhandled option");
3094 /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
3095 * argument list initially let's readjust it now, and move the "shell" verb to the back. */
3097 optind
-= 1; /* place the option index where the "shell" verb will be placed */
3100 for (i
= shell
; i
< optind
; i
++)
3101 argv
[i
] = argv
[i
+1];
3108 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
3110 static const Verb verbs
[] = {
3111 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
3112 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
3113 { "list-images", VERB_ANY
, 1, 0, list_images
},
3114 { "status", 2, VERB_ANY
, 0, show_machine
},
3115 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
3116 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
3117 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
3118 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
3119 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
3120 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
3121 { "stop", 2, VERB_ANY
, 0, poweroff_machine
}, /* Convenience alias */
3122 { "kill", 2, VERB_ANY
, 0, kill_machine
},
3123 { "login", VERB_ANY
, 2, 0, login_machine
},
3124 { "shell", VERB_ANY
, VERB_ANY
, 0, shell_machine
},
3125 { "bind", 3, 4, 0, bind_mount
},
3126 { "copy-to", 3, 4, 0, copy_files
},
3127 { "copy-from", 3, 4, 0, copy_files
},
3128 { "remove", 2, VERB_ANY
, 0, remove_image
},
3129 { "rename", 3, 3, 0, rename_image
},
3130 { "clone", 3, 3, 0, clone_image
},
3131 { "read-only", 2, 3, 0, read_only_image
},
3132 { "start", 2, VERB_ANY
, 0, start_machine
},
3133 { "enable", 2, VERB_ANY
, 0, enable_machine
},
3134 { "disable", 2, VERB_ANY
, 0, enable_machine
},
3135 { "import-tar", 2, 3, 0, import_tar
},
3136 { "import-raw", 2, 3, 0, import_raw
},
3137 { "export-tar", 2, 3, 0, export_tar
},
3138 { "export-raw", 2, 3, 0, export_raw
},
3139 { "pull-tar", 2, 3, 0, pull_tar
},
3140 { "pull-raw", 2, 3, 0, pull_raw
},
3141 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
3142 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
3143 { "set-limit", 2, 3, 0, set_limit
},
3144 { "clean", VERB_ANY
, 1, 0, clean_images
},
3148 return dispatch_verb(argc
, argv
, verbs
, bus
);
3151 int main(int argc
, char*argv
[]) {
3155 setlocale(LC_ALL
, "");
3156 log_parse_environment();
3160 r
= parse_argv(argc
, argv
);
3164 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
3166 log_error_errno(r
, "Failed to create bus connection: %m");
3170 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
3172 r
= machinectl_main(argc
, argv
, bus
);
3175 /* make sure we terminate the bus connection first, and then close the
3176 * pager, see issue #3543 for the details. */
3177 sd_bus_flush_close_unref(bus
);
3179 polkit_agent_close();
3181 strv_free(arg_property
);
3182 strv_free(arg_setenv
);
3184 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;