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"
57 #include "signal-util.h"
59 static char **arg_property
= NULL
;
60 static bool arg_all
= false;
61 static bool arg_full
= false;
62 static bool arg_no_pager
= false;
63 static bool arg_legend
= true;
64 static const char *arg_kill_who
= NULL
;
65 static int arg_signal
= SIGTERM
;
66 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
67 static char *arg_host
= NULL
;
68 static bool arg_read_only
= false;
69 static bool arg_mkdir
= false;
70 static bool arg_quiet
= false;
71 static bool arg_ask_password
= true;
72 static unsigned arg_lines
= 10;
73 static OutputMode arg_output
= OUTPUT_SHORT
;
74 static bool arg_force
= false;
75 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
76 static const char* arg_dkr_index_url
= NULL
;
77 static const char* arg_format
= NULL
;
79 static void pager_open_if_enabled(void) {
87 static void polkit_agent_open_if_enabled(void) {
89 /* Open the polkit agent as a child process if necessary */
91 if (!arg_ask_password
)
94 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
100 static OutputFlags
get_output_flags(void) {
102 arg_all
* OUTPUT_SHOW_ALL
|
103 arg_full
* OUTPUT_FULL_WIDTH
|
104 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
105 on_tty() * OUTPUT_COLOR
|
106 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
109 typedef struct MachineInfo
{
115 static int compare_machine_info(const void *a
, const void *b
) {
116 const MachineInfo
*x
= a
, *y
= b
;
118 return strcmp(x
->name
, y
->name
);
121 static int list_machines(int argc
, char *argv
[], void *userdata
) {
123 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
124 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
125 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
126 _cleanup_free_ MachineInfo
*machines
= NULL
;
127 const char *name
, *class, *service
, *object
;
128 size_t n_machines
= 0, n_allocated
= 0, j
;
129 sd_bus
*bus
= userdata
;
134 pager_open_if_enabled();
136 r
= sd_bus_call_method(
138 "org.freedesktop.machine1",
139 "/org/freedesktop/machine1",
140 "org.freedesktop.machine1.Manager",
146 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
150 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
152 return bus_log_parse_error(r
);
154 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
157 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
160 machines
[n_machines
].name
= name
;
161 machines
[n_machines
].class = class;
162 machines
[n_machines
].service
= service
;
179 return bus_log_parse_error(r
);
181 r
= sd_bus_message_exit_container(reply
);
183 return bus_log_parse_error(r
);
185 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
188 printf("%-*s %-*s %-*s\n",
189 (int) max_name
, "MACHINE",
190 (int) max_class
, "CLASS",
191 (int) max_service
, "SERVICE");
193 for (j
= 0; j
< n_machines
; j
++)
194 printf("%-*s %-*s %-*s\n",
195 (int) max_name
, machines
[j
].name
,
196 (int) max_class
, machines
[j
].class,
197 (int) max_service
, machines
[j
].service
);
200 printf("\n%zu machines listed.\n", n_machines
);
205 typedef struct ImageInfo
{
214 static int compare_image_info(const void *a
, const void *b
) {
215 const ImageInfo
*x
= a
, *y
= b
;
217 return strcmp(x
->name
, y
->name
);
220 static int list_images(int argc
, char *argv
[], void *userdata
) {
222 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
223 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
224 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
225 _cleanup_free_ ImageInfo
*images
= NULL
;
226 size_t n_images
= 0, n_allocated
= 0, j
;
227 const char *name
, *type
, *object
;
228 sd_bus
*bus
= userdata
;
229 uint64_t crtime
, mtime
, size
;
234 pager_open_if_enabled();
236 r
= sd_bus_call_method(
238 "org.freedesktop.machine1",
239 "/org/freedesktop/machine1",
240 "org.freedesktop.machine1.Manager",
246 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
250 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
252 return bus_log_parse_error(r
);
254 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
255 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
258 if (name
[0] == '.' && !arg_all
)
261 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
264 images
[n_images
].name
= name
;
265 images
[n_images
].type
= type
;
266 images
[n_images
].read_only
= read_only
;
267 images
[n_images
].crtime
= crtime
;
268 images
[n_images
].mtime
= mtime
;
269 images
[n_images
].size
= size
;
280 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
286 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
291 if (size
!= (uint64_t) -1) {
292 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
300 return bus_log_parse_error(r
);
302 r
= sd_bus_message_exit_container(reply
);
304 return bus_log_parse_error(r
);
306 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
309 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
310 (int) max_name
, "NAME",
311 (int) max_type
, "TYPE",
313 (int) max_size
, "USAGE",
314 (int) max_crtime
, "CREATED",
315 (int) max_mtime
, "MODIFIED");
317 for (j
= 0; j
< n_images
; j
++) {
318 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
320 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
321 (int) max_name
, images
[j
].name
,
322 (int) max_type
, images
[j
].type
,
323 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_highlight_off() : "",
324 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
325 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
326 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
330 printf("\n%zu images listed.\n", n_images
);
335 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
336 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
337 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
338 _cleanup_free_
char *path
= NULL
;
346 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
349 path
= unit_dbus_path_from_name(unit
);
353 r
= sd_bus_get_property(
355 "org.freedesktop.systemd1",
357 endswith(unit
, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
363 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
367 r
= sd_bus_message_read(reply
, "s", &cgroup
);
369 return bus_log_parse_error(r
);
374 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, false) != 0 && leader
<= 0)
383 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
387 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
388 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
396 r
= sd_bus_call_method(bus
,
397 "org.freedesktop.machine1",
398 "/org/freedesktop/machine1",
399 "org.freedesktop.machine1.Manager",
400 "GetMachineAddresses",
407 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
409 return bus_log_parse_error(r
);
411 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
415 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
417 r
= sd_bus_message_read(reply
, "i", &family
);
419 return bus_log_parse_error(r
);
421 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
423 return bus_log_parse_error(r
);
425 fputs(prefix
, stdout
);
426 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
427 if (family
== AF_INET6
&& ifi
> 0)
431 r
= sd_bus_message_exit_container(reply
);
433 return bus_log_parse_error(r
);
435 if (prefix
!= prefix2
)
439 return bus_log_parse_error(r
);
441 r
= sd_bus_message_exit_container(reply
);
443 return bus_log_parse_error(r
);
448 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
449 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
450 const char *k
, *v
, *pretty
= NULL
;
457 r
= sd_bus_call_method(bus
,
458 "org.freedesktop.machine1",
459 "/org/freedesktop/machine1",
460 "org.freedesktop.machine1.Manager",
461 "GetMachineOSRelease",
468 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
470 return bus_log_parse_error(r
);
472 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
473 if (streq(k
, "PRETTY_NAME"))
478 return bus_log_parse_error(r
);
480 r
= sd_bus_message_exit_container(reply
);
482 return bus_log_parse_error(r
);
485 printf("%s%s\n", prefix
, pretty
);
490 typedef struct MachineStatusInfo
{
496 char *root_directory
;
498 struct dual_timestamp timestamp
;
503 static void machine_status_info_clear(MachineStatusInfo
*info
) {
509 free(info
->root_directory
);
515 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
516 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
517 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
523 fputs(strna(i
->name
), stdout
);
525 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
526 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
530 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
531 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
534 printf("\t Since: %s; %s\n", s2
, s1
);
536 printf("\t Since: %s\n", s2
);
539 _cleanup_free_
char *t
= NULL
;
541 printf("\t Leader: %u", (unsigned) i
->leader
);
543 get_process_comm(i
->leader
, &t
);
551 printf("\t Service: %s", i
->service
);
554 printf("; class %s", i
->class);
558 printf("\t Class: %s\n", i
->class);
560 if (i
->root_directory
)
561 printf("\t Root: %s\n", i
->root_directory
);
563 if (i
->n_netif
> 0) {
566 fputs("\t Iface:", stdout
);
568 for (c
= 0; c
< i
->n_netif
; c
++) {
569 char name
[IF_NAMESIZE
+1] = "";
571 if (if_indextoname(i
->netif
[c
], name
)) {
580 printf(" %i", i
->netif
[c
]);
586 print_addresses(bus
, i
->name
, ifi
,
590 print_os_release(bus
, i
->name
, "\t OS: ");
593 printf("\t Unit: %s\n", i
->unit
);
594 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
596 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
598 show_journal_by_unit(
603 i
->timestamp
.monotonic
,
606 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
607 SD_JOURNAL_LOCAL_ONLY
,
614 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
615 MachineStatusInfo
*i
= userdata
;
620 assert_cc(sizeof(int32_t) == sizeof(int));
621 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
627 i
->n_netif
= l
/ sizeof(int32_t);
628 i
->netif
= memdup(v
, l
);
635 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
637 static const struct bus_properties_map map
[] = {
638 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
639 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
640 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
641 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
642 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
643 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
644 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
645 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
646 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
647 { "NetworkInterfaces", "ai", map_netif
, 0 },
651 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
659 r
= bus_map_all_properties(bus
,
660 "org.freedesktop.machine1",
665 return log_error_errno(r
, "Could not get properties: %m");
671 print_machine_status_info(bus
, &info
);
676 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
688 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
690 log_error_errno(r
, "Could not get properties: %m");
695 static int show_machine(int argc
, char *argv
[], void *userdata
) {
697 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
698 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
699 bool properties
, new_line
= false;
700 sd_bus
*bus
= userdata
;
705 properties
= !strstr(argv
[0], "status");
707 pager_open_if_enabled();
709 if (properties
&& argc
<= 1) {
711 /* If no argument is specified, inspect the manager
713 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
718 for (i
= 1; i
< argc
; i
++) {
719 const char *path
= NULL
;
721 r
= sd_bus_call_method(
723 "org.freedesktop.machine1",
724 "/org/freedesktop/machine1",
725 "org.freedesktop.machine1.Manager",
731 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
735 r
= sd_bus_message_read(reply
, "o", &path
);
737 return bus_log_parse_error(r
);
740 r
= show_machine_properties(bus
, path
, &new_line
);
742 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
748 typedef struct ImageStatusInfo
{
757 uint64_t usage_exclusive
;
758 uint64_t limit_exclusive
;
761 static void image_status_info_clear(ImageStatusInfo
*info
) {
770 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
771 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
772 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
773 char bs
[FORMAT_BYTES_MAX
], *s3
;
774 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
780 fputs(i
->name
, stdout
);
785 printf("\t Type: %s\n", i
->type
);
788 printf("\t Path: %s\n", i
->path
);
790 printf("\t RO: %s%s%s\n",
791 i
->read_only
? ansi_highlight_red() : "",
792 i
->read_only
? "read-only" : "writable",
793 i
->read_only
? ansi_highlight_off() : "");
795 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
796 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
798 printf("\t Created: %s; %s\n", s2
, s1
);
800 printf("\t Created: %s\n", s2
);
802 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
803 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
805 printf("\tModified: %s; %s\n", s2
, s1
);
807 printf("\tModified: %s\n", s2
);
809 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
810 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
812 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
814 printf("\t Usage: %s\n", s3
);
816 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
817 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
819 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
821 printf("\t Limit: %s\n", s3
);
824 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
826 static const struct bus_properties_map map
[] = {
827 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
828 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
829 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
830 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
831 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
832 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
833 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
834 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
835 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
836 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
840 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
847 r
= bus_map_all_properties(bus
,
848 "org.freedesktop.machine1",
853 return log_error_errno(r
, "Could not get properties: %m");
859 print_image_status_info(bus
, &info
);
864 typedef struct PoolStatusInfo
{
870 static void pool_status_info_clear(PoolStatusInfo
*info
) {
879 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
880 char bs
[FORMAT_BYTES_MAX
], *s
;
883 printf("\t Path: %s\n", i
->path
);
885 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
887 printf("\t Usage: %s\n", s
);
889 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
891 printf("\t Limit: %s\n", s
);
894 static int show_pool_info(sd_bus
*bus
) {
896 static const struct bus_properties_map map
[] = {
897 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
898 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
899 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
903 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
904 .usage
= (uint64_t) -1,
905 .limit
= (uint64_t) -1,
911 r
= bus_map_all_properties(bus
,
912 "org.freedesktop.machine1",
913 "/org/freedesktop/machine1",
917 return log_error_errno(r
, "Could not get properties: %m");
919 print_pool_status_info(bus
, &info
);
925 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
937 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
939 log_error_errno(r
, "Could not get properties: %m");
944 static int show_image(int argc
, char *argv
[], void *userdata
) {
946 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
947 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
948 bool properties
, new_line
= false;
949 sd_bus
*bus
= userdata
;
954 properties
= !strstr(argv
[0], "status");
956 pager_open_if_enabled();
960 /* If no argument is specified, inspect the manager
964 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
966 r
= show_pool_info(bus
);
971 for (i
= 1; i
< argc
; i
++) {
972 const char *path
= NULL
;
974 r
= sd_bus_call_method(
976 "org.freedesktop.machine1",
977 "/org/freedesktop/machine1",
978 "org.freedesktop.machine1.Manager",
984 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
988 r
= sd_bus_message_read(reply
, "o", &path
);
990 return bus_log_parse_error(r
);
993 r
= show_image_properties(bus
, path
, &new_line
);
995 r
= show_image_info(bus
, path
, &new_line
);
1001 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1002 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1003 sd_bus
*bus
= userdata
;
1008 polkit_agent_open_if_enabled();
1011 arg_kill_who
= "all";
1013 for (i
= 1; i
< argc
; i
++) {
1014 r
= sd_bus_call_method(
1016 "org.freedesktop.machine1",
1017 "/org/freedesktop/machine1",
1018 "org.freedesktop.machine1.Manager",
1022 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1024 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1032 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1033 arg_kill_who
= "leader";
1034 arg_signal
= SIGINT
; /* sysvinit + systemd */
1036 return kill_machine(argc
, argv
, userdata
);
1039 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1040 arg_kill_who
= "leader";
1041 arg_signal
= SIGRTMIN
+4; /* only systemd */
1043 return kill_machine(argc
, argv
, userdata
);
1046 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1047 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1048 sd_bus
*bus
= userdata
;
1053 polkit_agent_open_if_enabled();
1055 for (i
= 1; i
< argc
; i
++) {
1056 r
= sd_bus_call_method(
1058 "org.freedesktop.machine1",
1059 "/org/freedesktop/machine1",
1060 "org.freedesktop.machine1.Manager",
1066 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1074 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1075 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1076 sd_bus
*bus
= userdata
;
1082 polkit_agent_open_if_enabled();
1084 copy_from
= streq(argv
[0], "copy-from");
1086 r
= sd_bus_call_method(
1088 "org.freedesktop.machine1",
1089 "/org/freedesktop/machine1",
1090 "org.freedesktop.machine1.Manager",
1091 copy_from
? "CopyFromMachine" : "CopyToMachine",
1099 log_error("Failed to copy: %s", bus_error_message(&error
, -r
));
1106 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1107 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1108 sd_bus
*bus
= userdata
;
1113 polkit_agent_open_if_enabled();
1115 r
= sd_bus_call_method(
1117 "org.freedesktop.machine1",
1118 "/org/freedesktop/machine1",
1119 "org.freedesktop.machine1.Manager",
1130 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1137 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1138 PTYForward
** forward
= (PTYForward
**) userdata
;
1145 /* If the forwarder is already initialized, tell it to
1146 * exit on the next vhangup(), so that we still flush
1147 * out what might be queued and exit then. */
1149 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1153 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1156 /* On error, or when the forwarder is not initialized yet, quit immediately */
1157 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1161 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1162 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1163 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1164 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1165 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1166 _cleanup_event_unref_ sd_event
*event
= NULL
;
1167 int master
= -1, r
, ret
= 0;
1168 sd_bus
*bus
= userdata
;
1169 const char *pty
, *match
;
1175 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1176 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1177 log_error("Login only supported on local machines.");
1181 polkit_agent_open_if_enabled();
1183 r
= sd_event_default(&event
);
1185 return log_error_errno(r
, "Failed to get event loop: %m");
1187 r
= sd_bus_attach_event(bus
, event
, 0);
1189 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1191 match
= strjoina("type='signal',"
1192 "sender='org.freedesktop.machine1',"
1193 "path='/org/freedesktop/machine1',",
1194 "interface='org.freedesktop.machine1.Manager',"
1195 "member='MachineRemoved',"
1200 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1202 return log_error_errno(r
, "Failed to add machine removal match: %m");
1204 r
= sd_bus_call_method(
1206 "org.freedesktop.machine1",
1207 "/org/freedesktop/machine1",
1208 "org.freedesktop.machine1.Manager",
1214 log_error("Failed to get machine PTY: %s", bus_error_message(&error
, -r
));
1218 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1220 return bus_log_parse_error(r
);
1222 sigprocmask_many(SIG_BLOCK
, SIGWINCH
, SIGTERM
, SIGINT
, -1);
1224 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv
[1]);
1226 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1227 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1229 r
= pty_forward_new(event
, master
, true, false, &forward
);
1231 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1233 r
= sd_event_loop(event
);
1235 return log_error_errno(r
, "Failed to run event loop: %m");
1237 pty_forward_get_last_char(forward
, &last_char
);
1238 machine_died
= pty_forward_get_ignore_vhangup(forward
) == 0;
1240 forward
= pty_forward_free(forward
);
1242 if (last_char
!= '\n')
1243 fputc('\n', stdout
);
1246 log_info("Machine %s terminated.", argv
[1]);
1248 log_info("Connection to machine %s terminated.", argv
[1]);
1250 sd_event_get_exit_code(event
, &ret
);
1254 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1255 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1256 sd_bus
*bus
= userdata
;
1261 polkit_agent_open_if_enabled();
1263 for (i
= 1; i
< argc
; i
++) {
1264 r
= sd_bus_call_method(
1266 "org.freedesktop.machine1",
1267 "/org/freedesktop/machine1",
1268 "org.freedesktop.machine1.Manager",
1274 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1282 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1283 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1284 sd_bus
*bus
= userdata
;
1287 polkit_agent_open_if_enabled();
1289 r
= sd_bus_call_method(
1291 "org.freedesktop.machine1",
1292 "/org/freedesktop/machine1",
1293 "org.freedesktop.machine1.Manager",
1297 "ss", argv
[1], argv
[2]);
1299 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1306 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1307 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1308 sd_bus
*bus
= userdata
;
1311 polkit_agent_open_if_enabled();
1313 r
= sd_bus_call_method(
1315 "org.freedesktop.machine1",
1316 "/org/freedesktop/machine1",
1317 "org.freedesktop.machine1.Manager",
1321 "ssb", argv
[1], argv
[2], arg_read_only
);
1323 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1330 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1331 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1332 sd_bus
*bus
= userdata
;
1336 b
= parse_boolean(argv
[2]);
1338 log_error("Failed to parse boolean argument: %s", argv
[2]);
1343 polkit_agent_open_if_enabled();
1345 r
= sd_bus_call_method(
1347 "org.freedesktop.machine1",
1348 "/org/freedesktop/machine1",
1349 "org.freedesktop.machine1.Manager",
1350 "MarkImageReadOnly",
1355 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1362 static int make_service_name(const char *name
, char **ret
) {
1363 _cleanup_free_
char *e
= NULL
;
1369 if (!machine_name_is_valid(name
)) {
1370 log_error("Invalid machine name %s.", name
);
1374 e
= unit_name_escape(name
);
1378 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1380 return log_error_errno(r
, "Failed to build unit name: %m");
1385 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1386 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1387 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1388 sd_bus
*bus
= userdata
;
1393 polkit_agent_open_if_enabled();
1395 r
= bus_wait_for_jobs_new(bus
, &w
);
1399 for (i
= 1; i
< argc
; i
++) {
1400 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1401 _cleanup_free_
char *unit
= NULL
;
1404 r
= make_service_name(argv
[i
], &unit
);
1408 r
= sd_bus_call_method(
1410 "org.freedesktop.systemd1",
1411 "/org/freedesktop/systemd1",
1412 "org.freedesktop.systemd1.Manager",
1416 "ss", unit
, "fail");
1418 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1422 r
= sd_bus_message_read(reply
, "o", &object
);
1424 return bus_log_parse_error(r
);
1426 r
= bus_wait_for_jobs_add(w
, object
);
1431 r
= bus_wait_for_jobs(w
, arg_quiet
);
1438 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1439 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1440 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1441 int carries_install_info
= 0;
1442 const char *method
= NULL
;
1443 sd_bus
*bus
= userdata
;
1448 polkit_agent_open_if_enabled();
1450 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1452 r
= sd_bus_message_new_method_call(
1455 "org.freedesktop.systemd1",
1456 "/org/freedesktop/systemd1",
1457 "org.freedesktop.systemd1.Manager",
1460 return bus_log_create_error(r
);
1462 r
= sd_bus_message_open_container(m
, 'a', "s");
1464 return bus_log_create_error(r
);
1466 for (i
= 1; i
< argc
; i
++) {
1467 _cleanup_free_
char *unit
= NULL
;
1469 r
= make_service_name(argv
[i
], &unit
);
1473 r
= sd_bus_message_append(m
, "s", unit
);
1475 return bus_log_create_error(r
);
1478 r
= sd_bus_message_close_container(m
);
1480 return bus_log_create_error(r
);
1482 if (streq(argv
[0], "enable"))
1483 r
= sd_bus_message_append(m
, "bb", false, false);
1485 r
= sd_bus_message_append(m
, "b", false);
1487 return bus_log_create_error(r
);
1489 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1491 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1495 if (streq(argv
[0], "enable")) {
1496 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1498 return bus_log_parse_error(r
);
1501 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1505 r
= sd_bus_call_method(
1507 "org.freedesktop.systemd1",
1508 "/org/freedesktop/systemd1",
1509 "org.freedesktop.systemd1.Manager",
1515 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1522 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1523 const char **our_path
= userdata
, *line
;
1530 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1532 bus_log_parse_error(r
);
1536 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1539 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1542 log_full(priority
, "%s", line
);
1546 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1547 const char **our_path
= userdata
, *path
, *result
;
1554 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1556 bus_log_parse_error(r
);
1560 if (!streq_ptr(*our_path
, path
))
1563 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1567 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1572 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1574 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1578 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1579 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1580 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1581 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1582 _cleanup_event_unref_ sd_event
* event
= NULL
;
1583 const char *path
= NULL
;
1590 polkit_agent_open_if_enabled();
1592 r
= sd_event_default(&event
);
1594 return log_error_errno(r
, "Failed to get event loop: %m");
1596 r
= sd_bus_attach_event(bus
, event
, 0);
1598 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1600 r
= sd_bus_add_match(
1604 "sender='org.freedesktop.import1',"
1605 "interface='org.freedesktop.import1.Manager',"
1606 "member='TransferRemoved',"
1607 "path='/org/freedesktop/import1'",
1608 match_transfer_removed
, &path
);
1610 return log_error_errno(r
, "Failed to install match: %m");
1612 r
= sd_bus_add_match(
1616 "sender='org.freedesktop.import1',"
1617 "interface='org.freedesktop.import1.Transfer',"
1618 "member='LogMessage'",
1619 match_log_message
, &path
);
1621 return log_error_errno(r
, "Failed to install match: %m");
1623 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1625 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1629 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1631 return bus_log_parse_error(r
);
1633 sigprocmask_many(SIG_BLOCK
, SIGTERM
, SIGINT
, -1);
1636 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1638 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1639 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1641 r
= sd_event_loop(event
);
1643 return log_error_errno(r
, "Failed to run event loop: %m");
1648 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1649 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1650 _cleanup_free_
char *ll
= NULL
;
1651 _cleanup_close_
int fd
= -1;
1652 const char *local
= NULL
, *path
= NULL
;
1653 sd_bus
*bus
= userdata
;
1660 if (isempty(path
) || streq(path
, "-"))
1666 local
= basename(path
);
1667 if (isempty(local
) || streq(local
, "-"))
1671 log_error("Need either path or local name.");
1675 r
= tar_strip_suffixes(local
, &ll
);
1681 if (!machine_name_is_valid(local
)) {
1682 log_error("Local name %s is not a suitable machine name.", local
);
1687 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1689 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1692 r
= sd_bus_message_new_method_call(
1695 "org.freedesktop.import1",
1696 "/org/freedesktop/import1",
1697 "org.freedesktop.import1.Manager",
1700 return bus_log_create_error(r
);
1702 r
= sd_bus_message_append(
1705 fd
>= 0 ? fd
: STDIN_FILENO
,
1710 return bus_log_create_error(r
);
1712 return transfer_image_common(bus
, m
);
1715 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1716 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1717 _cleanup_free_
char *ll
= NULL
;
1718 _cleanup_close_
int fd
= -1;
1719 const char *local
= NULL
, *path
= NULL
;
1720 sd_bus
*bus
= userdata
;
1727 if (isempty(path
) || streq(path
, "-"))
1733 local
= basename(path
);
1734 if (isempty(local
) || streq(local
, "-"))
1738 log_error("Need either path or local name.");
1742 r
= raw_strip_suffixes(local
, &ll
);
1748 if (!machine_name_is_valid(local
)) {
1749 log_error("Local name %s is not a suitable machine name.", local
);
1754 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1756 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1759 r
= sd_bus_message_new_method_call(
1762 "org.freedesktop.import1",
1763 "/org/freedesktop/import1",
1764 "org.freedesktop.import1.Manager",
1767 return bus_log_create_error(r
);
1769 r
= sd_bus_message_append(
1772 fd
>= 0 ? fd
: STDIN_FILENO
,
1777 return bus_log_create_error(r
);
1779 return transfer_image_common(bus
, m
);
1782 static void determine_compression_from_filename(const char *p
) {
1789 if (endswith(p
, ".xz"))
1791 else if (endswith(p
, ".gz"))
1792 arg_format
= "gzip";
1793 else if (endswith(p
, ".bz2"))
1794 arg_format
= "bzip2";
1797 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1798 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1799 _cleanup_close_
int fd
= -1;
1800 const char *local
= NULL
, *path
= NULL
;
1801 sd_bus
*bus
= userdata
;
1807 if (!machine_name_is_valid(local
)) {
1808 log_error("Machine name %s is not valid.", local
);
1814 if (isempty(path
) || streq(path
, "-"))
1818 determine_compression_from_filename(path
);
1820 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1822 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1825 r
= sd_bus_message_new_method_call(
1828 "org.freedesktop.import1",
1829 "/org/freedesktop/import1",
1830 "org.freedesktop.import1.Manager",
1833 return bus_log_create_error(r
);
1835 r
= sd_bus_message_append(
1839 fd
>= 0 ? fd
: STDOUT_FILENO
,
1842 return bus_log_create_error(r
);
1844 return transfer_image_common(bus
, m
);
1847 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1848 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1849 _cleanup_close_
int fd
= -1;
1850 const char *local
= NULL
, *path
= NULL
;
1851 sd_bus
*bus
= userdata
;
1857 if (!machine_name_is_valid(local
)) {
1858 log_error("Machine name %s is not valid.", local
);
1864 if (isempty(path
) || streq(path
, "-"))
1868 determine_compression_from_filename(path
);
1870 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1872 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1875 r
= sd_bus_message_new_method_call(
1878 "org.freedesktop.import1",
1879 "/org/freedesktop/import1",
1880 "org.freedesktop.import1.Manager",
1883 return bus_log_create_error(r
);
1885 r
= sd_bus_message_append(
1889 fd
>= 0 ? fd
: STDOUT_FILENO
,
1892 return bus_log_create_error(r
);
1894 return transfer_image_common(bus
, m
);
1897 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
1898 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1899 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1900 const char *local
, *remote
;
1901 sd_bus
*bus
= userdata
;
1907 if (!http_url_is_valid(remote
)) {
1908 log_error("URL '%s' is not valid.", remote
);
1915 r
= import_url_last_component(remote
, &l
);
1917 return log_error_errno(r
, "Failed to get final component of URL: %m");
1922 if (isempty(local
) || streq(local
, "-"))
1926 r
= tar_strip_suffixes(local
, &ll
);
1932 if (!machine_name_is_valid(local
)) {
1933 log_error("Local name %s is not a suitable machine name.", local
);
1938 r
= sd_bus_message_new_method_call(
1941 "org.freedesktop.import1",
1942 "/org/freedesktop/import1",
1943 "org.freedesktop.import1.Manager",
1946 return bus_log_create_error(r
);
1948 r
= sd_bus_message_append(
1953 import_verify_to_string(arg_verify
),
1956 return bus_log_create_error(r
);
1958 return transfer_image_common(bus
, m
);
1961 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
1962 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1963 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1964 const char *local
, *remote
;
1965 sd_bus
*bus
= userdata
;
1971 if (!http_url_is_valid(remote
)) {
1972 log_error("URL '%s' is not valid.", remote
);
1979 r
= import_url_last_component(remote
, &l
);
1981 return log_error_errno(r
, "Failed to get final component of URL: %m");
1986 if (isempty(local
) || streq(local
, "-"))
1990 r
= raw_strip_suffixes(local
, &ll
);
1996 if (!machine_name_is_valid(local
)) {
1997 log_error("Local name %s is not a suitable machine name.", local
);
2002 r
= sd_bus_message_new_method_call(
2005 "org.freedesktop.import1",
2006 "/org/freedesktop/import1",
2007 "org.freedesktop.import1.Manager",
2010 return bus_log_create_error(r
);
2012 r
= sd_bus_message_append(
2017 import_verify_to_string(arg_verify
),
2020 return bus_log_create_error(r
);
2022 return transfer_image_common(bus
, m
);
2025 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2026 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2027 const char *local
, *remote
, *tag
;
2028 sd_bus
*bus
= userdata
;
2031 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2032 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2037 tag
= strchr(remote
, ':');
2039 remote
= strndupa(remote
, tag
- remote
);
2043 if (!dkr_name_is_valid(remote
)) {
2044 log_error("DKR name '%s' is invalid.", remote
);
2047 if (tag
&& !dkr_tag_is_valid(tag
)) {
2048 log_error("DKR tag '%s' is invalid.", remote
);
2055 local
= strchr(remote
, '/');
2062 if (isempty(local
) || streq(local
, "-"))
2066 if (!machine_name_is_valid(local
)) {
2067 log_error("Local name %s is not a suitable machine name.", local
);
2072 r
= sd_bus_message_new_method_call(
2075 "org.freedesktop.import1",
2076 "/org/freedesktop/import1",
2077 "org.freedesktop.import1.Manager",
2080 return bus_log_create_error(r
);
2082 r
= sd_bus_message_append(
2089 import_verify_to_string(arg_verify
),
2092 return bus_log_create_error(r
);
2094 return transfer_image_common(bus
, m
);
2097 typedef struct TransferInfo
{
2105 static int compare_transfer_info(const void *a
, const void *b
) {
2106 const TransferInfo
*x
= a
, *y
= b
;
2108 return strcmp(x
->local
, y
->local
);
2111 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2112 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2113 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2114 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2115 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2116 size_t n_transfers
= 0, n_allocated
= 0, j
;
2117 const char *type
, *remote
, *local
, *object
;
2118 sd_bus
*bus
= userdata
;
2119 uint32_t id
, max_id
= 0;
2123 pager_open_if_enabled();
2125 r
= sd_bus_call_method(
2127 "org.freedesktop.import1",
2128 "/org/freedesktop/import1",
2129 "org.freedesktop.import1.Manager",
2135 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2139 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2141 return bus_log_parse_error(r
);
2143 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2146 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2149 transfers
[n_transfers
].id
= id
;
2150 transfers
[n_transfers
].type
= type
;
2151 transfers
[n_transfers
].remote
= remote
;
2152 transfers
[n_transfers
].local
= local
;
2153 transfers
[n_transfers
].progress
= progress
;
2173 return bus_log_parse_error(r
);
2175 r
= sd_bus_message_exit_container(reply
);
2177 return bus_log_parse_error(r
);
2179 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2182 printf("%-*s %-*s %-*s %-*s %-*s\n",
2183 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2185 (int) max_type
, "TYPE",
2186 (int) max_local
, "LOCAL",
2187 (int) max_remote
, "REMOTE");
2189 for (j
= 0; j
< n_transfers
; j
++)
2190 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2191 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2192 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2193 (int) max_type
, transfers
[j
].type
,
2194 (int) max_local
, transfers
[j
].local
,
2195 (int) max_remote
, transfers
[j
].remote
);
2198 printf("\n%zu transfers listed.\n", n_transfers
);
2203 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2204 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2205 sd_bus
*bus
= userdata
;
2210 polkit_agent_open_if_enabled();
2212 for (i
= 1; i
< argc
; i
++) {
2215 r
= safe_atou32(argv
[i
], &id
);
2217 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2219 r
= sd_bus_call_method(
2221 "org.freedesktop.import1",
2222 "/org/freedesktop/import1",
2223 "org.freedesktop.import1.Manager",
2229 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2237 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2238 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2239 sd_bus
*bus
= userdata
;
2243 if (streq(argv
[argc
-1], "-"))
2244 limit
= (uint64_t) -1;
2248 r
= parse_size(argv
[argc
-1], 1024, &off
);
2250 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2252 limit
= (uint64_t) off
;
2256 /* With two arguments changes the quota limit of the
2257 * specified image */
2258 r
= sd_bus_call_method(
2260 "org.freedesktop.machine1",
2261 "/org/freedesktop/machine1",
2262 "org.freedesktop.machine1.Manager",
2266 "st", argv
[1], limit
);
2268 /* With one argument changes the pool quota limit */
2269 r
= sd_bus_call_method(
2271 "org.freedesktop.machine1",
2272 "/org/freedesktop/machine1",
2273 "org.freedesktop.machine1.Manager",
2280 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2287 static int help(int argc
, char *argv
[], void *userdata
) {
2289 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2290 "Send control commands to or query the virtual machine and container\n"
2291 "registration manager.\n\n"
2292 " -h --help Show this help\n"
2293 " --version Show package version\n"
2294 " --no-pager Do not pipe output into a pager\n"
2295 " --no-legend Do not show the headers and footers\n"
2296 " --no-ask-password Do not ask for system passwords\n"
2297 " -H --host=[USER@]HOST Operate on remote host\n"
2298 " -M --machine=CONTAINER Operate on local container\n"
2299 " -p --property=NAME Show only properties by this name\n"
2300 " -q --quiet Suppress output\n"
2301 " -a --all Show all properties, including empty ones\n"
2302 " -l --full Do not ellipsize output\n"
2303 " --kill-who=WHO Who to send signal to\n"
2304 " -s --signal=SIGNAL Which signal to send\n"
2305 " --read-only Create read-only bind mount\n"
2306 " --mkdir Create directory before bind mounting, if missing\n"
2307 " -n --lines=INTEGER Number of journal entries to show\n"
2308 " -o --output=STRING Change journal output mode (short,\n"
2309 " short-monotonic, verbose, export, json,\n"
2310 " json-pretty, json-sse, cat)\n"
2311 " --verify=MODE Verification mode for downloaded images (no,\n"
2312 " checksum, signature)\n"
2313 " --force Download image even if already exists\n"
2314 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2316 "Machine Commands:\n"
2317 " list List running VMs and containers\n"
2318 " status NAME... Show VM/container details\n"
2319 " show NAME... Show properties of one or more VMs/containers\n"
2320 " start NAME... Start container as a service\n"
2321 " login NAME Get a login prompt on a container\n"
2322 " enable NAME... Enable automatic container start at boot\n"
2323 " disable NAME... Disable automatic container start at boot\n"
2324 " poweroff NAME... Power off one or more containers\n"
2325 " reboot NAME... Reboot one or more containers\n"
2326 " terminate NAME... Terminate one or more VMs/containers\n"
2327 " kill NAME... Send signal to processes of a VM/container\n"
2328 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2329 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2330 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2332 " list-images Show available container and VM images\n"
2333 " image-status NAME... Show image details\n"
2334 " show-image NAME... Show properties of image\n"
2335 " clone NAME NAME Clone an image\n"
2336 " rename NAME NAME Rename an image\n"
2337 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2338 " remove NAME... Remove an image\n"
2339 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2340 "Image Transfer Commands:\n"
2341 " pull-tar URL [NAME] Download a TAR container image\n"
2342 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2343 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2344 " import-tar FILE [NAME] Import a local TAR container image\n"
2345 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2346 " export-tar NAME [FILE] Export a TAR container image locally\n"
2347 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2348 " list-transfers Show list of downloads in progress\n"
2349 " cancel-transfer Cancel a download\n"
2350 , program_invocation_short_name
);
2355 static int parse_argv(int argc
, char *argv
[]) {
2358 ARG_VERSION
= 0x100,
2364 ARG_NO_ASK_PASSWORD
,
2371 static const struct option options
[] = {
2372 { "help", no_argument
, NULL
, 'h' },
2373 { "version", no_argument
, NULL
, ARG_VERSION
},
2374 { "property", required_argument
, NULL
, 'p' },
2375 { "all", no_argument
, NULL
, 'a' },
2376 { "full", no_argument
, NULL
, 'l' },
2377 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2378 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2379 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2380 { "signal", required_argument
, NULL
, 's' },
2381 { "host", required_argument
, NULL
, 'H' },
2382 { "machine", required_argument
, NULL
, 'M' },
2383 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2384 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2385 { "quiet", no_argument
, NULL
, 'q' },
2386 { "lines", required_argument
, NULL
, 'n' },
2387 { "output", required_argument
, NULL
, 'o' },
2388 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2389 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2390 { "force", no_argument
, NULL
, ARG_FORCE
},
2391 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2392 { "format", required_argument
, NULL
, ARG_FORMAT
},
2401 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2406 return help(0, NULL
, NULL
);
2409 puts(PACKAGE_STRING
);
2410 puts(SYSTEMD_FEATURES
);
2414 r
= strv_extend(&arg_property
, optarg
);
2418 /* If the user asked for a particular
2419 * property, show it to him, even if it is
2433 if (safe_atou(optarg
, &arg_lines
) < 0) {
2434 log_error("Failed to parse lines '%s'", optarg
);
2440 arg_output
= output_mode_from_string(optarg
);
2441 if (arg_output
< 0) {
2442 log_error("Unknown output '%s'.", optarg
);
2448 arg_no_pager
= true;
2456 arg_kill_who
= optarg
;
2460 arg_signal
= signal_from_string_try_harder(optarg
);
2461 if (arg_signal
< 0) {
2462 log_error("Failed to parse signal string %s.", optarg
);
2467 case ARG_NO_ASK_PASSWORD
:
2468 arg_ask_password
= false;
2472 arg_transport
= BUS_TRANSPORT_REMOTE
;
2477 arg_transport
= BUS_TRANSPORT_MACHINE
;
2482 arg_read_only
= true;
2494 arg_verify
= import_verify_from_string(optarg
);
2495 if (arg_verify
< 0) {
2496 log_error("Failed to parse --verify= setting: %s", optarg
);
2505 case ARG_DKR_INDEX_URL
:
2506 if (!http_url_is_valid(optarg
)) {
2507 log_error("Index URL is invalid: %s", optarg
);
2511 arg_dkr_index_url
= optarg
;
2515 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2516 log_error("Unknown format: %s", optarg
);
2520 arg_format
= optarg
;
2527 assert_not_reached("Unhandled option");
2533 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2535 static const Verb verbs
[] = {
2536 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2537 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2538 { "list-images", VERB_ANY
, 1, 0, list_images
},
2539 { "status", 2, VERB_ANY
, 0, show_machine
},
2540 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2541 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2542 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2543 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2544 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2545 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2546 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2547 { "login", 2, 2, 0, login_machine
},
2548 { "bind", 3, 4, 0, bind_mount
},
2549 { "copy-to", 3, 4, 0, copy_files
},
2550 { "copy-from", 3, 4, 0, copy_files
},
2551 { "remove", 2, VERB_ANY
, 0, remove_image
},
2552 { "rename", 3, 3, 0, rename_image
},
2553 { "clone", 3, 3, 0, clone_image
},
2554 { "read-only", 2, 3, 0, read_only_image
},
2555 { "start", 2, VERB_ANY
, 0, start_machine
},
2556 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2557 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2558 { "import-tar", 2, 3, 0, import_tar
},
2559 { "import-raw", 2, 3, 0, import_raw
},
2560 { "export-tar", 2, 3, 0, export_tar
},
2561 { "export-raw", 2, 3, 0, export_raw
},
2562 { "pull-tar", 2, 3, 0, pull_tar
},
2563 { "pull-raw", 2, 3, 0, pull_raw
},
2564 { "pull-dkr", 2, 3, 0, pull_dkr
},
2565 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2566 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2567 { "set-limit", 2, 3, 0, set_limit
},
2571 return dispatch_verb(argc
, argv
, verbs
, bus
);
2574 int main(int argc
, char*argv
[]) {
2575 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
2578 setlocale(LC_ALL
, "");
2579 log_parse_environment();
2582 r
= parse_argv(argc
, argv
);
2586 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2588 log_error_errno(r
, "Failed to create bus connection: %m");
2592 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2594 r
= machinectl_main(argc
, argv
, bus
);
2598 polkit_agent_close();
2600 strv_free(arg_property
);
2602 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;