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>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <sys/mount.h>
35 /* When we include libgen.h because we need dirname() we immediately
36 * undefine basename() since libgen.h defines it as a macro to the XDG
37 * version which is really broken. */
46 #include "spawn-polkit-agent.h"
48 #include "bus-error.h"
51 #include "unit-name.h"
52 #include "cgroup-show.h"
53 #include "logs-show.h"
54 #include "cgroup-util.h"
56 #include "event-util.h"
57 #include "path-util.h"
61 #include "import-util.h"
63 static char **arg_property
= NULL
;
64 static bool arg_all
= false;
65 static bool arg_full
= false;
66 static bool arg_no_pager
= false;
67 static bool arg_legend
= true;
68 static const char *arg_kill_who
= NULL
;
69 static int arg_signal
= SIGTERM
;
70 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
71 static char *arg_host
= NULL
;
72 static bool arg_read_only
= false;
73 static bool arg_mkdir
= false;
74 static bool arg_quiet
= false;
75 static bool arg_ask_password
= true;
76 static unsigned arg_lines
= 10;
77 static OutputMode arg_output
= OUTPUT_SHORT
;
78 static bool arg_force
= false;
79 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
80 static const char* arg_dkr_index_url
= NULL
;
82 static void pager_open_if_enabled(void) {
90 static void polkit_agent_open_if_enabled(void) {
92 /* Open the polkit agent as a child process if necessary */
94 if (!arg_ask_password
)
97 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
103 static OutputFlags
get_output_flags(void) {
105 arg_all
* OUTPUT_SHOW_ALL
|
106 arg_full
* OUTPUT_FULL_WIDTH
|
107 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
108 on_tty() * OUTPUT_COLOR
|
109 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
112 typedef struct MachineInfo
{
118 static int compare_machine_info(const void *a
, const void *b
) {
119 const MachineInfo
*x
= a
, *y
= b
;
121 return strcmp(x
->name
, y
->name
);
124 static int list_machines(int argc
, char *argv
[], void *userdata
) {
126 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
127 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
128 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
129 _cleanup_free_ MachineInfo
*machines
= NULL
;
130 const char *name
, *class, *service
, *object
;
131 size_t n_machines
= 0, n_allocated
= 0, j
;
132 sd_bus
*bus
= userdata
;
137 pager_open_if_enabled();
139 r
= sd_bus_call_method(
141 "org.freedesktop.machine1",
142 "/org/freedesktop/machine1",
143 "org.freedesktop.machine1.Manager",
149 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
153 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
155 return bus_log_parse_error(r
);
157 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
160 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
163 machines
[n_machines
].name
= name
;
164 machines
[n_machines
].class = class;
165 machines
[n_machines
].service
= service
;
182 return bus_log_parse_error(r
);
184 r
= sd_bus_message_exit_container(reply
);
186 return bus_log_parse_error(r
);
188 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
191 printf("%-*s %-*s %-*s\n",
192 (int) max_name
, "MACHINE",
193 (int) max_class
, "CLASS",
194 (int) max_service
, "SERVICE");
196 for (j
= 0; j
< n_machines
; j
++)
197 printf("%-*s %-*s %-*s\n",
198 (int) max_name
, machines
[j
].name
,
199 (int) max_class
, machines
[j
].class,
200 (int) max_service
, machines
[j
].service
);
203 printf("\n%zu machines listed.\n", n_machines
);
208 typedef struct ImageInfo
{
217 static int compare_image_info(const void *a
, const void *b
) {
218 const ImageInfo
*x
= a
, *y
= b
;
220 return strcmp(x
->name
, y
->name
);
223 static int list_images(int argc
, char *argv
[], void *userdata
) {
225 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
226 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
227 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
228 _cleanup_free_ ImageInfo
*images
= NULL
;
229 size_t n_images
= 0, n_allocated
= 0, j
;
230 const char *name
, *type
, *object
;
231 sd_bus
*bus
= userdata
;
232 uint64_t crtime
, mtime
, size
;
237 pager_open_if_enabled();
239 r
= sd_bus_call_method(
241 "org.freedesktop.machine1",
242 "/org/freedesktop/machine1",
243 "org.freedesktop.machine1.Manager",
249 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
253 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
255 return bus_log_parse_error(r
);
257 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
258 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
261 if (name
[0] == '.' && !arg_all
)
264 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
267 images
[n_images
].name
= name
;
268 images
[n_images
].type
= type
;
269 images
[n_images
].read_only
= read_only
;
270 images
[n_images
].crtime
= crtime
;
271 images
[n_images
].mtime
= mtime
;
272 images
[n_images
].size
= size
;
283 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
289 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
294 if (size
!= (uint64_t) -1) {
295 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
303 return bus_log_parse_error(r
);
305 r
= sd_bus_message_exit_container(reply
);
307 return bus_log_parse_error(r
);
309 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
312 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
313 (int) max_name
, "NAME",
314 (int) max_type
, "TYPE",
316 (int) max_size
, "USAGE",
317 (int) max_crtime
, "CREATED",
318 (int) max_mtime
, "MODIFIED");
320 for (j
= 0; j
< n_images
; j
++) {
321 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
323 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
324 (int) max_name
, images
[j
].name
,
325 (int) max_type
, images
[j
].type
,
326 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_highlight_off() : "",
327 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
328 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
329 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
333 printf("\n%zu images listed.\n", n_images
);
338 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
339 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
340 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
341 _cleanup_free_
char *path
= NULL
;
349 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
352 path
= unit_dbus_path_from_name(unit
);
356 r
= sd_bus_get_property(
358 "org.freedesktop.systemd1",
360 endswith(unit
, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
366 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
370 r
= sd_bus_message_read(reply
, "s", &cgroup
);
372 return bus_log_parse_error(r
);
377 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, false) != 0 && leader
<= 0)
386 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
390 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
391 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
399 r
= sd_bus_call_method(bus
,
400 "org.freedesktop.machine1",
401 "/org/freedesktop/machine1",
402 "org.freedesktop.machine1.Manager",
403 "GetMachineAddresses",
410 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
412 return bus_log_parse_error(r
);
414 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
418 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
420 r
= sd_bus_message_read(reply
, "i", &family
);
422 return bus_log_parse_error(r
);
424 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
426 return bus_log_parse_error(r
);
428 fputs(prefix
, stdout
);
429 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
430 if (family
== AF_INET6
&& ifi
> 0)
434 r
= sd_bus_message_exit_container(reply
);
436 return bus_log_parse_error(r
);
438 if (prefix
!= prefix2
)
442 return bus_log_parse_error(r
);
444 r
= sd_bus_message_exit_container(reply
);
446 return bus_log_parse_error(r
);
451 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
452 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
453 const char *k
, *v
, *pretty
= NULL
;
460 r
= sd_bus_call_method(bus
,
461 "org.freedesktop.machine1",
462 "/org/freedesktop/machine1",
463 "org.freedesktop.machine1.Manager",
464 "GetMachineOSRelease",
471 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
473 return bus_log_parse_error(r
);
475 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
476 if (streq(k
, "PRETTY_NAME"))
481 return bus_log_parse_error(r
);
483 r
= sd_bus_message_exit_container(reply
);
485 return bus_log_parse_error(r
);
488 printf("%s%s\n", prefix
, pretty
);
493 typedef struct MachineStatusInfo
{
499 char *root_directory
;
501 struct dual_timestamp timestamp
;
506 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
507 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
508 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
514 fputs(strna(i
->name
), stdout
);
516 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
517 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
521 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
522 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
525 printf("\t Since: %s; %s\n", s2
, s1
);
527 printf("\t Since: %s\n", s2
);
530 _cleanup_free_
char *t
= NULL
;
532 printf("\t Leader: %u", (unsigned) i
->leader
);
534 get_process_comm(i
->leader
, &t
);
542 printf("\t Service: %s", i
->service
);
545 printf("; class %s", i
->class);
549 printf("\t Class: %s\n", i
->class);
551 if (i
->root_directory
)
552 printf("\t Root: %s\n", i
->root_directory
);
554 if (i
->n_netif
> 0) {
557 fputs("\t Iface:", stdout
);
559 for (c
= 0; c
< i
->n_netif
; c
++) {
560 char name
[IF_NAMESIZE
+1] = "";
562 if (if_indextoname(i
->netif
[c
], name
)) {
571 printf(" %i", i
->netif
[c
]);
577 print_addresses(bus
, i
->name
, ifi
,
581 print_os_release(bus
, i
->name
, "\t OS: ");
584 printf("\t Unit: %s\n", i
->unit
);
585 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
587 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
589 show_journal_by_unit(
594 i
->timestamp
.monotonic
,
597 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
598 SD_JOURNAL_LOCAL_ONLY
,
605 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
606 MachineStatusInfo
*i
= userdata
;
611 assert_cc(sizeof(int32_t) == sizeof(int));
612 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
618 i
->n_netif
= l
/ sizeof(int32_t);
619 i
->netif
= memdup(v
, l
);
626 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
628 static const struct bus_properties_map map
[] = {
629 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
630 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
631 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
632 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
633 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
634 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
635 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
636 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
637 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
638 { "NetworkInterfaces", "ai", map_netif
, 0 },
642 MachineStatusInfo info
= {};
650 r
= bus_map_all_properties(bus
,
651 "org.freedesktop.machine1",
656 return log_error_errno(r
, "Could not get properties: %m");
662 print_machine_status_info(bus
, &info
);
668 free(info
.root_directory
);
674 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
686 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
688 log_error_errno(r
, "Could not get properties: %m");
693 static int show_machine(int argc
, char *argv
[], void *userdata
) {
695 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
696 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
697 bool properties
, new_line
= false;
698 sd_bus
*bus
= userdata
;
703 properties
= !strstr(argv
[0], "status");
705 pager_open_if_enabled();
707 if (properties
&& argc
<= 1) {
709 /* If no argument is specified, inspect the manager
711 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
716 for (i
= 1; i
< argc
; i
++) {
717 const char *path
= NULL
;
719 r
= sd_bus_call_method(
721 "org.freedesktop.machine1",
722 "/org/freedesktop/machine1",
723 "org.freedesktop.machine1.Manager",
729 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
733 r
= sd_bus_message_read(reply
, "o", &path
);
735 return bus_log_parse_error(r
);
738 r
= show_machine_properties(bus
, path
, &new_line
);
740 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
746 typedef struct ImageStatusInfo
{
755 uint64_t usage_exclusive
;
756 uint64_t limit_exclusive
;
759 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
760 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
761 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
762 char bs
[FORMAT_BYTES_MAX
], *s3
;
763 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
769 fputs(i
->name
, stdout
);
774 printf("\t Type: %s\n", i
->type
);
777 printf("\t Path: %s\n", i
->path
);
779 printf("\t RO: %s%s%s\n",
780 i
->read_only
? ansi_highlight_red() : "",
781 i
->read_only
? "read-only" : "writable",
782 i
->read_only
? ansi_highlight_off() : "");
784 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
785 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
787 printf("\t Created: %s; %s\n", s2
, s1
);
789 printf("\t Created: %s\n", s2
);
791 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
792 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
794 printf("\tModified: %s; %s\n", s2
, s1
);
796 printf("\tModified: %s\n", s2
);
798 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
799 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
801 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
803 printf("\t Usage: %s\n", s3
);
805 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
806 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
808 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
810 printf("\t Limit: %s\n", s3
);
813 static int show_image_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
815 static const struct bus_properties_map map
[] = {
816 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
817 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
818 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
819 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
820 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
821 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
822 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
823 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
824 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
825 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
829 ImageStatusInfo info
= {};
837 r
= bus_map_all_properties(bus
,
838 "org.freedesktop.machine1",
843 return log_error_errno(r
, "Could not get properties: %m");
849 print_image_status_info(bus
, &info
);
858 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
870 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
872 log_error_errno(r
, "Could not get properties: %m");
877 static int show_image(int argc
, char *argv
[], void *userdata
) {
879 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
880 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
881 bool properties
, new_line
= false;
882 sd_bus
*bus
= userdata
;
887 properties
= !strstr(argv
[0], "status");
889 pager_open_if_enabled();
891 if (properties
&& argc
<= 1) {
893 /* If no argument is specified, inspect the manager
895 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
900 for (i
= 1; i
< argc
; i
++) {
901 const char *path
= NULL
;
903 r
= sd_bus_call_method(
905 "org.freedesktop.machine1",
906 "/org/freedesktop/machine1",
907 "org.freedesktop.machine1.Manager",
913 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
917 r
= sd_bus_message_read(reply
, "o", &path
);
919 return bus_log_parse_error(r
);
922 r
= show_image_properties(bus
, path
, &new_line
);
924 r
= show_image_info(argv
[0], bus
, path
, &new_line
);
930 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
931 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
932 sd_bus
*bus
= userdata
;
937 polkit_agent_open_if_enabled();
940 arg_kill_who
= "all";
942 for (i
= 1; i
< argc
; i
++) {
943 r
= sd_bus_call_method(
945 "org.freedesktop.machine1",
946 "/org/freedesktop/machine1",
947 "org.freedesktop.machine1.Manager",
951 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
953 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
961 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
962 arg_kill_who
= "leader";
963 arg_signal
= SIGINT
; /* sysvinit + systemd */
965 return kill_machine(argc
, argv
, userdata
);
968 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
969 arg_kill_who
= "leader";
970 arg_signal
= SIGRTMIN
+4; /* only systemd */
972 return kill_machine(argc
, argv
, userdata
);
975 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
976 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
977 sd_bus
*bus
= userdata
;
982 polkit_agent_open_if_enabled();
984 for (i
= 1; i
< argc
; i
++) {
987 r
= sd_bus_call_method(
989 "org.freedesktop.machine1",
990 "/org/freedesktop/machine1",
991 "org.freedesktop.machine1.Manager",
997 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1005 static int machine_get_leader(sd_bus
*bus
, const char *name
, pid_t
*ret
) {
1006 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1007 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *reply2
= NULL
;
1016 r
= sd_bus_call_method(
1018 "org.freedesktop.machine1",
1019 "/org/freedesktop/machine1",
1020 "org.freedesktop.machine1.Manager",
1026 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
1030 r
= sd_bus_message_read(reply
, "o", &object
);
1032 return bus_log_parse_error(r
);
1034 r
= sd_bus_get_property(
1036 "org.freedesktop.machine1",
1038 "org.freedesktop.machine1.Machine",
1044 return log_error_errno(r
, "Failed to retrieve PID of leader: %m");
1046 r
= sd_bus_message_read(reply2
, "u", &leader
);
1048 return bus_log_parse_error(r
);
1054 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1055 char *dest
, *host_path
, *container_path
, *host_dirname
, *host_basename
, *container_dirname
, *container_basename
, *t
;
1056 _cleanup_close_
int hostfd
= -1;
1057 sd_bus
*bus
= userdata
;
1058 pid_t child
, leader
;
1065 copy_from
= streq(argv
[0], "copy-from");
1066 dest
= argv
[3] ?: argv
[2];
1067 host_path
= strdupa(copy_from
? dest
: argv
[2]);
1068 container_path
= strdupa(copy_from
? argv
[2] : dest
);
1070 if (!path_is_absolute(container_path
)) {
1071 log_error("Container path not absolute.");
1075 t
= strdupa(host_path
);
1076 host_basename
= basename(t
);
1077 host_dirname
= dirname(host_path
);
1079 t
= strdupa(container_path
);
1080 container_basename
= basename(t
);
1081 container_dirname
= dirname(container_path
);
1083 r
= machine_get_leader(bus
, argv
[1], &leader
);
1087 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1089 return log_error_errno(errno
, "Failed to open source directory: %m");
1093 return log_error_errno(errno
, "Failed to fork(): %m");
1100 q
= procfs_file_alloca(leader
, "ns/mnt");
1101 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1103 log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1104 _exit(EXIT_FAILURE
);
1107 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1108 log_error_errno(errno
, "Failed to join namespace of leader: %m");
1109 _exit(EXIT_FAILURE
);
1112 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1113 if (containerfd
< 0) {
1114 log_error_errno(errno
, "Failed top open destination directory: %m");
1115 _exit(EXIT_FAILURE
);
1119 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, true);
1121 r
= copy_tree_at(hostfd
, host_basename
, containerfd
, container_basename
, true);
1123 log_error_errno(errno
, "Failed to copy tree: %m");
1124 _exit(EXIT_FAILURE
);
1127 _exit(EXIT_SUCCESS
);
1130 r
= wait_for_terminate(child
, &si
);
1132 return log_error_errno(r
, "Failed to wait for client: %m");
1133 if (si
.si_code
!= CLD_EXITED
) {
1134 log_error("Client died abnormally.");
1137 if (si
.si_status
!= EXIT_SUCCESS
)
1143 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1144 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1145 sd_bus
*bus
= userdata
;
1150 r
= sd_bus_call_method(
1152 "org.freedesktop.machine1",
1153 "/org/freedesktop/machine1",
1154 "org.freedesktop.machine1.Manager",
1165 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1172 static int on_machine_removed(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1173 PTYForward
** forward
= (PTYForward
**) userdata
;
1181 /* If the forwarder is already initialized, tell it to
1182 * exit on the next vhangup(), so that we still flush
1183 * out what might be queued and exit then. */
1185 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1189 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1192 /* On error, or when the forwarder is not initialized yet, quit immediately */
1193 sd_event_exit(sd_bus_get_event(bus
), EXIT_FAILURE
);
1197 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1198 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1199 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1200 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1201 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1202 _cleanup_event_unref_ sd_event
*event
= NULL
;
1203 int master
= -1, r
, ret
= 0;
1204 sd_bus
*bus
= userdata
;
1205 const char *pty
, *match
;
1211 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1212 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1213 log_error("Login only supported on local machines.");
1217 polkit_agent_open_if_enabled();
1219 r
= sd_event_default(&event
);
1221 return log_error_errno(r
, "Failed to get event loop: %m");
1223 r
= sd_bus_attach_event(bus
, event
, 0);
1225 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1227 match
= strjoina("type='signal',"
1228 "sender='org.freedesktop.machine1',"
1229 "path='/org/freedesktop/machine1',",
1230 "interface='org.freedesktop.machine1.Manager',"
1231 "member='MachineRemoved',"
1236 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1238 return log_error_errno(r
, "Failed to add machine removal match: %m");
1240 r
= sd_bus_message_new_method_call(bus
,
1242 "org.freedesktop.machine1",
1243 "/org/freedesktop/machine1",
1244 "org.freedesktop.machine1.Manager",
1245 "OpenMachineLogin");
1247 return bus_log_create_error(r
);
1249 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1251 return bus_log_create_error(r
);
1253 r
= sd_bus_message_append(m
, "s", argv
[1]);
1255 return bus_log_create_error(r
);
1257 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1259 log_error("Failed to get machine PTY: %s", bus_error_message(&error
, -r
));
1263 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1265 return bus_log_parse_error(r
);
1267 sigprocmask_many(SIG_BLOCK
, SIGWINCH
, SIGTERM
, SIGINT
, -1);
1269 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv
[1]);
1271 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1272 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1274 r
= pty_forward_new(event
, master
, true, &forward
);
1276 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1278 r
= sd_event_loop(event
);
1280 return log_error_errno(r
, "Failed to run event loop: %m");
1282 pty_forward_get_last_char(forward
, &last_char
);
1283 machine_died
= pty_forward_get_ignore_vhangup(forward
) == 0;
1285 forward
= pty_forward_free(forward
);
1287 if (last_char
!= '\n')
1288 fputc('\n', stdout
);
1291 log_info("Machine %s terminated.", argv
[1]);
1293 log_info("Connection to machine %s terminated.", argv
[1]);
1295 sd_event_get_exit_code(event
, &ret
);
1299 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1300 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1301 sd_bus
*bus
= userdata
;
1306 polkit_agent_open_if_enabled();
1308 for (i
= 1; i
< argc
; i
++) {
1309 r
= sd_bus_call_method(
1311 "org.freedesktop.machine1",
1312 "/org/freedesktop/machine1",
1313 "org.freedesktop.machine1.Manager",
1319 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1327 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1328 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1329 sd_bus
*bus
= userdata
;
1332 polkit_agent_open_if_enabled();
1334 r
= sd_bus_call_method(
1336 "org.freedesktop.machine1",
1337 "/org/freedesktop/machine1",
1338 "org.freedesktop.machine1.Manager",
1342 "ss", argv
[1], argv
[2]);
1344 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1351 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1352 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1353 sd_bus
*bus
= userdata
;
1356 polkit_agent_open_if_enabled();
1358 r
= sd_bus_call_method(
1360 "org.freedesktop.machine1",
1361 "/org/freedesktop/machine1",
1362 "org.freedesktop.machine1.Manager",
1366 "ssb", argv
[1], argv
[2], arg_read_only
);
1368 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1375 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1376 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1377 sd_bus
*bus
= userdata
;
1381 b
= parse_boolean(argv
[2]);
1383 log_error("Failed to parse boolean argument: %s", argv
[2]);
1388 polkit_agent_open_if_enabled();
1390 r
= sd_bus_call_method(
1392 "org.freedesktop.machine1",
1393 "/org/freedesktop/machine1",
1394 "org.freedesktop.machine1.Manager",
1395 "MarkImageReadOnly",
1400 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1407 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1408 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1409 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1410 sd_bus
*bus
= userdata
;
1415 polkit_agent_open_if_enabled();
1417 r
= bus_wait_for_jobs_new(bus
, &w
);
1421 for (i
= 1; i
< argc
; i
++) {
1422 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1423 _cleanup_free_
char *e
= NULL
, *unit
= NULL
;
1426 if (!machine_name_is_valid(argv
[i
])) {
1427 log_error("Invalid machine name %s.", argv
[i
]);
1431 e
= unit_name_escape(argv
[i
]);
1435 unit
= unit_name_build("systemd-nspawn", e
, ".service");
1439 r
= sd_bus_message_new_method_call(
1442 "org.freedesktop.systemd1",
1443 "/org/freedesktop/systemd1",
1444 "org.freedesktop.systemd1.Manager",
1447 return bus_log_create_error(r
);
1449 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1451 return bus_log_create_error(r
);
1453 r
= sd_bus_message_append(m
, "ss", unit
, "fail");
1455 return bus_log_create_error(r
);
1457 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1459 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1463 r
= sd_bus_message_read(reply
, "o", &object
);
1465 return bus_log_parse_error(r
);
1467 r
= bus_wait_for_jobs_add(w
, object
);
1472 r
= bus_wait_for_jobs(w
, arg_quiet
);
1479 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1480 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1481 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1482 int carries_install_info
= 0;
1483 const char *method
= NULL
;
1484 sd_bus
*bus
= userdata
;
1489 polkit_agent_open_if_enabled();
1491 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1493 r
= sd_bus_message_new_method_call(
1496 "org.freedesktop.systemd1",
1497 "/org/freedesktop/systemd1",
1498 "org.freedesktop.systemd1.Manager",
1501 return bus_log_create_error(r
);
1503 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1505 return bus_log_create_error(r
);
1507 r
= sd_bus_message_open_container(m
, 'a', "s");
1509 return bus_log_create_error(r
);
1511 for (i
= 1; i
< argc
; i
++) {
1512 _cleanup_free_
char *e
= NULL
, *unit
= NULL
;
1514 if (!machine_name_is_valid(argv
[i
])) {
1515 log_error("Invalid machine name %s.", argv
[i
]);
1519 e
= unit_name_escape(argv
[i
]);
1523 unit
= unit_name_build("systemd-nspawn", e
, ".service");
1527 r
= sd_bus_message_append(m
, "s", unit
);
1529 return bus_log_create_error(r
);
1532 r
= sd_bus_message_close_container(m
);
1534 return bus_log_create_error(r
);
1536 if (streq(argv
[0], "enable"))
1537 r
= sd_bus_message_append(m
, "bb", false, false);
1539 r
= sd_bus_message_append(m
, "b", false);
1541 return bus_log_create_error(r
);
1543 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1545 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1549 if (streq(argv
[0], "enable")) {
1550 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1552 return bus_log_parse_error(r
);
1555 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
);
1559 m
= sd_bus_message_unref(m
);
1561 r
= sd_bus_message_new_method_call(
1564 "org.freedesktop.systemd1",
1565 "/org/freedesktop/systemd1",
1566 "org.freedesktop.systemd1.Manager",
1569 return bus_log_create_error(r
);
1571 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1573 return bus_log_create_error(r
);
1575 r
= sd_bus_call(bus
, m
, 0, &error
, NULL
);
1577 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1584 static int match_log_message(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1585 const char **our_path
= userdata
, *line
;
1593 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1595 bus_log_parse_error(r
);
1599 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1602 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1605 log_full(priority
, "%s", line
);
1609 static int match_transfer_removed(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1610 const char **our_path
= userdata
, *path
, *result
;
1618 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1620 bus_log_parse_error(r
);
1624 if (!streq_ptr(*our_path
, path
))
1627 sd_event_exit(sd_bus_get_event(bus
), !streq_ptr(result
, "done"));
1631 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1636 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1638 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1642 static int pull_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1643 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1644 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1645 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1646 _cleanup_event_unref_ sd_event
* event
= NULL
;
1647 const char *path
= NULL
;
1654 polkit_agent_open_if_enabled();
1656 r
= sd_event_default(&event
);
1658 return log_error_errno(r
, "Failed to get event loop: %m");
1660 r
= sd_bus_attach_event(bus
, event
, 0);
1662 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1664 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1666 return bus_log_create_error(r
);
1668 r
= sd_bus_add_match(
1672 "sender='org.freedesktop.import1',"
1673 "interface='org.freedesktop.import1.Manager',"
1674 "member='TransferRemoved',"
1675 "path='/org/freedesktop/import1'",
1676 match_transfer_removed
, &path
);
1678 return log_error_errno(r
, "Failed to install match: %m");
1680 r
= sd_bus_add_match(
1684 "sender='org.freedesktop.import1',"
1685 "interface='org.freedesktop.import1.Transfer',"
1686 "member='LogMessage'",
1687 match_log_message
, &path
);
1689 return log_error_errno(r
, "Failed to install match: %m");
1691 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1693 log_error("Failed pull image: %s", bus_error_message(&error
, -r
));
1697 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1699 return bus_log_parse_error(r
);
1701 sigprocmask_many(SIG_BLOCK
, SIGTERM
, SIGINT
, -1);
1704 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1706 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1707 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1709 r
= sd_event_loop(event
);
1711 return log_error_errno(r
, "Failed to run event loop: %m");
1716 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
1717 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1718 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1719 const char *local
, *remote
;
1720 sd_bus
*bus
= userdata
;
1726 if (!http_url_is_valid(remote
)) {
1727 log_error("URL '%s' is not valid.", remote
);
1734 r
= import_url_last_component(remote
, &l
);
1736 return log_error_errno(r
, "Failed to get final component of URL: %m");
1741 if (isempty(local
) || streq(local
, "-"))
1745 r
= tar_strip_suffixes(local
, &ll
);
1747 return log_error_errno(r
, "Failed to strip tar suffixes: %m");
1751 if (!machine_name_is_valid(local
)) {
1752 log_error("Local name %s is not a suitable machine name.", local
);
1757 r
= sd_bus_message_new_method_call(
1760 "org.freedesktop.import1",
1761 "/org/freedesktop/import1",
1762 "org.freedesktop.import1.Manager",
1765 return bus_log_create_error(r
);
1767 r
= sd_bus_message_append(
1772 import_verify_to_string(arg_verify
),
1775 return bus_log_create_error(r
);
1777 return pull_image_common(bus
, m
);
1780 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
1781 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1782 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1783 const char *local
, *remote
;
1784 sd_bus
*bus
= userdata
;
1790 if (!http_url_is_valid(remote
)) {
1791 log_error("URL '%s' is not valid.", remote
);
1798 r
= import_url_last_component(remote
, &l
);
1800 return log_error_errno(r
, "Failed to get final component of URL: %m");
1805 if (isempty(local
) || streq(local
, "-"))
1809 r
= raw_strip_suffixes(local
, &ll
);
1811 return log_error_errno(r
, "Failed to strip tar suffixes: %m");
1815 if (!machine_name_is_valid(local
)) {
1816 log_error("Local name %s is not a suitable machine name.", local
);
1821 r
= sd_bus_message_new_method_call(
1824 "org.freedesktop.import1",
1825 "/org/freedesktop/import1",
1826 "org.freedesktop.import1.Manager",
1829 return bus_log_create_error(r
);
1831 r
= sd_bus_message_append(
1836 import_verify_to_string(arg_verify
),
1839 return bus_log_create_error(r
);
1841 return pull_image_common(bus
, m
);
1844 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
1845 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1846 const char *local
, *remote
, *tag
;
1847 sd_bus
*bus
= userdata
;
1850 if (arg_verify
!= IMPORT_VERIFY_NO
) {
1851 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
1856 tag
= strchr(remote
, ':');
1858 remote
= strndupa(remote
, tag
- remote
);
1862 if (!dkr_name_is_valid(remote
)) {
1863 log_error("DKR name '%s' is invalid.", remote
);
1866 if (tag
&& !dkr_tag_is_valid(tag
)) {
1867 log_error("DKR tag '%s' is invalid.", remote
);
1874 local
= strchr(remote
, '/');
1881 if (isempty(local
) || streq(local
, "-"))
1885 if (!machine_name_is_valid(local
)) {
1886 log_error("Local name %s is not a suitable machine name.", local
);
1891 r
= sd_bus_message_new_method_call(
1894 "org.freedesktop.import1",
1895 "/org/freedesktop/import1",
1896 "org.freedesktop.import1.Manager",
1899 return bus_log_create_error(r
);
1901 r
= sd_bus_message_append(
1908 import_verify_to_string(arg_verify
),
1911 return bus_log_create_error(r
);
1913 return pull_image_common(bus
, m
);
1916 typedef struct TransferInfo
{
1924 static int compare_transfer_info(const void *a
, const void *b
) {
1925 const TransferInfo
*x
= a
, *y
= b
;
1927 return strcmp(x
->local
, y
->local
);
1930 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
1931 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
1932 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1933 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1934 _cleanup_free_ TransferInfo
*transfers
= NULL
;
1935 size_t n_transfers
= 0, n_allocated
= 0, j
;
1936 const char *type
, *remote
, *local
, *object
;
1937 sd_bus
*bus
= userdata
;
1938 uint32_t id
, max_id
= 0;
1942 pager_open_if_enabled();
1944 r
= sd_bus_call_method(
1946 "org.freedesktop.import1",
1947 "/org/freedesktop/import1",
1948 "org.freedesktop.import1.Manager",
1954 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
1958 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
1960 return bus_log_parse_error(r
);
1962 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
1965 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
1968 transfers
[n_transfers
].id
= id
;
1969 transfers
[n_transfers
].type
= type
;
1970 transfers
[n_transfers
].remote
= remote
;
1971 transfers
[n_transfers
].local
= local
;
1972 transfers
[n_transfers
].progress
= progress
;
1992 return bus_log_parse_error(r
);
1994 r
= sd_bus_message_exit_container(reply
);
1996 return bus_log_parse_error(r
);
1998 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2001 printf("%-*s %-*s %-*s %-*s %-*s\n",
2002 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2004 (int) max_type
, "TYPE",
2005 (int) max_local
, "LOCAL",
2006 (int) max_remote
, "REMOTE");
2008 for (j
= 0; j
< n_transfers
; j
++)
2009 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2010 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2011 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2012 (int) max_type
, transfers
[j
].type
,
2013 (int) max_local
, transfers
[j
].local
,
2014 (int) max_remote
, transfers
[j
].remote
);
2017 printf("\n%zu transfers listed.\n", n_transfers
);
2022 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2023 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2024 sd_bus
*bus
= userdata
;
2029 polkit_agent_open_if_enabled();
2031 for (i
= 1; i
< argc
; i
++) {
2034 r
= safe_atou32(argv
[i
], &id
);
2036 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2038 r
= sd_bus_call_method(
2040 "org.freedesktop.import1",
2041 "/org/freedesktop/import1",
2042 "org.freedesktop.import1.Manager",
2048 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2056 static int help(int argc
, char *argv
[], void *userdata
) {
2058 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2059 "Send control commands to or query the virtual machine and container\n"
2060 "registration manager.\n\n"
2061 " -h --help Show this help\n"
2062 " --version Show package version\n"
2063 " --no-pager Do not pipe output into a pager\n"
2064 " --no-legend Do not show the headers and footers\n"
2065 " --no-ask-password Do not ask for system passwords\n"
2066 " -H --host=[USER@]HOST Operate on remote host\n"
2067 " -M --machine=CONTAINER Operate on local container\n"
2068 " -p --property=NAME Show only properties by this name\n"
2069 " -q --quiet Suppress output\n"
2070 " -a --all Show all properties, including empty ones\n"
2071 " -l --full Do not ellipsize output\n"
2072 " --kill-who=WHO Who to send signal to\n"
2073 " -s --signal=SIGNAL Which signal to send\n"
2074 " --read-only Create read-only bind mount\n"
2075 " --mkdir Create directory before bind mounting, if missing\n"
2076 " -n --lines=INTEGER Number of journal entries to show\n"
2077 " -o --output=STRING Change journal output mode (short,\n"
2078 " short-monotonic, verbose, export, json,\n"
2079 " json-pretty, json-sse, cat)\n"
2080 " --verify=MODE Verification mode for downloaded images (no,\n"
2081 " checksum, signature)\n"
2082 " --force Download image even if already exists\n"
2083 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2085 "Machine Commands:\n"
2086 " list List running VMs and containers\n"
2087 " status NAME... Show VM/container details\n"
2088 " show NAME... Show properties of one or more VMs/containers\n"
2089 " start NAME... Start container as a service\n"
2090 " login NAME Get a login prompt on a container\n"
2091 " enable NAME... Enable automatic container start at boot\n"
2092 " disable NAME... Disable automatic container start at boot\n"
2093 " poweroff NAME... Power off one or more containers\n"
2094 " reboot NAME... Reboot one or more containers\n"
2095 " terminate NAME... Terminate one or more VMs/containers\n"
2096 " kill NAME... Send signal to processes of a VM/container\n"
2097 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2098 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2099 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2101 " list-images Show available container and VM images\n"
2102 " image-status NAME... Show image details\n"
2103 " show-image NAME... Show properties of image\n"
2104 " clone NAME NAME Clone an image\n"
2105 " rename NAME NAME Rename an image\n"
2106 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2107 " remove NAME... Remove an image\n\n"
2108 "Image Transfer Commands:\n"
2109 " pull-tar URL [NAME] Download a TAR container image\n"
2110 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2111 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2112 " list-transfers Show list of downloads in progress\n"
2113 " cancel-transfer Cancel a download\n"
2114 , program_invocation_short_name
);
2119 static int parse_argv(int argc
, char *argv
[]) {
2122 ARG_VERSION
= 0x100,
2128 ARG_NO_ASK_PASSWORD
,
2134 static const struct option options
[] = {
2135 { "help", no_argument
, NULL
, 'h' },
2136 { "version", no_argument
, NULL
, ARG_VERSION
},
2137 { "property", required_argument
, NULL
, 'p' },
2138 { "all", no_argument
, NULL
, 'a' },
2139 { "full", no_argument
, NULL
, 'l' },
2140 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2141 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2142 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2143 { "signal", required_argument
, NULL
, 's' },
2144 { "host", required_argument
, NULL
, 'H' },
2145 { "machine", required_argument
, NULL
, 'M' },
2146 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2147 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2148 { "quiet", no_argument
, NULL
, 'q' },
2149 { "lines", required_argument
, NULL
, 'n' },
2150 { "output", required_argument
, NULL
, 'o' },
2151 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2152 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2153 { "force", no_argument
, NULL
, ARG_FORCE
},
2154 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2163 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2168 return help(0, NULL
, NULL
);
2171 puts(PACKAGE_STRING
);
2172 puts(SYSTEMD_FEATURES
);
2176 r
= strv_extend(&arg_property
, optarg
);
2180 /* If the user asked for a particular
2181 * property, show it to him, even if it is
2195 if (safe_atou(optarg
, &arg_lines
) < 0) {
2196 log_error("Failed to parse lines '%s'", optarg
);
2202 arg_output
= output_mode_from_string(optarg
);
2203 if (arg_output
< 0) {
2204 log_error("Unknown output '%s'.", optarg
);
2210 arg_no_pager
= true;
2218 arg_kill_who
= optarg
;
2222 arg_signal
= signal_from_string_try_harder(optarg
);
2223 if (arg_signal
< 0) {
2224 log_error("Failed to parse signal string %s.", optarg
);
2229 case ARG_NO_ASK_PASSWORD
:
2230 arg_ask_password
= false;
2234 arg_transport
= BUS_TRANSPORT_REMOTE
;
2239 arg_transport
= BUS_TRANSPORT_MACHINE
;
2244 arg_read_only
= true;
2256 arg_verify
= import_verify_from_string(optarg
);
2257 if (arg_verify
< 0) {
2258 log_error("Failed to parse --verify= setting: %s", optarg
);
2267 case ARG_DKR_INDEX_URL
:
2268 if (!http_url_is_valid(optarg
)) {
2269 log_error("Index URL is invalid: %s", optarg
);
2273 arg_dkr_index_url
= optarg
;
2280 assert_not_reached("Unhandled option");
2286 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2288 static const Verb verbs
[] = {
2289 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2290 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2291 { "list-images", VERB_ANY
, 1, 0, list_images
},
2292 { "status", 2, VERB_ANY
, 0, show_machine
},
2293 { "image-status", 2, VERB_ANY
, 0, show_image
},
2294 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2295 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2296 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2297 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2298 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2299 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2300 { "login", 2, 2, 0, login_machine
},
2301 { "bind", 3, 4, 0, bind_mount
},
2302 { "copy-to", 3, 4, 0, copy_files
},
2303 { "copy-from", 3, 4, 0, copy_files
},
2304 { "remove", 2, VERB_ANY
, 0, remove_image
},
2305 { "rename", 3, 3, 0, rename_image
},
2306 { "clone", 3, 3, 0, clone_image
},
2307 { "read-only", 2, 3, 0, read_only_image
},
2308 { "start", 2, VERB_ANY
, 0, start_machine
},
2309 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2310 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2311 { "pull-tar", 2, 3, 0, pull_tar
},
2312 { "pull-raw", 2, 3, 0, pull_raw
},
2313 { "pull-dkr", 2, 3, 0, pull_dkr
},
2314 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2315 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2319 return dispatch_verb(argc
, argv
, verbs
, bus
);
2322 int main(int argc
, char*argv
[]) {
2323 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
2326 setlocale(LC_ALL
, "");
2327 log_parse_environment();
2330 r
= parse_argv(argc
, argv
);
2334 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2336 log_error_errno(r
, "Failed to create bus connection: %m");
2340 r
= machinectl_main(argc
, argv
, bus
);
2344 polkit_agent_close();
2346 strv_free(arg_property
);
2348 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;