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 "path-util.h"
52 #include "process-util.h"
54 #include "signal-util.h"
55 #include "spawn-polkit-agent.h"
57 #include "terminal-util.h"
58 #include "unit-name.h"
62 static char **arg_property
= NULL
;
63 static bool arg_all
= false;
64 static bool arg_full
= false;
65 static bool arg_no_pager
= false;
66 static bool arg_legend
= true;
67 static const char *arg_kill_who
= NULL
;
68 static int arg_signal
= SIGTERM
;
69 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
70 static char *arg_host
= NULL
;
71 static bool arg_read_only
= false;
72 static bool arg_mkdir
= false;
73 static bool arg_quiet
= false;
74 static bool arg_ask_password
= true;
75 static unsigned arg_lines
= 10;
76 static OutputMode arg_output
= OUTPUT_SHORT
;
77 static bool arg_force
= false;
78 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
79 static const char* arg_dkr_index_url
= NULL
;
80 static const char* arg_format
= NULL
;
81 static const char *arg_uid
= NULL
;
82 static char **arg_setenv
= NULL
;
84 static void pager_open_if_enabled(void) {
92 static void polkit_agent_open_if_enabled(void) {
94 /* Open the polkit agent as a child process if necessary */
96 if (!arg_ask_password
)
99 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
105 static OutputFlags
get_output_flags(void) {
107 arg_all
* OUTPUT_SHOW_ALL
|
108 arg_full
* OUTPUT_FULL_WIDTH
|
109 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
110 on_tty() * OUTPUT_COLOR
|
111 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
114 typedef struct MachineInfo
{
120 static int compare_machine_info(const void *a
, const void *b
) {
121 const MachineInfo
*x
= a
, *y
= b
;
123 return strcmp(x
->name
, y
->name
);
126 static int list_machines(int argc
, char *argv
[], void *userdata
) {
128 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
129 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
130 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
131 _cleanup_free_ MachineInfo
*machines
= NULL
;
132 const char *name
, *class, *service
, *object
;
133 size_t n_machines
= 0, n_allocated
= 0, j
;
134 sd_bus
*bus
= userdata
;
139 pager_open_if_enabled();
141 r
= sd_bus_call_method(
143 "org.freedesktop.machine1",
144 "/org/freedesktop/machine1",
145 "org.freedesktop.machine1.Manager",
151 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
155 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
157 return bus_log_parse_error(r
);
159 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
162 if (name
[0] == '.' && !arg_all
)
165 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
168 machines
[n_machines
].name
= name
;
169 machines
[n_machines
].class = class;
170 machines
[n_machines
].service
= service
;
187 return bus_log_parse_error(r
);
189 r
= sd_bus_message_exit_container(reply
);
191 return bus_log_parse_error(r
);
193 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
196 printf("%-*s %-*s %-*s\n",
197 (int) max_name
, "MACHINE",
198 (int) max_class
, "CLASS",
199 (int) max_service
, "SERVICE");
201 for (j
= 0; j
< n_machines
; j
++)
202 printf("%-*s %-*s %-*s\n",
203 (int) max_name
, machines
[j
].name
,
204 (int) max_class
, machines
[j
].class,
205 (int) max_service
, machines
[j
].service
);
208 printf("\n%zu machines listed.\n", n_machines
);
213 typedef struct ImageInfo
{
222 static int compare_image_info(const void *a
, const void *b
) {
223 const ImageInfo
*x
= a
, *y
= b
;
225 return strcmp(x
->name
, y
->name
);
228 static int list_images(int argc
, char *argv
[], void *userdata
) {
230 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
231 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
232 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
233 _cleanup_free_ ImageInfo
*images
= NULL
;
234 size_t n_images
= 0, n_allocated
= 0, j
;
235 const char *name
, *type
, *object
;
236 sd_bus
*bus
= userdata
;
237 uint64_t crtime
, mtime
, size
;
242 pager_open_if_enabled();
244 r
= sd_bus_call_method(
246 "org.freedesktop.machine1",
247 "/org/freedesktop/machine1",
248 "org.freedesktop.machine1.Manager",
254 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
258 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
260 return bus_log_parse_error(r
);
262 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
263 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
266 if (name
[0] == '.' && !arg_all
)
269 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
272 images
[n_images
].name
= name
;
273 images
[n_images
].type
= type
;
274 images
[n_images
].read_only
= read_only
;
275 images
[n_images
].crtime
= crtime
;
276 images
[n_images
].mtime
= mtime
;
277 images
[n_images
].size
= size
;
288 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
294 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
299 if (size
!= (uint64_t) -1) {
300 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
308 return bus_log_parse_error(r
);
310 r
= sd_bus_message_exit_container(reply
);
312 return bus_log_parse_error(r
);
314 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
317 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
318 (int) max_name
, "NAME",
319 (int) max_type
, "TYPE",
321 (int) max_size
, "USAGE",
322 (int) max_crtime
, "CREATED",
323 (int) max_mtime
, "MODIFIED");
325 for (j
= 0; j
< n_images
; j
++) {
326 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
328 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
329 (int) max_name
, images
[j
].name
,
330 (int) max_type
, images
[j
].type
,
331 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_normal() : "",
332 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
333 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
334 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
338 printf("\n%zu images listed.\n", n_images
);
343 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
344 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
345 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
346 _cleanup_free_
char *path
= NULL
;
354 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
357 path
= unit_dbus_path_from_name(unit
);
361 r
= sd_bus_get_property(
363 "org.freedesktop.systemd1",
365 unit_dbus_interface_from_name(unit
),
371 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
375 r
= sd_bus_message_read(reply
, "s", &cgroup
);
377 return bus_log_parse_error(r
);
379 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
) != 0 && leader
<= 0)
388 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
392 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
393 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
401 r
= sd_bus_call_method(bus
,
402 "org.freedesktop.machine1",
403 "/org/freedesktop/machine1",
404 "org.freedesktop.machine1.Manager",
405 "GetMachineAddresses",
412 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
414 return bus_log_parse_error(r
);
416 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
420 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
422 r
= sd_bus_message_read(reply
, "i", &family
);
424 return bus_log_parse_error(r
);
426 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
428 return bus_log_parse_error(r
);
430 fputs(prefix
, stdout
);
431 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
432 if (family
== AF_INET6
&& ifi
> 0)
436 r
= sd_bus_message_exit_container(reply
);
438 return bus_log_parse_error(r
);
440 if (prefix
!= prefix2
)
444 return bus_log_parse_error(r
);
446 r
= sd_bus_message_exit_container(reply
);
448 return bus_log_parse_error(r
);
453 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
454 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
455 const char *k
, *v
, *pretty
= NULL
;
462 r
= sd_bus_call_method(bus
,
463 "org.freedesktop.machine1",
464 "/org/freedesktop/machine1",
465 "org.freedesktop.machine1.Manager",
466 "GetMachineOSRelease",
473 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
475 return bus_log_parse_error(r
);
477 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
478 if (streq(k
, "PRETTY_NAME"))
483 return bus_log_parse_error(r
);
485 r
= sd_bus_message_exit_container(reply
);
487 return bus_log_parse_error(r
);
490 printf("%s%s\n", prefix
, pretty
);
495 typedef struct MachineStatusInfo
{
501 char *root_directory
;
503 struct dual_timestamp timestamp
;
508 static void machine_status_info_clear(MachineStatusInfo
*info
) {
514 free(info
->root_directory
);
520 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
521 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
522 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
528 fputs(strna(i
->name
), stdout
);
530 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
531 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
535 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
536 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
539 printf("\t Since: %s; %s\n", s2
, s1
);
541 printf("\t Since: %s\n", s2
);
544 _cleanup_free_
char *t
= NULL
;
546 printf("\t Leader: %u", (unsigned) i
->leader
);
548 get_process_comm(i
->leader
, &t
);
556 printf("\t Service: %s", i
->service
);
559 printf("; class %s", i
->class);
563 printf("\t Class: %s\n", i
->class);
565 if (i
->root_directory
)
566 printf("\t Root: %s\n", i
->root_directory
);
568 if (i
->n_netif
> 0) {
571 fputs("\t Iface:", stdout
);
573 for (c
= 0; c
< i
->n_netif
; c
++) {
574 char name
[IF_NAMESIZE
+1] = "";
576 if (if_indextoname(i
->netif
[c
], name
)) {
585 printf(" %i", i
->netif
[c
]);
591 print_addresses(bus
, i
->name
, ifi
,
595 print_os_release(bus
, i
->name
, "\t OS: ");
598 printf("\t Unit: %s\n", i
->unit
);
599 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
601 if (arg_transport
== BUS_TRANSPORT_LOCAL
)
603 show_journal_by_unit(
608 i
->timestamp
.monotonic
,
611 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
612 SD_JOURNAL_LOCAL_ONLY
,
618 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
619 MachineStatusInfo
*i
= userdata
;
624 assert_cc(sizeof(int32_t) == sizeof(int));
625 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
631 i
->n_netif
= l
/ sizeof(int32_t);
632 i
->netif
= memdup(v
, l
);
639 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
641 static const struct bus_properties_map map
[] = {
642 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
643 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
644 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
645 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
646 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
647 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
648 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
649 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
650 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
651 { "NetworkInterfaces", "ai", map_netif
, 0 },
655 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
663 r
= bus_map_all_properties(bus
,
664 "org.freedesktop.machine1",
669 return log_error_errno(r
, "Could not get properties: %m");
675 print_machine_status_info(bus
, &info
);
680 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
692 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
694 log_error_errno(r
, "Could not get properties: %m");
699 static int show_machine(int argc
, char *argv
[], void *userdata
) {
701 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
702 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
703 bool properties
, new_line
= false;
704 sd_bus
*bus
= userdata
;
709 properties
= !strstr(argv
[0], "status");
711 pager_open_if_enabled();
713 if (properties
&& argc
<= 1) {
715 /* If no argument is specified, inspect the manager
717 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
722 for (i
= 1; i
< argc
; i
++) {
723 const char *path
= NULL
;
725 r
= sd_bus_call_method(
727 "org.freedesktop.machine1",
728 "/org/freedesktop/machine1",
729 "org.freedesktop.machine1.Manager",
735 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
739 r
= sd_bus_message_read(reply
, "o", &path
);
741 return bus_log_parse_error(r
);
744 r
= show_machine_properties(bus
, path
, &new_line
);
746 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
752 typedef struct ImageStatusInfo
{
761 uint64_t usage_exclusive
;
762 uint64_t limit_exclusive
;
765 static void image_status_info_clear(ImageStatusInfo
*info
) {
774 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
775 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
776 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
777 char bs
[FORMAT_BYTES_MAX
], *s3
;
778 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
784 fputs(i
->name
, stdout
);
789 printf("\t Type: %s\n", i
->type
);
792 printf("\t Path: %s\n", i
->path
);
794 printf("\t RO: %s%s%s\n",
795 i
->read_only
? ansi_highlight_red() : "",
796 i
->read_only
? "read-only" : "writable",
797 i
->read_only
? ansi_normal() : "");
799 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
800 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
802 printf("\t Created: %s; %s\n", s2
, s1
);
804 printf("\t Created: %s\n", s2
);
806 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
807 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
809 printf("\tModified: %s; %s\n", s2
, s1
);
811 printf("\tModified: %s\n", s2
);
813 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
814 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
816 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
818 printf("\t Usage: %s\n", s3
);
820 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
821 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
823 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
825 printf("\t Limit: %s\n", s3
);
828 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
830 static const struct bus_properties_map map
[] = {
831 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
832 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
833 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
834 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
835 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
836 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
837 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
838 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
839 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
840 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
844 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
851 r
= bus_map_all_properties(bus
,
852 "org.freedesktop.machine1",
857 return log_error_errno(r
, "Could not get properties: %m");
863 print_image_status_info(bus
, &info
);
868 typedef struct PoolStatusInfo
{
874 static void pool_status_info_clear(PoolStatusInfo
*info
) {
883 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
884 char bs
[FORMAT_BYTES_MAX
], *s
;
887 printf("\t Path: %s\n", i
->path
);
889 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
891 printf("\t Usage: %s\n", s
);
893 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
895 printf("\t Limit: %s\n", s
);
898 static int show_pool_info(sd_bus
*bus
) {
900 static const struct bus_properties_map map
[] = {
901 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
902 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
903 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
907 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
908 .usage
= (uint64_t) -1,
909 .limit
= (uint64_t) -1,
915 r
= bus_map_all_properties(bus
,
916 "org.freedesktop.machine1",
917 "/org/freedesktop/machine1",
921 return log_error_errno(r
, "Could not get properties: %m");
923 print_pool_status_info(bus
, &info
);
929 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
941 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
943 log_error_errno(r
, "Could not get properties: %m");
948 static int show_image(int argc
, char *argv
[], void *userdata
) {
950 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
951 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
952 bool properties
, new_line
= false;
953 sd_bus
*bus
= userdata
;
958 properties
= !strstr(argv
[0], "status");
960 pager_open_if_enabled();
964 /* If no argument is specified, inspect the manager
968 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
970 r
= show_pool_info(bus
);
975 for (i
= 1; i
< argc
; i
++) {
976 const char *path
= NULL
;
978 r
= sd_bus_call_method(
980 "org.freedesktop.machine1",
981 "/org/freedesktop/machine1",
982 "org.freedesktop.machine1.Manager",
988 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
992 r
= sd_bus_message_read(reply
, "o", &path
);
994 return bus_log_parse_error(r
);
997 r
= show_image_properties(bus
, path
, &new_line
);
999 r
= show_image_info(bus
, path
, &new_line
);
1005 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1006 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1007 sd_bus
*bus
= userdata
;
1012 polkit_agent_open_if_enabled();
1015 arg_kill_who
= "all";
1017 for (i
= 1; i
< argc
; i
++) {
1018 r
= sd_bus_call_method(
1020 "org.freedesktop.machine1",
1021 "/org/freedesktop/machine1",
1022 "org.freedesktop.machine1.Manager",
1026 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1028 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1036 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1037 arg_kill_who
= "leader";
1038 arg_signal
= SIGINT
; /* sysvinit + systemd */
1040 return kill_machine(argc
, argv
, userdata
);
1043 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1044 arg_kill_who
= "leader";
1045 arg_signal
= SIGRTMIN
+4; /* only systemd */
1047 return kill_machine(argc
, argv
, userdata
);
1050 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1051 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1052 sd_bus
*bus
= userdata
;
1057 polkit_agent_open_if_enabled();
1059 for (i
= 1; i
< argc
; i
++) {
1060 r
= sd_bus_call_method(
1062 "org.freedesktop.machine1",
1063 "/org/freedesktop/machine1",
1064 "org.freedesktop.machine1.Manager",
1070 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1078 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1079 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1080 _cleanup_free_
char *abs_host_path
= NULL
;
1081 char *dest
, *host_path
, *container_path
;
1082 sd_bus
*bus
= userdata
;
1088 polkit_agent_open_if_enabled();
1090 copy_from
= streq(argv
[0], "copy-from");
1091 dest
= argv
[3] ?: argv
[2];
1092 host_path
= copy_from
? dest
: argv
[2];
1093 container_path
= copy_from
? argv
[2] : dest
;
1095 if (!path_is_absolute(host_path
)) {
1096 r
= path_make_absolute_cwd(host_path
, &abs_host_path
);
1098 return log_error_errno(r
, "Failed to make path absolute: %m");
1100 host_path
= abs_host_path
;
1103 r
= sd_bus_call_method(
1105 "org.freedesktop.machine1",
1106 "/org/freedesktop/machine1",
1107 "org.freedesktop.machine1.Manager",
1108 copy_from
? "CopyFromMachine" : "CopyToMachine",
1113 copy_from
? container_path
: host_path
,
1114 copy_from
? host_path
: container_path
);
1116 return log_error_errno(r
, "Failed to copy: %s", bus_error_message(&error
, r
));
1121 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1122 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1123 sd_bus
*bus
= userdata
;
1128 polkit_agent_open_if_enabled();
1130 r
= sd_bus_call_method(
1132 "org.freedesktop.machine1",
1133 "/org/freedesktop/machine1",
1134 "org.freedesktop.machine1.Manager",
1145 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1152 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1153 PTYForward
** forward
= (PTYForward
**) userdata
;
1160 /* If the forwarder is already initialized, tell it to
1161 * exit on the next vhangup(), so that we still flush
1162 * out what might be queued and exit then. */
1164 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1168 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1171 /* On error, or when the forwarder is not initialized yet, quit immediately */
1172 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1176 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, PTYForwardFlags flags
, const char *name
) {
1182 assert(master
>= 0);
1185 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1187 if (streq(name
, ".host"))
1188 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1190 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1192 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1193 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1195 r
= pty_forward_new(event
, master
, flags
, forward
);
1197 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1199 r
= sd_event_loop(event
);
1201 return log_error_errno(r
, "Failed to run event loop: %m");
1203 pty_forward_get_last_char(*forward
, &last_char
);
1206 (flags
& PTY_FORWARD_IGNORE_VHANGUP
) &&
1207 pty_forward_get_ignore_vhangup(*forward
) == 0;
1209 *forward
= pty_forward_free(*forward
);
1211 if (last_char
!= '\n')
1212 fputc('\n', stdout
);
1215 log_info("Machine %s terminated.", name
);
1216 else if (streq(name
, ".host"))
1217 log_info("Connection to the local host terminated.");
1219 log_info("Connection to machine %s terminated.", name
);
1221 sd_event_get_exit_code(event
, &ret
);
1225 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1226 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1227 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1228 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1229 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1230 _cleanup_event_unref_ sd_event
*event
= NULL
;
1232 sd_bus
*bus
= userdata
;
1233 const char *pty
, *match
, *machine
;
1237 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1238 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1242 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1243 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1244 log_error("Login only supported on local machines.");
1248 polkit_agent_open_if_enabled();
1250 r
= sd_event_default(&event
);
1252 return log_error_errno(r
, "Failed to get event loop: %m");
1254 r
= sd_bus_attach_event(bus
, event
, 0);
1256 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1258 machine
= argc
< 2 || isempty(argv
[1]) ? ".host" : argv
[1];
1260 match
= strjoina("type='signal',"
1261 "sender='org.freedesktop.machine1',"
1262 "path='/org/freedesktop/machine1',",
1263 "interface='org.freedesktop.machine1.Manager',"
1264 "member='MachineRemoved',"
1265 "arg0='", machine
, "'");
1267 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1269 return log_error_errno(r
, "Failed to add machine removal match: %m");
1271 r
= sd_bus_call_method(
1273 "org.freedesktop.machine1",
1274 "/org/freedesktop/machine1",
1275 "org.freedesktop.machine1.Manager",
1281 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1285 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1287 return bus_log_parse_error(r
);
1289 return process_forward(event
, &forward
, master
, PTY_FORWARD_IGNORE_VHANGUP
, machine
);
1292 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1293 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *m
= NULL
;
1294 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1295 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1296 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1297 _cleanup_event_unref_ sd_event
*event
= NULL
;
1299 sd_bus
*bus
= userdata
;
1300 const char *pty
, *match
, *machine
, *path
, *uid
= NULL
;
1304 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1305 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1306 log_error("Shell only supported on local machines.");
1310 /* Pass $TERM to shell session, if not explicitly specified. */
1311 if (!strv_find_prefix(arg_setenv
, "TERM=")) {
1314 t
= strv_find_prefix(environ
, "TERM=");
1316 if (strv_extend(&arg_setenv
, t
) < 0)
1321 polkit_agent_open_if_enabled();
1323 r
= sd_event_default(&event
);
1325 return log_error_errno(r
, "Failed to get event loop: %m");
1327 r
= sd_bus_attach_event(bus
, event
, 0);
1329 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1331 machine
= argc
< 2 || isempty(argv
[1]) ? NULL
: argv
[1];
1338 at
= strchr(machine
, '@');
1340 uid
= strndupa(machine
, at
- machine
);
1345 if (isempty(machine
))
1348 match
= strjoina("type='signal',"
1349 "sender='org.freedesktop.machine1',"
1350 "path='/org/freedesktop/machine1',",
1351 "interface='org.freedesktop.machine1.Manager',"
1352 "member='MachineRemoved',"
1353 "arg0='", machine
, "'");
1355 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1357 return log_error_errno(r
, "Failed to add machine removal match: %m");
1359 r
= sd_bus_message_new_method_call(
1362 "org.freedesktop.machine1",
1363 "/org/freedesktop/machine1",
1364 "org.freedesktop.machine1.Manager",
1365 "OpenMachineShell");
1367 return bus_log_create_error(r
);
1369 path
= argc
< 3 || isempty(argv
[2]) ? NULL
: argv
[2];
1371 r
= sd_bus_message_append(m
, "sss", machine
, uid
, path
);
1373 return bus_log_create_error(r
);
1375 r
= sd_bus_message_append_strv(m
, strv_length(argv
) <= 3 ? NULL
: argv
+ 2);
1377 return bus_log_create_error(r
);
1379 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1381 return bus_log_create_error(r
);
1383 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1385 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1389 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1391 return bus_log_parse_error(r
);
1393 return process_forward(event
, &forward
, master
, 0, machine
);
1396 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1397 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1398 sd_bus
*bus
= userdata
;
1403 polkit_agent_open_if_enabled();
1405 for (i
= 1; i
< argc
; i
++) {
1406 r
= sd_bus_call_method(
1408 "org.freedesktop.machine1",
1409 "/org/freedesktop/machine1",
1410 "org.freedesktop.machine1.Manager",
1416 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1424 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1425 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1426 sd_bus
*bus
= userdata
;
1429 polkit_agent_open_if_enabled();
1431 r
= sd_bus_call_method(
1433 "org.freedesktop.machine1",
1434 "/org/freedesktop/machine1",
1435 "org.freedesktop.machine1.Manager",
1439 "ss", argv
[1], argv
[2]);
1441 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1448 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1449 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1450 sd_bus
*bus
= userdata
;
1453 polkit_agent_open_if_enabled();
1455 r
= sd_bus_call_method(
1457 "org.freedesktop.machine1",
1458 "/org/freedesktop/machine1",
1459 "org.freedesktop.machine1.Manager",
1463 "ssb", argv
[1], argv
[2], arg_read_only
);
1465 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1472 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1473 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1474 sd_bus
*bus
= userdata
;
1478 b
= parse_boolean(argv
[2]);
1480 log_error("Failed to parse boolean argument: %s", argv
[2]);
1485 polkit_agent_open_if_enabled();
1487 r
= sd_bus_call_method(
1489 "org.freedesktop.machine1",
1490 "/org/freedesktop/machine1",
1491 "org.freedesktop.machine1.Manager",
1492 "MarkImageReadOnly",
1497 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1504 static int make_service_name(const char *name
, char **ret
) {
1505 _cleanup_free_
char *e
= NULL
;
1511 if (!machine_name_is_valid(name
)) {
1512 log_error("Invalid machine name %s.", name
);
1516 e
= unit_name_escape(name
);
1520 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1522 return log_error_errno(r
, "Failed to build unit name: %m");
1527 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1528 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1529 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1530 sd_bus
*bus
= userdata
;
1535 polkit_agent_open_if_enabled();
1537 r
= bus_wait_for_jobs_new(bus
, &w
);
1541 for (i
= 1; i
< argc
; i
++) {
1542 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1543 _cleanup_free_
char *unit
= NULL
;
1546 r
= make_service_name(argv
[i
], &unit
);
1550 r
= sd_bus_call_method(
1552 "org.freedesktop.systemd1",
1553 "/org/freedesktop/systemd1",
1554 "org.freedesktop.systemd1.Manager",
1558 "ss", unit
, "fail");
1560 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1564 r
= sd_bus_message_read(reply
, "o", &object
);
1566 return bus_log_parse_error(r
);
1568 r
= bus_wait_for_jobs_add(w
, object
);
1573 r
= bus_wait_for_jobs(w
, arg_quiet
);
1580 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1581 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1582 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1583 int carries_install_info
= 0;
1584 const char *method
= NULL
;
1585 sd_bus
*bus
= userdata
;
1590 polkit_agent_open_if_enabled();
1592 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1594 r
= sd_bus_message_new_method_call(
1597 "org.freedesktop.systemd1",
1598 "/org/freedesktop/systemd1",
1599 "org.freedesktop.systemd1.Manager",
1602 return bus_log_create_error(r
);
1604 r
= sd_bus_message_open_container(m
, 'a', "s");
1606 return bus_log_create_error(r
);
1608 for (i
= 1; i
< argc
; i
++) {
1609 _cleanup_free_
char *unit
= NULL
;
1611 r
= make_service_name(argv
[i
], &unit
);
1615 r
= sd_bus_message_append(m
, "s", unit
);
1617 return bus_log_create_error(r
);
1620 r
= sd_bus_message_close_container(m
);
1622 return bus_log_create_error(r
);
1624 if (streq(argv
[0], "enable"))
1625 r
= sd_bus_message_append(m
, "bb", false, false);
1627 r
= sd_bus_message_append(m
, "b", false);
1629 return bus_log_create_error(r
);
1631 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1633 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1637 if (streq(argv
[0], "enable")) {
1638 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1640 return bus_log_parse_error(r
);
1643 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1647 r
= sd_bus_call_method(
1649 "org.freedesktop.systemd1",
1650 "/org/freedesktop/systemd1",
1651 "org.freedesktop.systemd1.Manager",
1657 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1664 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1665 const char **our_path
= userdata
, *line
;
1672 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1674 bus_log_parse_error(r
);
1678 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1681 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1684 log_full(priority
, "%s", line
);
1688 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1689 const char **our_path
= userdata
, *path
, *result
;
1696 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1698 bus_log_parse_error(r
);
1702 if (!streq_ptr(*our_path
, path
))
1705 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1709 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1714 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1716 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1720 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1721 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1722 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1723 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1724 _cleanup_event_unref_ sd_event
* event
= NULL
;
1725 const char *path
= NULL
;
1732 polkit_agent_open_if_enabled();
1734 r
= sd_event_default(&event
);
1736 return log_error_errno(r
, "Failed to get event loop: %m");
1738 r
= sd_bus_attach_event(bus
, event
, 0);
1740 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1742 r
= sd_bus_add_match(
1746 "sender='org.freedesktop.import1',"
1747 "interface='org.freedesktop.import1.Manager',"
1748 "member='TransferRemoved',"
1749 "path='/org/freedesktop/import1'",
1750 match_transfer_removed
, &path
);
1752 return log_error_errno(r
, "Failed to install match: %m");
1754 r
= sd_bus_add_match(
1758 "sender='org.freedesktop.import1',"
1759 "interface='org.freedesktop.import1.Transfer',"
1760 "member='LogMessage'",
1761 match_log_message
, &path
);
1763 return log_error_errno(r
, "Failed to install match: %m");
1765 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1767 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1771 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1773 return bus_log_parse_error(r
);
1775 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
1778 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1780 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1781 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1783 r
= sd_event_loop(event
);
1785 return log_error_errno(r
, "Failed to run event loop: %m");
1790 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1791 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1792 _cleanup_free_
char *ll
= NULL
;
1793 _cleanup_close_
int fd
= -1;
1794 const char *local
= NULL
, *path
= NULL
;
1795 sd_bus
*bus
= userdata
;
1802 if (isempty(path
) || streq(path
, "-"))
1808 local
= basename(path
);
1809 if (isempty(local
) || streq(local
, "-"))
1813 log_error("Need either path or local name.");
1817 r
= tar_strip_suffixes(local
, &ll
);
1823 if (!machine_name_is_valid(local
)) {
1824 log_error("Local name %s is not a suitable machine name.", local
);
1829 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1831 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1834 r
= sd_bus_message_new_method_call(
1837 "org.freedesktop.import1",
1838 "/org/freedesktop/import1",
1839 "org.freedesktop.import1.Manager",
1842 return bus_log_create_error(r
);
1844 r
= sd_bus_message_append(
1847 fd
>= 0 ? fd
: STDIN_FILENO
,
1852 return bus_log_create_error(r
);
1854 return transfer_image_common(bus
, m
);
1857 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1858 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1859 _cleanup_free_
char *ll
= NULL
;
1860 _cleanup_close_
int fd
= -1;
1861 const char *local
= NULL
, *path
= NULL
;
1862 sd_bus
*bus
= userdata
;
1869 if (isempty(path
) || streq(path
, "-"))
1875 local
= basename(path
);
1876 if (isempty(local
) || streq(local
, "-"))
1880 log_error("Need either path or local name.");
1884 r
= raw_strip_suffixes(local
, &ll
);
1890 if (!machine_name_is_valid(local
)) {
1891 log_error("Local name %s is not a suitable machine name.", local
);
1896 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1898 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1901 r
= sd_bus_message_new_method_call(
1904 "org.freedesktop.import1",
1905 "/org/freedesktop/import1",
1906 "org.freedesktop.import1.Manager",
1909 return bus_log_create_error(r
);
1911 r
= sd_bus_message_append(
1914 fd
>= 0 ? fd
: STDIN_FILENO
,
1919 return bus_log_create_error(r
);
1921 return transfer_image_common(bus
, m
);
1924 static void determine_compression_from_filename(const char *p
) {
1931 if (endswith(p
, ".xz"))
1933 else if (endswith(p
, ".gz"))
1934 arg_format
= "gzip";
1935 else if (endswith(p
, ".bz2"))
1936 arg_format
= "bzip2";
1939 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1940 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1941 _cleanup_close_
int fd
= -1;
1942 const char *local
= NULL
, *path
= NULL
;
1943 sd_bus
*bus
= userdata
;
1949 if (!machine_name_is_valid(local
)) {
1950 log_error("Machine name %s is not valid.", local
);
1956 if (isempty(path
) || streq(path
, "-"))
1960 determine_compression_from_filename(path
);
1962 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1964 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1967 r
= sd_bus_message_new_method_call(
1970 "org.freedesktop.import1",
1971 "/org/freedesktop/import1",
1972 "org.freedesktop.import1.Manager",
1975 return bus_log_create_error(r
);
1977 r
= sd_bus_message_append(
1981 fd
>= 0 ? fd
: STDOUT_FILENO
,
1984 return bus_log_create_error(r
);
1986 return transfer_image_common(bus
, m
);
1989 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1990 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1991 _cleanup_close_
int fd
= -1;
1992 const char *local
= NULL
, *path
= NULL
;
1993 sd_bus
*bus
= userdata
;
1999 if (!machine_name_is_valid(local
)) {
2000 log_error("Machine name %s is not valid.", local
);
2006 if (isempty(path
) || streq(path
, "-"))
2010 determine_compression_from_filename(path
);
2012 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2014 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2017 r
= sd_bus_message_new_method_call(
2020 "org.freedesktop.import1",
2021 "/org/freedesktop/import1",
2022 "org.freedesktop.import1.Manager",
2025 return bus_log_create_error(r
);
2027 r
= sd_bus_message_append(
2031 fd
>= 0 ? fd
: STDOUT_FILENO
,
2034 return bus_log_create_error(r
);
2036 return transfer_image_common(bus
, m
);
2039 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2040 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2041 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2042 const char *local
, *remote
;
2043 sd_bus
*bus
= userdata
;
2049 if (!http_url_is_valid(remote
)) {
2050 log_error("URL '%s' is not valid.", remote
);
2057 r
= import_url_last_component(remote
, &l
);
2059 return log_error_errno(r
, "Failed to get final component of URL: %m");
2064 if (isempty(local
) || streq(local
, "-"))
2068 r
= tar_strip_suffixes(local
, &ll
);
2074 if (!machine_name_is_valid(local
)) {
2075 log_error("Local name %s is not a suitable machine name.", local
);
2080 r
= sd_bus_message_new_method_call(
2083 "org.freedesktop.import1",
2084 "/org/freedesktop/import1",
2085 "org.freedesktop.import1.Manager",
2088 return bus_log_create_error(r
);
2090 r
= sd_bus_message_append(
2095 import_verify_to_string(arg_verify
),
2098 return bus_log_create_error(r
);
2100 return transfer_image_common(bus
, m
);
2103 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2104 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2105 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2106 const char *local
, *remote
;
2107 sd_bus
*bus
= userdata
;
2113 if (!http_url_is_valid(remote
)) {
2114 log_error("URL '%s' is not valid.", remote
);
2121 r
= import_url_last_component(remote
, &l
);
2123 return log_error_errno(r
, "Failed to get final component of URL: %m");
2128 if (isempty(local
) || streq(local
, "-"))
2132 r
= raw_strip_suffixes(local
, &ll
);
2138 if (!machine_name_is_valid(local
)) {
2139 log_error("Local name %s is not a suitable machine name.", local
);
2144 r
= sd_bus_message_new_method_call(
2147 "org.freedesktop.import1",
2148 "/org/freedesktop/import1",
2149 "org.freedesktop.import1.Manager",
2152 return bus_log_create_error(r
);
2154 r
= sd_bus_message_append(
2159 import_verify_to_string(arg_verify
),
2162 return bus_log_create_error(r
);
2164 return transfer_image_common(bus
, m
);
2167 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2168 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2169 const char *local
, *remote
, *tag
;
2170 sd_bus
*bus
= userdata
;
2173 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2174 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2179 tag
= strchr(remote
, ':');
2181 remote
= strndupa(remote
, tag
- remote
);
2185 if (!dkr_name_is_valid(remote
)) {
2186 log_error("DKR name '%s' is invalid.", remote
);
2189 if (tag
&& !dkr_tag_is_valid(tag
)) {
2190 log_error("DKR tag '%s' is invalid.", remote
);
2197 local
= strchr(remote
, '/');
2204 if (isempty(local
) || streq(local
, "-"))
2208 if (!machine_name_is_valid(local
)) {
2209 log_error("Local name %s is not a suitable machine name.", local
);
2214 r
= sd_bus_message_new_method_call(
2217 "org.freedesktop.import1",
2218 "/org/freedesktop/import1",
2219 "org.freedesktop.import1.Manager",
2222 return bus_log_create_error(r
);
2224 r
= sd_bus_message_append(
2231 import_verify_to_string(arg_verify
),
2234 return bus_log_create_error(r
);
2236 return transfer_image_common(bus
, m
);
2239 typedef struct TransferInfo
{
2247 static int compare_transfer_info(const void *a
, const void *b
) {
2248 const TransferInfo
*x
= a
, *y
= b
;
2250 return strcmp(x
->local
, y
->local
);
2253 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2254 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2255 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2256 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2257 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2258 size_t n_transfers
= 0, n_allocated
= 0, j
;
2259 const char *type
, *remote
, *local
, *object
;
2260 sd_bus
*bus
= userdata
;
2261 uint32_t id
, max_id
= 0;
2265 pager_open_if_enabled();
2267 r
= sd_bus_call_method(
2269 "org.freedesktop.import1",
2270 "/org/freedesktop/import1",
2271 "org.freedesktop.import1.Manager",
2277 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2281 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2283 return bus_log_parse_error(r
);
2285 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2288 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2291 transfers
[n_transfers
].id
= id
;
2292 transfers
[n_transfers
].type
= type
;
2293 transfers
[n_transfers
].remote
= remote
;
2294 transfers
[n_transfers
].local
= local
;
2295 transfers
[n_transfers
].progress
= progress
;
2315 return bus_log_parse_error(r
);
2317 r
= sd_bus_message_exit_container(reply
);
2319 return bus_log_parse_error(r
);
2321 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2324 printf("%-*s %-*s %-*s %-*s %-*s\n",
2325 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2327 (int) max_type
, "TYPE",
2328 (int) max_local
, "LOCAL",
2329 (int) max_remote
, "REMOTE");
2331 for (j
= 0; j
< n_transfers
; j
++)
2332 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2333 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2334 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2335 (int) max_type
, transfers
[j
].type
,
2336 (int) max_local
, transfers
[j
].local
,
2337 (int) max_remote
, transfers
[j
].remote
);
2340 printf("\n%zu transfers listed.\n", n_transfers
);
2345 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2346 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2347 sd_bus
*bus
= userdata
;
2352 polkit_agent_open_if_enabled();
2354 for (i
= 1; i
< argc
; i
++) {
2357 r
= safe_atou32(argv
[i
], &id
);
2359 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2361 r
= sd_bus_call_method(
2363 "org.freedesktop.import1",
2364 "/org/freedesktop/import1",
2365 "org.freedesktop.import1.Manager",
2371 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2379 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2380 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2381 sd_bus
*bus
= userdata
;
2385 if (STR_IN_SET(argv
[argc
-1], "-", "none", "infinity"))
2386 limit
= (uint64_t) -1;
2388 r
= parse_size(argv
[argc
-1], 1024, &limit
);
2390 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2394 /* With two arguments changes the quota limit of the
2395 * specified image */
2396 r
= sd_bus_call_method(
2398 "org.freedesktop.machine1",
2399 "/org/freedesktop/machine1",
2400 "org.freedesktop.machine1.Manager",
2404 "st", argv
[1], limit
);
2406 /* With one argument changes the pool quota limit */
2407 r
= sd_bus_call_method(
2409 "org.freedesktop.machine1",
2410 "/org/freedesktop/machine1",
2411 "org.freedesktop.machine1.Manager",
2418 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2425 static int help(int argc
, char *argv
[], void *userdata
) {
2427 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2428 "Send control commands to or query the virtual machine and container\n"
2429 "registration manager.\n\n"
2430 " -h --help Show this help\n"
2431 " --version Show package version\n"
2432 " --no-pager Do not pipe output into a pager\n"
2433 " --no-legend Do not show the headers and footers\n"
2434 " --no-ask-password Do not ask for system passwords\n"
2435 " -H --host=[USER@]HOST Operate on remote host\n"
2436 " -M --machine=CONTAINER Operate on local container\n"
2437 " -p --property=NAME Show only properties by this name\n"
2438 " -q --quiet Suppress output\n"
2439 " -a --all Show all properties, including empty ones\n"
2440 " -l --full Do not ellipsize output\n"
2441 " --kill-who=WHO Who to send signal to\n"
2442 " -s --signal=SIGNAL Which signal to send\n"
2443 " --uid=USER Specify user ID to invoke shell as\n"
2444 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2445 " --read-only Create read-only bind mount\n"
2446 " --mkdir Create directory before bind mounting, if missing\n"
2447 " -n --lines=INTEGER Number of journal entries to show\n"
2448 " -o --output=STRING Change journal output mode (short,\n"
2449 " short-monotonic, verbose, export, json,\n"
2450 " json-pretty, json-sse, cat)\n"
2451 " --verify=MODE Verification mode for downloaded images (no,\n"
2452 " checksum, signature)\n"
2453 " --force Download image even if already exists\n"
2454 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2456 "Machine Commands:\n"
2457 " list List running VMs and containers\n"
2458 " status NAME... Show VM/container details\n"
2459 " show [NAME...] Show properties of one or more VMs/containers\n"
2460 " start NAME... Start container as a service\n"
2461 " login [NAME] Get a login prompt in a container or on the\n"
2463 " shell [[USER@]NAME [COMMAND...]]\n"
2464 " Invoke a shell (or other command) in a container\n"
2465 " or on the local host\n"
2466 " enable NAME... Enable automatic container start at boot\n"
2467 " disable NAME... Disable automatic container start at boot\n"
2468 " poweroff NAME... Power off one or more containers\n"
2469 " reboot NAME... Reboot one or more containers\n"
2470 " terminate NAME... Terminate one or more VMs/containers\n"
2471 " kill NAME... Send signal to processes of a VM/container\n"
2472 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2473 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2474 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2476 " list-images Show available container and VM images\n"
2477 " image-status [NAME...] Show image details\n"
2478 " show-image [NAME...] Show properties of image\n"
2479 " clone NAME NAME Clone an image\n"
2480 " rename NAME NAME Rename an image\n"
2481 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2482 " remove NAME... Remove an image\n"
2483 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2484 "Image Transfer Commands:\n"
2485 " pull-tar URL [NAME] Download a TAR container image\n"
2486 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2487 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2488 " import-tar FILE [NAME] Import a local TAR container image\n"
2489 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2490 " export-tar NAME [FILE] Export a TAR container image locally\n"
2491 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2492 " list-transfers Show list of downloads in progress\n"
2493 " cancel-transfer Cancel a download\n"
2494 , program_invocation_short_name
);
2499 static int parse_argv(int argc
, char *argv
[]) {
2502 ARG_VERSION
= 0x100,
2508 ARG_NO_ASK_PASSWORD
,
2517 static const struct option options
[] = {
2518 { "help", no_argument
, NULL
, 'h' },
2519 { "version", no_argument
, NULL
, ARG_VERSION
},
2520 { "property", required_argument
, NULL
, 'p' },
2521 { "all", no_argument
, NULL
, 'a' },
2522 { "full", no_argument
, NULL
, 'l' },
2523 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2524 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2525 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2526 { "signal", required_argument
, NULL
, 's' },
2527 { "host", required_argument
, NULL
, 'H' },
2528 { "machine", required_argument
, NULL
, 'M' },
2529 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2530 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2531 { "quiet", no_argument
, NULL
, 'q' },
2532 { "lines", required_argument
, NULL
, 'n' },
2533 { "output", required_argument
, NULL
, 'o' },
2534 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2535 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2536 { "force", no_argument
, NULL
, ARG_FORCE
},
2537 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2538 { "format", required_argument
, NULL
, ARG_FORMAT
},
2539 { "uid", required_argument
, NULL
, ARG_UID
},
2540 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2549 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2554 return help(0, NULL
, NULL
);
2560 r
= strv_extend(&arg_property
, optarg
);
2564 /* If the user asked for a particular
2565 * property, show it to him, even if it is
2579 if (safe_atou(optarg
, &arg_lines
) < 0) {
2580 log_error("Failed to parse lines '%s'", optarg
);
2586 arg_output
= output_mode_from_string(optarg
);
2587 if (arg_output
< 0) {
2588 log_error("Unknown output '%s'.", optarg
);
2594 arg_no_pager
= true;
2602 arg_kill_who
= optarg
;
2606 arg_signal
= signal_from_string_try_harder(optarg
);
2607 if (arg_signal
< 0) {
2608 log_error("Failed to parse signal string %s.", optarg
);
2613 case ARG_NO_ASK_PASSWORD
:
2614 arg_ask_password
= false;
2618 arg_transport
= BUS_TRANSPORT_REMOTE
;
2623 arg_transport
= BUS_TRANSPORT_MACHINE
;
2628 arg_read_only
= true;
2640 arg_verify
= import_verify_from_string(optarg
);
2641 if (arg_verify
< 0) {
2642 log_error("Failed to parse --verify= setting: %s", optarg
);
2651 case ARG_DKR_INDEX_URL
:
2652 if (!http_url_is_valid(optarg
)) {
2653 log_error("Index URL is invalid: %s", optarg
);
2657 arg_dkr_index_url
= optarg
;
2661 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2662 log_error("Unknown format: %s", optarg
);
2666 arg_format
= optarg
;
2674 if (!env_assignment_is_valid(optarg
)) {
2675 log_error("Environment assignment invalid: %s", optarg
);
2679 r
= strv_extend(&arg_setenv
, optarg
);
2688 assert_not_reached("Unhandled option");
2694 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2696 static const Verb verbs
[] = {
2697 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2698 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2699 { "list-images", VERB_ANY
, 1, 0, list_images
},
2700 { "status", 2, VERB_ANY
, 0, show_machine
},
2701 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2702 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2703 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2704 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2705 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2706 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2707 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2708 { "login", VERB_ANY
, 2, 0, login_machine
},
2709 { "shell", VERB_ANY
, VERB_ANY
, 0, shell_machine
},
2710 { "bind", 3, 4, 0, bind_mount
},
2711 { "copy-to", 3, 4, 0, copy_files
},
2712 { "copy-from", 3, 4, 0, copy_files
},
2713 { "remove", 2, VERB_ANY
, 0, remove_image
},
2714 { "rename", 3, 3, 0, rename_image
},
2715 { "clone", 3, 3, 0, clone_image
},
2716 { "read-only", 2, 3, 0, read_only_image
},
2717 { "start", 2, VERB_ANY
, 0, start_machine
},
2718 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2719 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2720 { "import-tar", 2, 3, 0, import_tar
},
2721 { "import-raw", 2, 3, 0, import_raw
},
2722 { "export-tar", 2, 3, 0, export_tar
},
2723 { "export-raw", 2, 3, 0, export_raw
},
2724 { "pull-tar", 2, 3, 0, pull_tar
},
2725 { "pull-raw", 2, 3, 0, pull_raw
},
2726 { "pull-dkr", 2, 3, 0, pull_dkr
},
2727 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2728 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2729 { "set-limit", 2, 3, 0, set_limit
},
2733 return dispatch_verb(argc
, argv
, verbs
, bus
);
2736 int main(int argc
, char*argv
[]) {
2737 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
2740 setlocale(LC_ALL
, "");
2741 log_parse_environment();
2744 r
= parse_argv(argc
, argv
);
2748 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
2750 log_error_errno(r
, "Failed to create bus connection: %m");
2754 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2756 r
= machinectl_main(argc
, argv
, bus
);
2760 polkit_agent_close();
2762 strv_free(arg_property
);
2763 strv_free(arg_setenv
);
2765 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;