1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
32 #include <sys/mount.h>
39 #include "spawn-polkit-agent.h"
41 #include "bus-error.h"
44 #include "unit-name.h"
45 #include "cgroup-show.h"
46 #include "logs-show.h"
47 #include "cgroup-util.h"
49 #include "event-util.h"
50 #include "path-util.h"
54 #include "import-util.h"
55 #include "process-util.h"
56 #include "terminal-util.h"
57 #include "signal-util.h"
59 #include "hostname-util.h"
61 static char **arg_property
= NULL
;
62 static bool arg_all
= false;
63 static bool arg_full
= false;
64 static bool arg_no_pager
= false;
65 static bool arg_legend
= true;
66 static const char *arg_kill_who
= NULL
;
67 static int arg_signal
= SIGTERM
;
68 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
69 static char *arg_host
= NULL
;
70 static bool arg_read_only
= false;
71 static bool arg_mkdir
= false;
72 static bool arg_quiet
= false;
73 static bool arg_ask_password
= true;
74 static unsigned arg_lines
= 10;
75 static OutputMode arg_output
= OUTPUT_SHORT
;
76 static bool arg_force
= false;
77 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
78 static const char* arg_dkr_index_url
= NULL
;
79 static const char* arg_format
= NULL
;
80 static const char *arg_uid
= NULL
;
81 static char **arg_setenv
= NULL
;
83 static void pager_open_if_enabled(void) {
91 static void polkit_agent_open_if_enabled(void) {
93 /* Open the polkit agent as a child process if necessary */
95 if (!arg_ask_password
)
98 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
104 static OutputFlags
get_output_flags(void) {
106 arg_all
* OUTPUT_SHOW_ALL
|
107 arg_full
* OUTPUT_FULL_WIDTH
|
108 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
109 on_tty() * OUTPUT_COLOR
|
110 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
113 typedef struct MachineInfo
{
119 static int compare_machine_info(const void *a
, const void *b
) {
120 const MachineInfo
*x
= a
, *y
= b
;
122 return strcmp(x
->name
, y
->name
);
125 static int list_machines(int argc
, char *argv
[], void *userdata
) {
127 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
128 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
129 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
130 _cleanup_free_ MachineInfo
*machines
= NULL
;
131 const char *name
, *class, *service
, *object
;
132 size_t n_machines
= 0, n_allocated
= 0, j
;
133 sd_bus
*bus
= userdata
;
138 pager_open_if_enabled();
140 r
= sd_bus_call_method(
142 "org.freedesktop.machine1",
143 "/org/freedesktop/machine1",
144 "org.freedesktop.machine1.Manager",
150 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
154 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
156 return bus_log_parse_error(r
);
158 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
161 if (name
[0] == '.' && !arg_all
)
164 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
167 machines
[n_machines
].name
= name
;
168 machines
[n_machines
].class = class;
169 machines
[n_machines
].service
= service
;
186 return bus_log_parse_error(r
);
188 r
= sd_bus_message_exit_container(reply
);
190 return bus_log_parse_error(r
);
192 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
195 printf("%-*s %-*s %-*s\n",
196 (int) max_name
, "MACHINE",
197 (int) max_class
, "CLASS",
198 (int) max_service
, "SERVICE");
200 for (j
= 0; j
< n_machines
; j
++)
201 printf("%-*s %-*s %-*s\n",
202 (int) max_name
, machines
[j
].name
,
203 (int) max_class
, machines
[j
].class,
204 (int) max_service
, machines
[j
].service
);
207 printf("\n%zu machines listed.\n", n_machines
);
212 typedef struct ImageInfo
{
221 static int compare_image_info(const void *a
, const void *b
) {
222 const ImageInfo
*x
= a
, *y
= b
;
224 return strcmp(x
->name
, y
->name
);
227 static int list_images(int argc
, char *argv
[], void *userdata
) {
229 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
230 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
231 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
232 _cleanup_free_ ImageInfo
*images
= NULL
;
233 size_t n_images
= 0, n_allocated
= 0, j
;
234 const char *name
, *type
, *object
;
235 sd_bus
*bus
= userdata
;
236 uint64_t crtime
, mtime
, size
;
241 pager_open_if_enabled();
243 r
= sd_bus_call_method(
245 "org.freedesktop.machine1",
246 "/org/freedesktop/machine1",
247 "org.freedesktop.machine1.Manager",
253 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
257 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
259 return bus_log_parse_error(r
);
261 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
262 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
265 if (name
[0] == '.' && !arg_all
)
268 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
271 images
[n_images
].name
= name
;
272 images
[n_images
].type
= type
;
273 images
[n_images
].read_only
= read_only
;
274 images
[n_images
].crtime
= crtime
;
275 images
[n_images
].mtime
= mtime
;
276 images
[n_images
].size
= size
;
287 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
293 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
298 if (size
!= (uint64_t) -1) {
299 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
307 return bus_log_parse_error(r
);
309 r
= sd_bus_message_exit_container(reply
);
311 return bus_log_parse_error(r
);
313 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
316 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
317 (int) max_name
, "NAME",
318 (int) max_type
, "TYPE",
320 (int) max_size
, "USAGE",
321 (int) max_crtime
, "CREATED",
322 (int) max_mtime
, "MODIFIED");
324 for (j
= 0; j
< n_images
; j
++) {
325 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
327 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
328 (int) max_name
, images
[j
].name
,
329 (int) max_type
, images
[j
].type
,
330 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_highlight_off() : "",
331 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
332 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
333 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
337 printf("\n%zu images listed.\n", n_images
);
342 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
343 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
344 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
345 _cleanup_free_
char *path
= NULL
;
353 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
356 path
= unit_dbus_path_from_name(unit
);
360 r
= sd_bus_get_property(
362 "org.freedesktop.systemd1",
364 unit_dbus_interface_from_name(unit
),
370 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
374 r
= sd_bus_message_read(reply
, "s", &cgroup
);
376 return bus_log_parse_error(r
);
378 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
) != 0 && leader
<= 0)
387 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
391 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
392 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
400 r
= sd_bus_call_method(bus
,
401 "org.freedesktop.machine1",
402 "/org/freedesktop/machine1",
403 "org.freedesktop.machine1.Manager",
404 "GetMachineAddresses",
411 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
413 return bus_log_parse_error(r
);
415 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
419 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
421 r
= sd_bus_message_read(reply
, "i", &family
);
423 return bus_log_parse_error(r
);
425 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
427 return bus_log_parse_error(r
);
429 fputs(prefix
, stdout
);
430 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
431 if (family
== AF_INET6
&& ifi
> 0)
435 r
= sd_bus_message_exit_container(reply
);
437 return bus_log_parse_error(r
);
439 if (prefix
!= prefix2
)
443 return bus_log_parse_error(r
);
445 r
= sd_bus_message_exit_container(reply
);
447 return bus_log_parse_error(r
);
452 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
453 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
454 const char *k
, *v
, *pretty
= NULL
;
461 r
= sd_bus_call_method(bus
,
462 "org.freedesktop.machine1",
463 "/org/freedesktop/machine1",
464 "org.freedesktop.machine1.Manager",
465 "GetMachineOSRelease",
472 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
474 return bus_log_parse_error(r
);
476 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
477 if (streq(k
, "PRETTY_NAME"))
482 return bus_log_parse_error(r
);
484 r
= sd_bus_message_exit_container(reply
);
486 return bus_log_parse_error(r
);
489 printf("%s%s\n", prefix
, pretty
);
494 typedef struct MachineStatusInfo
{
500 char *root_directory
;
502 struct dual_timestamp timestamp
;
507 static void machine_status_info_clear(MachineStatusInfo
*info
) {
513 free(info
->root_directory
);
519 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
520 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
521 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
527 fputs(strna(i
->name
), stdout
);
529 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
530 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
534 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
535 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
538 printf("\t Since: %s; %s\n", s2
, s1
);
540 printf("\t Since: %s\n", s2
);
543 _cleanup_free_
char *t
= NULL
;
545 printf("\t Leader: %u", (unsigned) i
->leader
);
547 get_process_comm(i
->leader
, &t
);
555 printf("\t Service: %s", i
->service
);
558 printf("; class %s", i
->class);
562 printf("\t Class: %s\n", i
->class);
564 if (i
->root_directory
)
565 printf("\t Root: %s\n", i
->root_directory
);
567 if (i
->n_netif
> 0) {
570 fputs("\t Iface:", stdout
);
572 for (c
= 0; c
< i
->n_netif
; c
++) {
573 char name
[IF_NAMESIZE
+1] = "";
575 if (if_indextoname(i
->netif
[c
], name
)) {
584 printf(" %i", i
->netif
[c
]);
590 print_addresses(bus
, i
->name
, ifi
,
594 print_os_release(bus
, i
->name
, "\t OS: ");
597 printf("\t Unit: %s\n", i
->unit
);
598 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
600 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
602 show_journal_by_unit(
607 i
->timestamp
.monotonic
,
610 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
611 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_highlight_off() : "");
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 abs_host_path
= path_make_absolute_cwd(host_path
);
1099 host_path
= abs_host_path
;
1102 r
= sd_bus_call_method(
1104 "org.freedesktop.machine1",
1105 "/org/freedesktop/machine1",
1106 "org.freedesktop.machine1.Manager",
1107 copy_from
? "CopyFromMachine" : "CopyToMachine",
1112 copy_from
? container_path
: host_path
,
1113 copy_from
? host_path
: container_path
);
1115 log_error("Failed to copy: %s", bus_error_message(&error
, -r
));
1122 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1123 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1124 sd_bus
*bus
= userdata
;
1129 polkit_agent_open_if_enabled();
1131 r
= sd_bus_call_method(
1133 "org.freedesktop.machine1",
1134 "/org/freedesktop/machine1",
1135 "org.freedesktop.machine1.Manager",
1146 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1153 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1154 PTYForward
** forward
= (PTYForward
**) userdata
;
1161 /* If the forwarder is already initialized, tell it to
1162 * exit on the next vhangup(), so that we still flush
1163 * out what might be queued and exit then. */
1165 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1169 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1172 /* On error, or when the forwarder is not initialized yet, quit immediately */
1173 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1177 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, bool ignore_vhangup
, const char *name
) {
1183 assert(master
>= 0);
1186 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1188 if (streq(name
, ".host"))
1189 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1191 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1193 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1194 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1196 r
= pty_forward_new(event
, master
, ignore_vhangup
, false, forward
);
1198 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1200 r
= sd_event_loop(event
);
1202 return log_error_errno(r
, "Failed to run event loop: %m");
1204 pty_forward_get_last_char(*forward
, &last_char
);
1208 pty_forward_get_ignore_vhangup(*forward
) == 0;
1210 *forward
= pty_forward_free(*forward
);
1212 if (last_char
!= '\n')
1213 fputc('\n', stdout
);
1216 log_info("Machine %s terminated.", name
);
1217 else if (streq(name
, ".host"))
1218 log_info("Connection to the local host terminated.");
1220 log_info("Connection to machine %s terminated.", name
);
1222 sd_event_get_exit_code(event
, &ret
);
1226 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1227 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1228 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1229 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1230 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1231 _cleanup_event_unref_ sd_event
*event
= NULL
;
1233 sd_bus
*bus
= userdata
;
1234 const char *pty
, *match
, *machine
;
1238 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1239 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1243 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1244 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1245 log_error("Login only supported on local machines.");
1249 polkit_agent_open_if_enabled();
1251 r
= sd_event_default(&event
);
1253 return log_error_errno(r
, "Failed to get event loop: %m");
1255 r
= sd_bus_attach_event(bus
, event
, 0);
1257 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1259 machine
= argc
< 2 || isempty(argv
[1]) ? ".host" : argv
[1];
1261 match
= strjoina("type='signal',"
1262 "sender='org.freedesktop.machine1',"
1263 "path='/org/freedesktop/machine1',",
1264 "interface='org.freedesktop.machine1.Manager',"
1265 "member='MachineRemoved',"
1266 "arg0='", machine
, "'");
1268 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1270 return log_error_errno(r
, "Failed to add machine removal match: %m");
1272 r
= sd_bus_call_method(
1274 "org.freedesktop.machine1",
1275 "/org/freedesktop/machine1",
1276 "org.freedesktop.machine1.Manager",
1282 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1286 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1288 return bus_log_parse_error(r
);
1290 return process_forward(event
, &forward
, master
, true, machine
);
1293 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1294 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *m
= NULL
;
1295 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1296 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1297 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1298 _cleanup_event_unref_ sd_event
*event
= NULL
;
1300 sd_bus
*bus
= userdata
;
1301 const char *pty
, *match
, *machine
, *path
, *uid
= NULL
;
1305 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1306 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1307 log_error("Shell only supported on local machines.");
1311 /* Pass $TERM to shell session, if not explicitly specified. */
1312 if (!strv_find_prefix(arg_setenv
, "TERM=")) {
1315 t
= strv_find_prefix(environ
, "TERM=");
1317 if (strv_extend(&arg_setenv
, t
) < 0)
1322 polkit_agent_open_if_enabled();
1324 r
= sd_event_default(&event
);
1326 return log_error_errno(r
, "Failed to get event loop: %m");
1328 r
= sd_bus_attach_event(bus
, event
, 0);
1330 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1332 machine
= argc
< 2 || isempty(argv
[1]) ? NULL
: argv
[1];
1339 at
= strchr(machine
, '@');
1341 uid
= strndupa(machine
, at
- machine
);
1346 if (isempty(machine
))
1349 match
= strjoina("type='signal',"
1350 "sender='org.freedesktop.machine1',"
1351 "path='/org/freedesktop/machine1',",
1352 "interface='org.freedesktop.machine1.Manager',"
1353 "member='MachineRemoved',"
1354 "arg0='", machine
, "'");
1356 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1358 return log_error_errno(r
, "Failed to add machine removal match: %m");
1360 r
= sd_bus_message_new_method_call(
1363 "org.freedesktop.machine1",
1364 "/org/freedesktop/machine1",
1365 "org.freedesktop.machine1.Manager",
1366 "OpenMachineShell");
1368 return bus_log_create_error(r
);
1370 path
= argc
< 3 || isempty(argv
[2]) ? NULL
: argv
[2];
1372 r
= sd_bus_message_append(m
, "sss", machine
, uid
, path
);
1374 return bus_log_create_error(r
);
1376 r
= sd_bus_message_append_strv(m
, strv_length(argv
) <= 3 ? NULL
: argv
+ 2);
1378 return bus_log_create_error(r
);
1380 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1382 return bus_log_create_error(r
);
1384 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1386 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1390 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1392 return bus_log_parse_error(r
);
1394 return process_forward(event
, &forward
, master
, false, machine
);
1397 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1398 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1399 sd_bus
*bus
= userdata
;
1404 polkit_agent_open_if_enabled();
1406 for (i
= 1; i
< argc
; i
++) {
1407 r
= sd_bus_call_method(
1409 "org.freedesktop.machine1",
1410 "/org/freedesktop/machine1",
1411 "org.freedesktop.machine1.Manager",
1417 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1425 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1426 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1427 sd_bus
*bus
= userdata
;
1430 polkit_agent_open_if_enabled();
1432 r
= sd_bus_call_method(
1434 "org.freedesktop.machine1",
1435 "/org/freedesktop/machine1",
1436 "org.freedesktop.machine1.Manager",
1440 "ss", argv
[1], argv
[2]);
1442 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1449 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1450 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1451 sd_bus
*bus
= userdata
;
1454 polkit_agent_open_if_enabled();
1456 r
= sd_bus_call_method(
1458 "org.freedesktop.machine1",
1459 "/org/freedesktop/machine1",
1460 "org.freedesktop.machine1.Manager",
1464 "ssb", argv
[1], argv
[2], arg_read_only
);
1466 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1473 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1474 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1475 sd_bus
*bus
= userdata
;
1479 b
= parse_boolean(argv
[2]);
1481 log_error("Failed to parse boolean argument: %s", argv
[2]);
1486 polkit_agent_open_if_enabled();
1488 r
= sd_bus_call_method(
1490 "org.freedesktop.machine1",
1491 "/org/freedesktop/machine1",
1492 "org.freedesktop.machine1.Manager",
1493 "MarkImageReadOnly",
1498 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1505 static int make_service_name(const char *name
, char **ret
) {
1506 _cleanup_free_
char *e
= NULL
;
1512 if (!machine_name_is_valid(name
)) {
1513 log_error("Invalid machine name %s.", name
);
1517 e
= unit_name_escape(name
);
1521 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1523 return log_error_errno(r
, "Failed to build unit name: %m");
1528 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1529 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1530 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1531 sd_bus
*bus
= userdata
;
1536 polkit_agent_open_if_enabled();
1538 r
= bus_wait_for_jobs_new(bus
, &w
);
1542 for (i
= 1; i
< argc
; i
++) {
1543 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1544 _cleanup_free_
char *unit
= NULL
;
1547 r
= make_service_name(argv
[i
], &unit
);
1551 r
= sd_bus_call_method(
1553 "org.freedesktop.systemd1",
1554 "/org/freedesktop/systemd1",
1555 "org.freedesktop.systemd1.Manager",
1559 "ss", unit
, "fail");
1561 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1565 r
= sd_bus_message_read(reply
, "o", &object
);
1567 return bus_log_parse_error(r
);
1569 r
= bus_wait_for_jobs_add(w
, object
);
1574 r
= bus_wait_for_jobs(w
, arg_quiet
);
1581 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1582 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1583 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1584 int carries_install_info
= 0;
1585 const char *method
= NULL
;
1586 sd_bus
*bus
= userdata
;
1591 polkit_agent_open_if_enabled();
1593 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1595 r
= sd_bus_message_new_method_call(
1598 "org.freedesktop.systemd1",
1599 "/org/freedesktop/systemd1",
1600 "org.freedesktop.systemd1.Manager",
1603 return bus_log_create_error(r
);
1605 r
= sd_bus_message_open_container(m
, 'a', "s");
1607 return bus_log_create_error(r
);
1609 for (i
= 1; i
< argc
; i
++) {
1610 _cleanup_free_
char *unit
= NULL
;
1612 r
= make_service_name(argv
[i
], &unit
);
1616 r
= sd_bus_message_append(m
, "s", unit
);
1618 return bus_log_create_error(r
);
1621 r
= sd_bus_message_close_container(m
);
1623 return bus_log_create_error(r
);
1625 if (streq(argv
[0], "enable"))
1626 r
= sd_bus_message_append(m
, "bb", false, false);
1628 r
= sd_bus_message_append(m
, "b", false);
1630 return bus_log_create_error(r
);
1632 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1634 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1638 if (streq(argv
[0], "enable")) {
1639 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1641 return bus_log_parse_error(r
);
1644 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1648 r
= sd_bus_call_method(
1650 "org.freedesktop.systemd1",
1651 "/org/freedesktop/systemd1",
1652 "org.freedesktop.systemd1.Manager",
1658 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1665 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1666 const char **our_path
= userdata
, *line
;
1673 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1675 bus_log_parse_error(r
);
1679 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1682 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1685 log_full(priority
, "%s", line
);
1689 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1690 const char **our_path
= userdata
, *path
, *result
;
1697 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1699 bus_log_parse_error(r
);
1703 if (!streq_ptr(*our_path
, path
))
1706 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1710 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1715 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1717 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1721 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1722 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1723 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1724 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1725 _cleanup_event_unref_ sd_event
* event
= NULL
;
1726 const char *path
= NULL
;
1733 polkit_agent_open_if_enabled();
1735 r
= sd_event_default(&event
);
1737 return log_error_errno(r
, "Failed to get event loop: %m");
1739 r
= sd_bus_attach_event(bus
, event
, 0);
1741 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1743 r
= sd_bus_add_match(
1747 "sender='org.freedesktop.import1',"
1748 "interface='org.freedesktop.import1.Manager',"
1749 "member='TransferRemoved',"
1750 "path='/org/freedesktop/import1'",
1751 match_transfer_removed
, &path
);
1753 return log_error_errno(r
, "Failed to install match: %m");
1755 r
= sd_bus_add_match(
1759 "sender='org.freedesktop.import1',"
1760 "interface='org.freedesktop.import1.Transfer',"
1761 "member='LogMessage'",
1762 match_log_message
, &path
);
1764 return log_error_errno(r
, "Failed to install match: %m");
1766 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1768 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1772 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1774 return bus_log_parse_error(r
);
1776 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
1779 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1781 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1782 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1784 r
= sd_event_loop(event
);
1786 return log_error_errno(r
, "Failed to run event loop: %m");
1791 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1792 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1793 _cleanup_free_
char *ll
= NULL
;
1794 _cleanup_close_
int fd
= -1;
1795 const char *local
= NULL
, *path
= NULL
;
1796 sd_bus
*bus
= userdata
;
1803 if (isempty(path
) || streq(path
, "-"))
1809 local
= basename(path
);
1810 if (isempty(local
) || streq(local
, "-"))
1814 log_error("Need either path or local name.");
1818 r
= tar_strip_suffixes(local
, &ll
);
1824 if (!machine_name_is_valid(local
)) {
1825 log_error("Local name %s is not a suitable machine name.", local
);
1830 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1832 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1835 r
= sd_bus_message_new_method_call(
1838 "org.freedesktop.import1",
1839 "/org/freedesktop/import1",
1840 "org.freedesktop.import1.Manager",
1843 return bus_log_create_error(r
);
1845 r
= sd_bus_message_append(
1848 fd
>= 0 ? fd
: STDIN_FILENO
,
1853 return bus_log_create_error(r
);
1855 return transfer_image_common(bus
, m
);
1858 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1859 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1860 _cleanup_free_
char *ll
= NULL
;
1861 _cleanup_close_
int fd
= -1;
1862 const char *local
= NULL
, *path
= NULL
;
1863 sd_bus
*bus
= userdata
;
1870 if (isempty(path
) || streq(path
, "-"))
1876 local
= basename(path
);
1877 if (isempty(local
) || streq(local
, "-"))
1881 log_error("Need either path or local name.");
1885 r
= raw_strip_suffixes(local
, &ll
);
1891 if (!machine_name_is_valid(local
)) {
1892 log_error("Local name %s is not a suitable machine name.", local
);
1897 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1899 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1902 r
= sd_bus_message_new_method_call(
1905 "org.freedesktop.import1",
1906 "/org/freedesktop/import1",
1907 "org.freedesktop.import1.Manager",
1910 return bus_log_create_error(r
);
1912 r
= sd_bus_message_append(
1915 fd
>= 0 ? fd
: STDIN_FILENO
,
1920 return bus_log_create_error(r
);
1922 return transfer_image_common(bus
, m
);
1925 static void determine_compression_from_filename(const char *p
) {
1932 if (endswith(p
, ".xz"))
1934 else if (endswith(p
, ".gz"))
1935 arg_format
= "gzip";
1936 else if (endswith(p
, ".bz2"))
1937 arg_format
= "bzip2";
1940 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1941 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1942 _cleanup_close_
int fd
= -1;
1943 const char *local
= NULL
, *path
= NULL
;
1944 sd_bus
*bus
= userdata
;
1950 if (!machine_name_is_valid(local
)) {
1951 log_error("Machine name %s is not valid.", local
);
1957 if (isempty(path
) || streq(path
, "-"))
1961 determine_compression_from_filename(path
);
1963 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1965 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1968 r
= sd_bus_message_new_method_call(
1971 "org.freedesktop.import1",
1972 "/org/freedesktop/import1",
1973 "org.freedesktop.import1.Manager",
1976 return bus_log_create_error(r
);
1978 r
= sd_bus_message_append(
1982 fd
>= 0 ? fd
: STDOUT_FILENO
,
1985 return bus_log_create_error(r
);
1987 return transfer_image_common(bus
, m
);
1990 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1991 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1992 _cleanup_close_
int fd
= -1;
1993 const char *local
= NULL
, *path
= NULL
;
1994 sd_bus
*bus
= userdata
;
2000 if (!machine_name_is_valid(local
)) {
2001 log_error("Machine name %s is not valid.", local
);
2007 if (isempty(path
) || streq(path
, "-"))
2011 determine_compression_from_filename(path
);
2013 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2015 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2018 r
= sd_bus_message_new_method_call(
2021 "org.freedesktop.import1",
2022 "/org/freedesktop/import1",
2023 "org.freedesktop.import1.Manager",
2026 return bus_log_create_error(r
);
2028 r
= sd_bus_message_append(
2032 fd
>= 0 ? fd
: STDOUT_FILENO
,
2035 return bus_log_create_error(r
);
2037 return transfer_image_common(bus
, m
);
2040 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2041 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2042 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2043 const char *local
, *remote
;
2044 sd_bus
*bus
= userdata
;
2050 if (!http_url_is_valid(remote
)) {
2051 log_error("URL '%s' is not valid.", remote
);
2058 r
= import_url_last_component(remote
, &l
);
2060 return log_error_errno(r
, "Failed to get final component of URL: %m");
2065 if (isempty(local
) || streq(local
, "-"))
2069 r
= tar_strip_suffixes(local
, &ll
);
2075 if (!machine_name_is_valid(local
)) {
2076 log_error("Local name %s is not a suitable machine name.", local
);
2081 r
= sd_bus_message_new_method_call(
2084 "org.freedesktop.import1",
2085 "/org/freedesktop/import1",
2086 "org.freedesktop.import1.Manager",
2089 return bus_log_create_error(r
);
2091 r
= sd_bus_message_append(
2096 import_verify_to_string(arg_verify
),
2099 return bus_log_create_error(r
);
2101 return transfer_image_common(bus
, m
);
2104 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2105 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2106 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2107 const char *local
, *remote
;
2108 sd_bus
*bus
= userdata
;
2114 if (!http_url_is_valid(remote
)) {
2115 log_error("URL '%s' is not valid.", remote
);
2122 r
= import_url_last_component(remote
, &l
);
2124 return log_error_errno(r
, "Failed to get final component of URL: %m");
2129 if (isempty(local
) || streq(local
, "-"))
2133 r
= raw_strip_suffixes(local
, &ll
);
2139 if (!machine_name_is_valid(local
)) {
2140 log_error("Local name %s is not a suitable machine name.", local
);
2145 r
= sd_bus_message_new_method_call(
2148 "org.freedesktop.import1",
2149 "/org/freedesktop/import1",
2150 "org.freedesktop.import1.Manager",
2153 return bus_log_create_error(r
);
2155 r
= sd_bus_message_append(
2160 import_verify_to_string(arg_verify
),
2163 return bus_log_create_error(r
);
2165 return transfer_image_common(bus
, m
);
2168 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2169 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2170 const char *local
, *remote
, *tag
;
2171 sd_bus
*bus
= userdata
;
2174 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2175 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2180 tag
= strchr(remote
, ':');
2182 remote
= strndupa(remote
, tag
- remote
);
2186 if (!dkr_name_is_valid(remote
)) {
2187 log_error("DKR name '%s' is invalid.", remote
);
2190 if (tag
&& !dkr_tag_is_valid(tag
)) {
2191 log_error("DKR tag '%s' is invalid.", remote
);
2198 local
= strchr(remote
, '/');
2205 if (isempty(local
) || streq(local
, "-"))
2209 if (!machine_name_is_valid(local
)) {
2210 log_error("Local name %s is not a suitable machine name.", local
);
2215 r
= sd_bus_message_new_method_call(
2218 "org.freedesktop.import1",
2219 "/org/freedesktop/import1",
2220 "org.freedesktop.import1.Manager",
2223 return bus_log_create_error(r
);
2225 r
= sd_bus_message_append(
2232 import_verify_to_string(arg_verify
),
2235 return bus_log_create_error(r
);
2237 return transfer_image_common(bus
, m
);
2240 typedef struct TransferInfo
{
2248 static int compare_transfer_info(const void *a
, const void *b
) {
2249 const TransferInfo
*x
= a
, *y
= b
;
2251 return strcmp(x
->local
, y
->local
);
2254 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2255 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2256 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2257 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2258 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2259 size_t n_transfers
= 0, n_allocated
= 0, j
;
2260 const char *type
, *remote
, *local
, *object
;
2261 sd_bus
*bus
= userdata
;
2262 uint32_t id
, max_id
= 0;
2266 pager_open_if_enabled();
2268 r
= sd_bus_call_method(
2270 "org.freedesktop.import1",
2271 "/org/freedesktop/import1",
2272 "org.freedesktop.import1.Manager",
2278 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2282 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2284 return bus_log_parse_error(r
);
2286 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2289 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2292 transfers
[n_transfers
].id
= id
;
2293 transfers
[n_transfers
].type
= type
;
2294 transfers
[n_transfers
].remote
= remote
;
2295 transfers
[n_transfers
].local
= local
;
2296 transfers
[n_transfers
].progress
= progress
;
2316 return bus_log_parse_error(r
);
2318 r
= sd_bus_message_exit_container(reply
);
2320 return bus_log_parse_error(r
);
2322 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2325 printf("%-*s %-*s %-*s %-*s %-*s\n",
2326 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2328 (int) max_type
, "TYPE",
2329 (int) max_local
, "LOCAL",
2330 (int) max_remote
, "REMOTE");
2332 for (j
= 0; j
< n_transfers
; j
++)
2333 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2334 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2335 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2336 (int) max_type
, transfers
[j
].type
,
2337 (int) max_local
, transfers
[j
].local
,
2338 (int) max_remote
, transfers
[j
].remote
);
2341 printf("\n%zu transfers listed.\n", n_transfers
);
2346 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2347 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2348 sd_bus
*bus
= userdata
;
2353 polkit_agent_open_if_enabled();
2355 for (i
= 1; i
< argc
; i
++) {
2358 r
= safe_atou32(argv
[i
], &id
);
2360 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2362 r
= sd_bus_call_method(
2364 "org.freedesktop.import1",
2365 "/org/freedesktop/import1",
2366 "org.freedesktop.import1.Manager",
2372 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2380 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2381 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2382 sd_bus
*bus
= userdata
;
2386 if (streq(argv
[argc
-1], "-"))
2387 limit
= (uint64_t) -1;
2391 r
= parse_size(argv
[argc
-1], 1024, &off
);
2393 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2395 limit
= (uint64_t) off
;
2399 /* With two arguments changes the quota limit of the
2400 * specified image */
2401 r
= sd_bus_call_method(
2403 "org.freedesktop.machine1",
2404 "/org/freedesktop/machine1",
2405 "org.freedesktop.machine1.Manager",
2409 "st", argv
[1], limit
);
2411 /* With one argument changes the pool quota limit */
2412 r
= sd_bus_call_method(
2414 "org.freedesktop.machine1",
2415 "/org/freedesktop/machine1",
2416 "org.freedesktop.machine1.Manager",
2423 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2430 static int help(int argc
, char *argv
[], void *userdata
) {
2432 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2433 "Send control commands to or query the virtual machine and container\n"
2434 "registration manager.\n\n"
2435 " -h --help Show this help\n"
2436 " --version Show package version\n"
2437 " --no-pager Do not pipe output into a pager\n"
2438 " --no-legend Do not show the headers and footers\n"
2439 " --no-ask-password Do not ask for system passwords\n"
2440 " -H --host=[USER@]HOST Operate on remote host\n"
2441 " -M --machine=CONTAINER Operate on local container\n"
2442 " -p --property=NAME Show only properties by this name\n"
2443 " -q --quiet Suppress output\n"
2444 " -a --all Show all properties, including empty ones\n"
2445 " -l --full Do not ellipsize output\n"
2446 " --kill-who=WHO Who to send signal to\n"
2447 " -s --signal=SIGNAL Which signal to send\n"
2448 " --uid=USER Specify user ID to invoke shell as\n"
2449 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2450 " --read-only Create read-only bind mount\n"
2451 " --mkdir Create directory before bind mounting, if missing\n"
2452 " -n --lines=INTEGER Number of journal entries to show\n"
2453 " -o --output=STRING Change journal output mode (short,\n"
2454 " short-monotonic, verbose, export, json,\n"
2455 " json-pretty, json-sse, cat)\n"
2456 " --verify=MODE Verification mode for downloaded images (no,\n"
2457 " checksum, signature)\n"
2458 " --force Download image even if already exists\n"
2459 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2461 "Machine Commands:\n"
2462 " list List running VMs and containers\n"
2463 " status NAME... Show VM/container details\n"
2464 " show [NAME...] Show properties of one or more VMs/containers\n"
2465 " start NAME... Start container as a service\n"
2466 " login [NAME] Get a login prompt in a container or on the\n"
2468 " shell [[USER@]NAME [COMMAND...]]\n"
2469 " Invoke a shell (or other command) in a container\n"
2470 " or on the local host\n"
2471 " enable NAME... Enable automatic container start at boot\n"
2472 " disable NAME... Disable automatic container start at boot\n"
2473 " poweroff NAME... Power off one or more containers\n"
2474 " reboot NAME... Reboot one or more containers\n"
2475 " terminate NAME... Terminate one or more VMs/containers\n"
2476 " kill NAME... Send signal to processes of a VM/container\n"
2477 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2478 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2479 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2481 " list-images Show available container and VM images\n"
2482 " image-status [NAME...] Show image details\n"
2483 " show-image [NAME...] Show properties of image\n"
2484 " clone NAME NAME Clone an image\n"
2485 " rename NAME NAME Rename an image\n"
2486 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2487 " remove NAME... Remove an image\n"
2488 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2489 "Image Transfer Commands:\n"
2490 " pull-tar URL [NAME] Download a TAR container image\n"
2491 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2492 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2493 " import-tar FILE [NAME] Import a local TAR container image\n"
2494 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2495 " export-tar NAME [FILE] Export a TAR container image locally\n"
2496 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2497 " list-transfers Show list of downloads in progress\n"
2498 " cancel-transfer Cancel a download\n"
2499 , program_invocation_short_name
);
2504 static int parse_argv(int argc
, char *argv
[]) {
2507 ARG_VERSION
= 0x100,
2513 ARG_NO_ASK_PASSWORD
,
2522 static const struct option options
[] = {
2523 { "help", no_argument
, NULL
, 'h' },
2524 { "version", no_argument
, NULL
, ARG_VERSION
},
2525 { "property", required_argument
, NULL
, 'p' },
2526 { "all", no_argument
, NULL
, 'a' },
2527 { "full", no_argument
, NULL
, 'l' },
2528 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2529 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2530 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2531 { "signal", required_argument
, NULL
, 's' },
2532 { "host", required_argument
, NULL
, 'H' },
2533 { "machine", required_argument
, NULL
, 'M' },
2534 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2535 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2536 { "quiet", no_argument
, NULL
, 'q' },
2537 { "lines", required_argument
, NULL
, 'n' },
2538 { "output", required_argument
, NULL
, 'o' },
2539 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2540 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2541 { "force", no_argument
, NULL
, ARG_FORCE
},
2542 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2543 { "format", required_argument
, NULL
, ARG_FORMAT
},
2544 { "uid", required_argument
, NULL
, ARG_UID
},
2545 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2554 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2559 return help(0, NULL
, NULL
);
2562 puts(PACKAGE_STRING
);
2563 puts(SYSTEMD_FEATURES
);
2567 r
= strv_extend(&arg_property
, optarg
);
2571 /* If the user asked for a particular
2572 * property, show it to him, even if it is
2586 if (safe_atou(optarg
, &arg_lines
) < 0) {
2587 log_error("Failed to parse lines '%s'", optarg
);
2593 arg_output
= output_mode_from_string(optarg
);
2594 if (arg_output
< 0) {
2595 log_error("Unknown output '%s'.", optarg
);
2601 arg_no_pager
= true;
2609 arg_kill_who
= optarg
;
2613 arg_signal
= signal_from_string_try_harder(optarg
);
2614 if (arg_signal
< 0) {
2615 log_error("Failed to parse signal string %s.", optarg
);
2620 case ARG_NO_ASK_PASSWORD
:
2621 arg_ask_password
= false;
2625 arg_transport
= BUS_TRANSPORT_REMOTE
;
2630 arg_transport
= BUS_TRANSPORT_MACHINE
;
2635 arg_read_only
= true;
2647 arg_verify
= import_verify_from_string(optarg
);
2648 if (arg_verify
< 0) {
2649 log_error("Failed to parse --verify= setting: %s", optarg
);
2658 case ARG_DKR_INDEX_URL
:
2659 if (!http_url_is_valid(optarg
)) {
2660 log_error("Index URL is invalid: %s", optarg
);
2664 arg_dkr_index_url
= optarg
;
2668 if (!STR_IN_SET(optarg
, "uncompressed", "xz", "gzip", "bzip2")) {
2669 log_error("Unknown format: %s", optarg
);
2673 arg_format
= optarg
;
2681 if (!env_assignment_is_valid(optarg
)) {
2682 log_error("Environment assignment invalid: %s", optarg
);
2686 r
= strv_extend(&arg_setenv
, optarg
);
2695 assert_not_reached("Unhandled option");
2701 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2703 static const Verb verbs
[] = {
2704 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2705 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2706 { "list-images", VERB_ANY
, 1, 0, list_images
},
2707 { "status", 2, VERB_ANY
, 0, show_machine
},
2708 { "image-status", VERB_ANY
, VERB_ANY
, 0, show_image
},
2709 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2710 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2711 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2712 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2713 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2714 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2715 { "login", VERB_ANY
, 2, 0, login_machine
},
2716 { "shell", VERB_ANY
, VERB_ANY
, 0, shell_machine
},
2717 { "bind", 3, 4, 0, bind_mount
},
2718 { "copy-to", 3, 4, 0, copy_files
},
2719 { "copy-from", 3, 4, 0, copy_files
},
2720 { "remove", 2, VERB_ANY
, 0, remove_image
},
2721 { "rename", 3, 3, 0, rename_image
},
2722 { "clone", 3, 3, 0, clone_image
},
2723 { "read-only", 2, 3, 0, read_only_image
},
2724 { "start", 2, VERB_ANY
, 0, start_machine
},
2725 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2726 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2727 { "import-tar", 2, 3, 0, import_tar
},
2728 { "import-raw", 2, 3, 0, import_raw
},
2729 { "export-tar", 2, 3, 0, export_tar
},
2730 { "export-raw", 2, 3, 0, export_raw
},
2731 { "pull-tar", 2, 3, 0, pull_tar
},
2732 { "pull-raw", 2, 3, 0, pull_raw
},
2733 { "pull-dkr", 2, 3, 0, pull_dkr
},
2734 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2735 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2736 { "set-limit", 2, 3, 0, set_limit
},
2740 return dispatch_verb(argc
, argv
, verbs
, bus
);
2743 int main(int argc
, char*argv
[]) {
2744 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
2747 setlocale(LC_ALL
, "");
2748 log_parse_environment();
2751 r
= parse_argv(argc
, argv
);
2755 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2757 log_error_errno(r
, "Failed to create bus connection: %m");
2761 sd_bus_set_allow_interactive_authorization(bus
, arg_ask_password
);
2763 r
= machinectl_main(argc
, argv
, bus
);
2767 polkit_agent_close();
2769 strv_free(arg_property
);
2770 strv_free(arg_setenv
);
2772 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;