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 _cleanup_free_
char *abs_host_path
= NULL
;
1077 char *dest
, *host_path
, *container_path
;
1078 sd_bus
*bus
= userdata
;
1084 polkit_agent_open_if_enabled();
1086 copy_from
= streq(argv
[0], "copy-from");
1087 dest
= argv
[3] ?: argv
[2];
1088 host_path
= copy_from
? dest
: argv
[2];
1089 container_path
= copy_from
? argv
[2] : dest
;
1091 if (!path_is_absolute(host_path
)) {
1092 abs_host_path
= path_make_absolute_cwd(host_path
);
1095 host_path
= abs_host_path
;
1098 r
= sd_bus_call_method(
1100 "org.freedesktop.machine1",
1101 "/org/freedesktop/machine1",
1102 "org.freedesktop.machine1.Manager",
1103 copy_from
? "CopyFromMachine" : "CopyToMachine",
1108 copy_from
? container_path
: host_path
,
1109 copy_from
? host_path
: container_path
);
1111 log_error("Failed to copy: %s", bus_error_message(&error
, -r
));
1118 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1119 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1120 sd_bus
*bus
= userdata
;
1125 polkit_agent_open_if_enabled();
1127 r
= sd_bus_call_method(
1129 "org.freedesktop.machine1",
1130 "/org/freedesktop/machine1",
1131 "org.freedesktop.machine1.Manager",
1142 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1149 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1150 PTYForward
** forward
= (PTYForward
**) userdata
;
1157 /* If the forwarder is already initialized, tell it to
1158 * exit on the next vhangup(), so that we still flush
1159 * out what might be queued and exit then. */
1161 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1165 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1168 /* On error, or when the forwarder is not initialized yet, quit immediately */
1169 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1173 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1174 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1175 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1176 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1177 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1178 _cleanup_event_unref_ sd_event
*event
= NULL
;
1179 int master
= -1, r
, ret
= 0;
1180 sd_bus
*bus
= userdata
;
1181 const char *pty
, *match
;
1187 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1188 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1189 log_error("Login only supported on local machines.");
1193 polkit_agent_open_if_enabled();
1195 r
= sd_event_default(&event
);
1197 return log_error_errno(r
, "Failed to get event loop: %m");
1199 r
= sd_bus_attach_event(bus
, event
, 0);
1201 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1203 match
= strjoina("type='signal',"
1204 "sender='org.freedesktop.machine1',"
1205 "path='/org/freedesktop/machine1',",
1206 "interface='org.freedesktop.machine1.Manager',"
1207 "member='MachineRemoved',"
1212 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1214 return log_error_errno(r
, "Failed to add machine removal match: %m");
1216 r
= sd_bus_call_method(
1218 "org.freedesktop.machine1",
1219 "/org/freedesktop/machine1",
1220 "org.freedesktop.machine1.Manager",
1226 log_error("Failed to get machine PTY: %s", bus_error_message(&error
, -r
));
1230 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1232 return bus_log_parse_error(r
);
1234 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1236 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv
[1]);
1238 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1239 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1241 r
= pty_forward_new(event
, master
, true, false, &forward
);
1243 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1245 r
= sd_event_loop(event
);
1247 return log_error_errno(r
, "Failed to run event loop: %m");
1249 pty_forward_get_last_char(forward
, &last_char
);
1250 machine_died
= pty_forward_get_ignore_vhangup(forward
) == 0;
1252 forward
= pty_forward_free(forward
);
1254 if (last_char
!= '\n')
1255 fputc('\n', stdout
);
1258 log_info("Machine %s terminated.", argv
[1]);
1260 log_info("Connection to machine %s terminated.", argv
[1]);
1262 sd_event_get_exit_code(event
, &ret
);
1266 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1267 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1268 sd_bus
*bus
= userdata
;
1273 polkit_agent_open_if_enabled();
1275 for (i
= 1; i
< argc
; i
++) {
1276 r
= sd_bus_call_method(
1278 "org.freedesktop.machine1",
1279 "/org/freedesktop/machine1",
1280 "org.freedesktop.machine1.Manager",
1286 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1294 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1295 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1296 sd_bus
*bus
= userdata
;
1299 polkit_agent_open_if_enabled();
1301 r
= sd_bus_call_method(
1303 "org.freedesktop.machine1",
1304 "/org/freedesktop/machine1",
1305 "org.freedesktop.machine1.Manager",
1309 "ss", argv
[1], argv
[2]);
1311 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1318 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1319 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1320 sd_bus
*bus
= userdata
;
1323 polkit_agent_open_if_enabled();
1325 r
= sd_bus_call_method(
1327 "org.freedesktop.machine1",
1328 "/org/freedesktop/machine1",
1329 "org.freedesktop.machine1.Manager",
1333 "ssb", argv
[1], argv
[2], arg_read_only
);
1335 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1342 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1343 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1344 sd_bus
*bus
= userdata
;
1348 b
= parse_boolean(argv
[2]);
1350 log_error("Failed to parse boolean argument: %s", argv
[2]);
1355 polkit_agent_open_if_enabled();
1357 r
= sd_bus_call_method(
1359 "org.freedesktop.machine1",
1360 "/org/freedesktop/machine1",
1361 "org.freedesktop.machine1.Manager",
1362 "MarkImageReadOnly",
1367 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1374 static int make_service_name(const char *name
, char **ret
) {
1375 _cleanup_free_
char *e
= NULL
;
1381 if (!machine_name_is_valid(name
)) {
1382 log_error("Invalid machine name %s.", name
);
1386 e
= unit_name_escape(name
);
1390 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1392 return log_error_errno(r
, "Failed to build unit name: %m");
1397 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1398 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1399 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1400 sd_bus
*bus
= userdata
;
1405 polkit_agent_open_if_enabled();
1407 r
= bus_wait_for_jobs_new(bus
, &w
);
1411 for (i
= 1; i
< argc
; i
++) {
1412 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1413 _cleanup_free_
char *unit
= NULL
;
1416 r
= make_service_name(argv
[i
], &unit
);
1420 r
= sd_bus_call_method(
1422 "org.freedesktop.systemd1",
1423 "/org/freedesktop/systemd1",
1424 "org.freedesktop.systemd1.Manager",
1428 "ss", unit
, "fail");
1430 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1434 r
= sd_bus_message_read(reply
, "o", &object
);
1436 return bus_log_parse_error(r
);
1438 r
= bus_wait_for_jobs_add(w
, object
);
1443 r
= bus_wait_for_jobs(w
, arg_quiet
);
1450 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1451 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1452 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1453 int carries_install_info
= 0;
1454 const char *method
= NULL
;
1455 sd_bus
*bus
= userdata
;
1460 polkit_agent_open_if_enabled();
1462 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1464 r
= sd_bus_message_new_method_call(
1467 "org.freedesktop.systemd1",
1468 "/org/freedesktop/systemd1",
1469 "org.freedesktop.systemd1.Manager",
1472 return bus_log_create_error(r
);
1474 r
= sd_bus_message_open_container(m
, 'a', "s");
1476 return bus_log_create_error(r
);
1478 for (i
= 1; i
< argc
; i
++) {
1479 _cleanup_free_
char *unit
= NULL
;
1481 r
= make_service_name(argv
[i
], &unit
);
1485 r
= sd_bus_message_append(m
, "s", unit
);
1487 return bus_log_create_error(r
);
1490 r
= sd_bus_message_close_container(m
);
1492 return bus_log_create_error(r
);
1494 if (streq(argv
[0], "enable"))
1495 r
= sd_bus_message_append(m
, "bb", false, false);
1497 r
= sd_bus_message_append(m
, "b", false);
1499 return bus_log_create_error(r
);
1501 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1503 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1507 if (streq(argv
[0], "enable")) {
1508 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1510 return bus_log_parse_error(r
);
1513 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1517 r
= sd_bus_call_method(
1519 "org.freedesktop.systemd1",
1520 "/org/freedesktop/systemd1",
1521 "org.freedesktop.systemd1.Manager",
1527 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1534 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1535 const char **our_path
= userdata
, *line
;
1542 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1544 bus_log_parse_error(r
);
1548 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1551 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1554 log_full(priority
, "%s", line
);
1558 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1559 const char **our_path
= userdata
, *path
, *result
;
1566 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1568 bus_log_parse_error(r
);
1572 if (!streq_ptr(*our_path
, path
))
1575 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1579 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1584 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1586 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1590 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1591 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1592 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1593 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1594 _cleanup_event_unref_ sd_event
* event
= NULL
;
1595 const char *path
= NULL
;
1602 polkit_agent_open_if_enabled();
1604 r
= sd_event_default(&event
);
1606 return log_error_errno(r
, "Failed to get event loop: %m");
1608 r
= sd_bus_attach_event(bus
, event
, 0);
1610 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1612 r
= sd_bus_add_match(
1616 "sender='org.freedesktop.import1',"
1617 "interface='org.freedesktop.import1.Manager',"
1618 "member='TransferRemoved',"
1619 "path='/org/freedesktop/import1'",
1620 match_transfer_removed
, &path
);
1622 return log_error_errno(r
, "Failed to install match: %m");
1624 r
= sd_bus_add_match(
1628 "sender='org.freedesktop.import1',"
1629 "interface='org.freedesktop.import1.Transfer',"
1630 "member='LogMessage'",
1631 match_log_message
, &path
);
1633 return log_error_errno(r
, "Failed to install match: %m");
1635 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1637 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1641 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1643 return bus_log_parse_error(r
);
1645 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
1648 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1650 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1651 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1653 r
= sd_event_loop(event
);
1655 return log_error_errno(r
, "Failed to run event loop: %m");
1660 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1661 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1662 _cleanup_free_
char *ll
= NULL
;
1663 _cleanup_close_
int fd
= -1;
1664 const char *local
= NULL
, *path
= NULL
;
1665 sd_bus
*bus
= userdata
;
1672 if (isempty(path
) || streq(path
, "-"))
1678 local
= basename(path
);
1679 if (isempty(local
) || streq(local
, "-"))
1683 log_error("Need either path or local name.");
1687 r
= tar_strip_suffixes(local
, &ll
);
1693 if (!machine_name_is_valid(local
)) {
1694 log_error("Local name %s is not a suitable machine name.", local
);
1699 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1701 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1704 r
= sd_bus_message_new_method_call(
1707 "org.freedesktop.import1",
1708 "/org/freedesktop/import1",
1709 "org.freedesktop.import1.Manager",
1712 return bus_log_create_error(r
);
1714 r
= sd_bus_message_append(
1717 fd
>= 0 ? fd
: STDIN_FILENO
,
1722 return bus_log_create_error(r
);
1724 return transfer_image_common(bus
, m
);
1727 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1728 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1729 _cleanup_free_
char *ll
= NULL
;
1730 _cleanup_close_
int fd
= -1;
1731 const char *local
= NULL
, *path
= NULL
;
1732 sd_bus
*bus
= userdata
;
1739 if (isempty(path
) || streq(path
, "-"))
1745 local
= basename(path
);
1746 if (isempty(local
) || streq(local
, "-"))
1750 log_error("Need either path or local name.");
1754 r
= raw_strip_suffixes(local
, &ll
);
1760 if (!machine_name_is_valid(local
)) {
1761 log_error("Local name %s is not a suitable machine name.", local
);
1766 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1768 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1771 r
= sd_bus_message_new_method_call(
1774 "org.freedesktop.import1",
1775 "/org/freedesktop/import1",
1776 "org.freedesktop.import1.Manager",
1779 return bus_log_create_error(r
);
1781 r
= sd_bus_message_append(
1784 fd
>= 0 ? fd
: STDIN_FILENO
,
1789 return bus_log_create_error(r
);
1791 return transfer_image_common(bus
, m
);
1794 static void determine_compression_from_filename(const char *p
) {
1801 if (endswith(p
, ".xz"))
1803 else if (endswith(p
, ".gz"))
1804 arg_format
= "gzip";
1805 else if (endswith(p
, ".bz2"))
1806 arg_format
= "bzip2";
1809 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1810 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1811 _cleanup_close_
int fd
= -1;
1812 const char *local
= NULL
, *path
= NULL
;
1813 sd_bus
*bus
= userdata
;
1819 if (!machine_name_is_valid(local
)) {
1820 log_error("Machine name %s is not valid.", local
);
1826 if (isempty(path
) || streq(path
, "-"))
1830 determine_compression_from_filename(path
);
1832 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1834 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1837 r
= sd_bus_message_new_method_call(
1840 "org.freedesktop.import1",
1841 "/org/freedesktop/import1",
1842 "org.freedesktop.import1.Manager",
1845 return bus_log_create_error(r
);
1847 r
= sd_bus_message_append(
1851 fd
>= 0 ? fd
: STDOUT_FILENO
,
1854 return bus_log_create_error(r
);
1856 return transfer_image_common(bus
, m
);
1859 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1860 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1861 _cleanup_close_
int fd
= -1;
1862 const char *local
= NULL
, *path
= NULL
;
1863 sd_bus
*bus
= userdata
;
1869 if (!machine_name_is_valid(local
)) {
1870 log_error("Machine name %s is not valid.", local
);
1876 if (isempty(path
) || streq(path
, "-"))
1880 determine_compression_from_filename(path
);
1882 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1884 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1887 r
= sd_bus_message_new_method_call(
1890 "org.freedesktop.import1",
1891 "/org/freedesktop/import1",
1892 "org.freedesktop.import1.Manager",
1895 return bus_log_create_error(r
);
1897 r
= sd_bus_message_append(
1901 fd
>= 0 ? fd
: STDOUT_FILENO
,
1904 return bus_log_create_error(r
);
1906 return transfer_image_common(bus
, m
);
1909 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
1910 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1911 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1912 const char *local
, *remote
;
1913 sd_bus
*bus
= userdata
;
1919 if (!http_url_is_valid(remote
)) {
1920 log_error("URL '%s' is not valid.", remote
);
1927 r
= import_url_last_component(remote
, &l
);
1929 return log_error_errno(r
, "Failed to get final component of URL: %m");
1934 if (isempty(local
) || streq(local
, "-"))
1938 r
= tar_strip_suffixes(local
, &ll
);
1944 if (!machine_name_is_valid(local
)) {
1945 log_error("Local name %s is not a suitable machine name.", local
);
1950 r
= sd_bus_message_new_method_call(
1953 "org.freedesktop.import1",
1954 "/org/freedesktop/import1",
1955 "org.freedesktop.import1.Manager",
1958 return bus_log_create_error(r
);
1960 r
= sd_bus_message_append(
1965 import_verify_to_string(arg_verify
),
1968 return bus_log_create_error(r
);
1970 return transfer_image_common(bus
, m
);
1973 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
1974 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1975 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1976 const char *local
, *remote
;
1977 sd_bus
*bus
= userdata
;
1983 if (!http_url_is_valid(remote
)) {
1984 log_error("URL '%s' is not valid.", remote
);
1991 r
= import_url_last_component(remote
, &l
);
1993 return log_error_errno(r
, "Failed to get final component of URL: %m");
1998 if (isempty(local
) || streq(local
, "-"))
2002 r
= raw_strip_suffixes(local
, &ll
);
2008 if (!machine_name_is_valid(local
)) {
2009 log_error("Local name %s is not a suitable machine name.", local
);
2014 r
= sd_bus_message_new_method_call(
2017 "org.freedesktop.import1",
2018 "/org/freedesktop/import1",
2019 "org.freedesktop.import1.Manager",
2022 return bus_log_create_error(r
);
2024 r
= sd_bus_message_append(
2029 import_verify_to_string(arg_verify
),
2032 return bus_log_create_error(r
);
2034 return transfer_image_common(bus
, m
);
2037 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2038 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2039 const char *local
, *remote
, *tag
;
2040 sd_bus
*bus
= userdata
;
2043 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2044 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2049 tag
= strchr(remote
, ':');
2051 remote
= strndupa(remote
, tag
- remote
);
2055 if (!dkr_name_is_valid(remote
)) {
2056 log_error("DKR name '%s' is invalid.", remote
);
2059 if (tag
&& !dkr_tag_is_valid(tag
)) {
2060 log_error("DKR tag '%s' is invalid.", remote
);
2067 local
= strchr(remote
, '/');
2074 if (isempty(local
) || streq(local
, "-"))
2078 if (!machine_name_is_valid(local
)) {
2079 log_error("Local name %s is not a suitable machine name.", local
);
2084 r
= sd_bus_message_new_method_call(
2087 "org.freedesktop.import1",
2088 "/org/freedesktop/import1",
2089 "org.freedesktop.import1.Manager",
2092 return bus_log_create_error(r
);
2094 r
= sd_bus_message_append(
2101 import_verify_to_string(arg_verify
),
2104 return bus_log_create_error(r
);
2106 return transfer_image_common(bus
, m
);
2109 typedef struct TransferInfo
{
2117 static int compare_transfer_info(const void *a
, const void *b
) {
2118 const TransferInfo
*x
= a
, *y
= b
;
2120 return strcmp(x
->local
, y
->local
);
2123 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2124 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2125 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2126 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2127 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2128 size_t n_transfers
= 0, n_allocated
= 0, j
;
2129 const char *type
, *remote
, *local
, *object
;
2130 sd_bus
*bus
= userdata
;
2131 uint32_t id
, max_id
= 0;
2135 pager_open_if_enabled();
2137 r
= sd_bus_call_method(
2139 "org.freedesktop.import1",
2140 "/org/freedesktop/import1",
2141 "org.freedesktop.import1.Manager",
2147 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2151 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2153 return bus_log_parse_error(r
);
2155 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2158 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2161 transfers
[n_transfers
].id
= id
;
2162 transfers
[n_transfers
].type
= type
;
2163 transfers
[n_transfers
].remote
= remote
;
2164 transfers
[n_transfers
].local
= local
;
2165 transfers
[n_transfers
].progress
= progress
;
2185 return bus_log_parse_error(r
);
2187 r
= sd_bus_message_exit_container(reply
);
2189 return bus_log_parse_error(r
);
2191 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2194 printf("%-*s %-*s %-*s %-*s %-*s\n",
2195 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2197 (int) max_type
, "TYPE",
2198 (int) max_local
, "LOCAL",
2199 (int) max_remote
, "REMOTE");
2201 for (j
= 0; j
< n_transfers
; j
++)
2202 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2203 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2204 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2205 (int) max_type
, transfers
[j
].type
,
2206 (int) max_local
, transfers
[j
].local
,
2207 (int) max_remote
, transfers
[j
].remote
);
2210 printf("\n%zu transfers listed.\n", n_transfers
);
2215 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2216 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2217 sd_bus
*bus
= userdata
;
2222 polkit_agent_open_if_enabled();
2224 for (i
= 1; i
< argc
; i
++) {
2227 r
= safe_atou32(argv
[i
], &id
);
2229 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2231 r
= sd_bus_call_method(
2233 "org.freedesktop.import1",
2234 "/org/freedesktop/import1",
2235 "org.freedesktop.import1.Manager",
2241 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2249 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2250 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2251 sd_bus
*bus
= userdata
;
2255 if (streq(argv
[argc
-1], "-"))
2256 limit
= (uint64_t) -1;
2260 r
= parse_size(argv
[argc
-1], 1024, &off
);
2262 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2264 limit
= (uint64_t) off
;
2268 /* With two arguments changes the quota limit of the
2269 * specified image */
2270 r
= sd_bus_call_method(
2272 "org.freedesktop.machine1",
2273 "/org/freedesktop/machine1",
2274 "org.freedesktop.machine1.Manager",
2278 "st", argv
[1], limit
);
2280 /* With one argument changes the pool quota limit */
2281 r
= sd_bus_call_method(
2283 "org.freedesktop.machine1",
2284 "/org/freedesktop/machine1",
2285 "org.freedesktop.machine1.Manager",
2292 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2299 static int help(int argc
, char *argv
[], void *userdata
) {
2301 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2302 "Send control commands to or query the virtual machine and container\n"
2303 "registration manager.\n\n"
2304 " -h --help Show this help\n"
2305 " --version Show package version\n"
2306 " --no-pager Do not pipe output into a pager\n"
2307 " --no-legend Do not show the headers and footers\n"
2308 " --no-ask-password Do not ask for system passwords\n"
2309 " -H --host=[USER@]HOST Operate on remote host\n"
2310 " -M --machine=CONTAINER Operate on local container\n"
2311 " -p --property=NAME Show only properties by this name\n"
2312 " -q --quiet Suppress output\n"
2313 " -a --all Show all properties, including empty ones\n"
2314 " -l --full Do not ellipsize output\n"
2315 " --kill-who=WHO Who to send signal to\n"
2316 " -s --signal=SIGNAL Which signal to send\n"
2317 " --read-only Create read-only bind mount\n"
2318 " --mkdir Create directory before bind mounting, if missing\n"
2319 " -n --lines=INTEGER Number of journal entries to show\n"
2320 " -o --output=STRING Change journal output mode (short,\n"
2321 " short-monotonic, verbose, export, json,\n"
2322 " json-pretty, json-sse, cat)\n"
2323 " --verify=MODE Verification mode for downloaded images (no,\n"
2324 " checksum, signature)\n"
2325 " --force Download image even if already exists\n"
2326 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2328 "Machine Commands:\n"
2329 " list List running VMs and containers\n"
2330 " status NAME... Show VM/container details\n"
2331 " show NAME... Show properties of one or more VMs/containers\n"
2332 " start NAME... Start container as a service\n"
2333 " login NAME Get a login prompt on a container\n"
2334 " enable NAME... Enable automatic container start at boot\n"
2335 " disable NAME... Disable automatic container start at boot\n"
2336 " poweroff NAME... Power off one or more containers\n"
2337 " reboot NAME... Reboot one or more containers\n"
2338 " terminate NAME... Terminate one or more VMs/containers\n"
2339 " kill NAME... Send signal to processes of a VM/container\n"
2340 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2341 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2342 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2344 " list-images Show available container and VM images\n"
2345 " image-status NAME... Show image details\n"
2346 " show-image NAME... Show properties of image\n"
2347 " clone NAME NAME Clone an image\n"
2348 " rename NAME NAME Rename an image\n"
2349 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2350 " remove NAME... Remove an image\n"
2351 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2352 "Image Transfer Commands:\n"
2353 " pull-tar URL [NAME] Download a TAR container image\n"
2354 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2355 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2356 " import-tar FILE [NAME] Import a local TAR container image\n"
2357 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2358 " export-tar NAME [FILE] Export a TAR container image locally\n"
2359 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2360 " list-transfers Show list of downloads in progress\n"
2361 " cancel-transfer Cancel a download\n"
2362 , program_invocation_short_name
);
2367 static int parse_argv(int argc
, char *argv
[]) {
2370 ARG_VERSION
= 0x100,
2376 ARG_NO_ASK_PASSWORD
,
2383 static const struct option options
[] = {
2384 { "help", no_argument
, NULL
, 'h' },
2385 { "version", no_argument
, NULL
, ARG_VERSION
},
2386 { "property", required_argument
, NULL
, 'p' },
2387 { "all", no_argument
, NULL
, 'a' },
2388 { "full", no_argument
, NULL
, 'l' },
2389 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2390 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2391 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2392 { "signal", required_argument
, NULL
, 's' },
2393 { "host", required_argument
, NULL
, 'H' },
2394 { "machine", required_argument
, NULL
, 'M' },
2395 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2396 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2397 { "quiet", no_argument
, NULL
, 'q' },
2398 { "lines", required_argument
, NULL
, 'n' },
2399 { "output", required_argument
, NULL
, 'o' },
2400 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2401 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2402 { "force", no_argument
, NULL
, ARG_FORCE
},
2403 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2404 { "format", required_argument
, NULL
, ARG_FORMAT
},
2413 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2418 return help(0, NULL
, NULL
);
2421 puts(PACKAGE_STRING
);
2422 puts(SYSTEMD_FEATURES
);
2426 r
= strv_extend(&arg_property
, optarg
);
2430 /* If the user asked for a particular
2431 * property, show it to him, even if it is
2445 if (safe_atou(optarg
, &arg_lines
) < 0) {
2446 log_error("Failed to parse lines '%s'", optarg
);
2452 arg_output
= output_mode_from_string(optarg
);
2453 if (arg_output
< 0) {
2454 log_error("Unknown output '%s'.", optarg
);
2460 arg_no_pager
= true;
2468 arg_kill_who
= optarg
;
2472 arg_signal
= signal_from_string_try_harder(optarg
);
2473 if (arg_signal
< 0) {
2474 log_error("Failed to parse signal string %s.", optarg
);
2479 case ARG_NO_ASK_PASSWORD
:
2480 arg_ask_password
= false;
2484 arg_transport
= BUS_TRANSPORT_REMOTE
;
2489 arg_transport
= BUS_TRANSPORT_MACHINE
;
2494 arg_read_only
= true;
2506 arg_verify
= import_verify_from_string(optarg
);
2507 if (arg_verify
< 0) {
2508 log_error("Failed to parse --verify= setting: %s", optarg
);
2517 case ARG_DKR_INDEX_URL
:
2518 if (!http_url_is_valid(optarg
)) {
2519 log_error("Index URL is invalid: %s", optarg
);
2523 arg_dkr_index_url
= optarg
;
2527 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2528 log_error("Unknown format: %s", optarg
);
2532 arg_format
= optarg
;
2539 assert_not_reached("Unhandled option");
2545 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2547 static const Verb verbs
[] = {
2548 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2549 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2550 { "list-images", VERB_ANY
, 1, 0, list_images
},
2551 { "status", 2, VERB_ANY
, 0, show_machine
},
2552 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2553 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2554 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2555 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2556 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2557 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2558 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2559 { "login", 2, 2, 0, login_machine
},
2560 { "bind", 3, 4, 0, bind_mount
},
2561 { "copy-to", 3, 4, 0, copy_files
},
2562 { "copy-from", 3, 4, 0, copy_files
},
2563 { "remove", 2, VERB_ANY
, 0, remove_image
},
2564 { "rename", 3, 3, 0, rename_image
},
2565 { "clone", 3, 3, 0, clone_image
},
2566 { "read-only", 2, 3, 0, read_only_image
},
2567 { "start", 2, VERB_ANY
, 0, start_machine
},
2568 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2569 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2570 { "import-tar", 2, 3, 0, import_tar
},
2571 { "import-raw", 2, 3, 0, import_raw
},
2572 { "export-tar", 2, 3, 0, export_tar
},
2573 { "export-raw", 2, 3, 0, export_raw
},
2574 { "pull-tar", 2, 3, 0, pull_tar
},
2575 { "pull-raw", 2, 3, 0, pull_raw
},
2576 { "pull-dkr", 2, 3, 0, pull_dkr
},
2577 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2578 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2579 { "set-limit", 2, 3, 0, set_limit
},
2583 return dispatch_verb(argc
, argv
, verbs
, bus
);
2586 int main(int argc
, char*argv
[]) {
2587 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
2590 setlocale(LC_ALL
, "");
2591 log_parse_environment();
2594 r
= parse_argv(argc
, argv
);
2598 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2600 log_error_errno(r
, "Failed to create bus connection: %m");
2604 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2606 r
= machinectl_main(argc
, argv
, bus
);
2610 polkit_agent_close();
2612 strv_free(arg_property
);
2614 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;