1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
32 #include <sys/mount.h>
39 #include "spawn-polkit-agent.h"
41 #include "bus-error.h"
44 #include "unit-name.h"
45 #include "cgroup-show.h"
46 #include "logs-show.h"
47 #include "cgroup-util.h"
49 #include "event-util.h"
50 #include "path-util.h"
54 #include "import-util.h"
55 #include "process-util.h"
56 #include "terminal-util.h"
58 static char **arg_property
= NULL
;
59 static bool arg_all
= false;
60 static bool arg_full
= false;
61 static bool arg_no_pager
= false;
62 static bool arg_legend
= true;
63 static const char *arg_kill_who
= NULL
;
64 static int arg_signal
= SIGTERM
;
65 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
66 static char *arg_host
= NULL
;
67 static bool arg_read_only
= false;
68 static bool arg_mkdir
= false;
69 static bool arg_quiet
= false;
70 static bool arg_ask_password
= true;
71 static unsigned arg_lines
= 10;
72 static OutputMode arg_output
= OUTPUT_SHORT
;
73 static bool arg_force
= false;
74 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
75 static const char* arg_dkr_index_url
= NULL
;
76 static const char* arg_format
= NULL
;
78 static void pager_open_if_enabled(void) {
86 static void polkit_agent_open_if_enabled(void) {
88 /* Open the polkit agent as a child process if necessary */
90 if (!arg_ask_password
)
93 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
99 static OutputFlags
get_output_flags(void) {
101 arg_all
* OUTPUT_SHOW_ALL
|
102 arg_full
* OUTPUT_FULL_WIDTH
|
103 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
104 on_tty() * OUTPUT_COLOR
|
105 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
108 typedef struct MachineInfo
{
114 static int compare_machine_info(const void *a
, const void *b
) {
115 const MachineInfo
*x
= a
, *y
= b
;
117 return strcmp(x
->name
, y
->name
);
120 static int list_machines(int argc
, char *argv
[], void *userdata
) {
122 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
123 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
124 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
125 _cleanup_free_ MachineInfo
*machines
= NULL
;
126 const char *name
, *class, *service
, *object
;
127 size_t n_machines
= 0, n_allocated
= 0, j
;
128 sd_bus
*bus
= userdata
;
133 pager_open_if_enabled();
135 r
= sd_bus_call_method(
137 "org.freedesktop.machine1",
138 "/org/freedesktop/machine1",
139 "org.freedesktop.machine1.Manager",
145 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
149 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
151 return bus_log_parse_error(r
);
153 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
156 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
159 machines
[n_machines
].name
= name
;
160 machines
[n_machines
].class = class;
161 machines
[n_machines
].service
= service
;
178 return bus_log_parse_error(r
);
180 r
= sd_bus_message_exit_container(reply
);
182 return bus_log_parse_error(r
);
184 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
187 printf("%-*s %-*s %-*s\n",
188 (int) max_name
, "MACHINE",
189 (int) max_class
, "CLASS",
190 (int) max_service
, "SERVICE");
192 for (j
= 0; j
< n_machines
; j
++)
193 printf("%-*s %-*s %-*s\n",
194 (int) max_name
, machines
[j
].name
,
195 (int) max_class
, machines
[j
].class,
196 (int) max_service
, machines
[j
].service
);
199 printf("\n%zu machines listed.\n", n_machines
);
204 typedef struct ImageInfo
{
213 static int compare_image_info(const void *a
, const void *b
) {
214 const ImageInfo
*x
= a
, *y
= b
;
216 return strcmp(x
->name
, y
->name
);
219 static int list_images(int argc
, char *argv
[], void *userdata
) {
221 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
222 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
223 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
224 _cleanup_free_ ImageInfo
*images
= NULL
;
225 size_t n_images
= 0, n_allocated
= 0, j
;
226 const char *name
, *type
, *object
;
227 sd_bus
*bus
= userdata
;
228 uint64_t crtime
, mtime
, size
;
233 pager_open_if_enabled();
235 r
= sd_bus_call_method(
237 "org.freedesktop.machine1",
238 "/org/freedesktop/machine1",
239 "org.freedesktop.machine1.Manager",
245 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
249 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
251 return bus_log_parse_error(r
);
253 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
254 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
257 if (name
[0] == '.' && !arg_all
)
260 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
263 images
[n_images
].name
= name
;
264 images
[n_images
].type
= type
;
265 images
[n_images
].read_only
= read_only
;
266 images
[n_images
].crtime
= crtime
;
267 images
[n_images
].mtime
= mtime
;
268 images
[n_images
].size
= size
;
279 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
285 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
290 if (size
!= (uint64_t) -1) {
291 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
299 return bus_log_parse_error(r
);
301 r
= sd_bus_message_exit_container(reply
);
303 return bus_log_parse_error(r
);
305 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
308 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
309 (int) max_name
, "NAME",
310 (int) max_type
, "TYPE",
312 (int) max_size
, "USAGE",
313 (int) max_crtime
, "CREATED",
314 (int) max_mtime
, "MODIFIED");
316 for (j
= 0; j
< n_images
; j
++) {
317 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
319 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
320 (int) max_name
, images
[j
].name
,
321 (int) max_type
, images
[j
].type
,
322 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_highlight_off() : "",
323 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
324 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
325 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
329 printf("\n%zu images listed.\n", n_images
);
334 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
335 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
336 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
337 _cleanup_free_
char *path
= NULL
;
345 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
348 path
= unit_dbus_path_from_name(unit
);
352 r
= sd_bus_get_property(
354 "org.freedesktop.systemd1",
356 endswith(unit
, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
362 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
366 r
= sd_bus_message_read(reply
, "s", &cgroup
);
368 return bus_log_parse_error(r
);
373 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, false) != 0 && leader
<= 0)
382 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
386 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
387 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
395 r
= sd_bus_call_method(bus
,
396 "org.freedesktop.machine1",
397 "/org/freedesktop/machine1",
398 "org.freedesktop.machine1.Manager",
399 "GetMachineAddresses",
406 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
408 return bus_log_parse_error(r
);
410 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
414 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
416 r
= sd_bus_message_read(reply
, "i", &family
);
418 return bus_log_parse_error(r
);
420 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
422 return bus_log_parse_error(r
);
424 fputs(prefix
, stdout
);
425 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
426 if (family
== AF_INET6
&& ifi
> 0)
430 r
= sd_bus_message_exit_container(reply
);
432 return bus_log_parse_error(r
);
434 if (prefix
!= prefix2
)
438 return bus_log_parse_error(r
);
440 r
= sd_bus_message_exit_container(reply
);
442 return bus_log_parse_error(r
);
447 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
448 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
449 const char *k
, *v
, *pretty
= NULL
;
456 r
= sd_bus_call_method(bus
,
457 "org.freedesktop.machine1",
458 "/org/freedesktop/machine1",
459 "org.freedesktop.machine1.Manager",
460 "GetMachineOSRelease",
467 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
469 return bus_log_parse_error(r
);
471 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
472 if (streq(k
, "PRETTY_NAME"))
477 return bus_log_parse_error(r
);
479 r
= sd_bus_message_exit_container(reply
);
481 return bus_log_parse_error(r
);
484 printf("%s%s\n", prefix
, pretty
);
489 typedef struct MachineStatusInfo
{
495 char *root_directory
;
497 struct dual_timestamp timestamp
;
502 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
503 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
504 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
510 fputs(strna(i
->name
), stdout
);
512 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
513 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
517 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
518 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
521 printf("\t Since: %s; %s\n", s2
, s1
);
523 printf("\t Since: %s\n", s2
);
526 _cleanup_free_
char *t
= NULL
;
528 printf("\t Leader: %u", (unsigned) i
->leader
);
530 get_process_comm(i
->leader
, &t
);
538 printf("\t Service: %s", i
->service
);
541 printf("; class %s", i
->class);
545 printf("\t Class: %s\n", i
->class);
547 if (i
->root_directory
)
548 printf("\t Root: %s\n", i
->root_directory
);
550 if (i
->n_netif
> 0) {
553 fputs("\t Iface:", stdout
);
555 for (c
= 0; c
< i
->n_netif
; c
++) {
556 char name
[IF_NAMESIZE
+1] = "";
558 if (if_indextoname(i
->netif
[c
], name
)) {
567 printf(" %i", i
->netif
[c
]);
573 print_addresses(bus
, i
->name
, ifi
,
577 print_os_release(bus
, i
->name
, "\t OS: ");
580 printf("\t Unit: %s\n", i
->unit
);
581 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
583 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
585 show_journal_by_unit(
590 i
->timestamp
.monotonic
,
593 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
594 SD_JOURNAL_LOCAL_ONLY
,
601 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
602 MachineStatusInfo
*i
= userdata
;
607 assert_cc(sizeof(int32_t) == sizeof(int));
608 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
614 i
->n_netif
= l
/ sizeof(int32_t);
615 i
->netif
= memdup(v
, l
);
622 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
624 static const struct bus_properties_map map
[] = {
625 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
626 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
627 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
628 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
629 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
630 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
631 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
632 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
633 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
634 { "NetworkInterfaces", "ai", map_netif
, 0 },
638 MachineStatusInfo info
= {};
646 r
= bus_map_all_properties(bus
,
647 "org.freedesktop.machine1",
652 return log_error_errno(r
, "Could not get properties: %m");
658 print_machine_status_info(bus
, &info
);
664 free(info
.root_directory
);
670 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
682 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
684 log_error_errno(r
, "Could not get properties: %m");
689 static int show_machine(int argc
, char *argv
[], void *userdata
) {
691 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
692 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
693 bool properties
, new_line
= false;
694 sd_bus
*bus
= userdata
;
699 properties
= !strstr(argv
[0], "status");
701 pager_open_if_enabled();
703 if (properties
&& argc
<= 1) {
705 /* If no argument is specified, inspect the manager
707 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
712 for (i
= 1; i
< argc
; i
++) {
713 const char *path
= NULL
;
715 r
= sd_bus_call_method(
717 "org.freedesktop.machine1",
718 "/org/freedesktop/machine1",
719 "org.freedesktop.machine1.Manager",
725 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
729 r
= sd_bus_message_read(reply
, "o", &path
);
731 return bus_log_parse_error(r
);
734 r
= show_machine_properties(bus
, path
, &new_line
);
736 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
742 typedef struct ImageStatusInfo
{
751 uint64_t usage_exclusive
;
752 uint64_t limit_exclusive
;
755 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
756 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
757 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
758 char bs
[FORMAT_BYTES_MAX
], *s3
;
759 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
765 fputs(i
->name
, stdout
);
770 printf("\t Type: %s\n", i
->type
);
773 printf("\t Path: %s\n", i
->path
);
775 printf("\t RO: %s%s%s\n",
776 i
->read_only
? ansi_highlight_red() : "",
777 i
->read_only
? "read-only" : "writable",
778 i
->read_only
? ansi_highlight_off() : "");
780 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
781 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
783 printf("\t Created: %s; %s\n", s2
, s1
);
785 printf("\t Created: %s\n", s2
);
787 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
788 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
790 printf("\tModified: %s; %s\n", s2
, s1
);
792 printf("\tModified: %s\n", s2
);
794 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
795 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
797 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
799 printf("\t Usage: %s\n", s3
);
801 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
802 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
804 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
806 printf("\t Limit: %s\n", s3
);
809 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
811 static const struct bus_properties_map map
[] = {
812 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
813 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
814 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
815 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
816 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
817 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
818 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
819 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
820 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
821 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
825 ImageStatusInfo info
= {};
832 r
= bus_map_all_properties(bus
,
833 "org.freedesktop.machine1",
838 return log_error_errno(r
, "Could not get properties: %m");
844 print_image_status_info(bus
, &info
);
853 typedef struct PoolStatusInfo
{
859 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
860 char bs
[FORMAT_BYTES_MAX
], *s
;
863 printf("\t Path: %s\n", i
->path
);
865 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
867 printf("\t Usage: %s\n", s
);
869 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
871 printf("\t Limit: %s\n", s
);
874 static int show_pool_info(sd_bus
*bus
) {
876 static const struct bus_properties_map map
[] = {
877 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
878 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
879 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
883 PoolStatusInfo info
= {
884 .usage
= (uint64_t) -1,
885 .limit
= (uint64_t) -1,
891 r
= bus_map_all_properties(bus
,
892 "org.freedesktop.machine1",
893 "/org/freedesktop/machine1",
897 return log_error_errno(r
, "Could not get properties: %m");
899 print_pool_status_info(bus
, &info
);
906 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
918 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
920 log_error_errno(r
, "Could not get properties: %m");
925 static int show_image(int argc
, char *argv
[], void *userdata
) {
927 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
928 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
929 bool properties
, new_line
= false;
930 sd_bus
*bus
= userdata
;
935 properties
= !strstr(argv
[0], "status");
937 pager_open_if_enabled();
941 /* If no argument is specified, inspect the manager
945 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
947 r
= show_pool_info(bus
);
952 for (i
= 1; i
< argc
; i
++) {
953 const char *path
= NULL
;
955 r
= sd_bus_call_method(
957 "org.freedesktop.machine1",
958 "/org/freedesktop/machine1",
959 "org.freedesktop.machine1.Manager",
965 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
969 r
= sd_bus_message_read(reply
, "o", &path
);
971 return bus_log_parse_error(r
);
974 r
= show_image_properties(bus
, path
, &new_line
);
976 r
= show_image_info(bus
, path
, &new_line
);
982 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
983 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
984 sd_bus
*bus
= userdata
;
989 polkit_agent_open_if_enabled();
992 arg_kill_who
= "all";
994 for (i
= 1; i
< argc
; i
++) {
995 r
= sd_bus_call_method(
997 "org.freedesktop.machine1",
998 "/org/freedesktop/machine1",
999 "org.freedesktop.machine1.Manager",
1003 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1005 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1013 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1014 arg_kill_who
= "leader";
1015 arg_signal
= SIGINT
; /* sysvinit + systemd */
1017 return kill_machine(argc
, argv
, userdata
);
1020 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1021 arg_kill_who
= "leader";
1022 arg_signal
= SIGRTMIN
+4; /* only systemd */
1024 return kill_machine(argc
, argv
, userdata
);
1027 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1028 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1029 sd_bus
*bus
= userdata
;
1034 polkit_agent_open_if_enabled();
1036 for (i
= 1; i
< argc
; i
++) {
1037 r
= sd_bus_call_method(
1039 "org.freedesktop.machine1",
1040 "/org/freedesktop/machine1",
1041 "org.freedesktop.machine1.Manager",
1047 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1055 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1056 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1057 sd_bus
*bus
= userdata
;
1063 polkit_agent_open_if_enabled();
1065 copy_from
= streq(argv
[0], "copy-from");
1067 r
= sd_bus_call_method(
1069 "org.freedesktop.machine1",
1070 "/org/freedesktop/machine1",
1071 "org.freedesktop.machine1.Manager",
1072 copy_from
? "CopyFromMachine" : "CopyToMachine",
1080 log_error("Failed to copy: %s", bus_error_message(&error
, -r
));
1087 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1088 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1089 sd_bus
*bus
= userdata
;
1094 polkit_agent_open_if_enabled();
1096 r
= sd_bus_call_method(
1098 "org.freedesktop.machine1",
1099 "/org/freedesktop/machine1",
1100 "org.freedesktop.machine1.Manager",
1111 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1118 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1119 PTYForward
** forward
= (PTYForward
**) userdata
;
1126 /* If the forwarder is already initialized, tell it to
1127 * exit on the next vhangup(), so that we still flush
1128 * out what might be queued and exit then. */
1130 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1134 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1137 /* On error, or when the forwarder is not initialized yet, quit immediately */
1138 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1142 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1143 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1144 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1145 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1146 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1147 _cleanup_event_unref_ sd_event
*event
= NULL
;
1148 int master
= -1, r
, ret
= 0;
1149 sd_bus
*bus
= userdata
;
1150 const char *pty
, *match
;
1156 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1157 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1158 log_error("Login only supported on local machines.");
1162 polkit_agent_open_if_enabled();
1164 r
= sd_event_default(&event
);
1166 return log_error_errno(r
, "Failed to get event loop: %m");
1168 r
= sd_bus_attach_event(bus
, event
, 0);
1170 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1172 match
= strjoina("type='signal',"
1173 "sender='org.freedesktop.machine1',"
1174 "path='/org/freedesktop/machine1',",
1175 "interface='org.freedesktop.machine1.Manager',"
1176 "member='MachineRemoved',"
1181 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1183 return log_error_errno(r
, "Failed to add machine removal match: %m");
1185 r
= sd_bus_call_method(
1187 "org.freedesktop.machine1",
1188 "/org/freedesktop/machine1",
1189 "org.freedesktop.machine1.Manager",
1195 log_error("Failed to get machine PTY: %s", bus_error_message(&error
, -r
));
1199 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1201 return bus_log_parse_error(r
);
1203 sigprocmask_many(SIG_BLOCK
, SIGWINCH
, SIGTERM
, SIGINT
, -1);
1205 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv
[1]);
1207 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1208 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1210 r
= pty_forward_new(event
, master
, true, false, &forward
);
1212 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1214 r
= sd_event_loop(event
);
1216 return log_error_errno(r
, "Failed to run event loop: %m");
1218 pty_forward_get_last_char(forward
, &last_char
);
1219 machine_died
= pty_forward_get_ignore_vhangup(forward
) == 0;
1221 forward
= pty_forward_free(forward
);
1223 if (last_char
!= '\n')
1224 fputc('\n', stdout
);
1227 log_info("Machine %s terminated.", argv
[1]);
1229 log_info("Connection to machine %s terminated.", argv
[1]);
1231 sd_event_get_exit_code(event
, &ret
);
1235 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1236 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1237 sd_bus
*bus
= userdata
;
1242 polkit_agent_open_if_enabled();
1244 for (i
= 1; i
< argc
; i
++) {
1245 r
= sd_bus_call_method(
1247 "org.freedesktop.machine1",
1248 "/org/freedesktop/machine1",
1249 "org.freedesktop.machine1.Manager",
1255 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1263 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1264 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1265 sd_bus
*bus
= userdata
;
1268 polkit_agent_open_if_enabled();
1270 r
= sd_bus_call_method(
1272 "org.freedesktop.machine1",
1273 "/org/freedesktop/machine1",
1274 "org.freedesktop.machine1.Manager",
1278 "ss", argv
[1], argv
[2]);
1280 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1287 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1288 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1289 sd_bus
*bus
= userdata
;
1292 polkit_agent_open_if_enabled();
1294 r
= sd_bus_call_method(
1296 "org.freedesktop.machine1",
1297 "/org/freedesktop/machine1",
1298 "org.freedesktop.machine1.Manager",
1302 "ssb", argv
[1], argv
[2], arg_read_only
);
1304 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1311 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1312 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1313 sd_bus
*bus
= userdata
;
1317 b
= parse_boolean(argv
[2]);
1319 log_error("Failed to parse boolean argument: %s", argv
[2]);
1324 polkit_agent_open_if_enabled();
1326 r
= sd_bus_call_method(
1328 "org.freedesktop.machine1",
1329 "/org/freedesktop/machine1",
1330 "org.freedesktop.machine1.Manager",
1331 "MarkImageReadOnly",
1336 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1343 static int make_service_name(const char *name
, char **ret
) {
1344 _cleanup_free_
char *e
= NULL
;
1350 if (!machine_name_is_valid(name
)) {
1351 log_error("Invalid machine name %s.", name
);
1355 e
= unit_name_escape(name
);
1359 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1361 return log_error_errno(r
, "Failed to build unit name: %m");
1366 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1367 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1368 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1369 sd_bus
*bus
= userdata
;
1374 polkit_agent_open_if_enabled();
1376 r
= bus_wait_for_jobs_new(bus
, &w
);
1380 for (i
= 1; i
< argc
; i
++) {
1381 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1382 _cleanup_free_
char *unit
= NULL
;
1385 r
= make_service_name(argv
[i
], &unit
);
1389 r
= sd_bus_call_method(
1391 "org.freedesktop.systemd1",
1392 "/org/freedesktop/systemd1",
1393 "org.freedesktop.systemd1.Manager",
1397 "ss", unit
, "fail");
1399 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1403 r
= sd_bus_message_read(reply
, "o", &object
);
1405 return bus_log_parse_error(r
);
1407 r
= bus_wait_for_jobs_add(w
, object
);
1412 r
= bus_wait_for_jobs(w
, arg_quiet
);
1419 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1420 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1421 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1422 int carries_install_info
= 0;
1423 const char *method
= NULL
;
1424 sd_bus
*bus
= userdata
;
1429 polkit_agent_open_if_enabled();
1431 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1433 r
= sd_bus_message_new_method_call(
1436 "org.freedesktop.systemd1",
1437 "/org/freedesktop/systemd1",
1438 "org.freedesktop.systemd1.Manager",
1441 return bus_log_create_error(r
);
1443 r
= sd_bus_message_open_container(m
, 'a', "s");
1445 return bus_log_create_error(r
);
1447 for (i
= 1; i
< argc
; i
++) {
1448 _cleanup_free_
char *unit
= NULL
;
1450 r
= make_service_name(argv
[i
], &unit
);
1454 r
= sd_bus_message_append(m
, "s", unit
);
1456 return bus_log_create_error(r
);
1459 r
= sd_bus_message_close_container(m
);
1461 return bus_log_create_error(r
);
1463 if (streq(argv
[0], "enable"))
1464 r
= sd_bus_message_append(m
, "bb", false, false);
1466 r
= sd_bus_message_append(m
, "b", false);
1468 return bus_log_create_error(r
);
1470 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1472 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1476 if (streq(argv
[0], "enable")) {
1477 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1479 return bus_log_parse_error(r
);
1482 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1486 r
= sd_bus_call_method(
1488 "org.freedesktop.systemd1",
1489 "/org/freedesktop/systemd1",
1490 "org.freedesktop.systemd1.Manager",
1496 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1503 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1504 const char **our_path
= userdata
, *line
;
1511 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1513 bus_log_parse_error(r
);
1517 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1520 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1523 log_full(priority
, "%s", line
);
1527 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1528 const char **our_path
= userdata
, *path
, *result
;
1535 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1537 bus_log_parse_error(r
);
1541 if (!streq_ptr(*our_path
, path
))
1544 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1548 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1553 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1555 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1559 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1560 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1561 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1562 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1563 _cleanup_event_unref_ sd_event
* event
= NULL
;
1564 const char *path
= NULL
;
1571 polkit_agent_open_if_enabled();
1573 r
= sd_event_default(&event
);
1575 return log_error_errno(r
, "Failed to get event loop: %m");
1577 r
= sd_bus_attach_event(bus
, event
, 0);
1579 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1581 r
= sd_bus_add_match(
1585 "sender='org.freedesktop.import1',"
1586 "interface='org.freedesktop.import1.Manager',"
1587 "member='TransferRemoved',"
1588 "path='/org/freedesktop/import1'",
1589 match_transfer_removed
, &path
);
1591 return log_error_errno(r
, "Failed to install match: %m");
1593 r
= sd_bus_add_match(
1597 "sender='org.freedesktop.import1',"
1598 "interface='org.freedesktop.import1.Transfer',"
1599 "member='LogMessage'",
1600 match_log_message
, &path
);
1602 return log_error_errno(r
, "Failed to install match: %m");
1604 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1606 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1610 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1612 return bus_log_parse_error(r
);
1614 sigprocmask_many(SIG_BLOCK
, SIGTERM
, SIGINT
, -1);
1617 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1619 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1620 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1622 r
= sd_event_loop(event
);
1624 return log_error_errno(r
, "Failed to run event loop: %m");
1629 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1630 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1631 _cleanup_free_
char *ll
= NULL
;
1632 _cleanup_close_
int fd
= -1;
1633 const char *local
= NULL
, *path
= NULL
;
1634 sd_bus
*bus
= userdata
;
1641 if (isempty(path
) || streq(path
, "-"))
1647 local
= basename(path
);
1648 if (isempty(local
) || streq(local
, "-"))
1652 log_error("Need either path or local name.");
1656 r
= tar_strip_suffixes(local
, &ll
);
1662 if (!machine_name_is_valid(local
)) {
1663 log_error("Local name %s is not a suitable machine name.", local
);
1668 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1670 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1673 r
= sd_bus_message_new_method_call(
1676 "org.freedesktop.import1",
1677 "/org/freedesktop/import1",
1678 "org.freedesktop.import1.Manager",
1681 return bus_log_create_error(r
);
1683 r
= sd_bus_message_append(
1686 fd
>= 0 ? fd
: STDIN_FILENO
,
1691 return bus_log_create_error(r
);
1693 return transfer_image_common(bus
, m
);
1696 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1697 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1698 _cleanup_free_
char *ll
= NULL
;
1699 _cleanup_close_
int fd
= -1;
1700 const char *local
= NULL
, *path
= NULL
;
1701 sd_bus
*bus
= userdata
;
1708 if (isempty(path
) || streq(path
, "-"))
1714 local
= basename(path
);
1715 if (isempty(local
) || streq(local
, "-"))
1719 log_error("Need either path or local name.");
1723 r
= raw_strip_suffixes(local
, &ll
);
1729 if (!machine_name_is_valid(local
)) {
1730 log_error("Local name %s is not a suitable machine name.", local
);
1735 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1737 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1740 r
= sd_bus_message_new_method_call(
1743 "org.freedesktop.import1",
1744 "/org/freedesktop/import1",
1745 "org.freedesktop.import1.Manager",
1748 return bus_log_create_error(r
);
1750 r
= sd_bus_message_append(
1753 fd
>= 0 ? fd
: STDIN_FILENO
,
1758 return bus_log_create_error(r
);
1760 return transfer_image_common(bus
, m
);
1763 static void determine_compression_from_filename(const char *p
) {
1770 if (endswith(p
, ".xz"))
1772 else if (endswith(p
, ".gz"))
1773 arg_format
= "gzip";
1774 else if (endswith(p
, ".bz2"))
1775 arg_format
= "bzip2";
1778 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1779 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1780 _cleanup_close_
int fd
= -1;
1781 const char *local
= NULL
, *path
= NULL
;
1782 sd_bus
*bus
= userdata
;
1788 if (!machine_name_is_valid(local
)) {
1789 log_error("Machine name %s is not valid.", local
);
1795 if (isempty(path
) || streq(path
, "-"))
1799 determine_compression_from_filename(path
);
1801 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1803 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1806 r
= sd_bus_message_new_method_call(
1809 "org.freedesktop.import1",
1810 "/org/freedesktop/import1",
1811 "org.freedesktop.import1.Manager",
1814 return bus_log_create_error(r
);
1816 r
= sd_bus_message_append(
1820 fd
>= 0 ? fd
: STDOUT_FILENO
,
1823 return bus_log_create_error(r
);
1825 return transfer_image_common(bus
, m
);
1828 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1829 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1830 _cleanup_close_
int fd
= -1;
1831 const char *local
= NULL
, *path
= NULL
;
1832 sd_bus
*bus
= userdata
;
1838 if (!machine_name_is_valid(local
)) {
1839 log_error("Machine name %s is not valid.", local
);
1845 if (isempty(path
) || streq(path
, "-"))
1849 determine_compression_from_filename(path
);
1851 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1853 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1856 r
= sd_bus_message_new_method_call(
1859 "org.freedesktop.import1",
1860 "/org/freedesktop/import1",
1861 "org.freedesktop.import1.Manager",
1864 return bus_log_create_error(r
);
1866 r
= sd_bus_message_append(
1870 fd
>= 0 ? fd
: STDOUT_FILENO
,
1873 return bus_log_create_error(r
);
1875 return transfer_image_common(bus
, m
);
1878 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
1879 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1880 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1881 const char *local
, *remote
;
1882 sd_bus
*bus
= userdata
;
1888 if (!http_url_is_valid(remote
)) {
1889 log_error("URL '%s' is not valid.", remote
);
1896 r
= import_url_last_component(remote
, &l
);
1898 return log_error_errno(r
, "Failed to get final component of URL: %m");
1903 if (isempty(local
) || streq(local
, "-"))
1907 r
= tar_strip_suffixes(local
, &ll
);
1913 if (!machine_name_is_valid(local
)) {
1914 log_error("Local name %s is not a suitable machine name.", local
);
1919 r
= sd_bus_message_new_method_call(
1922 "org.freedesktop.import1",
1923 "/org/freedesktop/import1",
1924 "org.freedesktop.import1.Manager",
1927 return bus_log_create_error(r
);
1929 r
= sd_bus_message_append(
1934 import_verify_to_string(arg_verify
),
1937 return bus_log_create_error(r
);
1939 return transfer_image_common(bus
, m
);
1942 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
1943 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1944 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1945 const char *local
, *remote
;
1946 sd_bus
*bus
= userdata
;
1952 if (!http_url_is_valid(remote
)) {
1953 log_error("URL '%s' is not valid.", remote
);
1960 r
= import_url_last_component(remote
, &l
);
1962 return log_error_errno(r
, "Failed to get final component of URL: %m");
1967 if (isempty(local
) || streq(local
, "-"))
1971 r
= raw_strip_suffixes(local
, &ll
);
1977 if (!machine_name_is_valid(local
)) {
1978 log_error("Local name %s is not a suitable machine name.", local
);
1983 r
= sd_bus_message_new_method_call(
1986 "org.freedesktop.import1",
1987 "/org/freedesktop/import1",
1988 "org.freedesktop.import1.Manager",
1991 return bus_log_create_error(r
);
1993 r
= sd_bus_message_append(
1998 import_verify_to_string(arg_verify
),
2001 return bus_log_create_error(r
);
2003 return transfer_image_common(bus
, m
);
2006 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2007 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2008 const char *local
, *remote
, *tag
;
2009 sd_bus
*bus
= userdata
;
2012 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2013 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2018 tag
= strchr(remote
, ':');
2020 remote
= strndupa(remote
, tag
- remote
);
2024 if (!dkr_name_is_valid(remote
)) {
2025 log_error("DKR name '%s' is invalid.", remote
);
2028 if (tag
&& !dkr_tag_is_valid(tag
)) {
2029 log_error("DKR tag '%s' is invalid.", remote
);
2036 local
= strchr(remote
, '/');
2043 if (isempty(local
) || streq(local
, "-"))
2047 if (!machine_name_is_valid(local
)) {
2048 log_error("Local name %s is not a suitable machine name.", local
);
2053 r
= sd_bus_message_new_method_call(
2056 "org.freedesktop.import1",
2057 "/org/freedesktop/import1",
2058 "org.freedesktop.import1.Manager",
2061 return bus_log_create_error(r
);
2063 r
= sd_bus_message_append(
2070 import_verify_to_string(arg_verify
),
2073 return bus_log_create_error(r
);
2075 return transfer_image_common(bus
, m
);
2078 typedef struct TransferInfo
{
2086 static int compare_transfer_info(const void *a
, const void *b
) {
2087 const TransferInfo
*x
= a
, *y
= b
;
2089 return strcmp(x
->local
, y
->local
);
2092 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2093 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2094 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2095 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2096 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2097 size_t n_transfers
= 0, n_allocated
= 0, j
;
2098 const char *type
, *remote
, *local
, *object
;
2099 sd_bus
*bus
= userdata
;
2100 uint32_t id
, max_id
= 0;
2104 pager_open_if_enabled();
2106 r
= sd_bus_call_method(
2108 "org.freedesktop.import1",
2109 "/org/freedesktop/import1",
2110 "org.freedesktop.import1.Manager",
2116 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2120 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2122 return bus_log_parse_error(r
);
2124 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2127 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2130 transfers
[n_transfers
].id
= id
;
2131 transfers
[n_transfers
].type
= type
;
2132 transfers
[n_transfers
].remote
= remote
;
2133 transfers
[n_transfers
].local
= local
;
2134 transfers
[n_transfers
].progress
= progress
;
2154 return bus_log_parse_error(r
);
2156 r
= sd_bus_message_exit_container(reply
);
2158 return bus_log_parse_error(r
);
2160 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2163 printf("%-*s %-*s %-*s %-*s %-*s\n",
2164 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2166 (int) max_type
, "TYPE",
2167 (int) max_local
, "LOCAL",
2168 (int) max_remote
, "REMOTE");
2170 for (j
= 0; j
< n_transfers
; j
++)
2171 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2172 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2173 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2174 (int) max_type
, transfers
[j
].type
,
2175 (int) max_local
, transfers
[j
].local
,
2176 (int) max_remote
, transfers
[j
].remote
);
2179 printf("\n%zu transfers listed.\n", n_transfers
);
2184 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2185 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2186 sd_bus
*bus
= userdata
;
2191 polkit_agent_open_if_enabled();
2193 for (i
= 1; i
< argc
; i
++) {
2196 r
= safe_atou32(argv
[i
], &id
);
2198 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2200 r
= sd_bus_call_method(
2202 "org.freedesktop.import1",
2203 "/org/freedesktop/import1",
2204 "org.freedesktop.import1.Manager",
2210 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2218 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2219 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2220 sd_bus
*bus
= userdata
;
2224 if (streq(argv
[argc
-1], "-"))
2225 limit
= (uint64_t) -1;
2229 r
= parse_size(argv
[argc
-1], 1024, &off
);
2231 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2233 limit
= (uint64_t) off
;
2237 /* With two arguments changes the quota limit of the
2238 * specified image */
2239 r
= sd_bus_call_method(
2241 "org.freedesktop.machine1",
2242 "/org/freedesktop/machine1",
2243 "org.freedesktop.machine1.Manager",
2247 "st", argv
[1], limit
);
2249 /* With one argument changes the pool quota limit */
2250 r
= sd_bus_call_method(
2252 "org.freedesktop.machine1",
2253 "/org/freedesktop/machine1",
2254 "org.freedesktop.machine1.Manager",
2261 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2268 static int help(int argc
, char *argv
[], void *userdata
) {
2270 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2271 "Send control commands to or query the virtual machine and container\n"
2272 "registration manager.\n\n"
2273 " -h --help Show this help\n"
2274 " --version Show package version\n"
2275 " --no-pager Do not pipe output into a pager\n"
2276 " --no-legend Do not show the headers and footers\n"
2277 " --no-ask-password Do not ask for system passwords\n"
2278 " -H --host=[USER@]HOST Operate on remote host\n"
2279 " -M --machine=CONTAINER Operate on local container\n"
2280 " -p --property=NAME Show only properties by this name\n"
2281 " -q --quiet Suppress output\n"
2282 " -a --all Show all properties, including empty ones\n"
2283 " -l --full Do not ellipsize output\n"
2284 " --kill-who=WHO Who to send signal to\n"
2285 " -s --signal=SIGNAL Which signal to send\n"
2286 " --read-only Create read-only bind mount\n"
2287 " --mkdir Create directory before bind mounting, if missing\n"
2288 " -n --lines=INTEGER Number of journal entries to show\n"
2289 " -o --output=STRING Change journal output mode (short,\n"
2290 " short-monotonic, verbose, export, json,\n"
2291 " json-pretty, json-sse, cat)\n"
2292 " --verify=MODE Verification mode for downloaded images (no,\n"
2293 " checksum, signature)\n"
2294 " --force Download image even if already exists\n"
2295 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2297 "Machine Commands:\n"
2298 " list List running VMs and containers\n"
2299 " status NAME... Show VM/container details\n"
2300 " show NAME... Show properties of one or more VMs/containers\n"
2301 " start NAME... Start container as a service\n"
2302 " login NAME Get a login prompt on a container\n"
2303 " enable NAME... Enable automatic container start at boot\n"
2304 " disable NAME... Disable automatic container start at boot\n"
2305 " poweroff NAME... Power off one or more containers\n"
2306 " reboot NAME... Reboot one or more containers\n"
2307 " terminate NAME... Terminate one or more VMs/containers\n"
2308 " kill NAME... Send signal to processes of a VM/container\n"
2309 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2310 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2311 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2313 " list-images Show available container and VM images\n"
2314 " image-status NAME... Show image details\n"
2315 " show-image NAME... Show properties of image\n"
2316 " clone NAME NAME Clone an image\n"
2317 " rename NAME NAME Rename an image\n"
2318 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2319 " remove NAME... Remove an image\n"
2320 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2321 "Image Transfer Commands:\n"
2322 " pull-tar URL [NAME] Download a TAR container image\n"
2323 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2324 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2325 " import-tar FILE [NAME] Import a local TAR container image\n"
2326 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2327 " export-tar NAME [FILE] Export a TAR container image locally\n"
2328 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2329 " list-transfers Show list of downloads in progress\n"
2330 " cancel-transfer Cancel a download\n"
2331 , program_invocation_short_name
);
2336 static int parse_argv(int argc
, char *argv
[]) {
2339 ARG_VERSION
= 0x100,
2345 ARG_NO_ASK_PASSWORD
,
2352 static const struct option options
[] = {
2353 { "help", no_argument
, NULL
, 'h' },
2354 { "version", no_argument
, NULL
, ARG_VERSION
},
2355 { "property", required_argument
, NULL
, 'p' },
2356 { "all", no_argument
, NULL
, 'a' },
2357 { "full", no_argument
, NULL
, 'l' },
2358 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2359 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2360 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2361 { "signal", required_argument
, NULL
, 's' },
2362 { "host", required_argument
, NULL
, 'H' },
2363 { "machine", required_argument
, NULL
, 'M' },
2364 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2365 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2366 { "quiet", no_argument
, NULL
, 'q' },
2367 { "lines", required_argument
, NULL
, 'n' },
2368 { "output", required_argument
, NULL
, 'o' },
2369 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2370 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2371 { "force", no_argument
, NULL
, ARG_FORCE
},
2372 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2373 { "format", required_argument
, NULL
, ARG_FORMAT
},
2382 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2387 return help(0, NULL
, NULL
);
2390 puts(PACKAGE_STRING
);
2391 puts(SYSTEMD_FEATURES
);
2395 r
= strv_extend(&arg_property
, optarg
);
2399 /* If the user asked for a particular
2400 * property, show it to him, even if it is
2414 if (safe_atou(optarg
, &arg_lines
) < 0) {
2415 log_error("Failed to parse lines '%s'", optarg
);
2421 arg_output
= output_mode_from_string(optarg
);
2422 if (arg_output
< 0) {
2423 log_error("Unknown output '%s'.", optarg
);
2429 arg_no_pager
= true;
2437 arg_kill_who
= optarg
;
2441 arg_signal
= signal_from_string_try_harder(optarg
);
2442 if (arg_signal
< 0) {
2443 log_error("Failed to parse signal string %s.", optarg
);
2448 case ARG_NO_ASK_PASSWORD
:
2449 arg_ask_password
= false;
2453 arg_transport
= BUS_TRANSPORT_REMOTE
;
2458 arg_transport
= BUS_TRANSPORT_MACHINE
;
2463 arg_read_only
= true;
2475 arg_verify
= import_verify_from_string(optarg
);
2476 if (arg_verify
< 0) {
2477 log_error("Failed to parse --verify= setting: %s", optarg
);
2486 case ARG_DKR_INDEX_URL
:
2487 if (!http_url_is_valid(optarg
)) {
2488 log_error("Index URL is invalid: %s", optarg
);
2492 arg_dkr_index_url
= optarg
;
2496 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2497 log_error("Unknown format: %s", optarg
);
2501 arg_format
= optarg
;
2508 assert_not_reached("Unhandled option");
2514 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2516 static const Verb verbs
[] = {
2517 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2518 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2519 { "list-images", VERB_ANY
, 1, 0, list_images
},
2520 { "status", 2, VERB_ANY
, 0, show_machine
},
2521 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2522 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2523 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2524 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2525 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2526 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2527 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2528 { "login", 2, 2, 0, login_machine
},
2529 { "bind", 3, 4, 0, bind_mount
},
2530 { "copy-to", 3, 4, 0, copy_files
},
2531 { "copy-from", 3, 4, 0, copy_files
},
2532 { "remove", 2, VERB_ANY
, 0, remove_image
},
2533 { "rename", 3, 3, 0, rename_image
},
2534 { "clone", 3, 3, 0, clone_image
},
2535 { "read-only", 2, 3, 0, read_only_image
},
2536 { "start", 2, VERB_ANY
, 0, start_machine
},
2537 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2538 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2539 { "import-tar", 2, 3, 0, import_tar
},
2540 { "import-raw", 2, 3, 0, import_raw
},
2541 { "export-tar", 2, 3, 0, export_tar
},
2542 { "export-raw", 2, 3, 0, export_raw
},
2543 { "pull-tar", 2, 3, 0, pull_tar
},
2544 { "pull-raw", 2, 3, 0, pull_raw
},
2545 { "pull-dkr", 2, 3, 0, pull_dkr
},
2546 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2547 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2548 { "set-limit", 2, 3, 0, set_limit
},
2552 return dispatch_verb(argc
, argv
, verbs
, bus
);
2555 int main(int argc
, char*argv
[]) {
2556 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
2559 setlocale(LC_ALL
, "");
2560 log_parse_environment();
2563 r
= parse_argv(argc
, argv
);
2567 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2569 log_error_errno(r
, "Failed to create bus connection: %m");
2573 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2575 r
= machinectl_main(argc
, argv
, bus
);
2579 polkit_agent_close();
2581 strv_free(arg_property
);
2583 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;