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 "alloc-util.h"
37 #include "bus-error.h"
39 #include "cgroup-show.h"
40 #include "cgroup-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"
64 static char **arg_property
= NULL
;
65 static bool arg_all
= false;
66 static bool arg_full
= false;
67 static bool arg_no_pager
= false;
68 static bool arg_legend
= true;
69 static const char *arg_kill_who
= NULL
;
70 static int arg_signal
= SIGTERM
;
71 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
72 static char *arg_host
= NULL
;
73 static bool arg_read_only
= false;
74 static bool arg_mkdir
= false;
75 static bool arg_quiet
= false;
76 static bool arg_ask_password
= true;
77 static unsigned arg_lines
= 10;
78 static OutputMode arg_output
= OUTPUT_SHORT
;
79 static bool arg_force
= false;
80 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
81 static const char* arg_dkr_index_url
= NULL
;
82 static const char* arg_format
= NULL
;
83 static const char *arg_uid
= NULL
;
84 static char **arg_setenv
= NULL
;
86 static void pager_open_if_enabled(void) {
94 static void polkit_agent_open_if_enabled(void) {
96 /* Open the polkit agent as a child process if necessary */
98 if (!arg_ask_password
)
101 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
107 static OutputFlags
get_output_flags(void) {
109 arg_all
* OUTPUT_SHOW_ALL
|
110 arg_full
* OUTPUT_FULL_WIDTH
|
111 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
112 on_tty() * OUTPUT_COLOR
|
113 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
116 typedef struct MachineInfo
{
122 static int compare_machine_info(const void *a
, const void *b
) {
123 const MachineInfo
*x
= a
, *y
= b
;
125 return strcmp(x
->name
, y
->name
);
128 static int list_machines(int argc
, char *argv
[], void *userdata
) {
130 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
131 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
132 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
133 _cleanup_free_ MachineInfo
*machines
= NULL
;
134 const char *name
, *class, *service
, *object
;
135 size_t n_machines
= 0, n_allocated
= 0, j
;
136 sd_bus
*bus
= userdata
;
141 pager_open_if_enabled();
143 r
= sd_bus_call_method(
145 "org.freedesktop.machine1",
146 "/org/freedesktop/machine1",
147 "org.freedesktop.machine1.Manager",
153 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
157 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
159 return bus_log_parse_error(r
);
161 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
164 if (name
[0] == '.' && !arg_all
)
167 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
170 machines
[n_machines
].name
= name
;
171 machines
[n_machines
].class = class;
172 machines
[n_machines
].service
= service
;
189 return bus_log_parse_error(r
);
191 r
= sd_bus_message_exit_container(reply
);
193 return bus_log_parse_error(r
);
195 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
198 printf("%-*s %-*s %-*s\n",
199 (int) max_name
, "MACHINE",
200 (int) max_class
, "CLASS",
201 (int) max_service
, "SERVICE");
203 for (j
= 0; j
< n_machines
; j
++)
204 printf("%-*s %-*s %-*s\n",
205 (int) max_name
, machines
[j
].name
,
206 (int) max_class
, machines
[j
].class,
207 (int) max_service
, machines
[j
].service
);
210 printf("\n%zu machines listed.\n", n_machines
);
215 typedef struct ImageInfo
{
224 static int compare_image_info(const void *a
, const void *b
) {
225 const ImageInfo
*x
= a
, *y
= b
;
227 return strcmp(x
->name
, y
->name
);
230 static int list_images(int argc
, char *argv
[], void *userdata
) {
232 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
233 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
234 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
235 _cleanup_free_ ImageInfo
*images
= NULL
;
236 size_t n_images
= 0, n_allocated
= 0, j
;
237 const char *name
, *type
, *object
;
238 sd_bus
*bus
= userdata
;
239 uint64_t crtime
, mtime
, size
;
244 pager_open_if_enabled();
246 r
= sd_bus_call_method(
248 "org.freedesktop.machine1",
249 "/org/freedesktop/machine1",
250 "org.freedesktop.machine1.Manager",
256 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
260 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
262 return bus_log_parse_error(r
);
264 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
265 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
268 if (name
[0] == '.' && !arg_all
)
271 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
274 images
[n_images
].name
= name
;
275 images
[n_images
].type
= type
;
276 images
[n_images
].read_only
= read_only
;
277 images
[n_images
].crtime
= crtime
;
278 images
[n_images
].mtime
= mtime
;
279 images
[n_images
].size
= size
;
290 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
296 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
301 if (size
!= (uint64_t) -1) {
302 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
310 return bus_log_parse_error(r
);
312 r
= sd_bus_message_exit_container(reply
);
314 return bus_log_parse_error(r
);
316 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
319 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
320 (int) max_name
, "NAME",
321 (int) max_type
, "TYPE",
323 (int) max_size
, "USAGE",
324 (int) max_crtime
, "CREATED",
325 (int) max_mtime
, "MODIFIED");
327 for (j
= 0; j
< n_images
; j
++) {
328 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
330 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
331 (int) max_name
, images
[j
].name
,
332 (int) max_type
, images
[j
].type
,
333 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_normal() : "",
334 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
335 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
336 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
340 printf("\n%zu images listed.\n", n_images
);
345 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
346 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
347 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
348 _cleanup_free_
char *path
= NULL
;
356 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
359 path
= unit_dbus_path_from_name(unit
);
363 r
= sd_bus_get_property(
365 "org.freedesktop.systemd1",
367 unit_dbus_interface_from_name(unit
),
373 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
377 r
= sd_bus_message_read(reply
, "s", &cgroup
);
379 return bus_log_parse_error(r
);
381 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
) != 0 && leader
<= 0)
390 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
394 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
395 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
403 r
= sd_bus_call_method(bus
,
404 "org.freedesktop.machine1",
405 "/org/freedesktop/machine1",
406 "org.freedesktop.machine1.Manager",
407 "GetMachineAddresses",
414 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
416 return bus_log_parse_error(r
);
418 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
422 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
424 r
= sd_bus_message_read(reply
, "i", &family
);
426 return bus_log_parse_error(r
);
428 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
430 return bus_log_parse_error(r
);
432 fputs(prefix
, stdout
);
433 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
434 if (family
== AF_INET6
&& ifi
> 0)
438 r
= sd_bus_message_exit_container(reply
);
440 return bus_log_parse_error(r
);
442 if (prefix
!= prefix2
)
446 return bus_log_parse_error(r
);
448 r
= sd_bus_message_exit_container(reply
);
450 return bus_log_parse_error(r
);
455 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
456 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
457 const char *k
, *v
, *pretty
= NULL
;
464 r
= sd_bus_call_method(bus
,
465 "org.freedesktop.machine1",
466 "/org/freedesktop/machine1",
467 "org.freedesktop.machine1.Manager",
468 "GetMachineOSRelease",
475 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
477 return bus_log_parse_error(r
);
479 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
480 if (streq(k
, "PRETTY_NAME"))
485 return bus_log_parse_error(r
);
487 r
= sd_bus_message_exit_container(reply
);
489 return bus_log_parse_error(r
);
492 printf("%s%s\n", prefix
, pretty
);
497 typedef struct MachineStatusInfo
{
503 char *root_directory
;
505 struct dual_timestamp timestamp
;
510 static void machine_status_info_clear(MachineStatusInfo
*info
) {
516 free(info
->root_directory
);
522 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
523 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
524 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
530 fputs(strna(i
->name
), stdout
);
532 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
533 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
537 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
538 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
541 printf("\t Since: %s; %s\n", s2
, s1
);
543 printf("\t Since: %s\n", s2
);
546 _cleanup_free_
char *t
= NULL
;
548 printf("\t Leader: %u", (unsigned) i
->leader
);
550 get_process_comm(i
->leader
, &t
);
558 printf("\t Service: %s", i
->service
);
561 printf("; class %s", i
->class);
565 printf("\t Class: %s\n", i
->class);
567 if (i
->root_directory
)
568 printf("\t Root: %s\n", i
->root_directory
);
570 if (i
->n_netif
> 0) {
573 fputs("\t Iface:", stdout
);
575 for (c
= 0; c
< i
->n_netif
; c
++) {
576 char name
[IF_NAMESIZE
+1] = "";
578 if (if_indextoname(i
->netif
[c
], name
)) {
587 printf(" %i", i
->netif
[c
]);
593 print_addresses(bus
, i
->name
, ifi
,
597 print_os_release(bus
, i
->name
, "\t OS: ");
600 printf("\t Unit: %s\n", i
->unit
);
601 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
603 if (arg_transport
== BUS_TRANSPORT_LOCAL
)
605 show_journal_by_unit(
610 i
->timestamp
.monotonic
,
613 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
614 SD_JOURNAL_LOCAL_ONLY
,
620 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
621 MachineStatusInfo
*i
= userdata
;
626 assert_cc(sizeof(int32_t) == sizeof(int));
627 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
633 i
->n_netif
= l
/ sizeof(int32_t);
634 i
->netif
= memdup(v
, l
);
641 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
643 static const struct bus_properties_map map
[] = {
644 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
645 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
646 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
647 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
648 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
649 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
650 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
651 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
652 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
653 { "NetworkInterfaces", "ai", map_netif
, 0 },
657 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
665 r
= bus_map_all_properties(bus
,
666 "org.freedesktop.machine1",
671 return log_error_errno(r
, "Could not get properties: %m");
677 print_machine_status_info(bus
, &info
);
682 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
694 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
696 log_error_errno(r
, "Could not get properties: %m");
701 static int show_machine(int argc
, char *argv
[], void *userdata
) {
703 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
704 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
705 bool properties
, new_line
= false;
706 sd_bus
*bus
= userdata
;
711 properties
= !strstr(argv
[0], "status");
713 pager_open_if_enabled();
715 if (properties
&& argc
<= 1) {
717 /* If no argument is specified, inspect the manager
719 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
724 for (i
= 1; i
< argc
; i
++) {
725 const char *path
= NULL
;
727 r
= sd_bus_call_method(
729 "org.freedesktop.machine1",
730 "/org/freedesktop/machine1",
731 "org.freedesktop.machine1.Manager",
737 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
741 r
= sd_bus_message_read(reply
, "o", &path
);
743 return bus_log_parse_error(r
);
746 r
= show_machine_properties(bus
, path
, &new_line
);
748 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
754 typedef struct ImageStatusInfo
{
763 uint64_t usage_exclusive
;
764 uint64_t limit_exclusive
;
767 static void image_status_info_clear(ImageStatusInfo
*info
) {
776 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
777 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
778 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
779 char bs
[FORMAT_BYTES_MAX
], *s3
;
780 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
786 fputs(i
->name
, stdout
);
791 printf("\t Type: %s\n", i
->type
);
794 printf("\t Path: %s\n", i
->path
);
796 printf("\t RO: %s%s%s\n",
797 i
->read_only
? ansi_highlight_red() : "",
798 i
->read_only
? "read-only" : "writable",
799 i
->read_only
? ansi_normal() : "");
801 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
802 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
804 printf("\t Created: %s; %s\n", s2
, s1
);
806 printf("\t Created: %s\n", s2
);
808 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
809 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
811 printf("\tModified: %s; %s\n", s2
, s1
);
813 printf("\tModified: %s\n", s2
);
815 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
816 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
818 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
820 printf("\t Usage: %s\n", s3
);
822 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
823 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
825 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
827 printf("\t Limit: %s\n", s3
);
830 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
832 static const struct bus_properties_map map
[] = {
833 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
834 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
835 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
836 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
837 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
838 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
839 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
840 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
841 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
842 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
846 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
853 r
= bus_map_all_properties(bus
,
854 "org.freedesktop.machine1",
859 return log_error_errno(r
, "Could not get properties: %m");
865 print_image_status_info(bus
, &info
);
870 typedef struct PoolStatusInfo
{
876 static void pool_status_info_clear(PoolStatusInfo
*info
) {
885 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
886 char bs
[FORMAT_BYTES_MAX
], *s
;
889 printf("\t Path: %s\n", i
->path
);
891 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
893 printf("\t Usage: %s\n", s
);
895 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
897 printf("\t Limit: %s\n", s
);
900 static int show_pool_info(sd_bus
*bus
) {
902 static const struct bus_properties_map map
[] = {
903 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
904 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
905 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
909 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
910 .usage
= (uint64_t) -1,
911 .limit
= (uint64_t) -1,
917 r
= bus_map_all_properties(bus
,
918 "org.freedesktop.machine1",
919 "/org/freedesktop/machine1",
923 return log_error_errno(r
, "Could not get properties: %m");
925 print_pool_status_info(bus
, &info
);
931 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
943 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
945 log_error_errno(r
, "Could not get properties: %m");
950 static int show_image(int argc
, char *argv
[], void *userdata
) {
952 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
953 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
954 bool properties
, new_line
= false;
955 sd_bus
*bus
= userdata
;
960 properties
= !strstr(argv
[0], "status");
962 pager_open_if_enabled();
966 /* If no argument is specified, inspect the manager
970 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
972 r
= show_pool_info(bus
);
977 for (i
= 1; i
< argc
; i
++) {
978 const char *path
= NULL
;
980 r
= sd_bus_call_method(
982 "org.freedesktop.machine1",
983 "/org/freedesktop/machine1",
984 "org.freedesktop.machine1.Manager",
990 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
994 r
= sd_bus_message_read(reply
, "o", &path
);
996 return bus_log_parse_error(r
);
999 r
= show_image_properties(bus
, path
, &new_line
);
1001 r
= show_image_info(bus
, path
, &new_line
);
1007 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1008 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1009 sd_bus
*bus
= userdata
;
1014 polkit_agent_open_if_enabled();
1017 arg_kill_who
= "all";
1019 for (i
= 1; i
< argc
; i
++) {
1020 r
= sd_bus_call_method(
1022 "org.freedesktop.machine1",
1023 "/org/freedesktop/machine1",
1024 "org.freedesktop.machine1.Manager",
1028 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1030 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1038 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1039 arg_kill_who
= "leader";
1040 arg_signal
= SIGINT
; /* sysvinit + systemd */
1042 return kill_machine(argc
, argv
, userdata
);
1045 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1046 arg_kill_who
= "leader";
1047 arg_signal
= SIGRTMIN
+4; /* only systemd */
1049 return kill_machine(argc
, argv
, userdata
);
1052 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1053 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1054 sd_bus
*bus
= userdata
;
1059 polkit_agent_open_if_enabled();
1061 for (i
= 1; i
< argc
; i
++) {
1062 r
= sd_bus_call_method(
1064 "org.freedesktop.machine1",
1065 "/org/freedesktop/machine1",
1066 "org.freedesktop.machine1.Manager",
1072 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1080 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1081 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1082 _cleanup_free_
char *abs_host_path
= NULL
;
1083 char *dest
, *host_path
, *container_path
;
1084 sd_bus
*bus
= userdata
;
1090 polkit_agent_open_if_enabled();
1092 copy_from
= streq(argv
[0], "copy-from");
1093 dest
= argv
[3] ?: argv
[2];
1094 host_path
= copy_from
? dest
: argv
[2];
1095 container_path
= copy_from
? argv
[2] : dest
;
1097 if (!path_is_absolute(host_path
)) {
1098 r
= path_make_absolute_cwd(host_path
, &abs_host_path
);
1100 return log_error_errno(r
, "Failed to make path absolute: %m");
1102 host_path
= abs_host_path
;
1105 r
= sd_bus_call_method(
1107 "org.freedesktop.machine1",
1108 "/org/freedesktop/machine1",
1109 "org.freedesktop.machine1.Manager",
1110 copy_from
? "CopyFromMachine" : "CopyToMachine",
1115 copy_from
? container_path
: host_path
,
1116 copy_from
? host_path
: container_path
);
1118 return log_error_errno(r
, "Failed to copy: %s", bus_error_message(&error
, r
));
1123 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1124 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1125 sd_bus
*bus
= userdata
;
1130 polkit_agent_open_if_enabled();
1132 r
= sd_bus_call_method(
1134 "org.freedesktop.machine1",
1135 "/org/freedesktop/machine1",
1136 "org.freedesktop.machine1.Manager",
1147 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1154 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1155 PTYForward
** forward
= (PTYForward
**) userdata
;
1162 /* If the forwarder is already initialized, tell it to
1163 * exit on the next vhangup(), so that we still flush
1164 * out what might be queued and exit then. */
1166 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1170 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1173 /* On error, or when the forwarder is not initialized yet, quit immediately */
1174 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1178 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, PTYForwardFlags flags
, const char *name
) {
1184 assert(master
>= 0);
1187 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1189 if (streq(name
, ".host"))
1190 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1192 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1194 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1195 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1197 r
= pty_forward_new(event
, master
, flags
, forward
);
1199 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1201 r
= sd_event_loop(event
);
1203 return log_error_errno(r
, "Failed to run event loop: %m");
1205 pty_forward_get_last_char(*forward
, &last_char
);
1208 (flags
& PTY_FORWARD_IGNORE_VHANGUP
) &&
1209 pty_forward_get_ignore_vhangup(*forward
) == 0;
1211 *forward
= pty_forward_free(*forward
);
1213 if (last_char
!= '\n')
1214 fputc('\n', stdout
);
1217 log_info("Machine %s terminated.", name
);
1218 else if (streq(name
, ".host"))
1219 log_info("Connection to the local host terminated.");
1221 log_info("Connection to machine %s terminated.", name
);
1223 sd_event_get_exit_code(event
, &ret
);
1227 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1228 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1229 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1230 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1231 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot
= NULL
;
1232 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1234 sd_bus
*bus
= userdata
;
1235 const char *pty
, *match
, *machine
;
1239 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1240 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1244 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1245 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1246 log_error("Login only supported on local machines.");
1250 polkit_agent_open_if_enabled();
1252 r
= sd_event_default(&event
);
1254 return log_error_errno(r
, "Failed to get event loop: %m");
1256 r
= sd_bus_attach_event(bus
, event
, 0);
1258 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1260 machine
= argc
< 2 || isempty(argv
[1]) ? ".host" : argv
[1];
1262 match
= strjoina("type='signal',"
1263 "sender='org.freedesktop.machine1',"
1264 "path='/org/freedesktop/machine1',",
1265 "interface='org.freedesktop.machine1.Manager',"
1266 "member='MachineRemoved',"
1267 "arg0='", machine
, "'");
1269 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1271 return log_error_errno(r
, "Failed to add machine removal match: %m");
1273 r
= sd_bus_call_method(
1275 "org.freedesktop.machine1",
1276 "/org/freedesktop/machine1",
1277 "org.freedesktop.machine1.Manager",
1283 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1287 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1289 return bus_log_parse_error(r
);
1291 return process_forward(event
, &forward
, master
, PTY_FORWARD_IGNORE_VHANGUP
, machine
);
1294 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1295 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *m
= NULL
;
1296 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1297 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1298 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot
= NULL
;
1299 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
1301 sd_bus
*bus
= userdata
;
1302 const char *pty
, *match
, *machine
, *path
, *uid
= NULL
;
1306 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1307 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1308 log_error("Shell only supported on local machines.");
1312 /* Pass $TERM to shell session, if not explicitly specified. */
1313 if (!strv_find_prefix(arg_setenv
, "TERM=")) {
1316 t
= strv_find_prefix(environ
, "TERM=");
1318 if (strv_extend(&arg_setenv
, t
) < 0)
1323 polkit_agent_open_if_enabled();
1325 r
= sd_event_default(&event
);
1327 return log_error_errno(r
, "Failed to get event loop: %m");
1329 r
= sd_bus_attach_event(bus
, event
, 0);
1331 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1333 machine
= argc
< 2 || isempty(argv
[1]) ? NULL
: argv
[1];
1340 at
= strchr(machine
, '@');
1342 uid
= strndupa(machine
, at
- machine
);
1347 if (isempty(machine
))
1350 match
= strjoina("type='signal',"
1351 "sender='org.freedesktop.machine1',"
1352 "path='/org/freedesktop/machine1',",
1353 "interface='org.freedesktop.machine1.Manager',"
1354 "member='MachineRemoved',"
1355 "arg0='", machine
, "'");
1357 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1359 return log_error_errno(r
, "Failed to add machine removal match: %m");
1361 r
= sd_bus_message_new_method_call(
1364 "org.freedesktop.machine1",
1365 "/org/freedesktop/machine1",
1366 "org.freedesktop.machine1.Manager",
1367 "OpenMachineShell");
1369 return bus_log_create_error(r
);
1371 path
= argc
< 3 || isempty(argv
[2]) ? NULL
: argv
[2];
1373 r
= sd_bus_message_append(m
, "sss", machine
, uid
, path
);
1375 return bus_log_create_error(r
);
1377 r
= sd_bus_message_append_strv(m
, strv_length(argv
) <= 3 ? NULL
: argv
+ 2);
1379 return bus_log_create_error(r
);
1381 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1383 return bus_log_create_error(r
);
1385 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1387 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1391 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1393 return bus_log_parse_error(r
);
1395 return process_forward(event
, &forward
, master
, 0, machine
);
1398 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1399 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1400 sd_bus
*bus
= userdata
;
1405 polkit_agent_open_if_enabled();
1407 for (i
= 1; i
< argc
; i
++) {
1408 r
= sd_bus_call_method(
1410 "org.freedesktop.machine1",
1411 "/org/freedesktop/machine1",
1412 "org.freedesktop.machine1.Manager",
1418 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1426 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1427 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1428 sd_bus
*bus
= userdata
;
1431 polkit_agent_open_if_enabled();
1433 r
= sd_bus_call_method(
1435 "org.freedesktop.machine1",
1436 "/org/freedesktop/machine1",
1437 "org.freedesktop.machine1.Manager",
1441 "ss", argv
[1], argv
[2]);
1443 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1450 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1451 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1452 sd_bus
*bus
= userdata
;
1455 polkit_agent_open_if_enabled();
1457 r
= sd_bus_call_method(
1459 "org.freedesktop.machine1",
1460 "/org/freedesktop/machine1",
1461 "org.freedesktop.machine1.Manager",
1465 "ssb", argv
[1], argv
[2], arg_read_only
);
1467 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1474 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1475 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1476 sd_bus
*bus
= userdata
;
1480 b
= parse_boolean(argv
[2]);
1482 log_error("Failed to parse boolean argument: %s", argv
[2]);
1487 polkit_agent_open_if_enabled();
1489 r
= sd_bus_call_method(
1491 "org.freedesktop.machine1",
1492 "/org/freedesktop/machine1",
1493 "org.freedesktop.machine1.Manager",
1494 "MarkImageReadOnly",
1499 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1506 static int make_service_name(const char *name
, char **ret
) {
1507 _cleanup_free_
char *e
= NULL
;
1513 if (!machine_name_is_valid(name
)) {
1514 log_error("Invalid machine name %s.", name
);
1518 e
= unit_name_escape(name
);
1522 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1524 return log_error_errno(r
, "Failed to build unit name: %m");
1529 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1530 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1531 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1532 sd_bus
*bus
= userdata
;
1537 polkit_agent_open_if_enabled();
1539 r
= bus_wait_for_jobs_new(bus
, &w
);
1543 for (i
= 1; i
< argc
; i
++) {
1544 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1545 _cleanup_free_
char *unit
= NULL
;
1548 r
= make_service_name(argv
[i
], &unit
);
1552 r
= sd_bus_call_method(
1554 "org.freedesktop.systemd1",
1555 "/org/freedesktop/systemd1",
1556 "org.freedesktop.systemd1.Manager",
1560 "ss", unit
, "fail");
1562 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1566 r
= sd_bus_message_read(reply
, "o", &object
);
1568 return bus_log_parse_error(r
);
1570 r
= bus_wait_for_jobs_add(w
, object
);
1575 r
= bus_wait_for_jobs(w
, arg_quiet
);
1582 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1583 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
1584 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1585 int carries_install_info
= 0;
1586 const char *method
= NULL
;
1587 sd_bus
*bus
= userdata
;
1592 polkit_agent_open_if_enabled();
1594 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1596 r
= sd_bus_message_new_method_call(
1599 "org.freedesktop.systemd1",
1600 "/org/freedesktop/systemd1",
1601 "org.freedesktop.systemd1.Manager",
1604 return bus_log_create_error(r
);
1606 r
= sd_bus_message_open_container(m
, 'a', "s");
1608 return bus_log_create_error(r
);
1610 for (i
= 1; i
< argc
; i
++) {
1611 _cleanup_free_
char *unit
= NULL
;
1613 r
= make_service_name(argv
[i
], &unit
);
1617 r
= sd_bus_message_append(m
, "s", unit
);
1619 return bus_log_create_error(r
);
1622 r
= sd_bus_message_close_container(m
);
1624 return bus_log_create_error(r
);
1626 if (streq(argv
[0], "enable"))
1627 r
= sd_bus_message_append(m
, "bb", false, false);
1629 r
= sd_bus_message_append(m
, "b", false);
1631 return bus_log_create_error(r
);
1633 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1635 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1639 if (streq(argv
[0], "enable")) {
1640 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1642 return bus_log_parse_error(r
);
1645 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1649 r
= sd_bus_call_method(
1651 "org.freedesktop.systemd1",
1652 "/org/freedesktop/systemd1",
1653 "org.freedesktop.systemd1.Manager",
1659 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1666 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1667 const char **our_path
= userdata
, *line
;
1674 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1676 bus_log_parse_error(r
);
1680 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1683 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1686 log_full(priority
, "%s", line
);
1690 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1691 const char **our_path
= userdata
, *path
, *result
;
1698 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1700 bus_log_parse_error(r
);
1704 if (!streq_ptr(*our_path
, path
))
1707 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1711 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1716 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1718 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1722 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1723 _cleanup_(sd_bus_slot_unrefp
) sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1724 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1725 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1726 _cleanup_(sd_event_unrefp
) sd_event
* event
= NULL
;
1727 const char *path
= NULL
;
1734 polkit_agent_open_if_enabled();
1736 r
= sd_event_default(&event
);
1738 return log_error_errno(r
, "Failed to get event loop: %m");
1740 r
= sd_bus_attach_event(bus
, event
, 0);
1742 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1744 r
= sd_bus_add_match(
1748 "sender='org.freedesktop.import1',"
1749 "interface='org.freedesktop.import1.Manager',"
1750 "member='TransferRemoved',"
1751 "path='/org/freedesktop/import1'",
1752 match_transfer_removed
, &path
);
1754 return log_error_errno(r
, "Failed to install match: %m");
1756 r
= sd_bus_add_match(
1760 "sender='org.freedesktop.import1',"
1761 "interface='org.freedesktop.import1.Transfer',"
1762 "member='LogMessage'",
1763 match_log_message
, &path
);
1765 return log_error_errno(r
, "Failed to install match: %m");
1767 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1769 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1773 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1775 return bus_log_parse_error(r
);
1777 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
1780 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1782 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1783 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1785 r
= sd_event_loop(event
);
1787 return log_error_errno(r
, "Failed to run event loop: %m");
1792 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1793 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1794 _cleanup_free_
char *ll
= NULL
;
1795 _cleanup_close_
int fd
= -1;
1796 const char *local
= NULL
, *path
= NULL
;
1797 sd_bus
*bus
= userdata
;
1804 if (isempty(path
) || streq(path
, "-"))
1810 local
= basename(path
);
1811 if (isempty(local
) || streq(local
, "-"))
1815 log_error("Need either path or local name.");
1819 r
= tar_strip_suffixes(local
, &ll
);
1825 if (!machine_name_is_valid(local
)) {
1826 log_error("Local name %s is not a suitable machine name.", local
);
1831 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1833 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1836 r
= sd_bus_message_new_method_call(
1839 "org.freedesktop.import1",
1840 "/org/freedesktop/import1",
1841 "org.freedesktop.import1.Manager",
1844 return bus_log_create_error(r
);
1846 r
= sd_bus_message_append(
1849 fd
>= 0 ? fd
: STDIN_FILENO
,
1854 return bus_log_create_error(r
);
1856 return transfer_image_common(bus
, m
);
1859 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1860 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1861 _cleanup_free_
char *ll
= NULL
;
1862 _cleanup_close_
int fd
= -1;
1863 const char *local
= NULL
, *path
= NULL
;
1864 sd_bus
*bus
= userdata
;
1871 if (isempty(path
) || streq(path
, "-"))
1877 local
= basename(path
);
1878 if (isempty(local
) || streq(local
, "-"))
1882 log_error("Need either path or local name.");
1886 r
= raw_strip_suffixes(local
, &ll
);
1892 if (!machine_name_is_valid(local
)) {
1893 log_error("Local name %s is not a suitable machine name.", local
);
1898 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1900 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1903 r
= sd_bus_message_new_method_call(
1906 "org.freedesktop.import1",
1907 "/org/freedesktop/import1",
1908 "org.freedesktop.import1.Manager",
1911 return bus_log_create_error(r
);
1913 r
= sd_bus_message_append(
1916 fd
>= 0 ? fd
: STDIN_FILENO
,
1921 return bus_log_create_error(r
);
1923 return transfer_image_common(bus
, m
);
1926 static void determine_compression_from_filename(const char *p
) {
1933 if (endswith(p
, ".xz"))
1935 else if (endswith(p
, ".gz"))
1936 arg_format
= "gzip";
1937 else if (endswith(p
, ".bz2"))
1938 arg_format
= "bzip2";
1941 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1942 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1943 _cleanup_close_
int fd
= -1;
1944 const char *local
= NULL
, *path
= NULL
;
1945 sd_bus
*bus
= userdata
;
1951 if (!machine_name_is_valid(local
)) {
1952 log_error("Machine name %s is not valid.", local
);
1958 if (isempty(path
) || streq(path
, "-"))
1962 determine_compression_from_filename(path
);
1964 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1966 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1969 r
= sd_bus_message_new_method_call(
1972 "org.freedesktop.import1",
1973 "/org/freedesktop/import1",
1974 "org.freedesktop.import1.Manager",
1977 return bus_log_create_error(r
);
1979 r
= sd_bus_message_append(
1983 fd
>= 0 ? fd
: STDOUT_FILENO
,
1986 return bus_log_create_error(r
);
1988 return transfer_image_common(bus
, m
);
1991 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1992 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1993 _cleanup_close_
int fd
= -1;
1994 const char *local
= NULL
, *path
= NULL
;
1995 sd_bus
*bus
= userdata
;
2001 if (!machine_name_is_valid(local
)) {
2002 log_error("Machine name %s is not valid.", local
);
2008 if (isempty(path
) || streq(path
, "-"))
2012 determine_compression_from_filename(path
);
2014 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2016 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2019 r
= sd_bus_message_new_method_call(
2022 "org.freedesktop.import1",
2023 "/org/freedesktop/import1",
2024 "org.freedesktop.import1.Manager",
2027 return bus_log_create_error(r
);
2029 r
= sd_bus_message_append(
2033 fd
>= 0 ? fd
: STDOUT_FILENO
,
2036 return bus_log_create_error(r
);
2038 return transfer_image_common(bus
, m
);
2041 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2042 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2043 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2044 const char *local
, *remote
;
2045 sd_bus
*bus
= userdata
;
2051 if (!http_url_is_valid(remote
)) {
2052 log_error("URL '%s' is not valid.", remote
);
2059 r
= import_url_last_component(remote
, &l
);
2061 return log_error_errno(r
, "Failed to get final component of URL: %m");
2066 if (isempty(local
) || streq(local
, "-"))
2070 r
= tar_strip_suffixes(local
, &ll
);
2076 if (!machine_name_is_valid(local
)) {
2077 log_error("Local name %s is not a suitable machine name.", local
);
2082 r
= sd_bus_message_new_method_call(
2085 "org.freedesktop.import1",
2086 "/org/freedesktop/import1",
2087 "org.freedesktop.import1.Manager",
2090 return bus_log_create_error(r
);
2092 r
= sd_bus_message_append(
2097 import_verify_to_string(arg_verify
),
2100 return bus_log_create_error(r
);
2102 return transfer_image_common(bus
, m
);
2105 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2106 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2107 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2108 const char *local
, *remote
;
2109 sd_bus
*bus
= userdata
;
2115 if (!http_url_is_valid(remote
)) {
2116 log_error("URL '%s' is not valid.", remote
);
2123 r
= import_url_last_component(remote
, &l
);
2125 return log_error_errno(r
, "Failed to get final component of URL: %m");
2130 if (isempty(local
) || streq(local
, "-"))
2134 r
= raw_strip_suffixes(local
, &ll
);
2140 if (!machine_name_is_valid(local
)) {
2141 log_error("Local name %s is not a suitable machine name.", local
);
2146 r
= sd_bus_message_new_method_call(
2149 "org.freedesktop.import1",
2150 "/org/freedesktop/import1",
2151 "org.freedesktop.import1.Manager",
2154 return bus_log_create_error(r
);
2156 r
= sd_bus_message_append(
2161 import_verify_to_string(arg_verify
),
2164 return bus_log_create_error(r
);
2166 return transfer_image_common(bus
, m
);
2169 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2170 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2171 const char *local
, *remote
, *tag
;
2172 sd_bus
*bus
= userdata
;
2175 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2176 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2181 tag
= strchr(remote
, ':');
2183 remote
= strndupa(remote
, tag
- remote
);
2187 if (!dkr_name_is_valid(remote
)) {
2188 log_error("DKR name '%s' is invalid.", remote
);
2191 if (tag
&& !dkr_tag_is_valid(tag
)) {
2192 log_error("DKR tag '%s' is invalid.", remote
);
2199 local
= strchr(remote
, '/');
2206 if (isempty(local
) || streq(local
, "-"))
2210 if (!machine_name_is_valid(local
)) {
2211 log_error("Local name %s is not a suitable machine name.", local
);
2216 r
= sd_bus_message_new_method_call(
2219 "org.freedesktop.import1",
2220 "/org/freedesktop/import1",
2221 "org.freedesktop.import1.Manager",
2224 return bus_log_create_error(r
);
2226 r
= sd_bus_message_append(
2233 import_verify_to_string(arg_verify
),
2236 return bus_log_create_error(r
);
2238 return transfer_image_common(bus
, m
);
2241 typedef struct TransferInfo
{
2249 static int compare_transfer_info(const void *a
, const void *b
) {
2250 const TransferInfo
*x
= a
, *y
= b
;
2252 return strcmp(x
->local
, y
->local
);
2255 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2256 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2257 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
2258 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2259 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2260 size_t n_transfers
= 0, n_allocated
= 0, j
;
2261 const char *type
, *remote
, *local
, *object
;
2262 sd_bus
*bus
= userdata
;
2263 uint32_t id
, max_id
= 0;
2267 pager_open_if_enabled();
2269 r
= sd_bus_call_method(
2271 "org.freedesktop.import1",
2272 "/org/freedesktop/import1",
2273 "org.freedesktop.import1.Manager",
2279 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2283 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2285 return bus_log_parse_error(r
);
2287 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2290 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2293 transfers
[n_transfers
].id
= id
;
2294 transfers
[n_transfers
].type
= type
;
2295 transfers
[n_transfers
].remote
= remote
;
2296 transfers
[n_transfers
].local
= local
;
2297 transfers
[n_transfers
].progress
= progress
;
2317 return bus_log_parse_error(r
);
2319 r
= sd_bus_message_exit_container(reply
);
2321 return bus_log_parse_error(r
);
2323 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2326 printf("%-*s %-*s %-*s %-*s %-*s\n",
2327 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2329 (int) max_type
, "TYPE",
2330 (int) max_local
, "LOCAL",
2331 (int) max_remote
, "REMOTE");
2333 for (j
= 0; j
< n_transfers
; j
++)
2334 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2335 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2336 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2337 (int) max_type
, transfers
[j
].type
,
2338 (int) max_local
, transfers
[j
].local
,
2339 (int) max_remote
, transfers
[j
].remote
);
2342 printf("\n%zu transfers listed.\n", n_transfers
);
2347 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2348 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2349 sd_bus
*bus
= userdata
;
2354 polkit_agent_open_if_enabled();
2356 for (i
= 1; i
< argc
; i
++) {
2359 r
= safe_atou32(argv
[i
], &id
);
2361 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2363 r
= sd_bus_call_method(
2365 "org.freedesktop.import1",
2366 "/org/freedesktop/import1",
2367 "org.freedesktop.import1.Manager",
2373 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2381 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2382 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2383 sd_bus
*bus
= userdata
;
2387 if (STR_IN_SET(argv
[argc
-1], "-", "none", "infinity"))
2388 limit
= (uint64_t) -1;
2390 r
= parse_size(argv
[argc
-1], 1024, &limit
);
2392 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2396 /* With two arguments changes the quota limit of the
2397 * specified image */
2398 r
= sd_bus_call_method(
2400 "org.freedesktop.machine1",
2401 "/org/freedesktop/machine1",
2402 "org.freedesktop.machine1.Manager",
2406 "st", argv
[1], limit
);
2408 /* With one argument changes the pool quota limit */
2409 r
= sd_bus_call_method(
2411 "org.freedesktop.machine1",
2412 "/org/freedesktop/machine1",
2413 "org.freedesktop.machine1.Manager",
2420 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2427 static int help(int argc
, char *argv
[], void *userdata
) {
2429 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2430 "Send control commands to or query the virtual machine and container\n"
2431 "registration manager.\n\n"
2432 " -h --help Show this help\n"
2433 " --version Show package version\n"
2434 " --no-pager Do not pipe output into a pager\n"
2435 " --no-legend Do not show the headers and footers\n"
2436 " --no-ask-password Do not ask for system passwords\n"
2437 " -H --host=[USER@]HOST Operate on remote host\n"
2438 " -M --machine=CONTAINER Operate on local container\n"
2439 " -p --property=NAME Show only properties by this name\n"
2440 " -q --quiet Suppress output\n"
2441 " -a --all Show all properties, including empty ones\n"
2442 " -l --full Do not ellipsize output\n"
2443 " --kill-who=WHO Who to send signal to\n"
2444 " -s --signal=SIGNAL Which signal to send\n"
2445 " --uid=USER Specify user ID to invoke shell as\n"
2446 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2447 " --read-only Create read-only bind mount\n"
2448 " --mkdir Create directory before bind mounting, if missing\n"
2449 " -n --lines=INTEGER Number of journal entries to show\n"
2450 " -o --output=STRING Change journal output mode (short,\n"
2451 " short-monotonic, verbose, export, json,\n"
2452 " json-pretty, json-sse, cat)\n"
2453 " --verify=MODE Verification mode for downloaded images (no,\n"
2454 " checksum, signature)\n"
2455 " --force Download image even if already exists\n"
2456 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2458 "Machine Commands:\n"
2459 " list List running VMs and containers\n"
2460 " status NAME... Show VM/container details\n"
2461 " show [NAME...] Show properties of one or more VMs/containers\n"
2462 " start NAME... Start container as a service\n"
2463 " login [NAME] Get a login prompt in a container or on the\n"
2465 " shell [[USER@]NAME [COMMAND...]]\n"
2466 " Invoke a shell (or other command) in a container\n"
2467 " or on the local host\n"
2468 " enable NAME... Enable automatic container start at boot\n"
2469 " disable NAME... Disable automatic container start at boot\n"
2470 " poweroff NAME... Power off one or more containers\n"
2471 " reboot NAME... Reboot one or more containers\n"
2472 " terminate NAME... Terminate one or more VMs/containers\n"
2473 " kill NAME... Send signal to processes of a VM/container\n"
2474 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2475 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2476 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2478 " list-images Show available container and VM images\n"
2479 " image-status [NAME...] Show image details\n"
2480 " show-image [NAME...] Show properties of image\n"
2481 " clone NAME NAME Clone an image\n"
2482 " rename NAME NAME Rename an image\n"
2483 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2484 " remove NAME... Remove an image\n"
2485 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2486 "Image Transfer Commands:\n"
2487 " pull-tar URL [NAME] Download a TAR container image\n"
2488 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2489 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2490 " import-tar FILE [NAME] Import a local TAR container image\n"
2491 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2492 " export-tar NAME [FILE] Export a TAR container image locally\n"
2493 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2494 " list-transfers Show list of downloads in progress\n"
2495 " cancel-transfer Cancel a download\n"
2496 , program_invocation_short_name
);
2501 static int parse_argv(int argc
, char *argv
[]) {
2504 ARG_VERSION
= 0x100,
2510 ARG_NO_ASK_PASSWORD
,
2519 static const struct option options
[] = {
2520 { "help", no_argument
, NULL
, 'h' },
2521 { "version", no_argument
, NULL
, ARG_VERSION
},
2522 { "property", required_argument
, NULL
, 'p' },
2523 { "all", no_argument
, NULL
, 'a' },
2524 { "full", no_argument
, NULL
, 'l' },
2525 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2526 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2527 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2528 { "signal", required_argument
, NULL
, 's' },
2529 { "host", required_argument
, NULL
, 'H' },
2530 { "machine", required_argument
, NULL
, 'M' },
2531 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2532 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2533 { "quiet", no_argument
, NULL
, 'q' },
2534 { "lines", required_argument
, NULL
, 'n' },
2535 { "output", required_argument
, NULL
, 'o' },
2536 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2537 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2538 { "force", no_argument
, NULL
, ARG_FORCE
},
2539 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2540 { "format", required_argument
, NULL
, ARG_FORMAT
},
2541 { "uid", required_argument
, NULL
, ARG_UID
},
2542 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2551 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2556 return help(0, NULL
, NULL
);
2562 r
= strv_extend(&arg_property
, optarg
);
2566 /* If the user asked for a particular
2567 * property, show it to him, even if it is
2581 if (safe_atou(optarg
, &arg_lines
) < 0) {
2582 log_error("Failed to parse lines '%s'", optarg
);
2588 arg_output
= output_mode_from_string(optarg
);
2589 if (arg_output
< 0) {
2590 log_error("Unknown output '%s'.", optarg
);
2596 arg_no_pager
= true;
2604 arg_kill_who
= optarg
;
2608 arg_signal
= signal_from_string_try_harder(optarg
);
2609 if (arg_signal
< 0) {
2610 log_error("Failed to parse signal string %s.", optarg
);
2615 case ARG_NO_ASK_PASSWORD
:
2616 arg_ask_password
= false;
2620 arg_transport
= BUS_TRANSPORT_REMOTE
;
2625 arg_transport
= BUS_TRANSPORT_MACHINE
;
2630 arg_read_only
= true;
2642 arg_verify
= import_verify_from_string(optarg
);
2643 if (arg_verify
< 0) {
2644 log_error("Failed to parse --verify= setting: %s", optarg
);
2653 case ARG_DKR_INDEX_URL
:
2654 if (!http_url_is_valid(optarg
)) {
2655 log_error("Index URL is invalid: %s", optarg
);
2659 arg_dkr_index_url
= optarg
;
2663 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2664 log_error("Unknown format: %s", optarg
);
2668 arg_format
= optarg
;
2676 if (!env_assignment_is_valid(optarg
)) {
2677 log_error("Environment assignment invalid: %s", optarg
);
2681 r
= strv_extend(&arg_setenv
, optarg
);
2690 assert_not_reached("Unhandled option");
2696 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2698 static const Verb verbs
[] = {
2699 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2700 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2701 { "list-images", VERB_ANY
, 1, 0, list_images
},
2702 { "status", 2, VERB_ANY
, 0, show_machine
},
2703 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2704 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2705 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2706 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2707 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2708 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2709 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2710 { "login", VERB_ANY
, 2, 0, login_machine
},
2711 { "shell", VERB_ANY
, VERB_ANY
, 0, shell_machine
},
2712 { "bind", 3, 4, 0, bind_mount
},
2713 { "copy-to", 3, 4, 0, copy_files
},
2714 { "copy-from", 3, 4, 0, copy_files
},
2715 { "remove", 2, VERB_ANY
, 0, remove_image
},
2716 { "rename", 3, 3, 0, rename_image
},
2717 { "clone", 3, 3, 0, clone_image
},
2718 { "read-only", 2, 3, 0, read_only_image
},
2719 { "start", 2, VERB_ANY
, 0, start_machine
},
2720 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2721 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2722 { "import-tar", 2, 3, 0, import_tar
},
2723 { "import-raw", 2, 3, 0, import_raw
},
2724 { "export-tar", 2, 3, 0, export_tar
},
2725 { "export-raw", 2, 3, 0, export_raw
},
2726 { "pull-tar", 2, 3, 0, pull_tar
},
2727 { "pull-raw", 2, 3, 0, pull_raw
},
2728 { "pull-dkr", 2, 3, 0, pull_dkr
},
2729 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2730 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2731 { "set-limit", 2, 3, 0, set_limit
},
2735 return dispatch_verb(argc
, argv
, verbs
, bus
);
2738 int main(int argc
, char*argv
[]) {
2739 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2742 setlocale(LC_ALL
, "");
2743 log_parse_environment();
2746 r
= parse_argv(argc
, argv
);
2750 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
2752 log_error_errno(r
, "Failed to create bus connection: %m");
2756 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2758 r
= machinectl_main(argc
, argv
, bus
);
2762 polkit_agent_close();
2764 strv_free(arg_property
);
2765 strv_free(arg_setenv
);
2767 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;