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 <arpa/inet.h>
28 #include <netinet/in.h>
30 #include <sys/mount.h>
31 #include <sys/socket.h>
36 #include "bus-error.h"
38 #include "cgroup-show.h"
39 #include "cgroup-util.h"
42 #include "event-util.h"
44 #include "hostname-util.h"
45 #include "import-util.h"
47 #include "logs-show.h"
51 #include "parse-util.h"
52 #include "path-util.h"
53 #include "process-util.h"
55 #include "signal-util.h"
56 #include "spawn-polkit-agent.h"
58 #include "terminal-util.h"
59 #include "unit-name.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
;
81 static const char* arg_format
= NULL
;
82 static const char *arg_uid
= NULL
;
83 static char **arg_setenv
= NULL
;
85 static void pager_open_if_enabled(void) {
93 static void polkit_agent_open_if_enabled(void) {
95 /* Open the polkit agent as a child process if necessary */
97 if (!arg_ask_password
)
100 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
106 static OutputFlags
get_output_flags(void) {
108 arg_all
* OUTPUT_SHOW_ALL
|
109 arg_full
* OUTPUT_FULL_WIDTH
|
110 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
111 on_tty() * OUTPUT_COLOR
|
112 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
115 typedef struct MachineInfo
{
121 static int compare_machine_info(const void *a
, const void *b
) {
122 const MachineInfo
*x
= a
, *y
= b
;
124 return strcmp(x
->name
, y
->name
);
127 static int list_machines(int argc
, char *argv
[], void *userdata
) {
129 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
130 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
131 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
132 _cleanup_free_ MachineInfo
*machines
= NULL
;
133 const char *name
, *class, *service
, *object
;
134 size_t n_machines
= 0, n_allocated
= 0, j
;
135 sd_bus
*bus
= userdata
;
140 pager_open_if_enabled();
142 r
= sd_bus_call_method(
144 "org.freedesktop.machine1",
145 "/org/freedesktop/machine1",
146 "org.freedesktop.machine1.Manager",
152 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
156 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
158 return bus_log_parse_error(r
);
160 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
163 if (name
[0] == '.' && !arg_all
)
166 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
169 machines
[n_machines
].name
= name
;
170 machines
[n_machines
].class = class;
171 machines
[n_machines
].service
= service
;
188 return bus_log_parse_error(r
);
190 r
= sd_bus_message_exit_container(reply
);
192 return bus_log_parse_error(r
);
194 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
197 printf("%-*s %-*s %-*s\n",
198 (int) max_name
, "MACHINE",
199 (int) max_class
, "CLASS",
200 (int) max_service
, "SERVICE");
202 for (j
= 0; j
< n_machines
; j
++)
203 printf("%-*s %-*s %-*s\n",
204 (int) max_name
, machines
[j
].name
,
205 (int) max_class
, machines
[j
].class,
206 (int) max_service
, machines
[j
].service
);
209 printf("\n%zu machines listed.\n", n_machines
);
214 typedef struct ImageInfo
{
223 static int compare_image_info(const void *a
, const void *b
) {
224 const ImageInfo
*x
= a
, *y
= b
;
226 return strcmp(x
->name
, y
->name
);
229 static int list_images(int argc
, char *argv
[], void *userdata
) {
231 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
232 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
233 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
234 _cleanup_free_ ImageInfo
*images
= NULL
;
235 size_t n_images
= 0, n_allocated
= 0, j
;
236 const char *name
, *type
, *object
;
237 sd_bus
*bus
= userdata
;
238 uint64_t crtime
, mtime
, size
;
243 pager_open_if_enabled();
245 r
= sd_bus_call_method(
247 "org.freedesktop.machine1",
248 "/org/freedesktop/machine1",
249 "org.freedesktop.machine1.Manager",
255 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
259 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
261 return bus_log_parse_error(r
);
263 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
264 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
267 if (name
[0] == '.' && !arg_all
)
270 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
273 images
[n_images
].name
= name
;
274 images
[n_images
].type
= type
;
275 images
[n_images
].read_only
= read_only
;
276 images
[n_images
].crtime
= crtime
;
277 images
[n_images
].mtime
= mtime
;
278 images
[n_images
].size
= size
;
289 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
295 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
300 if (size
!= (uint64_t) -1) {
301 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
309 return bus_log_parse_error(r
);
311 r
= sd_bus_message_exit_container(reply
);
313 return bus_log_parse_error(r
);
315 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
318 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
319 (int) max_name
, "NAME",
320 (int) max_type
, "TYPE",
322 (int) max_size
, "USAGE",
323 (int) max_crtime
, "CREATED",
324 (int) max_mtime
, "MODIFIED");
326 for (j
= 0; j
< n_images
; j
++) {
327 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
329 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
330 (int) max_name
, images
[j
].name
,
331 (int) max_type
, images
[j
].type
,
332 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_normal() : "",
333 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
334 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
335 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
339 printf("\n%zu images listed.\n", n_images
);
344 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
345 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
346 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
347 _cleanup_free_
char *path
= NULL
;
355 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
358 path
= unit_dbus_path_from_name(unit
);
362 r
= sd_bus_get_property(
364 "org.freedesktop.systemd1",
366 unit_dbus_interface_from_name(unit
),
372 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
376 r
= sd_bus_message_read(reply
, "s", &cgroup
);
378 return bus_log_parse_error(r
);
380 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
) != 0 && leader
<= 0)
389 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
393 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
394 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
402 r
= sd_bus_call_method(bus
,
403 "org.freedesktop.machine1",
404 "/org/freedesktop/machine1",
405 "org.freedesktop.machine1.Manager",
406 "GetMachineAddresses",
413 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
415 return bus_log_parse_error(r
);
417 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
421 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
423 r
= sd_bus_message_read(reply
, "i", &family
);
425 return bus_log_parse_error(r
);
427 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
429 return bus_log_parse_error(r
);
431 fputs(prefix
, stdout
);
432 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
433 if (family
== AF_INET6
&& ifi
> 0)
437 r
= sd_bus_message_exit_container(reply
);
439 return bus_log_parse_error(r
);
441 if (prefix
!= prefix2
)
445 return bus_log_parse_error(r
);
447 r
= sd_bus_message_exit_container(reply
);
449 return bus_log_parse_error(r
);
454 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
455 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
456 const char *k
, *v
, *pretty
= NULL
;
463 r
= sd_bus_call_method(bus
,
464 "org.freedesktop.machine1",
465 "/org/freedesktop/machine1",
466 "org.freedesktop.machine1.Manager",
467 "GetMachineOSRelease",
474 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
476 return bus_log_parse_error(r
);
478 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
479 if (streq(k
, "PRETTY_NAME"))
484 return bus_log_parse_error(r
);
486 r
= sd_bus_message_exit_container(reply
);
488 return bus_log_parse_error(r
);
491 printf("%s%s\n", prefix
, pretty
);
496 typedef struct MachineStatusInfo
{
502 char *root_directory
;
504 struct dual_timestamp timestamp
;
509 static void machine_status_info_clear(MachineStatusInfo
*info
) {
515 free(info
->root_directory
);
521 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
522 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
523 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
529 fputs(strna(i
->name
), stdout
);
531 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
532 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
536 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
537 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
540 printf("\t Since: %s; %s\n", s2
, s1
);
542 printf("\t Since: %s\n", s2
);
545 _cleanup_free_
char *t
= NULL
;
547 printf("\t Leader: %u", (unsigned) i
->leader
);
549 get_process_comm(i
->leader
, &t
);
557 printf("\t Service: %s", i
->service
);
560 printf("; class %s", i
->class);
564 printf("\t Class: %s\n", i
->class);
566 if (i
->root_directory
)
567 printf("\t Root: %s\n", i
->root_directory
);
569 if (i
->n_netif
> 0) {
572 fputs("\t Iface:", stdout
);
574 for (c
= 0; c
< i
->n_netif
; c
++) {
575 char name
[IF_NAMESIZE
+1] = "";
577 if (if_indextoname(i
->netif
[c
], name
)) {
586 printf(" %i", i
->netif
[c
]);
592 print_addresses(bus
, i
->name
, ifi
,
596 print_os_release(bus
, i
->name
, "\t OS: ");
599 printf("\t Unit: %s\n", i
->unit
);
600 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
602 if (arg_transport
== BUS_TRANSPORT_LOCAL
)
604 show_journal_by_unit(
609 i
->timestamp
.monotonic
,
612 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
613 SD_JOURNAL_LOCAL_ONLY
,
619 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
620 MachineStatusInfo
*i
= userdata
;
625 assert_cc(sizeof(int32_t) == sizeof(int));
626 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
632 i
->n_netif
= l
/ sizeof(int32_t);
633 i
->netif
= memdup(v
, l
);
640 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
642 static const struct bus_properties_map map
[] = {
643 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
644 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
645 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
646 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
647 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
648 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
649 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
650 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
651 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
652 { "NetworkInterfaces", "ai", map_netif
, 0 },
656 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
664 r
= bus_map_all_properties(bus
,
665 "org.freedesktop.machine1",
670 return log_error_errno(r
, "Could not get properties: %m");
676 print_machine_status_info(bus
, &info
);
681 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
693 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
695 log_error_errno(r
, "Could not get properties: %m");
700 static int show_machine(int argc
, char *argv
[], void *userdata
) {
702 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
703 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
704 bool properties
, new_line
= false;
705 sd_bus
*bus
= userdata
;
710 properties
= !strstr(argv
[0], "status");
712 pager_open_if_enabled();
714 if (properties
&& argc
<= 1) {
716 /* If no argument is specified, inspect the manager
718 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
723 for (i
= 1; i
< argc
; i
++) {
724 const char *path
= NULL
;
726 r
= sd_bus_call_method(
728 "org.freedesktop.machine1",
729 "/org/freedesktop/machine1",
730 "org.freedesktop.machine1.Manager",
736 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
740 r
= sd_bus_message_read(reply
, "o", &path
);
742 return bus_log_parse_error(r
);
745 r
= show_machine_properties(bus
, path
, &new_line
);
747 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
753 typedef struct ImageStatusInfo
{
762 uint64_t usage_exclusive
;
763 uint64_t limit_exclusive
;
766 static void image_status_info_clear(ImageStatusInfo
*info
) {
775 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
776 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
777 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
778 char bs
[FORMAT_BYTES_MAX
], *s3
;
779 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
785 fputs(i
->name
, stdout
);
790 printf("\t Type: %s\n", i
->type
);
793 printf("\t Path: %s\n", i
->path
);
795 printf("\t RO: %s%s%s\n",
796 i
->read_only
? ansi_highlight_red() : "",
797 i
->read_only
? "read-only" : "writable",
798 i
->read_only
? ansi_normal() : "");
800 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
801 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
803 printf("\t Created: %s; %s\n", s2
, s1
);
805 printf("\t Created: %s\n", s2
);
807 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
808 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
810 printf("\tModified: %s; %s\n", s2
, s1
);
812 printf("\tModified: %s\n", s2
);
814 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
815 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
817 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
819 printf("\t Usage: %s\n", s3
);
821 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
822 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
824 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
826 printf("\t Limit: %s\n", s3
);
829 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
831 static const struct bus_properties_map map
[] = {
832 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
833 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
834 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
835 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
836 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
837 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
838 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
839 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
840 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
841 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
845 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
852 r
= bus_map_all_properties(bus
,
853 "org.freedesktop.machine1",
858 return log_error_errno(r
, "Could not get properties: %m");
864 print_image_status_info(bus
, &info
);
869 typedef struct PoolStatusInfo
{
875 static void pool_status_info_clear(PoolStatusInfo
*info
) {
884 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
885 char bs
[FORMAT_BYTES_MAX
], *s
;
888 printf("\t Path: %s\n", i
->path
);
890 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
892 printf("\t Usage: %s\n", s
);
894 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
896 printf("\t Limit: %s\n", s
);
899 static int show_pool_info(sd_bus
*bus
) {
901 static const struct bus_properties_map map
[] = {
902 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
903 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
904 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
908 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
909 .usage
= (uint64_t) -1,
910 .limit
= (uint64_t) -1,
916 r
= bus_map_all_properties(bus
,
917 "org.freedesktop.machine1",
918 "/org/freedesktop/machine1",
922 return log_error_errno(r
, "Could not get properties: %m");
924 print_pool_status_info(bus
, &info
);
930 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
942 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
944 log_error_errno(r
, "Could not get properties: %m");
949 static int show_image(int argc
, char *argv
[], void *userdata
) {
951 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
952 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
953 bool properties
, new_line
= false;
954 sd_bus
*bus
= userdata
;
959 properties
= !strstr(argv
[0], "status");
961 pager_open_if_enabled();
965 /* If no argument is specified, inspect the manager
969 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
971 r
= show_pool_info(bus
);
976 for (i
= 1; i
< argc
; i
++) {
977 const char *path
= NULL
;
979 r
= sd_bus_call_method(
981 "org.freedesktop.machine1",
982 "/org/freedesktop/machine1",
983 "org.freedesktop.machine1.Manager",
989 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
993 r
= sd_bus_message_read(reply
, "o", &path
);
995 return bus_log_parse_error(r
);
998 r
= show_image_properties(bus
, path
, &new_line
);
1000 r
= show_image_info(bus
, path
, &new_line
);
1006 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1007 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1008 sd_bus
*bus
= userdata
;
1013 polkit_agent_open_if_enabled();
1016 arg_kill_who
= "all";
1018 for (i
= 1; i
< argc
; i
++) {
1019 r
= sd_bus_call_method(
1021 "org.freedesktop.machine1",
1022 "/org/freedesktop/machine1",
1023 "org.freedesktop.machine1.Manager",
1027 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1029 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1037 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1038 arg_kill_who
= "leader";
1039 arg_signal
= SIGINT
; /* sysvinit + systemd */
1041 return kill_machine(argc
, argv
, userdata
);
1044 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1045 arg_kill_who
= "leader";
1046 arg_signal
= SIGRTMIN
+4; /* only systemd */
1048 return kill_machine(argc
, argv
, userdata
);
1051 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1052 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1053 sd_bus
*bus
= userdata
;
1058 polkit_agent_open_if_enabled();
1060 for (i
= 1; i
< argc
; i
++) {
1061 r
= sd_bus_call_method(
1063 "org.freedesktop.machine1",
1064 "/org/freedesktop/machine1",
1065 "org.freedesktop.machine1.Manager",
1071 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1079 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1080 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1081 _cleanup_free_
char *abs_host_path
= NULL
;
1082 char *dest
, *host_path
, *container_path
;
1083 sd_bus
*bus
= userdata
;
1089 polkit_agent_open_if_enabled();
1091 copy_from
= streq(argv
[0], "copy-from");
1092 dest
= argv
[3] ?: argv
[2];
1093 host_path
= copy_from
? dest
: argv
[2];
1094 container_path
= copy_from
? argv
[2] : dest
;
1096 if (!path_is_absolute(host_path
)) {
1097 r
= path_make_absolute_cwd(host_path
, &abs_host_path
);
1099 return log_error_errno(r
, "Failed to make path absolute: %m");
1101 host_path
= abs_host_path
;
1104 r
= sd_bus_call_method(
1106 "org.freedesktop.machine1",
1107 "/org/freedesktop/machine1",
1108 "org.freedesktop.machine1.Manager",
1109 copy_from
? "CopyFromMachine" : "CopyToMachine",
1114 copy_from
? container_path
: host_path
,
1115 copy_from
? host_path
: container_path
);
1117 return log_error_errno(r
, "Failed to copy: %s", bus_error_message(&error
, r
));
1122 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1123 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1124 sd_bus
*bus
= userdata
;
1129 polkit_agent_open_if_enabled();
1131 r
= sd_bus_call_method(
1133 "org.freedesktop.machine1",
1134 "/org/freedesktop/machine1",
1135 "org.freedesktop.machine1.Manager",
1146 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1153 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1154 PTYForward
** forward
= (PTYForward
**) userdata
;
1161 /* If the forwarder is already initialized, tell it to
1162 * exit on the next vhangup(), so that we still flush
1163 * out what might be queued and exit then. */
1165 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1169 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1172 /* On error, or when the forwarder is not initialized yet, quit immediately */
1173 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1177 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, PTYForwardFlags flags
, const char *name
) {
1183 assert(master
>= 0);
1186 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1188 if (streq(name
, ".host"))
1189 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1191 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1193 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1194 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1196 r
= pty_forward_new(event
, master
, flags
, forward
);
1198 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1200 r
= sd_event_loop(event
);
1202 return log_error_errno(r
, "Failed to run event loop: %m");
1204 pty_forward_get_last_char(*forward
, &last_char
);
1207 (flags
& PTY_FORWARD_IGNORE_VHANGUP
) &&
1208 pty_forward_get_ignore_vhangup(*forward
) == 0;
1210 *forward
= pty_forward_free(*forward
);
1212 if (last_char
!= '\n')
1213 fputc('\n', stdout
);
1216 log_info("Machine %s terminated.", name
);
1217 else if (streq(name
, ".host"))
1218 log_info("Connection to the local host terminated.");
1220 log_info("Connection to machine %s terminated.", name
);
1222 sd_event_get_exit_code(event
, &ret
);
1226 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1227 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1228 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1229 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1230 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1231 _cleanup_event_unref_ sd_event
*event
= NULL
;
1233 sd_bus
*bus
= userdata
;
1234 const char *pty
, *match
, *machine
;
1238 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1239 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1243 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1244 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1245 log_error("Login only supported on local machines.");
1249 polkit_agent_open_if_enabled();
1251 r
= sd_event_default(&event
);
1253 return log_error_errno(r
, "Failed to get event loop: %m");
1255 r
= sd_bus_attach_event(bus
, event
, 0);
1257 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1259 machine
= argc
< 2 || isempty(argv
[1]) ? ".host" : argv
[1];
1261 match
= strjoina("type='signal',"
1262 "sender='org.freedesktop.machine1',"
1263 "path='/org/freedesktop/machine1',",
1264 "interface='org.freedesktop.machine1.Manager',"
1265 "member='MachineRemoved',"
1266 "arg0='", machine
, "'");
1268 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1270 return log_error_errno(r
, "Failed to add machine removal match: %m");
1272 r
= sd_bus_call_method(
1274 "org.freedesktop.machine1",
1275 "/org/freedesktop/machine1",
1276 "org.freedesktop.machine1.Manager",
1282 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1286 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1288 return bus_log_parse_error(r
);
1290 return process_forward(event
, &forward
, master
, PTY_FORWARD_IGNORE_VHANGUP
, machine
);
1293 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1294 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *m
= NULL
;
1295 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1296 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1297 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1298 _cleanup_event_unref_ sd_event
*event
= NULL
;
1300 sd_bus
*bus
= userdata
;
1301 const char *pty
, *match
, *machine
, *path
, *uid
= NULL
;
1305 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1306 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1307 log_error("Shell only supported on local machines.");
1311 /* Pass $TERM to shell session, if not explicitly specified. */
1312 if (!strv_find_prefix(arg_setenv
, "TERM=")) {
1315 t
= strv_find_prefix(environ
, "TERM=");
1317 if (strv_extend(&arg_setenv
, t
) < 0)
1322 polkit_agent_open_if_enabled();
1324 r
= sd_event_default(&event
);
1326 return log_error_errno(r
, "Failed to get event loop: %m");
1328 r
= sd_bus_attach_event(bus
, event
, 0);
1330 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1332 machine
= argc
< 2 || isempty(argv
[1]) ? NULL
: argv
[1];
1339 at
= strchr(machine
, '@');
1341 uid
= strndupa(machine
, at
- machine
);
1346 if (isempty(machine
))
1349 match
= strjoina("type='signal',"
1350 "sender='org.freedesktop.machine1',"
1351 "path='/org/freedesktop/machine1',",
1352 "interface='org.freedesktop.machine1.Manager',"
1353 "member='MachineRemoved',"
1354 "arg0='", machine
, "'");
1356 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1358 return log_error_errno(r
, "Failed to add machine removal match: %m");
1360 r
= sd_bus_message_new_method_call(
1363 "org.freedesktop.machine1",
1364 "/org/freedesktop/machine1",
1365 "org.freedesktop.machine1.Manager",
1366 "OpenMachineShell");
1368 return bus_log_create_error(r
);
1370 path
= argc
< 3 || isempty(argv
[2]) ? NULL
: argv
[2];
1372 r
= sd_bus_message_append(m
, "sss", machine
, uid
, path
);
1374 return bus_log_create_error(r
);
1376 r
= sd_bus_message_append_strv(m
, strv_length(argv
) <= 3 ? NULL
: argv
+ 2);
1378 return bus_log_create_error(r
);
1380 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1382 return bus_log_create_error(r
);
1384 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1386 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1390 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1392 return bus_log_parse_error(r
);
1394 return process_forward(event
, &forward
, master
, 0, machine
);
1397 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1398 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1399 sd_bus
*bus
= userdata
;
1404 polkit_agent_open_if_enabled();
1406 for (i
= 1; i
< argc
; i
++) {
1407 r
= sd_bus_call_method(
1409 "org.freedesktop.machine1",
1410 "/org/freedesktop/machine1",
1411 "org.freedesktop.machine1.Manager",
1417 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1425 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1426 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1427 sd_bus
*bus
= userdata
;
1430 polkit_agent_open_if_enabled();
1432 r
= sd_bus_call_method(
1434 "org.freedesktop.machine1",
1435 "/org/freedesktop/machine1",
1436 "org.freedesktop.machine1.Manager",
1440 "ss", argv
[1], argv
[2]);
1442 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1449 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1450 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1451 sd_bus
*bus
= userdata
;
1454 polkit_agent_open_if_enabled();
1456 r
= sd_bus_call_method(
1458 "org.freedesktop.machine1",
1459 "/org/freedesktop/machine1",
1460 "org.freedesktop.machine1.Manager",
1464 "ssb", argv
[1], argv
[2], arg_read_only
);
1466 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1473 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1474 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1475 sd_bus
*bus
= userdata
;
1479 b
= parse_boolean(argv
[2]);
1481 log_error("Failed to parse boolean argument: %s", argv
[2]);
1486 polkit_agent_open_if_enabled();
1488 r
= sd_bus_call_method(
1490 "org.freedesktop.machine1",
1491 "/org/freedesktop/machine1",
1492 "org.freedesktop.machine1.Manager",
1493 "MarkImageReadOnly",
1498 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1505 static int make_service_name(const char *name
, char **ret
) {
1506 _cleanup_free_
char *e
= NULL
;
1512 if (!machine_name_is_valid(name
)) {
1513 log_error("Invalid machine name %s.", name
);
1517 e
= unit_name_escape(name
);
1521 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1523 return log_error_errno(r
, "Failed to build unit name: %m");
1528 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1529 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1530 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1531 sd_bus
*bus
= userdata
;
1536 polkit_agent_open_if_enabled();
1538 r
= bus_wait_for_jobs_new(bus
, &w
);
1542 for (i
= 1; i
< argc
; i
++) {
1543 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1544 _cleanup_free_
char *unit
= NULL
;
1547 r
= make_service_name(argv
[i
], &unit
);
1551 r
= sd_bus_call_method(
1553 "org.freedesktop.systemd1",
1554 "/org/freedesktop/systemd1",
1555 "org.freedesktop.systemd1.Manager",
1559 "ss", unit
, "fail");
1561 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1565 r
= sd_bus_message_read(reply
, "o", &object
);
1567 return bus_log_parse_error(r
);
1569 r
= bus_wait_for_jobs_add(w
, object
);
1574 r
= bus_wait_for_jobs(w
, arg_quiet
);
1581 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1582 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1583 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1584 int carries_install_info
= 0;
1585 const char *method
= NULL
;
1586 sd_bus
*bus
= userdata
;
1591 polkit_agent_open_if_enabled();
1593 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1595 r
= sd_bus_message_new_method_call(
1598 "org.freedesktop.systemd1",
1599 "/org/freedesktop/systemd1",
1600 "org.freedesktop.systemd1.Manager",
1603 return bus_log_create_error(r
);
1605 r
= sd_bus_message_open_container(m
, 'a', "s");
1607 return bus_log_create_error(r
);
1609 for (i
= 1; i
< argc
; i
++) {
1610 _cleanup_free_
char *unit
= NULL
;
1612 r
= make_service_name(argv
[i
], &unit
);
1616 r
= sd_bus_message_append(m
, "s", unit
);
1618 return bus_log_create_error(r
);
1621 r
= sd_bus_message_close_container(m
);
1623 return bus_log_create_error(r
);
1625 if (streq(argv
[0], "enable"))
1626 r
= sd_bus_message_append(m
, "bb", false, false);
1628 r
= sd_bus_message_append(m
, "b", false);
1630 return bus_log_create_error(r
);
1632 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1634 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1638 if (streq(argv
[0], "enable")) {
1639 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1641 return bus_log_parse_error(r
);
1644 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1648 r
= sd_bus_call_method(
1650 "org.freedesktop.systemd1",
1651 "/org/freedesktop/systemd1",
1652 "org.freedesktop.systemd1.Manager",
1658 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1665 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1666 const char **our_path
= userdata
, *line
;
1673 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1675 bus_log_parse_error(r
);
1679 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1682 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1685 log_full(priority
, "%s", line
);
1689 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1690 const char **our_path
= userdata
, *path
, *result
;
1697 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1699 bus_log_parse_error(r
);
1703 if (!streq_ptr(*our_path
, path
))
1706 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1710 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1715 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1717 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1721 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1722 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1723 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1724 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1725 _cleanup_event_unref_ sd_event
* event
= NULL
;
1726 const char *path
= NULL
;
1733 polkit_agent_open_if_enabled();
1735 r
= sd_event_default(&event
);
1737 return log_error_errno(r
, "Failed to get event loop: %m");
1739 r
= sd_bus_attach_event(bus
, event
, 0);
1741 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1743 r
= sd_bus_add_match(
1747 "sender='org.freedesktop.import1',"
1748 "interface='org.freedesktop.import1.Manager',"
1749 "member='TransferRemoved',"
1750 "path='/org/freedesktop/import1'",
1751 match_transfer_removed
, &path
);
1753 return log_error_errno(r
, "Failed to install match: %m");
1755 r
= sd_bus_add_match(
1759 "sender='org.freedesktop.import1',"
1760 "interface='org.freedesktop.import1.Transfer',"
1761 "member='LogMessage'",
1762 match_log_message
, &path
);
1764 return log_error_errno(r
, "Failed to install match: %m");
1766 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1768 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1772 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1774 return bus_log_parse_error(r
);
1776 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
1779 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1781 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1782 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1784 r
= sd_event_loop(event
);
1786 return log_error_errno(r
, "Failed to run event loop: %m");
1791 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1792 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1793 _cleanup_free_
char *ll
= NULL
;
1794 _cleanup_close_
int fd
= -1;
1795 const char *local
= NULL
, *path
= NULL
;
1796 sd_bus
*bus
= userdata
;
1803 if (isempty(path
) || streq(path
, "-"))
1809 local
= basename(path
);
1810 if (isempty(local
) || streq(local
, "-"))
1814 log_error("Need either path or local name.");
1818 r
= tar_strip_suffixes(local
, &ll
);
1824 if (!machine_name_is_valid(local
)) {
1825 log_error("Local name %s is not a suitable machine name.", local
);
1830 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1832 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1835 r
= sd_bus_message_new_method_call(
1838 "org.freedesktop.import1",
1839 "/org/freedesktop/import1",
1840 "org.freedesktop.import1.Manager",
1843 return bus_log_create_error(r
);
1845 r
= sd_bus_message_append(
1848 fd
>= 0 ? fd
: STDIN_FILENO
,
1853 return bus_log_create_error(r
);
1855 return transfer_image_common(bus
, m
);
1858 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1859 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1860 _cleanup_free_
char *ll
= NULL
;
1861 _cleanup_close_
int fd
= -1;
1862 const char *local
= NULL
, *path
= NULL
;
1863 sd_bus
*bus
= userdata
;
1870 if (isempty(path
) || streq(path
, "-"))
1876 local
= basename(path
);
1877 if (isempty(local
) || streq(local
, "-"))
1881 log_error("Need either path or local name.");
1885 r
= raw_strip_suffixes(local
, &ll
);
1891 if (!machine_name_is_valid(local
)) {
1892 log_error("Local name %s is not a suitable machine name.", local
);
1897 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1899 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1902 r
= sd_bus_message_new_method_call(
1905 "org.freedesktop.import1",
1906 "/org/freedesktop/import1",
1907 "org.freedesktop.import1.Manager",
1910 return bus_log_create_error(r
);
1912 r
= sd_bus_message_append(
1915 fd
>= 0 ? fd
: STDIN_FILENO
,
1920 return bus_log_create_error(r
);
1922 return transfer_image_common(bus
, m
);
1925 static void determine_compression_from_filename(const char *p
) {
1932 if (endswith(p
, ".xz"))
1934 else if (endswith(p
, ".gz"))
1935 arg_format
= "gzip";
1936 else if (endswith(p
, ".bz2"))
1937 arg_format
= "bzip2";
1940 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1941 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1942 _cleanup_close_
int fd
= -1;
1943 const char *local
= NULL
, *path
= NULL
;
1944 sd_bus
*bus
= userdata
;
1950 if (!machine_name_is_valid(local
)) {
1951 log_error("Machine name %s is not valid.", local
);
1957 if (isempty(path
) || streq(path
, "-"))
1961 determine_compression_from_filename(path
);
1963 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1965 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1968 r
= sd_bus_message_new_method_call(
1971 "org.freedesktop.import1",
1972 "/org/freedesktop/import1",
1973 "org.freedesktop.import1.Manager",
1976 return bus_log_create_error(r
);
1978 r
= sd_bus_message_append(
1982 fd
>= 0 ? fd
: STDOUT_FILENO
,
1985 return bus_log_create_error(r
);
1987 return transfer_image_common(bus
, m
);
1990 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1991 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1992 _cleanup_close_
int fd
= -1;
1993 const char *local
= NULL
, *path
= NULL
;
1994 sd_bus
*bus
= userdata
;
2000 if (!machine_name_is_valid(local
)) {
2001 log_error("Machine name %s is not valid.", local
);
2007 if (isempty(path
) || streq(path
, "-"))
2011 determine_compression_from_filename(path
);
2013 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2015 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2018 r
= sd_bus_message_new_method_call(
2021 "org.freedesktop.import1",
2022 "/org/freedesktop/import1",
2023 "org.freedesktop.import1.Manager",
2026 return bus_log_create_error(r
);
2028 r
= sd_bus_message_append(
2032 fd
>= 0 ? fd
: STDOUT_FILENO
,
2035 return bus_log_create_error(r
);
2037 return transfer_image_common(bus
, m
);
2040 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2041 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2042 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2043 const char *local
, *remote
;
2044 sd_bus
*bus
= userdata
;
2050 if (!http_url_is_valid(remote
)) {
2051 log_error("URL '%s' is not valid.", remote
);
2058 r
= import_url_last_component(remote
, &l
);
2060 return log_error_errno(r
, "Failed to get final component of URL: %m");
2065 if (isempty(local
) || streq(local
, "-"))
2069 r
= tar_strip_suffixes(local
, &ll
);
2075 if (!machine_name_is_valid(local
)) {
2076 log_error("Local name %s is not a suitable machine name.", local
);
2081 r
= sd_bus_message_new_method_call(
2084 "org.freedesktop.import1",
2085 "/org/freedesktop/import1",
2086 "org.freedesktop.import1.Manager",
2089 return bus_log_create_error(r
);
2091 r
= sd_bus_message_append(
2096 import_verify_to_string(arg_verify
),
2099 return bus_log_create_error(r
);
2101 return transfer_image_common(bus
, m
);
2104 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2105 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2106 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2107 const char *local
, *remote
;
2108 sd_bus
*bus
= userdata
;
2114 if (!http_url_is_valid(remote
)) {
2115 log_error("URL '%s' is not valid.", remote
);
2122 r
= import_url_last_component(remote
, &l
);
2124 return log_error_errno(r
, "Failed to get final component of URL: %m");
2129 if (isempty(local
) || streq(local
, "-"))
2133 r
= raw_strip_suffixes(local
, &ll
);
2139 if (!machine_name_is_valid(local
)) {
2140 log_error("Local name %s is not a suitable machine name.", local
);
2145 r
= sd_bus_message_new_method_call(
2148 "org.freedesktop.import1",
2149 "/org/freedesktop/import1",
2150 "org.freedesktop.import1.Manager",
2153 return bus_log_create_error(r
);
2155 r
= sd_bus_message_append(
2160 import_verify_to_string(arg_verify
),
2163 return bus_log_create_error(r
);
2165 return transfer_image_common(bus
, m
);
2168 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2169 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2170 const char *local
, *remote
, *tag
;
2171 sd_bus
*bus
= userdata
;
2174 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2175 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2180 tag
= strchr(remote
, ':');
2182 remote
= strndupa(remote
, tag
- remote
);
2186 if (!dkr_name_is_valid(remote
)) {
2187 log_error("DKR name '%s' is invalid.", remote
);
2190 if (tag
&& !dkr_tag_is_valid(tag
)) {
2191 log_error("DKR tag '%s' is invalid.", remote
);
2198 local
= strchr(remote
, '/');
2205 if (isempty(local
) || streq(local
, "-"))
2209 if (!machine_name_is_valid(local
)) {
2210 log_error("Local name %s is not a suitable machine name.", local
);
2215 r
= sd_bus_message_new_method_call(
2218 "org.freedesktop.import1",
2219 "/org/freedesktop/import1",
2220 "org.freedesktop.import1.Manager",
2223 return bus_log_create_error(r
);
2225 r
= sd_bus_message_append(
2232 import_verify_to_string(arg_verify
),
2235 return bus_log_create_error(r
);
2237 return transfer_image_common(bus
, m
);
2240 typedef struct TransferInfo
{
2248 static int compare_transfer_info(const void *a
, const void *b
) {
2249 const TransferInfo
*x
= a
, *y
= b
;
2251 return strcmp(x
->local
, y
->local
);
2254 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2255 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2256 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2257 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2258 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2259 size_t n_transfers
= 0, n_allocated
= 0, j
;
2260 const char *type
, *remote
, *local
, *object
;
2261 sd_bus
*bus
= userdata
;
2262 uint32_t id
, max_id
= 0;
2266 pager_open_if_enabled();
2268 r
= sd_bus_call_method(
2270 "org.freedesktop.import1",
2271 "/org/freedesktop/import1",
2272 "org.freedesktop.import1.Manager",
2278 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2282 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2284 return bus_log_parse_error(r
);
2286 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2289 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2292 transfers
[n_transfers
].id
= id
;
2293 transfers
[n_transfers
].type
= type
;
2294 transfers
[n_transfers
].remote
= remote
;
2295 transfers
[n_transfers
].local
= local
;
2296 transfers
[n_transfers
].progress
= progress
;
2316 return bus_log_parse_error(r
);
2318 r
= sd_bus_message_exit_container(reply
);
2320 return bus_log_parse_error(r
);
2322 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2325 printf("%-*s %-*s %-*s %-*s %-*s\n",
2326 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2328 (int) max_type
, "TYPE",
2329 (int) max_local
, "LOCAL",
2330 (int) max_remote
, "REMOTE");
2332 for (j
= 0; j
< n_transfers
; j
++)
2333 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2334 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2335 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2336 (int) max_type
, transfers
[j
].type
,
2337 (int) max_local
, transfers
[j
].local
,
2338 (int) max_remote
, transfers
[j
].remote
);
2341 printf("\n%zu transfers listed.\n", n_transfers
);
2346 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2347 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2348 sd_bus
*bus
= userdata
;
2353 polkit_agent_open_if_enabled();
2355 for (i
= 1; i
< argc
; i
++) {
2358 r
= safe_atou32(argv
[i
], &id
);
2360 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2362 r
= sd_bus_call_method(
2364 "org.freedesktop.import1",
2365 "/org/freedesktop/import1",
2366 "org.freedesktop.import1.Manager",
2372 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2380 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2381 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2382 sd_bus
*bus
= userdata
;
2386 if (STR_IN_SET(argv
[argc
-1], "-", "none", "infinity"))
2387 limit
= (uint64_t) -1;
2389 r
= parse_size(argv
[argc
-1], 1024, &limit
);
2391 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2395 /* With two arguments changes the quota limit of the
2396 * specified image */
2397 r
= sd_bus_call_method(
2399 "org.freedesktop.machine1",
2400 "/org/freedesktop/machine1",
2401 "org.freedesktop.machine1.Manager",
2405 "st", argv
[1], limit
);
2407 /* With one argument changes the pool quota limit */
2408 r
= sd_bus_call_method(
2410 "org.freedesktop.machine1",
2411 "/org/freedesktop/machine1",
2412 "org.freedesktop.machine1.Manager",
2419 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2426 static int help(int argc
, char *argv
[], void *userdata
) {
2428 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2429 "Send control commands to or query the virtual machine and container\n"
2430 "registration manager.\n\n"
2431 " -h --help Show this help\n"
2432 " --version Show package version\n"
2433 " --no-pager Do not pipe output into a pager\n"
2434 " --no-legend Do not show the headers and footers\n"
2435 " --no-ask-password Do not ask for system passwords\n"
2436 " -H --host=[USER@]HOST Operate on remote host\n"
2437 " -M --machine=CONTAINER Operate on local container\n"
2438 " -p --property=NAME Show only properties by this name\n"
2439 " -q --quiet Suppress output\n"
2440 " -a --all Show all properties, including empty ones\n"
2441 " -l --full Do not ellipsize output\n"
2442 " --kill-who=WHO Who to send signal to\n"
2443 " -s --signal=SIGNAL Which signal to send\n"
2444 " --uid=USER Specify user ID to invoke shell as\n"
2445 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2446 " --read-only Create read-only bind mount\n"
2447 " --mkdir Create directory before bind mounting, if missing\n"
2448 " -n --lines=INTEGER Number of journal entries to show\n"
2449 " -o --output=STRING Change journal output mode (short,\n"
2450 " short-monotonic, verbose, export, json,\n"
2451 " json-pretty, json-sse, cat)\n"
2452 " --verify=MODE Verification mode for downloaded images (no,\n"
2453 " checksum, signature)\n"
2454 " --force Download image even if already exists\n"
2455 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2457 "Machine Commands:\n"
2458 " list List running VMs and containers\n"
2459 " status NAME... Show VM/container details\n"
2460 " show [NAME...] Show properties of one or more VMs/containers\n"
2461 " start NAME... Start container as a service\n"
2462 " login [NAME] Get a login prompt in a container or on the\n"
2464 " shell [[USER@]NAME [COMMAND...]]\n"
2465 " Invoke a shell (or other command) in a container\n"
2466 " or on the local host\n"
2467 " enable NAME... Enable automatic container start at boot\n"
2468 " disable NAME... Disable automatic container start at boot\n"
2469 " poweroff NAME... Power off one or more containers\n"
2470 " reboot NAME... Reboot one or more containers\n"
2471 " terminate NAME... Terminate one or more VMs/containers\n"
2472 " kill NAME... Send signal to processes of a VM/container\n"
2473 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2474 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2475 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2477 " list-images Show available container and VM images\n"
2478 " image-status [NAME...] Show image details\n"
2479 " show-image [NAME...] Show properties of image\n"
2480 " clone NAME NAME Clone an image\n"
2481 " rename NAME NAME Rename an image\n"
2482 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2483 " remove NAME... Remove an image\n"
2484 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2485 "Image Transfer Commands:\n"
2486 " pull-tar URL [NAME] Download a TAR container image\n"
2487 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2488 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2489 " import-tar FILE [NAME] Import a local TAR container image\n"
2490 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2491 " export-tar NAME [FILE] Export a TAR container image locally\n"
2492 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2493 " list-transfers Show list of downloads in progress\n"
2494 " cancel-transfer Cancel a download\n"
2495 , program_invocation_short_name
);
2500 static int parse_argv(int argc
, char *argv
[]) {
2503 ARG_VERSION
= 0x100,
2509 ARG_NO_ASK_PASSWORD
,
2518 static const struct option options
[] = {
2519 { "help", no_argument
, NULL
, 'h' },
2520 { "version", no_argument
, NULL
, ARG_VERSION
},
2521 { "property", required_argument
, NULL
, 'p' },
2522 { "all", no_argument
, NULL
, 'a' },
2523 { "full", no_argument
, NULL
, 'l' },
2524 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2525 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2526 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2527 { "signal", required_argument
, NULL
, 's' },
2528 { "host", required_argument
, NULL
, 'H' },
2529 { "machine", required_argument
, NULL
, 'M' },
2530 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2531 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2532 { "quiet", no_argument
, NULL
, 'q' },
2533 { "lines", required_argument
, NULL
, 'n' },
2534 { "output", required_argument
, NULL
, 'o' },
2535 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2536 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2537 { "force", no_argument
, NULL
, ARG_FORCE
},
2538 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2539 { "format", required_argument
, NULL
, ARG_FORMAT
},
2540 { "uid", required_argument
, NULL
, ARG_UID
},
2541 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2550 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2555 return help(0, NULL
, NULL
);
2561 r
= strv_extend(&arg_property
, optarg
);
2565 /* If the user asked for a particular
2566 * property, show it to him, even if it is
2580 if (safe_atou(optarg
, &arg_lines
) < 0) {
2581 log_error("Failed to parse lines '%s'", optarg
);
2587 arg_output
= output_mode_from_string(optarg
);
2588 if (arg_output
< 0) {
2589 log_error("Unknown output '%s'.", optarg
);
2595 arg_no_pager
= true;
2603 arg_kill_who
= optarg
;
2607 arg_signal
= signal_from_string_try_harder(optarg
);
2608 if (arg_signal
< 0) {
2609 log_error("Failed to parse signal string %s.", optarg
);
2614 case ARG_NO_ASK_PASSWORD
:
2615 arg_ask_password
= false;
2619 arg_transport
= BUS_TRANSPORT_REMOTE
;
2624 arg_transport
= BUS_TRANSPORT_MACHINE
;
2629 arg_read_only
= true;
2641 arg_verify
= import_verify_from_string(optarg
);
2642 if (arg_verify
< 0) {
2643 log_error("Failed to parse --verify= setting: %s", optarg
);
2652 case ARG_DKR_INDEX_URL
:
2653 if (!http_url_is_valid(optarg
)) {
2654 log_error("Index URL is invalid: %s", optarg
);
2658 arg_dkr_index_url
= optarg
;
2662 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2663 log_error("Unknown format: %s", optarg
);
2667 arg_format
= optarg
;
2675 if (!env_assignment_is_valid(optarg
)) {
2676 log_error("Environment assignment invalid: %s", optarg
);
2680 r
= strv_extend(&arg_setenv
, optarg
);
2689 assert_not_reached("Unhandled option");
2695 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2697 static const Verb verbs
[] = {
2698 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2699 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2700 { "list-images", VERB_ANY
, 1, 0, list_images
},
2701 { "status", 2, VERB_ANY
, 0, show_machine
},
2702 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2703 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2704 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2705 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2706 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2707 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2708 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2709 { "login", VERB_ANY
, 2, 0, login_machine
},
2710 { "shell", VERB_ANY
, VERB_ANY
, 0, shell_machine
},
2711 { "bind", 3, 4, 0, bind_mount
},
2712 { "copy-to", 3, 4, 0, copy_files
},
2713 { "copy-from", 3, 4, 0, copy_files
},
2714 { "remove", 2, VERB_ANY
, 0, remove_image
},
2715 { "rename", 3, 3, 0, rename_image
},
2716 { "clone", 3, 3, 0, clone_image
},
2717 { "read-only", 2, 3, 0, read_only_image
},
2718 { "start", 2, VERB_ANY
, 0, start_machine
},
2719 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2720 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2721 { "import-tar", 2, 3, 0, import_tar
},
2722 { "import-raw", 2, 3, 0, import_raw
},
2723 { "export-tar", 2, 3, 0, export_tar
},
2724 { "export-raw", 2, 3, 0, export_raw
},
2725 { "pull-tar", 2, 3, 0, pull_tar
},
2726 { "pull-raw", 2, 3, 0, pull_raw
},
2727 { "pull-dkr", 2, 3, 0, pull_dkr
},
2728 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2729 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2730 { "set-limit", 2, 3, 0, set_limit
},
2734 return dispatch_verb(argc
, argv
, verbs
, bus
);
2737 int main(int argc
, char*argv
[]) {
2738 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
2741 setlocale(LC_ALL
, "");
2742 log_parse_environment();
2745 r
= parse_argv(argc
, argv
);
2749 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
2751 log_error_errno(r
, "Failed to create bus connection: %m");
2755 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2757 r
= machinectl_main(argc
, argv
, bus
);
2761 polkit_agent_close();
2763 strv_free(arg_property
);
2764 strv_free(arg_setenv
);
2766 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;