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 typedef struct ImageStatusInfo
{
912 uint64_t usage_exclusive
;
913 uint64_t limit_exclusive
;
916 static void image_status_info_clear(ImageStatusInfo
*info
) {
925 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
926 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
927 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
928 char bs
[FORMAT_BYTES_MAX
], *s3
;
929 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
935 fputs(i
->name
, stdout
);
940 printf("\t Type: %s\n", i
->type
);
943 printf("\t Path: %s\n", i
->path
);
945 print_os_release(bus
, "GetImageOSRelease", i
->name
, "\t OS: ");
947 printf("\t RO: %s%s%s\n",
948 i
->read_only
? ansi_highlight_red() : "",
949 i
->read_only
? "read-only" : "writable",
950 i
->read_only
? ansi_normal() : "");
952 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
953 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
955 printf("\t Created: %s; %s\n", s2
, s1
);
957 printf("\t Created: %s\n", s2
);
959 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
960 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
962 printf("\tModified: %s; %s\n", s2
, s1
);
964 printf("\tModified: %s\n", s2
);
966 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
967 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
969 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
971 printf("\t Usage: %s\n", s3
);
973 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
974 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
976 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
978 printf("\t Limit: %s\n", s3
);
981 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
983 static const struct bus_properties_map map
[] = {
984 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
985 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
986 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
987 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
988 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
989 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
990 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
991 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
992 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
993 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
997 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
998 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
1005 r
= bus_map_all_properties(bus
,
1006 "org.freedesktop.machine1",
1012 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
1018 print_image_status_info(bus
, &info
);
1023 typedef struct PoolStatusInfo
{
1029 static void pool_status_info_clear(PoolStatusInfo
*info
) {
1038 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
1039 char bs
[FORMAT_BYTES_MAX
], *s
;
1042 printf("\t Path: %s\n", i
->path
);
1044 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
1046 printf("\t Usage: %s\n", s
);
1048 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
1050 printf("\t Limit: %s\n", s
);
1053 static int show_pool_info(sd_bus
*bus
) {
1055 static const struct bus_properties_map map
[] = {
1056 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
1057 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
1058 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
1062 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
1063 .usage
= (uint64_t) -1,
1064 .limit
= (uint64_t) -1,
1067 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1072 r
= bus_map_all_properties(bus
,
1073 "org.freedesktop.machine1",
1074 "/org/freedesktop/machine1",
1079 return log_error_errno(r
, "Could not get properties: %s", bus_error_message(&error
, r
));
1081 print_pool_status_info(bus
, &info
);
1087 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
1099 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_value
, arg_all
);
1101 log_error_errno(r
, "Could not get properties: %m");
1106 static int show_image(int argc
, char *argv
[], void *userdata
) {
1108 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1109 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1110 bool properties
, new_line
= false;
1111 sd_bus
*bus
= userdata
;
1116 properties
= !strstr(argv
[0], "status");
1118 pager_open(arg_no_pager
, false);
1122 /* If no argument is specified, inspect the manager
1126 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
1128 r
= show_pool_info(bus
);
1133 for (i
= 1; i
< argc
; i
++) {
1134 const char *path
= NULL
;
1136 r
= sd_bus_call_method(
1138 "org.freedesktop.machine1",
1139 "/org/freedesktop/machine1",
1140 "org.freedesktop.machine1.Manager",
1146 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
1150 r
= sd_bus_message_read(reply
, "o", &path
);
1152 return bus_log_parse_error(r
);
1155 r
= show_image_properties(bus
, path
, &new_line
);
1157 r
= show_image_info(bus
, path
, &new_line
);
1163 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1164 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1165 sd_bus
*bus
= userdata
;
1170 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1173 arg_kill_who
= "all";
1175 for (i
= 1; i
< argc
; i
++) {
1176 r
= sd_bus_call_method(
1178 "org.freedesktop.machine1",
1179 "/org/freedesktop/machine1",
1180 "org.freedesktop.machine1.Manager",
1184 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1186 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1194 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1195 arg_kill_who
= "leader";
1196 arg_signal
= SIGINT
; /* sysvinit + systemd */
1198 return kill_machine(argc
, argv
, userdata
);
1201 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1202 arg_kill_who
= "leader";
1203 arg_signal
= SIGRTMIN
+4; /* only systemd */
1205 return kill_machine(argc
, argv
, userdata
);
1208 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1209 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1210 sd_bus
*bus
= userdata
;
1215 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1217 for (i
= 1; i
< argc
; i
++) {
1218 r
= sd_bus_call_method(
1220 "org.freedesktop.machine1",
1221 "/org/freedesktop/machine1",
1222 "org.freedesktop.machine1.Manager",
1228 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1236 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1237 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1238 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1239 _cleanup_free_
char *abs_host_path
= NULL
;
1240 char *dest
, *host_path
, *container_path
;
1241 sd_bus
*bus
= userdata
;
1247 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1249 copy_from
= streq(argv
[0], "copy-from");
1250 dest
= argv
[3] ?: argv
[2];
1251 host_path
= copy_from
? dest
: argv
[2];
1252 container_path
= copy_from
? argv
[2] : dest
;
1254 if (!path_is_absolute(host_path
)) {
1255 r
= path_make_absolute_cwd(host_path
, &abs_host_path
);
1257 return log_error_errno(r
, "Failed to make path absolute: %m");
1259 host_path
= abs_host_path
;
1262 r
= sd_bus_message_new_method_call(
1265 "org.freedesktop.machine1",
1266 "/org/freedesktop/machine1",
1267 "org.freedesktop.machine1.Manager",
1268 copy_from
? "CopyFromMachine" : "CopyToMachine");
1270 return bus_log_create_error(r
);
1272 r
= sd_bus_message_append(
1276 copy_from
? container_path
: host_path
,
1277 copy_from
? host_path
: container_path
);
1279 return bus_log_create_error(r
);
1281 /* This is a slow operation, hence turn off any method call timeouts */
1282 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1284 return log_error_errno(r
, "Failed to copy: %s", bus_error_message(&error
, r
));
1289 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1290 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1291 sd_bus
*bus
= userdata
;
1296 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1298 r
= sd_bus_call_method(
1300 "org.freedesktop.machine1",
1301 "/org/freedesktop/machine1",
1302 "org.freedesktop.machine1.Manager",
1313 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1320 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1321 PTYForward
** forward
= (PTYForward
**) userdata
;
1328 /* If the forwarder is already initialized, tell it to
1329 * exit on the next vhangup(), so that we still flush
1330 * out what might be queued and exit then. */
1332 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1336 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1339 /* On error, or when the forwarder is not initialized yet, quit immediately */
1340 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1344 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, PTYForwardFlags flags
, const char *name
) {
1350 assert(master
>= 0);
1353 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1356 if (streq(name
, ".host"))
1357 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1359 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1362 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1363 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1365 r
= pty_forward_new(event
, master
, flags
, forward
);
1367 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1369 r
= sd_event_loop(event
);
1371 return log_error_errno(r
, "Failed to run event loop: %m");
1373 pty_forward_get_last_char(*forward
, &last_char
);
1376 (flags
& PTY_FORWARD_IGNORE_VHANGUP
) &&
1377 pty_forward_get_ignore_vhangup(*forward
) == 0;
1379 *forward
= pty_forward_free(*forward
);
1381 if (last_char
!= '\n')
1382 fputc('\n', stdout
);
1386 log_info("Machine %s terminated.", name
);
1387 else if (streq(name
, ".host"))
1388 log_info("Connection to the local host terminated.");
1390 log_info("Connection to machine %s terminated.", name
);
1393 sd_event_get_exit_code(event
, &ret
);
1397 static int parse_machine_uid(const char *spec
, const char **machine
, char **uid
) {
1399 * Whatever is specified in the spec takes priority over global arguments.
1402 const char *_machine
= NULL
;
1407 at
= strchr(spec
, '@');
1410 /* Do the same as ssh and refuse "@host". */
1414 _uid
= strndup(spec
, at
- spec
);
1421 if (arg_uid
&& !_uid
) {
1422 _uid
= strdup(arg_uid
);
1428 *machine
= isempty(_machine
) ? ".host" : _machine
;
1432 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1433 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1434 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1435 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1436 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot
= NULL
;
1437 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1439 sd_bus
*bus
= userdata
;
1440 const char *pty
, *match
, *machine
;
1444 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1445 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1449 if (!IN_SET(arg_transport
, BUS_TRANSPORT_LOCAL
, BUS_TRANSPORT_MACHINE
)) {
1450 log_error("Login only supported on local machines.");
1454 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1456 r
= sd_event_default(&event
);
1458 return log_error_errno(r
, "Failed to get event loop: %m");
1460 r
= sd_bus_attach_event(bus
, event
, 0);
1462 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1464 machine
= argc
< 2 || isempty(argv
[1]) ? ".host" : argv
[1];
1466 match
= strjoina("type='signal',"
1467 "sender='org.freedesktop.machine1',"
1468 "path='/org/freedesktop/machine1',",
1469 "interface='org.freedesktop.machine1.Manager',"
1470 "member='MachineRemoved',"
1471 "arg0='", machine
, "'");
1473 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1475 return log_error_errno(r
, "Failed to add machine removal match: %m");
1477 r
= sd_bus_call_method(
1479 "org.freedesktop.machine1",
1480 "/org/freedesktop/machine1",
1481 "org.freedesktop.machine1.Manager",
1487 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1491 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1493 return bus_log_parse_error(r
);
1495 return process_forward(event
, &forward
, master
, PTY_FORWARD_IGNORE_VHANGUP
, machine
);
1498 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1499 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
1500 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1501 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1502 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot
= NULL
;
1503 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1505 sd_bus
*bus
= userdata
;
1506 const char *pty
, *match
, *machine
, *path
;
1507 _cleanup_free_
char *uid
= NULL
;
1511 if (!IN_SET(arg_transport
, BUS_TRANSPORT_LOCAL
, BUS_TRANSPORT_MACHINE
)) {
1512 log_error("Shell only supported on local machines.");
1516 /* Pass $TERM to shell session, if not explicitly specified. */
1517 if (!strv_find_prefix(arg_setenv
, "TERM=")) {
1520 t
= strv_find_prefix(environ
, "TERM=");
1522 if (strv_extend(&arg_setenv
, t
) < 0)
1527 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1529 r
= sd_event_default(&event
);
1531 return log_error_errno(r
, "Failed to get event loop: %m");
1533 r
= sd_bus_attach_event(bus
, event
, 0);
1535 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1537 r
= parse_machine_uid(argc
>= 2 ? argv
[1] : NULL
, &machine
, &uid
);
1539 return log_error_errno(r
, "Failed to parse machine specification: %m");
1541 match
= strjoina("type='signal',"
1542 "sender='org.freedesktop.machine1',"
1543 "path='/org/freedesktop/machine1',",
1544 "interface='org.freedesktop.machine1.Manager',"
1545 "member='MachineRemoved',"
1546 "arg0='", machine
, "'");
1548 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1550 return log_error_errno(r
, "Failed to add machine removal match: %m");
1552 r
= sd_bus_message_new_method_call(
1555 "org.freedesktop.machine1",
1556 "/org/freedesktop/machine1",
1557 "org.freedesktop.machine1.Manager",
1558 "OpenMachineShell");
1560 return bus_log_create_error(r
);
1562 path
= argc
< 3 || isempty(argv
[2]) ? NULL
: argv
[2];
1564 r
= sd_bus_message_append(m
, "sss", machine
, uid
, path
);
1566 return bus_log_create_error(r
);
1568 r
= sd_bus_message_append_strv(m
, strv_length(argv
) <= 3 ? NULL
: argv
+ 2);
1570 return bus_log_create_error(r
);
1572 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1574 return bus_log_create_error(r
);
1576 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1578 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1582 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1584 return bus_log_parse_error(r
);
1586 return process_forward(event
, &forward
, master
, 0, machine
);
1589 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1590 sd_bus
*bus
= userdata
;
1595 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1597 for (i
= 1; i
< argc
; i
++) {
1598 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1599 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1601 r
= sd_bus_message_new_method_call(
1604 "org.freedesktop.machine1",
1605 "/org/freedesktop/machine1",
1606 "org.freedesktop.machine1.Manager",
1609 return bus_log_create_error(r
);
1611 r
= sd_bus_message_append(m
, "s", argv
[i
]);
1613 return bus_log_create_error(r
);
1615 /* This is a slow operation, hence turn off any method call timeouts */
1616 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1618 return log_error_errno(r
, "Could not remove image: %s", bus_error_message(&error
, r
));
1624 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1625 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1626 sd_bus
*bus
= userdata
;
1629 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1631 r
= sd_bus_call_method(
1633 "org.freedesktop.machine1",
1634 "/org/freedesktop/machine1",
1635 "org.freedesktop.machine1.Manager",
1639 "ss", argv
[1], argv
[2]);
1641 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1648 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1649 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1650 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1651 sd_bus
*bus
= userdata
;
1654 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1656 r
= sd_bus_message_new_method_call(
1659 "org.freedesktop.machine1",
1660 "/org/freedesktop/machine1",
1661 "org.freedesktop.machine1.Manager",
1664 return bus_log_create_error(r
);
1666 r
= sd_bus_message_append(m
, "ssb", argv
[1], argv
[2], arg_read_only
);
1668 return bus_log_create_error(r
);
1670 /* This is a slow operation, hence turn off any method call timeouts */
1671 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, NULL
);
1673 return log_error_errno(r
, "Could not clone image: %s", bus_error_message(&error
, r
));
1678 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1679 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1680 sd_bus
*bus
= userdata
;
1684 b
= parse_boolean(argv
[2]);
1686 log_error("Failed to parse boolean argument: %s", argv
[2]);
1691 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1693 r
= sd_bus_call_method(
1695 "org.freedesktop.machine1",
1696 "/org/freedesktop/machine1",
1697 "org.freedesktop.machine1.Manager",
1698 "MarkImageReadOnly",
1703 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1710 static int image_exists(sd_bus
*bus
, const char *name
) {
1711 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1717 r
= sd_bus_call_method(
1719 "org.freedesktop.machine1",
1720 "/org/freedesktop/machine1",
1721 "org.freedesktop.machine1.Manager",
1727 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_IMAGE
))
1730 return log_error_errno(r
, "Failed to check whether image %s exists: %s", name
, bus_error_message(&error
, -r
));
1736 static int make_service_name(const char *name
, char **ret
) {
1742 if (!machine_name_is_valid(name
)) {
1743 log_error("Invalid machine name %s.", name
);
1747 r
= unit_name_build("systemd-nspawn", name
, ".service", ret
);
1749 return log_error_errno(r
, "Failed to build unit name: %m");
1754 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1755 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1756 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1757 sd_bus
*bus
= userdata
;
1762 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1764 r
= bus_wait_for_jobs_new(bus
, &w
);
1768 for (i
= 1; i
< argc
; i
++) {
1769 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1770 _cleanup_free_
char *unit
= NULL
;
1773 r
= make_service_name(argv
[i
], &unit
);
1777 r
= image_exists(bus
, argv
[i
]);
1781 log_error("Machine image '%s' does not exist.", argv
[1]);
1785 r
= sd_bus_call_method(
1787 "org.freedesktop.systemd1",
1788 "/org/freedesktop/systemd1",
1789 "org.freedesktop.systemd1.Manager",
1793 "ss", unit
, "fail");
1795 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1799 r
= sd_bus_message_read(reply
, "o", &object
);
1801 return bus_log_parse_error(r
);
1803 r
= bus_wait_for_jobs_add(w
, object
);
1808 r
= bus_wait_for_jobs(w
, arg_quiet
, NULL
);
1815 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1816 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1817 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1818 UnitFileChange
*changes
= NULL
;
1819 unsigned n_changes
= 0;
1820 int carries_install_info
= 0;
1821 const char *method
= NULL
;
1822 sd_bus
*bus
= userdata
;
1827 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1829 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1831 r
= sd_bus_message_new_method_call(
1834 "org.freedesktop.systemd1",
1835 "/org/freedesktop/systemd1",
1836 "org.freedesktop.systemd1.Manager",
1839 return bus_log_create_error(r
);
1841 r
= sd_bus_message_open_container(m
, 'a', "s");
1843 return bus_log_create_error(r
);
1845 for (i
= 1; i
< argc
; i
++) {
1846 _cleanup_free_
char *unit
= NULL
;
1848 r
= make_service_name(argv
[i
], &unit
);
1852 r
= image_exists(bus
, argv
[i
]);
1856 log_error("Machine image '%s' does not exist.", argv
[1]);
1860 r
= sd_bus_message_append(m
, "s", unit
);
1862 return bus_log_create_error(r
);
1865 r
= sd_bus_message_close_container(m
);
1867 return bus_log_create_error(r
);
1869 if (streq(argv
[0], "enable"))
1870 r
= sd_bus_message_append(m
, "bb", false, false);
1872 r
= sd_bus_message_append(m
, "b", false);
1874 return bus_log_create_error(r
);
1876 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1878 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1882 if (streq(argv
[0], "enable")) {
1883 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1885 return bus_log_parse_error(r
);
1888 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, &changes
, &n_changes
);
1892 r
= sd_bus_call_method(
1894 "org.freedesktop.systemd1",
1895 "/org/freedesktop/systemd1",
1896 "org.freedesktop.systemd1.Manager",
1902 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1909 unit_file_changes_free(changes
, n_changes
);
1914 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1915 const char **our_path
= userdata
, *line
;
1922 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1924 bus_log_parse_error(r
);
1928 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1931 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1934 log_full(priority
, "%s", line
);
1938 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1939 const char **our_path
= userdata
, *path
, *result
;
1946 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1948 bus_log_parse_error(r
);
1952 if (!streq_ptr(*our_path
, path
))
1955 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1959 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1964 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1966 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1970 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1971 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1972 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1973 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1974 _cleanup_(sd_event_unrefp
) sd_event
* event
= NULL
;
1975 const char *path
= NULL
;
1982 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
1984 r
= sd_event_default(&event
);
1986 return log_error_errno(r
, "Failed to get event loop: %m");
1988 r
= sd_bus_attach_event(bus
, event
, 0);
1990 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1992 r
= sd_bus_add_match(
1996 "sender='org.freedesktop.import1',"
1997 "interface='org.freedesktop.import1.Manager',"
1998 "member='TransferRemoved',"
1999 "path='/org/freedesktop/import1'",
2000 match_transfer_removed
, &path
);
2002 return log_error_errno(r
, "Failed to install match: %m");
2004 r
= sd_bus_add_match(
2008 "sender='org.freedesktop.import1',"
2009 "interface='org.freedesktop.import1.Transfer',"
2010 "member='LogMessage'",
2011 match_log_message
, &path
);
2013 return log_error_errno(r
, "Failed to install match: %m");
2015 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
2017 log_error("Failed to transfer image: %s", bus_error_message(&error
, -r
));
2021 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
2023 return bus_log_parse_error(r
);
2025 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
2028 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
2030 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
2031 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
2033 r
= sd_event_loop(event
);
2035 return log_error_errno(r
, "Failed to run event loop: %m");
2040 static int import_tar(int argc
, char *argv
[], void *userdata
) {
2041 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2042 _cleanup_free_
char *ll
= NULL
;
2043 _cleanup_close_
int fd
= -1;
2044 const char *local
= NULL
, *path
= NULL
;
2045 sd_bus
*bus
= userdata
;
2052 if (isempty(path
) || streq(path
, "-"))
2058 local
= basename(path
);
2059 if (isempty(local
) || streq(local
, "-"))
2063 log_error("Need either path or local name.");
2067 r
= tar_strip_suffixes(local
, &ll
);
2073 if (!machine_name_is_valid(local
)) {
2074 log_error("Local name %s is not a suitable machine name.", local
);
2079 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
2081 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2084 r
= sd_bus_message_new_method_call(
2087 "org.freedesktop.import1",
2088 "/org/freedesktop/import1",
2089 "org.freedesktop.import1.Manager",
2092 return bus_log_create_error(r
);
2094 r
= sd_bus_message_append(
2097 fd
>= 0 ? fd
: STDIN_FILENO
,
2102 return bus_log_create_error(r
);
2104 return transfer_image_common(bus
, m
);
2107 static int import_raw(int argc
, char *argv
[], void *userdata
) {
2108 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2109 _cleanup_free_
char *ll
= NULL
;
2110 _cleanup_close_
int fd
= -1;
2111 const char *local
= NULL
, *path
= NULL
;
2112 sd_bus
*bus
= userdata
;
2119 if (isempty(path
) || streq(path
, "-"))
2125 local
= basename(path
);
2126 if (isempty(local
) || streq(local
, "-"))
2130 log_error("Need either path or local name.");
2134 r
= raw_strip_suffixes(local
, &ll
);
2140 if (!machine_name_is_valid(local
)) {
2141 log_error("Local name %s is not a suitable machine name.", local
);
2146 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
2148 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2151 r
= sd_bus_message_new_method_call(
2154 "org.freedesktop.import1",
2155 "/org/freedesktop/import1",
2156 "org.freedesktop.import1.Manager",
2159 return bus_log_create_error(r
);
2161 r
= sd_bus_message_append(
2164 fd
>= 0 ? fd
: STDIN_FILENO
,
2169 return bus_log_create_error(r
);
2171 return transfer_image_common(bus
, m
);
2174 static void determine_compression_from_filename(const char *p
) {
2181 if (endswith(p
, ".xz"))
2183 else if (endswith(p
, ".gz"))
2184 arg_format
= "gzip";
2185 else if (endswith(p
, ".bz2"))
2186 arg_format
= "bzip2";
2189 static int export_tar(int argc
, char *argv
[], void *userdata
) {
2190 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2191 _cleanup_close_
int fd
= -1;
2192 const char *local
= NULL
, *path
= NULL
;
2193 sd_bus
*bus
= userdata
;
2199 if (!machine_name_is_valid(local
)) {
2200 log_error("Machine name %s is not valid.", local
);
2206 if (isempty(path
) || streq(path
, "-"))
2210 determine_compression_from_filename(path
);
2212 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2214 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2217 r
= sd_bus_message_new_method_call(
2220 "org.freedesktop.import1",
2221 "/org/freedesktop/import1",
2222 "org.freedesktop.import1.Manager",
2225 return bus_log_create_error(r
);
2227 r
= sd_bus_message_append(
2231 fd
>= 0 ? fd
: STDOUT_FILENO
,
2234 return bus_log_create_error(r
);
2236 return transfer_image_common(bus
, m
);
2239 static int export_raw(int argc
, char *argv
[], void *userdata
) {
2240 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2241 _cleanup_close_
int fd
= -1;
2242 const char *local
= NULL
, *path
= NULL
;
2243 sd_bus
*bus
= userdata
;
2249 if (!machine_name_is_valid(local
)) {
2250 log_error("Machine name %s is not valid.", local
);
2256 if (isempty(path
) || streq(path
, "-"))
2260 determine_compression_from_filename(path
);
2262 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2264 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2267 r
= sd_bus_message_new_method_call(
2270 "org.freedesktop.import1",
2271 "/org/freedesktop/import1",
2272 "org.freedesktop.import1.Manager",
2275 return bus_log_create_error(r
);
2277 r
= sd_bus_message_append(
2281 fd
>= 0 ? fd
: STDOUT_FILENO
,
2284 return bus_log_create_error(r
);
2286 return transfer_image_common(bus
, m
);
2289 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2290 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2291 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2292 const char *local
, *remote
;
2293 sd_bus
*bus
= userdata
;
2299 if (!http_url_is_valid(remote
)) {
2300 log_error("URL '%s' is not valid.", remote
);
2307 r
= import_url_last_component(remote
, &l
);
2309 return log_error_errno(r
, "Failed to get final component of URL: %m");
2314 if (isempty(local
) || streq(local
, "-"))
2318 r
= tar_strip_suffixes(local
, &ll
);
2324 if (!machine_name_is_valid(local
)) {
2325 log_error("Local name %s is not a suitable machine name.", local
);
2330 r
= sd_bus_message_new_method_call(
2333 "org.freedesktop.import1",
2334 "/org/freedesktop/import1",
2335 "org.freedesktop.import1.Manager",
2338 return bus_log_create_error(r
);
2340 r
= sd_bus_message_append(
2345 import_verify_to_string(arg_verify
),
2348 return bus_log_create_error(r
);
2350 return transfer_image_common(bus
, m
);
2353 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2354 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2355 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2356 const char *local
, *remote
;
2357 sd_bus
*bus
= userdata
;
2363 if (!http_url_is_valid(remote
)) {
2364 log_error("URL '%s' is not valid.", remote
);
2371 r
= import_url_last_component(remote
, &l
);
2373 return log_error_errno(r
, "Failed to get final component of URL: %m");
2378 if (isempty(local
) || streq(local
, "-"))
2382 r
= raw_strip_suffixes(local
, &ll
);
2388 if (!machine_name_is_valid(local
)) {
2389 log_error("Local name %s is not a suitable machine name.", local
);
2394 r
= sd_bus_message_new_method_call(
2397 "org.freedesktop.import1",
2398 "/org/freedesktop/import1",
2399 "org.freedesktop.import1.Manager",
2402 return bus_log_create_error(r
);
2404 r
= sd_bus_message_append(
2409 import_verify_to_string(arg_verify
),
2412 return bus_log_create_error(r
);
2414 return transfer_image_common(bus
, m
);
2417 typedef struct TransferInfo
{
2425 static int compare_transfer_info(const void *a
, const void *b
) {
2426 const TransferInfo
*x
= a
, *y
= b
;
2428 return strcmp(x
->local
, y
->local
);
2431 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2432 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2433 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2434 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2435 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2436 size_t n_transfers
= 0, n_allocated
= 0, j
;
2437 const char *type
, *remote
, *local
, *object
;
2438 sd_bus
*bus
= userdata
;
2439 uint32_t id
, max_id
= 0;
2443 pager_open(arg_no_pager
, false);
2445 r
= sd_bus_call_method(bus
,
2446 "org.freedesktop.import1",
2447 "/org/freedesktop/import1",
2448 "org.freedesktop.import1.Manager",
2454 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2458 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2460 return bus_log_parse_error(r
);
2462 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2465 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2468 transfers
[n_transfers
].id
= id
;
2469 transfers
[n_transfers
].type
= type
;
2470 transfers
[n_transfers
].remote
= remote
;
2471 transfers
[n_transfers
].local
= local
;
2472 transfers
[n_transfers
].progress
= progress
;
2492 return bus_log_parse_error(r
);
2494 r
= sd_bus_message_exit_container(reply
);
2496 return bus_log_parse_error(r
);
2498 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2500 if (arg_legend
&& n_transfers
> 0)
2501 printf("%-*s %-*s %-*s %-*s %-*s\n",
2502 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2504 (int) max_type
, "TYPE",
2505 (int) max_local
, "LOCAL",
2506 (int) max_remote
, "REMOTE");
2508 for (j
= 0; j
< n_transfers
; j
++)
2509 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2510 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2511 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2512 (int) max_type
, transfers
[j
].type
,
2513 (int) max_local
, transfers
[j
].local
,
2514 (int) max_remote
, transfers
[j
].remote
);
2517 if (n_transfers
> 0)
2518 printf("\n%zu transfers listed.\n", n_transfers
);
2520 printf("No transfers.\n");
2526 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2527 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2528 sd_bus
*bus
= userdata
;
2533 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
2535 for (i
= 1; i
< argc
; i
++) {
2538 r
= safe_atou32(argv
[i
], &id
);
2540 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2542 r
= sd_bus_call_method(
2544 "org.freedesktop.import1",
2545 "/org/freedesktop/import1",
2546 "org.freedesktop.import1.Manager",
2552 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2560 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2561 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2562 sd_bus
*bus
= userdata
;
2566 if (STR_IN_SET(argv
[argc
-1], "-", "none", "infinity"))
2567 limit
= (uint64_t) -1;
2569 r
= parse_size(argv
[argc
-1], 1024, &limit
);
2571 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2575 /* With two arguments changes the quota limit of the
2576 * specified image */
2577 r
= sd_bus_call_method(
2579 "org.freedesktop.machine1",
2580 "/org/freedesktop/machine1",
2581 "org.freedesktop.machine1.Manager",
2585 "st", argv
[1], limit
);
2587 /* With one argument changes the pool quota limit */
2588 r
= sd_bus_call_method(
2590 "org.freedesktop.machine1",
2591 "/org/freedesktop/machine1",
2592 "org.freedesktop.machine1.Manager",
2599 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2606 static int clean_images(int argc
, char *argv
[], void *userdata
) {
2607 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
2608 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2609 uint64_t usage
, total
= 0;
2610 char fb
[FORMAT_BYTES_MAX
];
2611 sd_bus
*bus
= userdata
;
2616 r
= sd_bus_message_new_method_call(
2619 "org.freedesktop.machine1",
2620 "/org/freedesktop/machine1",
2621 "org.freedesktop.machine1.Manager",
2624 return bus_log_create_error(r
);
2626 r
= sd_bus_message_append(m
, "s", arg_all
? "all" : "hidden");
2628 return bus_log_create_error(r
);
2630 /* This is a slow operation, hence permit a longer time for completion. */
2631 r
= sd_bus_call(bus
, m
, USEC_INFINITY
, &error
, &reply
);
2633 return log_error_errno(r
, "Could not clean pool: %s", bus_error_message(&error
, r
));
2635 r
= sd_bus_message_enter_container(reply
, 'a', "(st)");
2637 return bus_log_parse_error(r
);
2639 while ((r
= sd_bus_message_read(reply
, "(st)", &name
, &usage
)) > 0) {
2640 log_info("Removed image '%s'. Freed exclusive disk space: %s",
2641 name
, format_bytes(fb
, sizeof(fb
), usage
));
2647 r
= sd_bus_message_exit_container(reply
);
2649 return bus_log_parse_error(r
);
2651 log_info("Removed %u images in total. Total freed exclusive disk space %s.",
2652 c
, format_bytes(fb
, sizeof(fb
), total
));
2657 static int help(int argc
, char *argv
[], void *userdata
) {
2658 pager_open(arg_no_pager
, false);
2660 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2661 "Send control commands to or query the virtual machine and container\n"
2662 "registration manager.\n\n"
2663 " -h --help Show this help\n"
2664 " --version Show package version\n"
2665 " --no-pager Do not pipe output into a pager\n"
2666 " --no-legend Do not show the headers and footers\n"
2667 " --no-ask-password Do not ask for system passwords\n"
2668 " -H --host=[USER@]HOST Operate on remote host\n"
2669 " -M --machine=CONTAINER Operate on local container\n"
2670 " -p --property=NAME Show only properties by this name\n"
2671 " -q --quiet Suppress output\n"
2672 " -a --all Show all properties, including empty ones\n"
2673 " --value When showing properties, only print the value\n"
2674 " -l --full Do not ellipsize output\n"
2675 " --kill-who=WHO Who to send signal to\n"
2676 " -s --signal=SIGNAL Which signal to send\n"
2677 " --uid=USER Specify user ID to invoke shell as\n"
2678 " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
2679 " --read-only Create read-only bind mount\n"
2680 " --mkdir Create directory before bind mounting, if missing\n"
2681 " -n --lines=INTEGER Number of journal entries to show\n"
2682 " --max-addresses=INTEGER Number of internet addresses to show at most\n"
2683 " -o --output=STRING Change journal output mode (short, short-precise,\n"
2684 " short-iso, short-iso-precise, short-full,\n"
2685 " short-monotonic, short-unix, verbose, export,\n"
2686 " json, json-pretty, json-sse, cat)\n"
2687 " --verify=MODE Verification mode for downloaded images (no,\n"
2688 " checksum, signature)\n"
2689 " --force Download image even if already exists\n\n"
2690 "Machine Commands:\n"
2691 " list List running VMs and containers\n"
2692 " status NAME... Show VM/container details\n"
2693 " show [NAME...] Show properties of one or more VMs/containers\n"
2694 " start NAME... Start container as a service\n"
2695 " login [NAME] Get a login prompt in a container or on the\n"
2697 " shell [[USER@]NAME [COMMAND...]]\n"
2698 " Invoke a shell (or other command) in a container\n"
2699 " or on the local host\n"
2700 " enable NAME... Enable automatic container start at boot\n"
2701 " disable NAME... Disable automatic container start at boot\n"
2702 " poweroff NAME... Power off one or more containers\n"
2703 " reboot NAME... Reboot one or more containers\n"
2704 " terminate NAME... Terminate one or more VMs/containers\n"
2705 " kill NAME... Send signal to processes of a VM/container\n"
2706 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2707 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2708 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2710 " list-images Show available container and VM images\n"
2711 " image-status [NAME...] Show image details\n"
2712 " show-image [NAME...] Show properties of image\n"
2713 " clone NAME NAME Clone an image\n"
2714 " rename NAME NAME Rename an image\n"
2715 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2716 " remove NAME... Remove an image\n"
2717 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
2718 " clean Remove hidden (or all) images\n\n"
2719 "Image Transfer Commands:\n"
2720 " pull-tar URL [NAME] Download a TAR container image\n"
2721 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2722 " import-tar FILE [NAME] Import a local TAR container image\n"
2723 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2724 " export-tar NAME [FILE] Export a TAR container image locally\n"
2725 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2726 " list-transfers Show list of downloads in progress\n"
2727 " cancel-transfer Cancel a download\n"
2728 , program_invocation_short_name
);
2733 static int parse_argv(int argc
, char *argv
[]) {
2736 ARG_VERSION
= 0x100,
2743 ARG_NO_ASK_PASSWORD
,
2751 static const struct option options
[] = {
2752 { "help", no_argument
, NULL
, 'h' },
2753 { "version", no_argument
, NULL
, ARG_VERSION
},
2754 { "property", required_argument
, NULL
, 'p' },
2755 { "all", no_argument
, NULL
, 'a' },
2756 { "value", no_argument
, NULL
, ARG_VALUE
},
2757 { "full", no_argument
, NULL
, 'l' },
2758 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2759 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2760 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2761 { "signal", required_argument
, NULL
, 's' },
2762 { "host", required_argument
, NULL
, 'H' },
2763 { "machine", required_argument
, NULL
, 'M' },
2764 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2765 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2766 { "quiet", no_argument
, NULL
, 'q' },
2767 { "lines", required_argument
, NULL
, 'n' },
2768 { "output", required_argument
, NULL
, 'o' },
2769 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2770 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2771 { "force", no_argument
, NULL
, ARG_FORCE
},
2772 { "format", required_argument
, NULL
, ARG_FORMAT
},
2773 { "uid", required_argument
, NULL
, ARG_UID
},
2774 { "setenv", required_argument
, NULL
, 'E' },
2775 { "max-addresses", required_argument
, NULL
, ARG_NUMBER_IPS
},
2779 bool reorder
= false;
2780 int c
, r
, shell
= -1;
2786 static const char option_string
[] = "-hp:als:H:M:qn:o:E:";
2788 c
= getopt_long(argc
, argv
, option_string
+ reorder
, options
, NULL
);
2794 case 1: /* getopt_long() returns 1 if "-" was the first character of the option string, and a
2795 * non-option argument was discovered. */
2799 /* We generally are fine with the fact that getopt_long() reorders the command line, and looks
2800 * for switches after the main verb. However, for "shell" we really don't want that, since we
2801 * want that switches specified after the machine name are passed to the program to execute,
2802 * and not processed by us. To make this possible, we'll first invoke getopt_long() with
2803 * reordering disabled (i.e. with the "-" prefix in the option string), looking for the first
2804 * non-option parameter. If it's the verb "shell" we remember its position and continue
2805 * processing options. In this case, as soon as we hit the next non-option argument we found
2806 * the machine name, and stop further processing. If the first non-option argument is any other
2807 * verb than "shell" we switch to normal reordering mode and continue processing arguments
2811 /* If we already found the "shell" verb on the command line, and now found the next
2812 * non-option argument, then this is the machine name and we should stop processing
2813 * further arguments. */
2814 optind
--; /* don't process this argument, go one step back */
2817 if (streq(optarg
, "shell"))
2818 /* Remember the position of the "shell" verb, and continue processing normally. */
2823 /* OK, this is some other verb. In this case, turn on reordering again, and continue
2824 * processing normally. */
2827 /* We changed the option string. getopt_long() only looks at it again if we invoke it
2828 * at least once with a reset option index. Hence, let's reset the option index here,
2829 * then invoke getopt_long() again (ignoring what it has to say, after all we most
2830 * likely already processed it), and the bump the option index so that we read the
2831 * intended argument again. */
2832 saved_optind
= optind
;
2834 (void) getopt_long(argc
, argv
, option_string
+ reorder
, options
, NULL
);
2835 optind
= saved_optind
- 1; /* go one step back, process this argument again */
2841 return help(0, NULL
, NULL
);
2847 r
= strv_extend(&arg_property
, optarg
);
2851 /* If the user asked for a particular
2852 * property, show it to him, even if it is
2870 if (safe_atou(optarg
, &arg_lines
) < 0) {
2871 log_error("Failed to parse lines '%s'", optarg
);
2877 arg_output
= output_mode_from_string(optarg
);
2878 if (arg_output
< 0) {
2879 log_error("Unknown output '%s'.", optarg
);
2885 arg_no_pager
= true;
2893 arg_kill_who
= optarg
;
2897 arg_signal
= signal_from_string_try_harder(optarg
);
2898 if (arg_signal
< 0) {
2899 log_error("Failed to parse signal string %s.", optarg
);
2904 case ARG_NO_ASK_PASSWORD
:
2905 arg_ask_password
= false;
2909 arg_transport
= BUS_TRANSPORT_REMOTE
;
2914 arg_transport
= BUS_TRANSPORT_MACHINE
;
2919 arg_read_only
= true;
2931 arg_verify
= import_verify_from_string(optarg
);
2932 if (arg_verify
< 0) {
2933 log_error("Failed to parse --verify= setting: %s", optarg
);
2943 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2944 log_error("Unknown format: %s", optarg
);
2948 arg_format
= optarg
;
2956 if (!env_assignment_is_valid(optarg
)) {
2957 log_error("Environment assignment invalid: %s", optarg
);
2961 r
= strv_extend(&arg_setenv
, optarg
);
2966 case ARG_NUMBER_IPS
:
2967 if (streq(optarg
, "all"))
2968 arg_addrs
= ALL_IP_ADDRESSES
;
2969 else if (safe_atoi(optarg
, &arg_addrs
) < 0) {
2970 log_error("Invalid number of IPs");
2972 } else if (arg_addrs
< 0) {
2973 log_error("Number of IPs cannot be negative");
2982 assert_not_reached("Unhandled option");
2991 /* We found the "shell" verb while processing the argument list. Since we turned off reordering of the
2992 * argument list initially let's readjust it now, and move the "shell" verb to the back. */
2994 optind
-= 1; /* place the option index where the "shell" verb will be placed */
2997 for (i
= shell
; i
< optind
; i
++)
2998 argv
[i
] = argv
[i
+1];
3005 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
3007 static const Verb verbs
[] = {
3008 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
3009 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
3010 { "list-images", VERB_ANY
, 1, 0, list_images
},
3011 { "status", 2, VERB_ANY
, 0, show_machine
},
3012 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
3013 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
3014 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
3015 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
3016 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
3017 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
3018 { "stop", 2, VERB_ANY
, 0, poweroff_machine
}, /* Convenience alias */
3019 { "kill", 2, VERB_ANY
, 0, kill_machine
},
3020 { "login", VERB_ANY
, 2, 0, login_machine
},
3021 { "shell", VERB_ANY
, VERB_ANY
, 0, shell_machine
},
3022 { "bind", 3, 4, 0, bind_mount
},
3023 { "copy-to", 3, 4, 0, copy_files
},
3024 { "copy-from", 3, 4, 0, copy_files
},
3025 { "remove", 2, VERB_ANY
, 0, remove_image
},
3026 { "rename", 3, 3, 0, rename_image
},
3027 { "clone", 3, 3, 0, clone_image
},
3028 { "read-only", 2, 3, 0, read_only_image
},
3029 { "start", 2, VERB_ANY
, 0, start_machine
},
3030 { "enable", 2, VERB_ANY
, 0, enable_machine
},
3031 { "disable", 2, VERB_ANY
, 0, enable_machine
},
3032 { "import-tar", 2, 3, 0, import_tar
},
3033 { "import-raw", 2, 3, 0, import_raw
},
3034 { "export-tar", 2, 3, 0, export_tar
},
3035 { "export-raw", 2, 3, 0, export_raw
},
3036 { "pull-tar", 2, 3, 0, pull_tar
},
3037 { "pull-raw", 2, 3, 0, pull_raw
},
3038 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
3039 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
3040 { "set-limit", 2, 3, 0, set_limit
},
3041 { "clean", VERB_ANY
, 1, 0, clean_images
},
3045 return dispatch_verb(argc
, argv
, verbs
, bus
);
3048 int main(int argc
, char*argv
[]) {
3049 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
3052 setlocale(LC_ALL
, "");
3053 log_parse_environment();
3057 r
= parse_argv(argc
, argv
);
3061 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
3063 log_error_errno(r
, "Failed to create bus connection: %m");
3067 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
3069 r
= machinectl_main(argc
, argv
, bus
);
3073 polkit_agent_close();
3075 strv_free(arg_property
);
3076 strv_free(arg_setenv
);
3078 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;