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 print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
504 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
505 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
511 fputs(strna(i
->name
), stdout
);
513 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
514 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
518 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
519 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
522 printf("\t Since: %s; %s\n", s2
, s1
);
524 printf("\t Since: %s\n", s2
);
527 _cleanup_free_
char *t
= NULL
;
529 printf("\t Leader: %u", (unsigned) i
->leader
);
531 get_process_comm(i
->leader
, &t
);
539 printf("\t Service: %s", i
->service
);
542 printf("; class %s", i
->class);
546 printf("\t Class: %s\n", i
->class);
548 if (i
->root_directory
)
549 printf("\t Root: %s\n", i
->root_directory
);
551 if (i
->n_netif
> 0) {
554 fputs("\t Iface:", stdout
);
556 for (c
= 0; c
< i
->n_netif
; c
++) {
557 char name
[IF_NAMESIZE
+1] = "";
559 if (if_indextoname(i
->netif
[c
], name
)) {
568 printf(" %i", i
->netif
[c
]);
574 print_addresses(bus
, i
->name
, ifi
,
578 print_os_release(bus
, i
->name
, "\t OS: ");
581 printf("\t Unit: %s\n", i
->unit
);
582 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
584 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
586 show_journal_by_unit(
591 i
->timestamp
.monotonic
,
594 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
595 SD_JOURNAL_LOCAL_ONLY
,
602 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
603 MachineStatusInfo
*i
= userdata
;
608 assert_cc(sizeof(int32_t) == sizeof(int));
609 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
615 i
->n_netif
= l
/ sizeof(int32_t);
616 i
->netif
= memdup(v
, l
);
623 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
625 static const struct bus_properties_map map
[] = {
626 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
627 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
628 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
629 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
630 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
631 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
632 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
633 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
634 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
635 { "NetworkInterfaces", "ai", map_netif
, 0 },
639 MachineStatusInfo info
= {};
647 r
= bus_map_all_properties(bus
,
648 "org.freedesktop.machine1",
653 return log_error_errno(r
, "Could not get properties: %m");
659 print_machine_status_info(bus
, &info
);
665 free(info
.root_directory
);
671 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
683 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
685 log_error_errno(r
, "Could not get properties: %m");
690 static int show_machine(int argc
, char *argv
[], void *userdata
) {
692 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
693 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
694 bool properties
, new_line
= false;
695 sd_bus
*bus
= userdata
;
700 properties
= !strstr(argv
[0], "status");
702 pager_open_if_enabled();
704 if (properties
&& argc
<= 1) {
706 /* If no argument is specified, inspect the manager
708 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
713 for (i
= 1; i
< argc
; i
++) {
714 const char *path
= NULL
;
716 r
= sd_bus_call_method(
718 "org.freedesktop.machine1",
719 "/org/freedesktop/machine1",
720 "org.freedesktop.machine1.Manager",
726 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
730 r
= sd_bus_message_read(reply
, "o", &path
);
732 return bus_log_parse_error(r
);
735 r
= show_machine_properties(bus
, path
, &new_line
);
737 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
743 typedef struct ImageStatusInfo
{
752 uint64_t usage_exclusive
;
753 uint64_t limit_exclusive
;
756 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
757 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
758 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
759 char bs
[FORMAT_BYTES_MAX
], *s3
;
760 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
766 fputs(i
->name
, stdout
);
771 printf("\t Type: %s\n", i
->type
);
774 printf("\t Path: %s\n", i
->path
);
776 printf("\t RO: %s%s%s\n",
777 i
->read_only
? ansi_highlight_red() : "",
778 i
->read_only
? "read-only" : "writable",
779 i
->read_only
? ansi_highlight_off() : "");
781 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
782 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
784 printf("\t Created: %s; %s\n", s2
, s1
);
786 printf("\t Created: %s\n", s2
);
788 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
789 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
791 printf("\tModified: %s; %s\n", s2
, s1
);
793 printf("\tModified: %s\n", s2
);
795 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
796 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
798 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
800 printf("\t Usage: %s\n", s3
);
802 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
803 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
805 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
807 printf("\t Limit: %s\n", s3
);
810 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
812 static const struct bus_properties_map map
[] = {
813 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
814 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
815 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
816 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
817 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
818 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
819 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
820 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
821 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
822 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
826 ImageStatusInfo info
= {};
833 r
= bus_map_all_properties(bus
,
834 "org.freedesktop.machine1",
839 return log_error_errno(r
, "Could not get properties: %m");
845 print_image_status_info(bus
, &info
);
854 typedef struct PoolStatusInfo
{
860 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
861 char bs
[FORMAT_BYTES_MAX
], *s
;
864 printf("\t Path: %s\n", i
->path
);
866 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
868 printf("\t Usage: %s\n", s
);
870 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
872 printf("\t Limit: %s\n", s
);
875 static int show_pool_info(sd_bus
*bus
) {
877 static const struct bus_properties_map map
[] = {
878 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
879 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
880 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
884 PoolStatusInfo info
= {
885 .usage
= (uint64_t) -1,
886 .limit
= (uint64_t) -1,
892 r
= bus_map_all_properties(bus
,
893 "org.freedesktop.machine1",
894 "/org/freedesktop/machine1",
898 return log_error_errno(r
, "Could not get properties: %m");
900 print_pool_status_info(bus
, &info
);
907 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
919 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
921 log_error_errno(r
, "Could not get properties: %m");
926 static int show_image(int argc
, char *argv
[], void *userdata
) {
928 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
929 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
930 bool properties
, new_line
= false;
931 sd_bus
*bus
= userdata
;
936 properties
= !strstr(argv
[0], "status");
938 pager_open_if_enabled();
942 /* If no argument is specified, inspect the manager
946 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
948 r
= show_pool_info(bus
);
953 for (i
= 1; i
< argc
; i
++) {
954 const char *path
= NULL
;
956 r
= sd_bus_call_method(
958 "org.freedesktop.machine1",
959 "/org/freedesktop/machine1",
960 "org.freedesktop.machine1.Manager",
966 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
970 r
= sd_bus_message_read(reply
, "o", &path
);
972 return bus_log_parse_error(r
);
975 r
= show_image_properties(bus
, path
, &new_line
);
977 r
= show_image_info(bus
, path
, &new_line
);
983 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
984 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
985 sd_bus
*bus
= userdata
;
990 polkit_agent_open_if_enabled();
993 arg_kill_who
= "all";
995 for (i
= 1; i
< argc
; i
++) {
996 r
= sd_bus_call_method(
998 "org.freedesktop.machine1",
999 "/org/freedesktop/machine1",
1000 "org.freedesktop.machine1.Manager",
1004 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1006 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1014 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1015 arg_kill_who
= "leader";
1016 arg_signal
= SIGINT
; /* sysvinit + systemd */
1018 return kill_machine(argc
, argv
, userdata
);
1021 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1022 arg_kill_who
= "leader";
1023 arg_signal
= SIGRTMIN
+4; /* only systemd */
1025 return kill_machine(argc
, argv
, userdata
);
1028 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1029 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1030 sd_bus
*bus
= userdata
;
1035 polkit_agent_open_if_enabled();
1037 for (i
= 1; i
< argc
; i
++) {
1038 r
= sd_bus_call_method(
1040 "org.freedesktop.machine1",
1041 "/org/freedesktop/machine1",
1042 "org.freedesktop.machine1.Manager",
1048 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1056 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1057 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1058 sd_bus
*bus
= userdata
;
1064 polkit_agent_open_if_enabled();
1066 copy_from
= streq(argv
[0], "copy-from");
1068 r
= sd_bus_call_method(
1070 "org.freedesktop.machine1",
1071 "/org/freedesktop/machine1",
1072 "org.freedesktop.machine1.Manager",
1073 copy_from
? "CopyFromMachine" : "CopyToMachine",
1081 log_error("Failed to copy: %s", bus_error_message(&error
, -r
));
1088 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1089 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1090 sd_bus
*bus
= userdata
;
1095 polkit_agent_open_if_enabled();
1097 r
= sd_bus_call_method(
1099 "org.freedesktop.machine1",
1100 "/org/freedesktop/machine1",
1101 "org.freedesktop.machine1.Manager",
1112 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1119 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1120 PTYForward
** forward
= (PTYForward
**) userdata
;
1127 /* If the forwarder is already initialized, tell it to
1128 * exit on the next vhangup(), so that we still flush
1129 * out what might be queued and exit then. */
1131 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1135 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1138 /* On error, or when the forwarder is not initialized yet, quit immediately */
1139 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1143 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1144 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1145 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1146 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1147 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1148 _cleanup_event_unref_ sd_event
*event
= NULL
;
1149 int master
= -1, r
, ret
= 0;
1150 sd_bus
*bus
= userdata
;
1151 const char *pty
, *match
;
1157 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1158 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1159 log_error("Login only supported on local machines.");
1163 polkit_agent_open_if_enabled();
1165 r
= sd_event_default(&event
);
1167 return log_error_errno(r
, "Failed to get event loop: %m");
1169 r
= sd_bus_attach_event(bus
, event
, 0);
1171 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1173 match
= strjoina("type='signal',"
1174 "sender='org.freedesktop.machine1',"
1175 "path='/org/freedesktop/machine1',",
1176 "interface='org.freedesktop.machine1.Manager',"
1177 "member='MachineRemoved',"
1182 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1184 return log_error_errno(r
, "Failed to add machine removal match: %m");
1186 r
= sd_bus_call_method(
1188 "org.freedesktop.machine1",
1189 "/org/freedesktop/machine1",
1190 "org.freedesktop.machine1.Manager",
1196 log_error("Failed to get machine PTY: %s", bus_error_message(&error
, -r
));
1200 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1202 return bus_log_parse_error(r
);
1204 sigprocmask_many(SIG_BLOCK
, SIGWINCH
, SIGTERM
, SIGINT
, -1);
1206 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv
[1]);
1208 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1209 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1211 r
= pty_forward_new(event
, master
, true, false, &forward
);
1213 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1215 r
= sd_event_loop(event
);
1217 return log_error_errno(r
, "Failed to run event loop: %m");
1219 pty_forward_get_last_char(forward
, &last_char
);
1220 machine_died
= pty_forward_get_ignore_vhangup(forward
) == 0;
1222 forward
= pty_forward_free(forward
);
1224 if (last_char
!= '\n')
1225 fputc('\n', stdout
);
1228 log_info("Machine %s terminated.", argv
[1]);
1230 log_info("Connection to machine %s terminated.", argv
[1]);
1232 sd_event_get_exit_code(event
, &ret
);
1236 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1237 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1238 sd_bus
*bus
= userdata
;
1243 polkit_agent_open_if_enabled();
1245 for (i
= 1; i
< argc
; i
++) {
1246 r
= sd_bus_call_method(
1248 "org.freedesktop.machine1",
1249 "/org/freedesktop/machine1",
1250 "org.freedesktop.machine1.Manager",
1256 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1264 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1265 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1266 sd_bus
*bus
= userdata
;
1269 polkit_agent_open_if_enabled();
1271 r
= sd_bus_call_method(
1273 "org.freedesktop.machine1",
1274 "/org/freedesktop/machine1",
1275 "org.freedesktop.machine1.Manager",
1279 "ss", argv
[1], argv
[2]);
1281 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1288 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1289 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1290 sd_bus
*bus
= userdata
;
1293 polkit_agent_open_if_enabled();
1295 r
= sd_bus_call_method(
1297 "org.freedesktop.machine1",
1298 "/org/freedesktop/machine1",
1299 "org.freedesktop.machine1.Manager",
1303 "ssb", argv
[1], argv
[2], arg_read_only
);
1305 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1312 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1313 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1314 sd_bus
*bus
= userdata
;
1318 b
= parse_boolean(argv
[2]);
1320 log_error("Failed to parse boolean argument: %s", argv
[2]);
1325 polkit_agent_open_if_enabled();
1327 r
= sd_bus_call_method(
1329 "org.freedesktop.machine1",
1330 "/org/freedesktop/machine1",
1331 "org.freedesktop.machine1.Manager",
1332 "MarkImageReadOnly",
1337 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1344 static int make_service_name(const char *name
, char **ret
) {
1345 _cleanup_free_
char *e
= NULL
;
1351 if (!machine_name_is_valid(name
)) {
1352 log_error("Invalid machine name %s.", name
);
1356 e
= unit_name_escape(name
);
1360 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1362 return log_error_errno(r
, "Failed to build unit name: %m");
1367 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1368 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1369 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1370 sd_bus
*bus
= userdata
;
1375 polkit_agent_open_if_enabled();
1377 r
= bus_wait_for_jobs_new(bus
, &w
);
1381 for (i
= 1; i
< argc
; i
++) {
1382 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1383 _cleanup_free_
char *unit
= NULL
;
1386 r
= make_service_name(argv
[i
], &unit
);
1390 r
= sd_bus_call_method(
1392 "org.freedesktop.systemd1",
1393 "/org/freedesktop/systemd1",
1394 "org.freedesktop.systemd1.Manager",
1398 "ss", unit
, "fail");
1400 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1404 r
= sd_bus_message_read(reply
, "o", &object
);
1406 return bus_log_parse_error(r
);
1408 r
= bus_wait_for_jobs_add(w
, object
);
1413 r
= bus_wait_for_jobs(w
, arg_quiet
);
1420 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1421 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1422 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1423 int carries_install_info
= 0;
1424 const char *method
= NULL
;
1425 sd_bus
*bus
= userdata
;
1430 polkit_agent_open_if_enabled();
1432 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1434 r
= sd_bus_message_new_method_call(
1437 "org.freedesktop.systemd1",
1438 "/org/freedesktop/systemd1",
1439 "org.freedesktop.systemd1.Manager",
1442 return bus_log_create_error(r
);
1444 r
= sd_bus_message_open_container(m
, 'a', "s");
1446 return bus_log_create_error(r
);
1448 for (i
= 1; i
< argc
; i
++) {
1449 _cleanup_free_
char *unit
= NULL
;
1451 r
= make_service_name(argv
[i
], &unit
);
1455 r
= sd_bus_message_append(m
, "s", unit
);
1457 return bus_log_create_error(r
);
1460 r
= sd_bus_message_close_container(m
);
1462 return bus_log_create_error(r
);
1464 if (streq(argv
[0], "enable"))
1465 r
= sd_bus_message_append(m
, "bb", false, false);
1467 r
= sd_bus_message_append(m
, "b", false);
1469 return bus_log_create_error(r
);
1471 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1473 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1477 if (streq(argv
[0], "enable")) {
1478 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1480 return bus_log_parse_error(r
);
1483 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1487 r
= sd_bus_call_method(
1489 "org.freedesktop.systemd1",
1490 "/org/freedesktop/systemd1",
1491 "org.freedesktop.systemd1.Manager",
1497 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1504 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1505 const char **our_path
= userdata
, *line
;
1512 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1514 bus_log_parse_error(r
);
1518 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1521 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1524 log_full(priority
, "%s", line
);
1528 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1529 const char **our_path
= userdata
, *path
, *result
;
1536 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1538 bus_log_parse_error(r
);
1542 if (!streq_ptr(*our_path
, path
))
1545 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1549 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1554 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1556 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1560 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1561 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1562 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1563 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1564 _cleanup_event_unref_ sd_event
* event
= NULL
;
1565 const char *path
= NULL
;
1572 polkit_agent_open_if_enabled();
1574 r
= sd_event_default(&event
);
1576 return log_error_errno(r
, "Failed to get event loop: %m");
1578 r
= sd_bus_attach_event(bus
, event
, 0);
1580 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1582 r
= sd_bus_add_match(
1586 "sender='org.freedesktop.import1',"
1587 "interface='org.freedesktop.import1.Manager',"
1588 "member='TransferRemoved',"
1589 "path='/org/freedesktop/import1'",
1590 match_transfer_removed
, &path
);
1592 return log_error_errno(r
, "Failed to install match: %m");
1594 r
= sd_bus_add_match(
1598 "sender='org.freedesktop.import1',"
1599 "interface='org.freedesktop.import1.Transfer',"
1600 "member='LogMessage'",
1601 match_log_message
, &path
);
1603 return log_error_errno(r
, "Failed to install match: %m");
1605 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1607 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1611 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1613 return bus_log_parse_error(r
);
1615 sigprocmask_many(SIG_BLOCK
, SIGTERM
, SIGINT
, -1);
1618 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1620 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1621 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1623 r
= sd_event_loop(event
);
1625 return log_error_errno(r
, "Failed to run event loop: %m");
1630 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1631 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1632 _cleanup_free_
char *ll
= NULL
;
1633 _cleanup_close_
int fd
= -1;
1634 const char *local
= NULL
, *path
= NULL
;
1635 sd_bus
*bus
= userdata
;
1642 if (isempty(path
) || streq(path
, "-"))
1648 local
= basename(path
);
1649 if (isempty(local
) || streq(local
, "-"))
1653 log_error("Need either path or local name.");
1657 r
= tar_strip_suffixes(local
, &ll
);
1663 if (!machine_name_is_valid(local
)) {
1664 log_error("Local name %s is not a suitable machine name.", local
);
1669 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1671 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1674 r
= sd_bus_message_new_method_call(
1677 "org.freedesktop.import1",
1678 "/org/freedesktop/import1",
1679 "org.freedesktop.import1.Manager",
1682 return bus_log_create_error(r
);
1684 r
= sd_bus_message_append(
1687 fd
>= 0 ? fd
: STDIN_FILENO
,
1692 return bus_log_create_error(r
);
1694 return transfer_image_common(bus
, m
);
1697 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1698 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1699 _cleanup_free_
char *ll
= NULL
;
1700 _cleanup_close_
int fd
= -1;
1701 const char *local
= NULL
, *path
= NULL
;
1702 sd_bus
*bus
= userdata
;
1709 if (isempty(path
) || streq(path
, "-"))
1715 local
= basename(path
);
1716 if (isempty(local
) || streq(local
, "-"))
1720 log_error("Need either path or local name.");
1724 r
= raw_strip_suffixes(local
, &ll
);
1730 if (!machine_name_is_valid(local
)) {
1731 log_error("Local name %s is not a suitable machine name.", local
);
1736 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1738 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1741 r
= sd_bus_message_new_method_call(
1744 "org.freedesktop.import1",
1745 "/org/freedesktop/import1",
1746 "org.freedesktop.import1.Manager",
1749 return bus_log_create_error(r
);
1751 r
= sd_bus_message_append(
1754 fd
>= 0 ? fd
: STDIN_FILENO
,
1759 return bus_log_create_error(r
);
1761 return transfer_image_common(bus
, m
);
1764 static void determine_compression_from_filename(const char *p
) {
1771 if (endswith(p
, ".xz"))
1773 else if (endswith(p
, ".gz"))
1774 arg_format
= "gzip";
1775 else if (endswith(p
, ".bz2"))
1776 arg_format
= "bzip2";
1779 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1780 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1781 _cleanup_close_
int fd
= -1;
1782 const char *local
= NULL
, *path
= NULL
;
1783 sd_bus
*bus
= userdata
;
1789 if (!machine_name_is_valid(local
)) {
1790 log_error("Machine name %s is not valid.", local
);
1796 if (isempty(path
) || streq(path
, "-"))
1800 determine_compression_from_filename(path
);
1802 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1804 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1807 r
= sd_bus_message_new_method_call(
1810 "org.freedesktop.import1",
1811 "/org/freedesktop/import1",
1812 "org.freedesktop.import1.Manager",
1815 return bus_log_create_error(r
);
1817 r
= sd_bus_message_append(
1821 fd
>= 0 ? fd
: STDOUT_FILENO
,
1824 return bus_log_create_error(r
);
1826 return transfer_image_common(bus
, m
);
1829 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1830 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1831 _cleanup_close_
int fd
= -1;
1832 const char *local
= NULL
, *path
= NULL
;
1833 sd_bus
*bus
= userdata
;
1839 if (!machine_name_is_valid(local
)) {
1840 log_error("Machine name %s is not valid.", local
);
1846 if (isempty(path
) || streq(path
, "-"))
1850 determine_compression_from_filename(path
);
1852 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1854 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1857 r
= sd_bus_message_new_method_call(
1860 "org.freedesktop.import1",
1861 "/org/freedesktop/import1",
1862 "org.freedesktop.import1.Manager",
1865 return bus_log_create_error(r
);
1867 r
= sd_bus_message_append(
1871 fd
>= 0 ? fd
: STDOUT_FILENO
,
1874 return bus_log_create_error(r
);
1876 return transfer_image_common(bus
, m
);
1879 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
1880 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1881 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1882 const char *local
, *remote
;
1883 sd_bus
*bus
= userdata
;
1889 if (!http_url_is_valid(remote
)) {
1890 log_error("URL '%s' is not valid.", remote
);
1897 r
= import_url_last_component(remote
, &l
);
1899 return log_error_errno(r
, "Failed to get final component of URL: %m");
1904 if (isempty(local
) || streq(local
, "-"))
1908 r
= tar_strip_suffixes(local
, &ll
);
1914 if (!machine_name_is_valid(local
)) {
1915 log_error("Local name %s is not a suitable machine name.", local
);
1920 r
= sd_bus_message_new_method_call(
1923 "org.freedesktop.import1",
1924 "/org/freedesktop/import1",
1925 "org.freedesktop.import1.Manager",
1928 return bus_log_create_error(r
);
1930 r
= sd_bus_message_append(
1935 import_verify_to_string(arg_verify
),
1938 return bus_log_create_error(r
);
1940 return transfer_image_common(bus
, m
);
1943 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
1944 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1945 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1946 const char *local
, *remote
;
1947 sd_bus
*bus
= userdata
;
1953 if (!http_url_is_valid(remote
)) {
1954 log_error("URL '%s' is not valid.", remote
);
1961 r
= import_url_last_component(remote
, &l
);
1963 return log_error_errno(r
, "Failed to get final component of URL: %m");
1968 if (isempty(local
) || streq(local
, "-"))
1972 r
= raw_strip_suffixes(local
, &ll
);
1978 if (!machine_name_is_valid(local
)) {
1979 log_error("Local name %s is not a suitable machine name.", local
);
1984 r
= sd_bus_message_new_method_call(
1987 "org.freedesktop.import1",
1988 "/org/freedesktop/import1",
1989 "org.freedesktop.import1.Manager",
1992 return bus_log_create_error(r
);
1994 r
= sd_bus_message_append(
1999 import_verify_to_string(arg_verify
),
2002 return bus_log_create_error(r
);
2004 return transfer_image_common(bus
, m
);
2007 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2008 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2009 const char *local
, *remote
, *tag
;
2010 sd_bus
*bus
= userdata
;
2013 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2014 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2019 tag
= strchr(remote
, ':');
2021 remote
= strndupa(remote
, tag
- remote
);
2025 if (!dkr_name_is_valid(remote
)) {
2026 log_error("DKR name '%s' is invalid.", remote
);
2029 if (tag
&& !dkr_tag_is_valid(tag
)) {
2030 log_error("DKR tag '%s' is invalid.", remote
);
2037 local
= strchr(remote
, '/');
2044 if (isempty(local
) || streq(local
, "-"))
2048 if (!machine_name_is_valid(local
)) {
2049 log_error("Local name %s is not a suitable machine name.", local
);
2054 r
= sd_bus_message_new_method_call(
2057 "org.freedesktop.import1",
2058 "/org/freedesktop/import1",
2059 "org.freedesktop.import1.Manager",
2062 return bus_log_create_error(r
);
2064 r
= sd_bus_message_append(
2071 import_verify_to_string(arg_verify
),
2074 return bus_log_create_error(r
);
2076 return transfer_image_common(bus
, m
);
2079 typedef struct TransferInfo
{
2087 static int compare_transfer_info(const void *a
, const void *b
) {
2088 const TransferInfo
*x
= a
, *y
= b
;
2090 return strcmp(x
->local
, y
->local
);
2093 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2094 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2095 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2096 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2097 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2098 size_t n_transfers
= 0, n_allocated
= 0, j
;
2099 const char *type
, *remote
, *local
, *object
;
2100 sd_bus
*bus
= userdata
;
2101 uint32_t id
, max_id
= 0;
2105 pager_open_if_enabled();
2107 r
= sd_bus_call_method(
2109 "org.freedesktop.import1",
2110 "/org/freedesktop/import1",
2111 "org.freedesktop.import1.Manager",
2117 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2121 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2123 return bus_log_parse_error(r
);
2125 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2128 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2131 transfers
[n_transfers
].id
= id
;
2132 transfers
[n_transfers
].type
= type
;
2133 transfers
[n_transfers
].remote
= remote
;
2134 transfers
[n_transfers
].local
= local
;
2135 transfers
[n_transfers
].progress
= progress
;
2155 return bus_log_parse_error(r
);
2157 r
= sd_bus_message_exit_container(reply
);
2159 return bus_log_parse_error(r
);
2161 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2164 printf("%-*s %-*s %-*s %-*s %-*s\n",
2165 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2167 (int) max_type
, "TYPE",
2168 (int) max_local
, "LOCAL",
2169 (int) max_remote
, "REMOTE");
2171 for (j
= 0; j
< n_transfers
; j
++)
2172 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2173 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2174 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2175 (int) max_type
, transfers
[j
].type
,
2176 (int) max_local
, transfers
[j
].local
,
2177 (int) max_remote
, transfers
[j
].remote
);
2180 printf("\n%zu transfers listed.\n", n_transfers
);
2185 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2186 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2187 sd_bus
*bus
= userdata
;
2192 polkit_agent_open_if_enabled();
2194 for (i
= 1; i
< argc
; i
++) {
2197 r
= safe_atou32(argv
[i
], &id
);
2199 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2201 r
= sd_bus_call_method(
2203 "org.freedesktop.import1",
2204 "/org/freedesktop/import1",
2205 "org.freedesktop.import1.Manager",
2211 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2219 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2220 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2221 sd_bus
*bus
= userdata
;
2225 if (streq(argv
[argc
-1], "-"))
2226 limit
= (uint64_t) -1;
2230 r
= parse_size(argv
[argc
-1], 1024, &off
);
2232 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2234 limit
= (uint64_t) off
;
2238 /* With two arguments changes the quota limit of the
2239 * specified image */
2240 r
= sd_bus_call_method(
2242 "org.freedesktop.machine1",
2243 "/org/freedesktop/machine1",
2244 "org.freedesktop.machine1.Manager",
2248 "st", argv
[1], limit
);
2250 /* With one argument changes the pool quota limit */
2251 r
= sd_bus_call_method(
2253 "org.freedesktop.machine1",
2254 "/org/freedesktop/machine1",
2255 "org.freedesktop.machine1.Manager",
2262 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2269 static int help(int argc
, char *argv
[], void *userdata
) {
2271 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2272 "Send control commands to or query the virtual machine and container\n"
2273 "registration manager.\n\n"
2274 " -h --help Show this help\n"
2275 " --version Show package version\n"
2276 " --no-pager Do not pipe output into a pager\n"
2277 " --no-legend Do not show the headers and footers\n"
2278 " --no-ask-password Do not ask for system passwords\n"
2279 " -H --host=[USER@]HOST Operate on remote host\n"
2280 " -M --machine=CONTAINER Operate on local container\n"
2281 " -p --property=NAME Show only properties by this name\n"
2282 " -q --quiet Suppress output\n"
2283 " -a --all Show all properties, including empty ones\n"
2284 " -l --full Do not ellipsize output\n"
2285 " --kill-who=WHO Who to send signal to\n"
2286 " -s --signal=SIGNAL Which signal to send\n"
2287 " --read-only Create read-only bind mount\n"
2288 " --mkdir Create directory before bind mounting, if missing\n"
2289 " -n --lines=INTEGER Number of journal entries to show\n"
2290 " -o --output=STRING Change journal output mode (short,\n"
2291 " short-monotonic, verbose, export, json,\n"
2292 " json-pretty, json-sse, cat)\n"
2293 " --verify=MODE Verification mode for downloaded images (no,\n"
2294 " checksum, signature)\n"
2295 " --force Download image even if already exists\n"
2296 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2298 "Machine Commands:\n"
2299 " list List running VMs and containers\n"
2300 " status NAME... Show VM/container details\n"
2301 " show NAME... Show properties of one or more VMs/containers\n"
2302 " start NAME... Start container as a service\n"
2303 " login NAME Get a login prompt on a container\n"
2304 " enable NAME... Enable automatic container start at boot\n"
2305 " disable NAME... Disable automatic container start at boot\n"
2306 " poweroff NAME... Power off one or more containers\n"
2307 " reboot NAME... Reboot one or more containers\n"
2308 " terminate NAME... Terminate one or more VMs/containers\n"
2309 " kill NAME... Send signal to processes of a VM/container\n"
2310 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2311 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2312 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2314 " list-images Show available container and VM images\n"
2315 " image-status NAME... Show image details\n"
2316 " show-image NAME... Show properties of image\n"
2317 " clone NAME NAME Clone an image\n"
2318 " rename NAME NAME Rename an image\n"
2319 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2320 " remove NAME... Remove an image\n"
2321 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2322 "Image Transfer Commands:\n"
2323 " pull-tar URL [NAME] Download a TAR container image\n"
2324 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2325 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2326 " import-tar FILE [NAME] Import a local TAR container image\n"
2327 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2328 " export-tar NAME [FILE] Export a TAR container image locally\n"
2329 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2330 " list-transfers Show list of downloads in progress\n"
2331 " cancel-transfer Cancel a download\n"
2332 , program_invocation_short_name
);
2337 static int parse_argv(int argc
, char *argv
[]) {
2340 ARG_VERSION
= 0x100,
2346 ARG_NO_ASK_PASSWORD
,
2353 static const struct option options
[] = {
2354 { "help", no_argument
, NULL
, 'h' },
2355 { "version", no_argument
, NULL
, ARG_VERSION
},
2356 { "property", required_argument
, NULL
, 'p' },
2357 { "all", no_argument
, NULL
, 'a' },
2358 { "full", no_argument
, NULL
, 'l' },
2359 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2360 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2361 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2362 { "signal", required_argument
, NULL
, 's' },
2363 { "host", required_argument
, NULL
, 'H' },
2364 { "machine", required_argument
, NULL
, 'M' },
2365 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2366 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2367 { "quiet", no_argument
, NULL
, 'q' },
2368 { "lines", required_argument
, NULL
, 'n' },
2369 { "output", required_argument
, NULL
, 'o' },
2370 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2371 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2372 { "force", no_argument
, NULL
, ARG_FORCE
},
2373 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2374 { "format", required_argument
, NULL
, ARG_FORMAT
},
2383 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2388 return help(0, NULL
, NULL
);
2391 puts(PACKAGE_STRING
);
2392 puts(SYSTEMD_FEATURES
);
2396 r
= strv_extend(&arg_property
, optarg
);
2400 /* If the user asked for a particular
2401 * property, show it to him, even if it is
2415 if (safe_atou(optarg
, &arg_lines
) < 0) {
2416 log_error("Failed to parse lines '%s'", optarg
);
2422 arg_output
= output_mode_from_string(optarg
);
2423 if (arg_output
< 0) {
2424 log_error("Unknown output '%s'.", optarg
);
2430 arg_no_pager
= true;
2438 arg_kill_who
= optarg
;
2442 arg_signal
= signal_from_string_try_harder(optarg
);
2443 if (arg_signal
< 0) {
2444 log_error("Failed to parse signal string %s.", optarg
);
2449 case ARG_NO_ASK_PASSWORD
:
2450 arg_ask_password
= false;
2454 arg_transport
= BUS_TRANSPORT_REMOTE
;
2459 arg_transport
= BUS_TRANSPORT_MACHINE
;
2464 arg_read_only
= true;
2476 arg_verify
= import_verify_from_string(optarg
);
2477 if (arg_verify
< 0) {
2478 log_error("Failed to parse --verify= setting: %s", optarg
);
2487 case ARG_DKR_INDEX_URL
:
2488 if (!http_url_is_valid(optarg
)) {
2489 log_error("Index URL is invalid: %s", optarg
);
2493 arg_dkr_index_url
= optarg
;
2497 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2498 log_error("Unknown format: %s", optarg
);
2502 arg_format
= optarg
;
2509 assert_not_reached("Unhandled option");
2515 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2517 static const Verb verbs
[] = {
2518 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2519 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2520 { "list-images", VERB_ANY
, 1, 0, list_images
},
2521 { "status", 2, VERB_ANY
, 0, show_machine
},
2522 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2523 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2524 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2525 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2526 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2527 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2528 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2529 { "login", 2, 2, 0, login_machine
},
2530 { "bind", 3, 4, 0, bind_mount
},
2531 { "copy-to", 3, 4, 0, copy_files
},
2532 { "copy-from", 3, 4, 0, copy_files
},
2533 { "remove", 2, VERB_ANY
, 0, remove_image
},
2534 { "rename", 3, 3, 0, rename_image
},
2535 { "clone", 3, 3, 0, clone_image
},
2536 { "read-only", 2, 3, 0, read_only_image
},
2537 { "start", 2, VERB_ANY
, 0, start_machine
},
2538 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2539 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2540 { "import-tar", 2, 3, 0, import_tar
},
2541 { "import-raw", 2, 3, 0, import_raw
},
2542 { "export-tar", 2, 3, 0, export_tar
},
2543 { "export-raw", 2, 3, 0, export_raw
},
2544 { "pull-tar", 2, 3, 0, pull_tar
},
2545 { "pull-raw", 2, 3, 0, pull_raw
},
2546 { "pull-dkr", 2, 3, 0, pull_dkr
},
2547 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2548 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2549 { "set-limit", 2, 3, 0, set_limit
},
2553 return dispatch_verb(argc
, argv
, verbs
, bus
);
2556 int main(int argc
, char*argv
[]) {
2557 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
2560 setlocale(LC_ALL
, "");
2561 log_parse_environment();
2564 r
= parse_argv(argc
, argv
);
2568 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2570 log_error_errno(r
, "Failed to create bus connection: %m");
2574 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2576 r
= machinectl_main(argc
, argv
, bus
);
2580 polkit_agent_close();
2582 strv_free(arg_property
);
2584 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;