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(bus
, &slot
, match
, on_machine_removed
, &forward
);
1573 return log_error_errno(r
, "Failed to add 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(bus
, &slot
, match
, on_machine_removed
, &forward
);
1648 return log_error_errno(r
, "Failed to add 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
;
1727 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1729 r
= sd_bus_call_method(
1731 "org.freedesktop.machine1",
1732 "/org/freedesktop/machine1",
1733 "org.freedesktop.machine1.Manager",
1737 "ss", argv
[1], argv
[2]);
1739 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1746 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1747 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1748 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1749 sd_bus
*bus
= userdata
;
1752 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1754 r
= sd_bus_message_new_method_call(
1757 "org.freedesktop.machine1",
1758 "/org/freedesktop/machine1",
1759 "org.freedesktop.machine1.Manager",
1762 return bus_log_create_error(r
);
1764 r
= sd_bus_message_append(m
, "ssb", argv
[1], argv
[2], arg_read_only
);
1766 return bus_log_create_error(r
);
1768 /* This is a slow operation, hence turn off any method call timeouts */
1769 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1771 return log_error_errno(r
, "Could not clone image: %s", bus_error_message(&error
, r
));
1776 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1777 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1778 sd_bus
*bus
= userdata
;
1782 b
= parse_boolean(argv
[2]);
1784 log_error("Failed to parse boolean argument: %s", argv
[2]);
1789 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1791 r
= sd_bus_call_method(
1793 "org.freedesktop.machine1",
1794 "/org/freedesktop/machine1",
1795 "org.freedesktop.machine1.Manager",
1796 "MarkImageReadOnly",
1801 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1808 static int image_exists(sd_bus
*bus
, const char *name
) {
1809 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1815 r
= sd_bus_call_method(
1817 "org.freedesktop.machine1",
1818 "/org/freedesktop/machine1",
1819 "org.freedesktop.machine1.Manager",
1825 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_IMAGE
))
1828 return log_error_errno(r
, "Failed to check whether image %s exists: %s", name
, bus_error_message(&error
, -r
));
1834 static int make_service_name(const char *name
, char **ret
) {
1840 if (!machine_name_is_valid(name
)) {
1841 log_error("Invalid machine name %s.", name
);
1845 r
= unit_name_build("systemd-nspawn", name
, ".service", ret
);
1847 return log_error_errno(r
, "Failed to build unit name: %m");
1852 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1853 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1854 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1855 sd_bus
*bus
= userdata
;
1860 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1862 r
= bus_wait_for_jobs_new(bus
, &w
);
1866 for (i
= 1; i
< argc
; i
++) {
1867 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1868 _cleanup_free_
char *unit
= NULL
;
1871 r
= make_service_name(argv
[i
], &unit
);
1875 r
= image_exists(bus
, argv
[i
]);
1879 log_error("Machine image '%s' does not exist.", argv
[1]);
1883 r
= sd_bus_call_method(
1885 "org.freedesktop.systemd1",
1886 "/org/freedesktop/systemd1",
1887 "org.freedesktop.systemd1.Manager",
1891 "ss", unit
, "fail");
1893 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1897 r
= sd_bus_message_read(reply
, "o", &object
);
1899 return bus_log_parse_error(r
);
1901 r
= bus_wait_for_jobs_add(w
, object
);
1906 r
= bus_wait_for_jobs(w
, arg_quiet
, NULL
);
1913 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1914 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1915 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1916 UnitFileChange
*changes
= NULL
;
1917 unsigned n_changes
= 0;
1918 int carries_install_info
= 0;
1919 const char *method
= NULL
;
1920 sd_bus
*bus
= userdata
;
1925 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1927 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1929 r
= sd_bus_message_new_method_call(
1932 "org.freedesktop.systemd1",
1933 "/org/freedesktop/systemd1",
1934 "org.freedesktop.systemd1.Manager",
1937 return bus_log_create_error(r
);
1939 r
= sd_bus_message_open_container(m
, 'a', "s");
1941 return bus_log_create_error(r
);
1943 for (i
= 1; i
< argc
; i
++) {
1944 _cleanup_free_
char *unit
= NULL
;
1946 r
= make_service_name(argv
[i
], &unit
);
1950 r
= image_exists(bus
, argv
[i
]);
1954 log_error("Machine image '%s' does not exist.", argv
[1]);
1958 r
= sd_bus_message_append(m
, "s", unit
);
1960 return bus_log_create_error(r
);
1963 r
= sd_bus_message_close_container(m
);
1965 return bus_log_create_error(r
);
1967 if (streq(argv
[0], "enable"))
1968 r
= sd_bus_message_append(m
, "bb", false, false);
1970 r
= sd_bus_message_append(m
, "b", false);
1972 return bus_log_create_error(r
);
1974 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1976 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1980 if (streq(argv
[0], "enable")) {
1981 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1983 return bus_log_parse_error(r
);
1986 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, &changes
, &n_changes
);
1990 r
= sd_bus_call_method(
1992 "org.freedesktop.systemd1",
1993 "/org/freedesktop/systemd1",
1994 "org.freedesktop.systemd1.Manager",
2000 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
2007 unit_file_changes_free(changes
, n_changes
);
2012 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
2013 const char **our_path
= userdata
, *line
;
2020 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
2022 bus_log_parse_error(r
);
2026 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
2029 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
2032 log_full(priority
, "%s", line
);
2036 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
2037 const char **our_path
= userdata
, *path
, *result
;
2044 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
2046 bus_log_parse_error(r
);
2050 if (!streq_ptr(*our_path
, path
))
2053 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
2057 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
2062 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
2064 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
2068 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
2069 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
2070 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2071 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2072 _cleanup_(sd_event_unrefp
) sd_event
* event
= NULL
;
2073 const char *path
= NULL
;
2080 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2082 r
= sd_event_default(&event
);
2084 return log_error_errno(r
, "Failed to get event loop: %m");
2086 r
= sd_bus_attach_event(bus
, event
, 0);
2088 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
2090 r
= sd_bus_add_match(
2094 "sender='org.freedesktop.import1',"
2095 "interface='org.freedesktop.import1.Manager',"
2096 "member='TransferRemoved',"
2097 "path='/org/freedesktop/import1'",
2098 match_transfer_removed
, &path
);
2100 return log_error_errno(r
, "Failed to install match: %m");
2102 r
= sd_bus_add_match(
2106 "sender='org.freedesktop.import1',"
2107 "interface='org.freedesktop.import1.Transfer',"
2108 "member='LogMessage'",
2109 match_log_message
, &path
);
2111 return log_error_errno(r
, "Failed to install match: %m");
2113 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
2115 log_error("Failed to transfer image: %s", bus_error_message(&error
, -r
));
2119 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
2121 return bus_log_parse_error(r
);
2123 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
2126 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
2128 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
2129 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
2131 r
= sd_event_loop(event
);
2133 return log_error_errno(r
, "Failed to run event loop: %m");
2138 static int import_tar(int argc
, char *argv
[], void *userdata
) {
2139 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2140 _cleanup_free_
char *ll
= NULL
;
2141 _cleanup_close_
int fd
= -1;
2142 const char *local
= NULL
, *path
= NULL
;
2143 sd_bus
*bus
= userdata
;
2150 if (isempty(path
) || streq(path
, "-"))
2156 local
= basename(path
);
2157 if (isempty(local
) || streq(local
, "-"))
2161 log_error("Need either path or local name.");
2165 r
= tar_strip_suffixes(local
, &ll
);
2171 if (!machine_name_is_valid(local
)) {
2172 log_error("Local name %s is not a suitable machine name.", local
);
2177 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
2179 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2182 r
= sd_bus_message_new_method_call(
2185 "org.freedesktop.import1",
2186 "/org/freedesktop/import1",
2187 "org.freedesktop.import1.Manager",
2190 return bus_log_create_error(r
);
2192 r
= sd_bus_message_append(
2195 fd
>= 0 ? fd
: STDIN_FILENO
,
2200 return bus_log_create_error(r
);
2202 return transfer_image_common(bus
, m
);
2205 static int import_raw(int argc
, char *argv
[], void *userdata
) {
2206 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2207 _cleanup_free_
char *ll
= NULL
;
2208 _cleanup_close_
int fd
= -1;
2209 const char *local
= NULL
, *path
= NULL
;
2210 sd_bus
*bus
= userdata
;
2217 if (isempty(path
) || streq(path
, "-"))
2223 local
= basename(path
);
2224 if (isempty(local
) || streq(local
, "-"))
2228 log_error("Need either path or local name.");
2232 r
= raw_strip_suffixes(local
, &ll
);
2238 if (!machine_name_is_valid(local
)) {
2239 log_error("Local name %s is not a suitable machine name.", local
);
2244 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
2246 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2249 r
= sd_bus_message_new_method_call(
2252 "org.freedesktop.import1",
2253 "/org/freedesktop/import1",
2254 "org.freedesktop.import1.Manager",
2257 return bus_log_create_error(r
);
2259 r
= sd_bus_message_append(
2262 fd
>= 0 ? fd
: STDIN_FILENO
,
2267 return bus_log_create_error(r
);
2269 return transfer_image_common(bus
, m
);
2272 static void determine_compression_from_filename(const char *p
) {
2279 if (endswith(p
, ".xz"))
2281 else if (endswith(p
, ".gz"))
2282 arg_format
= "gzip";
2283 else if (endswith(p
, ".bz2"))
2284 arg_format
= "bzip2";
2287 static int export_tar(int argc
, char *argv
[], void *userdata
) {
2288 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2289 _cleanup_close_
int fd
= -1;
2290 const char *local
= NULL
, *path
= NULL
;
2291 sd_bus
*bus
= userdata
;
2297 if (!machine_name_is_valid(local
)) {
2298 log_error("Machine name %s is not valid.", local
);
2304 if (isempty(path
) || streq(path
, "-"))
2308 determine_compression_from_filename(path
);
2310 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2312 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2315 r
= sd_bus_message_new_method_call(
2318 "org.freedesktop.import1",
2319 "/org/freedesktop/import1",
2320 "org.freedesktop.import1.Manager",
2323 return bus_log_create_error(r
);
2325 r
= sd_bus_message_append(
2329 fd
>= 0 ? fd
: STDOUT_FILENO
,
2332 return bus_log_create_error(r
);
2334 return transfer_image_common(bus
, m
);
2337 static int export_raw(int argc
, char *argv
[], void *userdata
) {
2338 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2339 _cleanup_close_
int fd
= -1;
2340 const char *local
= NULL
, *path
= NULL
;
2341 sd_bus
*bus
= userdata
;
2347 if (!machine_name_is_valid(local
)) {
2348 log_error("Machine name %s is not valid.", local
);
2354 if (isempty(path
) || streq(path
, "-"))
2358 determine_compression_from_filename(path
);
2360 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2362 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2365 r
= sd_bus_message_new_method_call(
2368 "org.freedesktop.import1",
2369 "/org/freedesktop/import1",
2370 "org.freedesktop.import1.Manager",
2373 return bus_log_create_error(r
);
2375 r
= sd_bus_message_append(
2379 fd
>= 0 ? fd
: STDOUT_FILENO
,
2382 return bus_log_create_error(r
);
2384 return transfer_image_common(bus
, m
);
2387 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2388 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2389 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2390 const char *local
, *remote
;
2391 sd_bus
*bus
= userdata
;
2397 if (!http_url_is_valid(remote
)) {
2398 log_error("URL '%s' is not valid.", remote
);
2405 r
= import_url_last_component(remote
, &l
);
2407 return log_error_errno(r
, "Failed to get final component of URL: %m");
2412 if (isempty(local
) || streq(local
, "-"))
2416 r
= tar_strip_suffixes(local
, &ll
);
2422 if (!machine_name_is_valid(local
)) {
2423 log_error("Local name %s is not a suitable machine name.", local
);
2428 r
= sd_bus_message_new_method_call(
2431 "org.freedesktop.import1",
2432 "/org/freedesktop/import1",
2433 "org.freedesktop.import1.Manager",
2436 return bus_log_create_error(r
);
2438 r
= sd_bus_message_append(
2443 import_verify_to_string(arg_verify
),
2446 return bus_log_create_error(r
);
2448 return transfer_image_common(bus
, m
);
2451 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2452 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2453 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2454 const char *local
, *remote
;
2455 sd_bus
*bus
= userdata
;
2461 if (!http_url_is_valid(remote
)) {
2462 log_error("URL '%s' is not valid.", remote
);
2469 r
= import_url_last_component(remote
, &l
);
2471 return log_error_errno(r
, "Failed to get final component of URL: %m");
2476 if (isempty(local
) || streq(local
, "-"))
2480 r
= raw_strip_suffixes(local
, &ll
);
2486 if (!machine_name_is_valid(local
)) {
2487 log_error("Local name %s is not a suitable machine name.", local
);
2492 r
= sd_bus_message_new_method_call(
2495 "org.freedesktop.import1",
2496 "/org/freedesktop/import1",
2497 "org.freedesktop.import1.Manager",
2500 return bus_log_create_error(r
);
2502 r
= sd_bus_message_append(
2507 import_verify_to_string(arg_verify
),
2510 return bus_log_create_error(r
);
2512 return transfer_image_common(bus
, m
);
2515 typedef struct TransferInfo
{
2523 static int compare_transfer_info(const void *a
, const void *b
) {
2524 const TransferInfo
*x
= a
, *y
= b
;
2526 return strcmp(x
->local
, y
->local
);
2529 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2530 size_t max_type
= STRLEN("TYPE"), max_local
= STRLEN("LOCAL"), max_remote
= STRLEN("REMOTE");
2531 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2532 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2533 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2534 size_t n_transfers
= 0, n_allocated
= 0, j
;
2535 const char *type
, *remote
, *local
, *object
;
2536 sd_bus
*bus
= userdata
;
2537 uint32_t id
, max_id
= 0;
2541 pager_open(arg_no_pager
, false);
2543 r
= sd_bus_call_method(bus
,
2544 "org.freedesktop.import1",
2545 "/org/freedesktop/import1",
2546 "org.freedesktop.import1.Manager",
2552 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2556 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2558 return bus_log_parse_error(r
);
2560 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2563 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2566 transfers
[n_transfers
].id
= id
;
2567 transfers
[n_transfers
].type
= type
;
2568 transfers
[n_transfers
].remote
= remote
;
2569 transfers
[n_transfers
].local
= local
;
2570 transfers
[n_transfers
].progress
= progress
;
2590 return bus_log_parse_error(r
);
2592 r
= sd_bus_message_exit_container(reply
);
2594 return bus_log_parse_error(r
);
2596 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2598 if (arg_legend
&& n_transfers
> 0)
2599 printf("%-*s %-*s %-*s %-*s %-*s\n",
2600 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2602 (int) max_type
, "TYPE",
2603 (int) max_local
, "LOCAL",
2604 (int) max_remote
, "REMOTE");
2606 for (j
= 0; j
< n_transfers
; j
++)
2607 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2608 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2609 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2610 (int) max_type
, transfers
[j
].type
,
2611 (int) max_local
, transfers
[j
].local
,
2612 (int) max_remote
, transfers
[j
].remote
);
2615 if (n_transfers
> 0)
2616 printf("\n%zu transfers listed.\n", n_transfers
);
2618 printf("No transfers.\n");
2624 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2625 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2626 sd_bus
*bus
= userdata
;
2631 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2633 for (i
= 1; i
< argc
; i
++) {
2636 r
= safe_atou32(argv
[i
], &id
);
2638 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2640 r
= sd_bus_call_method(
2642 "org.freedesktop.import1",
2643 "/org/freedesktop/import1",
2644 "org.freedesktop.import1.Manager",
2650 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2658 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2659 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2660 sd_bus
*bus
= userdata
;
2664 if (STR_IN_SET(argv
[argc
-1], "-", "none", "infinity"))
2665 limit
= (uint64_t) -1;
2667 r
= parse_size(argv
[argc
-1], 1024, &limit
);
2669 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2673 /* With two arguments changes the quota limit of the
2674 * specified image */
2675 r
= sd_bus_call_method(
2677 "org.freedesktop.machine1",
2678 "/org/freedesktop/machine1",
2679 "org.freedesktop.machine1.Manager",
2683 "st", argv
[1], limit
);
2685 /* With one argument changes the pool quota limit */
2686 r
= sd_bus_call_method(
2688 "org.freedesktop.machine1",
2689 "/org/freedesktop/machine1",
2690 "org.freedesktop.machine1.Manager",
2697 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2704 static int clean_images(int argc
, char *argv
[], void *userdata
) {
2705 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2706 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2707 uint64_t usage
, total
= 0;
2708 char fb
[FORMAT_BYTES_MAX
];
2709 sd_bus
*bus
= userdata
;
2714 r
= sd_bus_message_new_method_call(
2717 "org.freedesktop.machine1",
2718 "/org/freedesktop/machine1",
2719 "org.freedesktop.machine1.Manager",
2722 return bus_log_create_error(r
);
2724 r
= sd_bus_message_append(m
, "s", arg_all
? "all" : "hidden");
2726 return bus_log_create_error(r
);
2728 /* This is a slow operation, hence permit a longer time for completion. */
2729 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, &reply
);
2731 return log_error_errno(r
, "Could not clean pool: %s", bus_error_message(&error
, r
));
2733 r
= sd_bus_message_enter_container(reply
, 'a', "(st)");
2735 return bus_log_parse_error(r
);
2737 while ((r
= sd_bus_message_read(reply
, "(st)", &name
, &usage
)) > 0) {
2738 log_info("Removed image '%s'. Freed exclusive disk space: %s",
2739 name
, format_bytes(fb
, sizeof(fb
), usage
));
2745 r
= sd_bus_message_exit_container(reply
);
2747 return bus_log_parse_error(r
);
2749 log_info("Removed %u images in total. Total freed exclusive disk space %s.",
2750 c
, format_bytes(fb
, sizeof(fb
), total
));
2755 static int help(int argc
, char *argv
[], void *userdata
) {
2756 pager_open(arg_no_pager
, false);
2758 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2759 "Send control commands to or query the virtual machine and container\n"
2760 "registration manager.\n\n"
2761 " -h --help Show this help\n"
2762 " --version Show package version\n"
2763 " --no-pager Do not pipe output into a pager\n"
2764 " --no-legend Do not show the headers and footers\n"
2765 " --no-ask-password Do not ask for system passwords\n"
2766 " -H --host=[USER@]HOST Operate on remote host\n"
2767 " -M --machine=CONTAINER Operate on local container\n"
2768 " -p --property=NAME Show only properties by this name\n"
2769 " -q --quiet Suppress output\n"
2770 " -a --all Show all properties, including empty ones\n"
2771 " --value When showing properties, only print the value\n"
2772 " -l --full Do not ellipsize output\n"
2773 " --kill-who=WHO Who to send signal to\n"
2774 " -s --signal=SIGNAL Which signal to send\n"
2775 " --uid=USER Specify user ID to invoke shell as\n"
2776 " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
2777 " --read-only Create read-only bind mount\n"
2778 " --mkdir Create directory before bind mounting, if missing\n"
2779 " -n --lines=INTEGER Number of journal entries to show\n"
2780 " --max-addresses=INTEGER Number of internet addresses to show at most\n"
2781 " -o --output=STRING Change journal output mode (short, short-precise,\n"
2782 " short-iso, short-iso-precise, short-full,\n"
2783 " short-monotonic, short-unix, verbose, export,\n"
2784 " json, json-pretty, json-sse, cat)\n"
2785 " --verify=MODE Verification mode for downloaded images (no,\n"
2786 " checksum, signature)\n"
2787 " --force Download image even if already exists\n\n"
2788 "Machine Commands:\n"
2789 " list List running VMs and containers\n"
2790 " status NAME... Show VM/container details\n"
2791 " show [NAME...] Show properties of one or more VMs/containers\n"
2792 " start NAME... Start container as a service\n"
2793 " login [NAME] Get a login prompt in a container or on the\n"
2795 " shell [[USER@]NAME [COMMAND...]]\n"
2796 " Invoke a shell (or other command) in a container\n"
2797 " or on the local host\n"
2798 " enable NAME... Enable automatic container start at boot\n"
2799 " disable NAME... Disable automatic container start at boot\n"
2800 " poweroff NAME... Power off one or more containers\n"
2801 " reboot NAME... Reboot one or more containers\n"
2802 " terminate NAME... Terminate one or more VMs/containers\n"
2803 " kill NAME... Send signal to processes of a VM/container\n"
2804 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2805 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2806 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2808 " list-images Show available container and VM images\n"
2809 " image-status [NAME...] Show image details\n"
2810 " show-image [NAME...] Show properties of image\n"
2811 " clone NAME NAME Clone an image\n"
2812 " rename NAME NAME Rename an image\n"
2813 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2814 " remove NAME... Remove an image\n"
2815 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
2816 " clean Remove hidden (or all) images\n\n"
2817 "Image Transfer Commands:\n"
2818 " pull-tar URL [NAME] Download a TAR container image\n"
2819 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2820 " import-tar FILE [NAME] Import a local TAR container image\n"
2821 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2822 " export-tar NAME [FILE] Export a TAR container image locally\n"
2823 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2824 " list-transfers Show list of downloads in progress\n"
2825 " cancel-transfer Cancel a download\n"
2826 , program_invocation_short_name
);
2831 static int parse_argv(int argc
, char *argv
[]) {
2834 ARG_VERSION
= 0x100,
2841 ARG_NO_ASK_PASSWORD
,
2849 static const struct option options
[] = {
2850 { "help", no_argument
, NULL
, 'h' },
2851 { "version", no_argument
, NULL
, ARG_VERSION
},
2852 { "property", required_argument
, NULL
, 'p' },
2853 { "all", no_argument
, NULL
, 'a' },
2854 { "value", no_argument
, NULL
, ARG_VALUE
},
2855 { "full", no_argument
, NULL
, 'l' },
2856 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2857 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2858 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2859 { "signal", required_argument
, NULL
, 's' },
2860 { "host", required_argument
, NULL
, 'H' },
2861 { "machine", required_argument
, NULL
, 'M' },
2862 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2863 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2864 { "quiet", no_argument
, NULL
, 'q' },
2865 { "lines", required_argument
, NULL
, 'n' },
2866 { "output", required_argument
, NULL
, 'o' },
2867 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2868 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2869 { "force", no_argument
, NULL
, ARG_FORCE
},
2870 { "format", required_argument
, NULL
, ARG_FORMAT
},
2871 { "uid", required_argument
, NULL
, ARG_UID
},
2872 { "setenv", required_argument
, NULL
, 'E' },
2873 { "max-addresses", required_argument
, NULL
, ARG_NUMBER_IPS
},
2877 bool reorder
= false;
2878 int c
, r
, shell
= -1;
2884 static const char option_string
[] = "-hp:als:H:M:qn:o:E:";
2886 c
= getopt_long(argc
, argv
, option_string
+ reorder
, options
, NULL
);
2892 case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
2893 * non-option argument was discovered. */
2897 /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2898 * for switches after the main verb. However, for "shell" we really don't want that, since we
2899 * want that switches specified after the machine name are passed to the program to execute,
2900 * and not processed by us. To make this possible, we'll first invoke getopt_long() with
2901 * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
2902 * non-option parameter. If it's the verb "shell" we remember its position and continue
2903 * processing options. In this case, as soon as we hit the next non-option argument we found
2904 * the machine name, and stop further processing. If the first non-option argument is any other
2905 * verb than "shell" we switch to normal reordering mode and continue processing arguments
2909 /* If we already found the "shell" verb on the command line, and now found the next
2910 * non-option argument, then this is the machine name and we should stop processing
2911 * further arguments. */
2912 optind
--; /* don't process this argument, go one step back */
2915 if (streq(optarg
, "shell"))
2916 /* Remember the position of the "shell" verb, and continue processing normally. */
2921 /* OK, this is some other verb. In this case, turn on reordering again, and continue
2922 * processing normally. */
2925 /* We changed the option string. getopt_long() only looks at it again if we invoke it
2926 * at least once with a reset option index. Hence, let's reset the option index here,
2927 * then invoke getopt_long() again (ignoring what it has to say, after all we most
2928 * likely already processed it), and the bump the option index so that we read the
2929 * intended argument again. */
2930 saved_optind
= optind
;
2932 (void) getopt_long(argc
, argv
, option_string
+ reorder
, options
, NULL
);
2933 optind
= saved_optind
- 1; /* go one step back, process this argument again */
2939 return help(0, NULL
, NULL
);
2945 r
= strv_extend(&arg_property
, optarg
);
2949 /* If the user asked for a particular
2950 * property, show it to him, even if it is
2968 if (safe_atou(optarg
, &arg_lines
) < 0) {
2969 log_error("Failed to parse lines '%s'", optarg
);
2975 arg_output
= output_mode_from_string(optarg
);
2976 if (arg_output
< 0) {
2977 log_error("Unknown output '%s'.", optarg
);
2983 arg_no_pager
= true;
2991 arg_kill_who
= optarg
;
2995 arg_signal
= signal_from_string_try_harder(optarg
);
2996 if (arg_signal
< 0) {
2997 log_error("Failed to parse signal string %s.", optarg
);
3002 case ARG_NO_ASK_PASSWORD
:
3003 arg_ask_password
= false;
3007 arg_transport
= BUS_TRANSPORT_REMOTE
;
3012 arg_transport
= BUS_TRANSPORT_MACHINE
;
3017 arg_read_only
= true;
3029 arg_verify
= import_verify_from_string(optarg
);
3030 if (arg_verify
< 0) {
3031 log_error("Failed to parse --verify= setting: %s", optarg
);
3041 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
3042 log_error("Unknown format: %s", optarg
);
3046 arg_format
= optarg
;
3054 if (!env_assignment_is_valid(optarg
)) {
3055 log_error("Environment assignment invalid: %s", optarg
);
3059 r
= strv_extend(&arg_setenv
, optarg
);
3064 case ARG_NUMBER_IPS
:
3065 if (streq(optarg
, "all"))
3066 arg_addrs
= ALL_IP_ADDRESSES
;
3067 else if (safe_atoi(optarg
, &arg_addrs
) < 0) {
3068 log_error("Invalid number of IPs");
3070 } else if (arg_addrs
< 0) {
3071 log_error("Number of IPs cannot be negative");
3080 assert_not_reached("Unhandled option");
3089 /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
3090 * argument list initially let's readjust it now, and move the "shell" verb to the back. */
3092 optind
-= 1; /* place the option index where the "shell" verb will be placed */
3095 for (i
= shell
; i
< optind
; i
++)
3096 argv
[i
] = argv
[i
+1];
3103 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
3105 static const Verb verbs
[] = {
3106 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
3107 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
3108 { "list-images", VERB_ANY
, 1, 0, list_images
},
3109 { "status", 2, VERB_ANY
, 0, show_machine
},
3110 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
3111 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
3112 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
3113 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
3114 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
3115 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
3116 { "stop", 2, VERB_ANY
, 0, poweroff_machine
}, /* Convenience alias */
3117 { "kill", 2, VERB_ANY
, 0, kill_machine
},
3118 { "login", VERB_ANY
, 2, 0, login_machine
},
3119 { "shell", VERB_ANY
, VERB_ANY
, 0, shell_machine
},
3120 { "bind", 3, 4, 0, bind_mount
},
3121 { "copy-to", 3, 4, 0, copy_files
},
3122 { "copy-from", 3, 4, 0, copy_files
},
3123 { "remove", 2, VERB_ANY
, 0, remove_image
},
3124 { "rename", 3, 3, 0, rename_image
},
3125 { "clone", 3, 3, 0, clone_image
},
3126 { "read-only", 2, 3, 0, read_only_image
},
3127 { "start", 2, VERB_ANY
, 0, start_machine
},
3128 { "enable", 2, VERB_ANY
, 0, enable_machine
},
3129 { "disable", 2, VERB_ANY
, 0, enable_machine
},
3130 { "import-tar", 2, 3, 0, import_tar
},
3131 { "import-raw", 2, 3, 0, import_raw
},
3132 { "export-tar", 2, 3, 0, export_tar
},
3133 { "export-raw", 2, 3, 0, export_raw
},
3134 { "pull-tar", 2, 3, 0, pull_tar
},
3135 { "pull-raw", 2, 3, 0, pull_raw
},
3136 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
3137 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
3138 { "set-limit", 2, 3, 0, set_limit
},
3139 { "clean", VERB_ANY
, 1, 0, clean_images
},
3143 return dispatch_verb(argc
, argv
, verbs
, bus
);
3146 int main(int argc
, char*argv
[]) {
3147 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
3150 setlocale(LC_ALL
, "");
3151 log_parse_environment();
3155 r
= parse_argv(argc
, argv
);
3159 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
3161 log_error_errno(r
, "Failed to create bus connection: %m");
3165 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
3167 r
= machinectl_main(argc
, argv
, bus
);
3171 polkit_agent_close();
3173 strv_free(arg_property
);
3174 strv_free(arg_setenv
);
3176 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;