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
,
617 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
618 MachineStatusInfo
*i
= userdata
;
623 assert_cc(sizeof(int32_t) == sizeof(int));
624 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
630 i
->n_netif
= l
/ sizeof(int32_t);
631 i
->netif
= memdup(v
, l
);
638 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
640 static const struct bus_properties_map map
[] = {
641 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
642 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
643 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
644 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
645 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
646 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
647 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
648 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
649 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
650 { "NetworkInterfaces", "ai", map_netif
, 0 },
654 _cleanup_(machine_status_info_clear
) MachineStatusInfo info
= {};
662 r
= bus_map_all_properties(bus
,
663 "org.freedesktop.machine1",
668 return log_error_errno(r
, "Could not get properties: %m");
674 print_machine_status_info(bus
, &info
);
679 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
691 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
693 log_error_errno(r
, "Could not get properties: %m");
698 static int show_machine(int argc
, char *argv
[], void *userdata
) {
700 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
701 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
702 bool properties
, new_line
= false;
703 sd_bus
*bus
= userdata
;
708 properties
= !strstr(argv
[0], "status");
710 pager_open_if_enabled();
712 if (properties
&& argc
<= 1) {
714 /* If no argument is specified, inspect the manager
716 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
721 for (i
= 1; i
< argc
; i
++) {
722 const char *path
= NULL
;
724 r
= sd_bus_call_method(
726 "org.freedesktop.machine1",
727 "/org/freedesktop/machine1",
728 "org.freedesktop.machine1.Manager",
734 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
738 r
= sd_bus_message_read(reply
, "o", &path
);
740 return bus_log_parse_error(r
);
743 r
= show_machine_properties(bus
, path
, &new_line
);
745 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
751 typedef struct ImageStatusInfo
{
760 uint64_t usage_exclusive
;
761 uint64_t limit_exclusive
;
764 static void image_status_info_clear(ImageStatusInfo
*info
) {
773 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
774 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
775 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
776 char bs
[FORMAT_BYTES_MAX
], *s3
;
777 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
783 fputs(i
->name
, stdout
);
788 printf("\t Type: %s\n", i
->type
);
791 printf("\t Path: %s\n", i
->path
);
793 printf("\t RO: %s%s%s\n",
794 i
->read_only
? ansi_highlight_red() : "",
795 i
->read_only
? "read-only" : "writable",
796 i
->read_only
? ansi_highlight_off() : "");
798 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
799 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
801 printf("\t Created: %s; %s\n", s2
, s1
);
803 printf("\t Created: %s\n", s2
);
805 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
806 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
808 printf("\tModified: %s; %s\n", s2
, s1
);
810 printf("\tModified: %s\n", s2
);
812 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
813 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
815 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
817 printf("\t Usage: %s\n", s3
);
819 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
820 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
822 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
824 printf("\t Limit: %s\n", s3
);
827 static int show_image_info(sd_bus
*bus
, const char *path
, bool *new_line
) {
829 static const struct bus_properties_map map
[] = {
830 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
831 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
832 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
833 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
834 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
835 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
836 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
837 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
838 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
839 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
843 _cleanup_(image_status_info_clear
) ImageStatusInfo info
= {};
850 r
= bus_map_all_properties(bus
,
851 "org.freedesktop.machine1",
856 return log_error_errno(r
, "Could not get properties: %m");
862 print_image_status_info(bus
, &info
);
867 typedef struct PoolStatusInfo
{
873 static void pool_status_info_clear(PoolStatusInfo
*info
) {
882 static void print_pool_status_info(sd_bus
*bus
, PoolStatusInfo
*i
) {
883 char bs
[FORMAT_BYTES_MAX
], *s
;
886 printf("\t Path: %s\n", i
->path
);
888 s
= format_bytes(bs
, sizeof(bs
), i
->usage
);
890 printf("\t Usage: %s\n", s
);
892 s
= format_bytes(bs
, sizeof(bs
), i
->limit
);
894 printf("\t Limit: %s\n", s
);
897 static int show_pool_info(sd_bus
*bus
) {
899 static const struct bus_properties_map map
[] = {
900 { "PoolPath", "s", NULL
, offsetof(PoolStatusInfo
, path
) },
901 { "PoolUsage", "t", NULL
, offsetof(PoolStatusInfo
, usage
) },
902 { "PoolLimit", "t", NULL
, offsetof(PoolStatusInfo
, limit
) },
906 _cleanup_(pool_status_info_clear
) PoolStatusInfo info
= {
907 .usage
= (uint64_t) -1,
908 .limit
= (uint64_t) -1,
914 r
= bus_map_all_properties(bus
,
915 "org.freedesktop.machine1",
916 "/org/freedesktop/machine1",
920 return log_error_errno(r
, "Could not get properties: %m");
922 print_pool_status_info(bus
, &info
);
928 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
940 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
942 log_error_errno(r
, "Could not get properties: %m");
947 static int show_image(int argc
, char *argv
[], void *userdata
) {
949 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
950 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
951 bool properties
, new_line
= false;
952 sd_bus
*bus
= userdata
;
957 properties
= !strstr(argv
[0], "status");
959 pager_open_if_enabled();
963 /* If no argument is specified, inspect the manager
967 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
969 r
= show_pool_info(bus
);
974 for (i
= 1; i
< argc
; i
++) {
975 const char *path
= NULL
;
977 r
= sd_bus_call_method(
979 "org.freedesktop.machine1",
980 "/org/freedesktop/machine1",
981 "org.freedesktop.machine1.Manager",
987 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
991 r
= sd_bus_message_read(reply
, "o", &path
);
993 return bus_log_parse_error(r
);
996 r
= show_image_properties(bus
, path
, &new_line
);
998 r
= show_image_info(bus
, path
, &new_line
);
1004 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
1005 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1006 sd_bus
*bus
= userdata
;
1011 polkit_agent_open_if_enabled();
1014 arg_kill_who
= "all";
1016 for (i
= 1; i
< argc
; i
++) {
1017 r
= sd_bus_call_method(
1019 "org.freedesktop.machine1",
1020 "/org/freedesktop/machine1",
1021 "org.freedesktop.machine1.Manager",
1025 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
1027 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
1035 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
1036 arg_kill_who
= "leader";
1037 arg_signal
= SIGINT
; /* sysvinit + systemd */
1039 return kill_machine(argc
, argv
, userdata
);
1042 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
1043 arg_kill_who
= "leader";
1044 arg_signal
= SIGRTMIN
+4; /* only systemd */
1046 return kill_machine(argc
, argv
, userdata
);
1049 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
1050 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1051 sd_bus
*bus
= userdata
;
1056 polkit_agent_open_if_enabled();
1058 for (i
= 1; i
< argc
; i
++) {
1059 r
= sd_bus_call_method(
1061 "org.freedesktop.machine1",
1062 "/org/freedesktop/machine1",
1063 "org.freedesktop.machine1.Manager",
1069 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1077 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1078 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1079 _cleanup_free_
char *abs_host_path
= NULL
;
1080 char *dest
, *host_path
, *container_path
;
1081 sd_bus
*bus
= userdata
;
1087 polkit_agent_open_if_enabled();
1089 copy_from
= streq(argv
[0], "copy-from");
1090 dest
= argv
[3] ?: argv
[2];
1091 host_path
= copy_from
? dest
: argv
[2];
1092 container_path
= copy_from
? argv
[2] : dest
;
1094 if (!path_is_absolute(host_path
)) {
1095 abs_host_path
= path_make_absolute_cwd(host_path
);
1098 host_path
= abs_host_path
;
1101 r
= sd_bus_call_method(
1103 "org.freedesktop.machine1",
1104 "/org/freedesktop/machine1",
1105 "org.freedesktop.machine1.Manager",
1106 copy_from
? "CopyFromMachine" : "CopyToMachine",
1111 copy_from
? container_path
: host_path
,
1112 copy_from
? host_path
: container_path
);
1114 log_error("Failed to copy: %s", bus_error_message(&error
, -r
));
1121 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1122 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1123 sd_bus
*bus
= userdata
;
1128 polkit_agent_open_if_enabled();
1130 r
= sd_bus_call_method(
1132 "org.freedesktop.machine1",
1133 "/org/freedesktop/machine1",
1134 "org.freedesktop.machine1.Manager",
1145 log_error("Failed to bind mount: %s", bus_error_message(&error
, -r
));
1152 static int on_machine_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1153 PTYForward
** forward
= (PTYForward
**) userdata
;
1160 /* If the forwarder is already initialized, tell it to
1161 * exit on the next vhangup(), so that we still flush
1162 * out what might be queued and exit then. */
1164 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1168 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1171 /* On error, or when the forwarder is not initialized yet, quit immediately */
1172 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), EXIT_FAILURE
);
1176 static int process_forward(sd_event
*event
, PTYForward
**forward
, int master
, bool ignore_vhangup
, const char *name
) {
1182 assert(master
>= 0);
1185 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGWINCH
, SIGTERM
, SIGINT
, -1) >= 0);
1187 if (streq(name
, ".host"))
1188 log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
1190 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name
);
1192 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1193 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1195 r
= pty_forward_new(event
, master
, ignore_vhangup
, false, forward
);
1197 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1199 r
= sd_event_loop(event
);
1201 return log_error_errno(r
, "Failed to run event loop: %m");
1203 pty_forward_get_last_char(*forward
, &last_char
);
1207 pty_forward_get_ignore_vhangup(*forward
) == 0;
1209 *forward
= pty_forward_free(*forward
);
1211 if (last_char
!= '\n')
1212 fputc('\n', stdout
);
1215 log_info("Machine %s terminated.", name
);
1216 else if (streq(name
, ".host"))
1217 log_info("Connection to the local host terminated.");
1219 log_info("Connection to machine %s terminated.", name
);
1221 sd_event_get_exit_code(event
, &ret
);
1225 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1226 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1227 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1228 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1229 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1230 _cleanup_event_unref_ sd_event
*event
= NULL
;
1232 sd_bus
*bus
= userdata
;
1233 const char *pty
, *match
, *machine
;
1237 if (!strv_isempty(arg_setenv
) || arg_uid
) {
1238 log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead.");
1242 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1243 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1244 log_error("Login only supported on local machines.");
1248 polkit_agent_open_if_enabled();
1250 r
= sd_event_default(&event
);
1252 return log_error_errno(r
, "Failed to get event loop: %m");
1254 r
= sd_bus_attach_event(bus
, event
, 0);
1256 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1258 machine
= argc
< 2 || isempty(argv
[1]) ? ".host" : argv
[1];
1260 match
= strjoina("type='signal',"
1261 "sender='org.freedesktop.machine1',"
1262 "path='/org/freedesktop/machine1',",
1263 "interface='org.freedesktop.machine1.Manager',"
1264 "member='MachineRemoved',"
1265 "arg0='", machine
, "'");
1267 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1269 return log_error_errno(r
, "Failed to add machine removal match: %m");
1271 r
= sd_bus_call_method(
1273 "org.freedesktop.machine1",
1274 "/org/freedesktop/machine1",
1275 "org.freedesktop.machine1.Manager",
1281 log_error("Failed to get login PTY: %s", bus_error_message(&error
, -r
));
1285 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1287 return bus_log_parse_error(r
);
1289 return process_forward(event
, &forward
, master
, true, machine
);
1292 static int shell_machine(int argc
, char *argv
[], void *userdata
) {
1293 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *m
= NULL
;
1294 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1295 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1296 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1297 _cleanup_event_unref_ sd_event
*event
= NULL
;
1299 sd_bus
*bus
= userdata
;
1300 const char *pty
, *match
, *machine
, *path
, *uid
= NULL
;
1304 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1305 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1306 log_error("Shell only supported on local machines.");
1310 /* Pass $TERM to shell session, if not explicitly specified. */
1311 if (!strv_find_prefix(arg_setenv
, "TERM=")) {
1314 t
= strv_find_prefix(environ
, "TERM=");
1316 if (strv_extend(&arg_setenv
, t
) < 0)
1321 polkit_agent_open_if_enabled();
1323 r
= sd_event_default(&event
);
1325 return log_error_errno(r
, "Failed to get event loop: %m");
1327 r
= sd_bus_attach_event(bus
, event
, 0);
1329 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1331 machine
= argc
< 2 || isempty(argv
[1]) ? NULL
: argv
[1];
1338 at
= strchr(machine
, '@');
1340 uid
= strndupa(machine
, at
- machine
);
1345 if (isempty(machine
))
1348 match
= strjoina("type='signal',"
1349 "sender='org.freedesktop.machine1',"
1350 "path='/org/freedesktop/machine1',",
1351 "interface='org.freedesktop.machine1.Manager',"
1352 "member='MachineRemoved',"
1353 "arg0='", machine
, "'");
1355 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1357 return log_error_errno(r
, "Failed to add machine removal match: %m");
1359 r
= sd_bus_message_new_method_call(
1362 "org.freedesktop.machine1",
1363 "/org/freedesktop/machine1",
1364 "org.freedesktop.machine1.Manager",
1365 "OpenMachineShell");
1367 return bus_log_create_error(r
);
1369 path
= argc
< 3 || isempty(argv
[2]) ? NULL
: argv
[2];
1371 r
= sd_bus_message_append(m
, "sss", machine
, uid
, path
);
1373 return bus_log_create_error(r
);
1375 r
= sd_bus_message_append_strv(m
, strv_length(argv
) <= 3 ? NULL
: argv
+ 2);
1377 return bus_log_create_error(r
);
1379 r
= sd_bus_message_append_strv(m
, arg_setenv
);
1381 return bus_log_create_error(r
);
1383 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1385 log_error("Failed to get shell PTY: %s", bus_error_message(&error
, -r
));
1389 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1391 return bus_log_parse_error(r
);
1393 return process_forward(event
, &forward
, master
, false, machine
);
1396 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1397 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1398 sd_bus
*bus
= userdata
;
1403 polkit_agent_open_if_enabled();
1405 for (i
= 1; i
< argc
; i
++) {
1406 r
= sd_bus_call_method(
1408 "org.freedesktop.machine1",
1409 "/org/freedesktop/machine1",
1410 "org.freedesktop.machine1.Manager",
1416 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1424 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1425 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1426 sd_bus
*bus
= userdata
;
1429 polkit_agent_open_if_enabled();
1431 r
= sd_bus_call_method(
1433 "org.freedesktop.machine1",
1434 "/org/freedesktop/machine1",
1435 "org.freedesktop.machine1.Manager",
1439 "ss", argv
[1], argv
[2]);
1441 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1448 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1449 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1450 sd_bus
*bus
= userdata
;
1453 polkit_agent_open_if_enabled();
1455 r
= sd_bus_call_method(
1457 "org.freedesktop.machine1",
1458 "/org/freedesktop/machine1",
1459 "org.freedesktop.machine1.Manager",
1463 "ssb", argv
[1], argv
[2], arg_read_only
);
1465 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1472 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1473 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1474 sd_bus
*bus
= userdata
;
1478 b
= parse_boolean(argv
[2]);
1480 log_error("Failed to parse boolean argument: %s", argv
[2]);
1485 polkit_agent_open_if_enabled();
1487 r
= sd_bus_call_method(
1489 "org.freedesktop.machine1",
1490 "/org/freedesktop/machine1",
1491 "org.freedesktop.machine1.Manager",
1492 "MarkImageReadOnly",
1497 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1504 static int make_service_name(const char *name
, char **ret
) {
1505 _cleanup_free_
char *e
= NULL
;
1511 if (!machine_name_is_valid(name
)) {
1512 log_error("Invalid machine name %s.", name
);
1516 e
= unit_name_escape(name
);
1520 r
= unit_name_build("systemd-nspawn", e
, ".service", ret
);
1522 return log_error_errno(r
, "Failed to build unit name: %m");
1527 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1528 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1529 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1530 sd_bus
*bus
= userdata
;
1535 polkit_agent_open_if_enabled();
1537 r
= bus_wait_for_jobs_new(bus
, &w
);
1541 for (i
= 1; i
< argc
; i
++) {
1542 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1543 _cleanup_free_
char *unit
= NULL
;
1546 r
= make_service_name(argv
[i
], &unit
);
1550 r
= sd_bus_call_method(
1552 "org.freedesktop.systemd1",
1553 "/org/freedesktop/systemd1",
1554 "org.freedesktop.systemd1.Manager",
1558 "ss", unit
, "fail");
1560 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1564 r
= sd_bus_message_read(reply
, "o", &object
);
1566 return bus_log_parse_error(r
);
1568 r
= bus_wait_for_jobs_add(w
, object
);
1573 r
= bus_wait_for_jobs(w
, arg_quiet
);
1580 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1581 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1582 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1583 int carries_install_info
= 0;
1584 const char *method
= NULL
;
1585 sd_bus
*bus
= userdata
;
1590 polkit_agent_open_if_enabled();
1592 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1594 r
= sd_bus_message_new_method_call(
1597 "org.freedesktop.systemd1",
1598 "/org/freedesktop/systemd1",
1599 "org.freedesktop.systemd1.Manager",
1602 return bus_log_create_error(r
);
1604 r
= sd_bus_message_open_container(m
, 'a', "s");
1606 return bus_log_create_error(r
);
1608 for (i
= 1; i
< argc
; i
++) {
1609 _cleanup_free_
char *unit
= NULL
;
1611 r
= make_service_name(argv
[i
], &unit
);
1615 r
= sd_bus_message_append(m
, "s", unit
);
1617 return bus_log_create_error(r
);
1620 r
= sd_bus_message_close_container(m
);
1622 return bus_log_create_error(r
);
1624 if (streq(argv
[0], "enable"))
1625 r
= sd_bus_message_append(m
, "bb", false, false);
1627 r
= sd_bus_message_append(m
, "b", false);
1629 return bus_log_create_error(r
);
1631 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1633 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1637 if (streq(argv
[0], "enable")) {
1638 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1640 return bus_log_parse_error(r
);
1643 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
, NULL
, NULL
);
1647 r
= sd_bus_call_method(
1649 "org.freedesktop.systemd1",
1650 "/org/freedesktop/systemd1",
1651 "org.freedesktop.systemd1.Manager",
1657 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1664 static int match_log_message(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1665 const char **our_path
= userdata
, *line
;
1672 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1674 bus_log_parse_error(r
);
1678 if (!streq_ptr(*our_path
, sd_bus_message_get_path(m
)))
1681 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1684 log_full(priority
, "%s", line
);
1688 static int match_transfer_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1689 const char **our_path
= userdata
, *path
, *result
;
1696 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1698 bus_log_parse_error(r
);
1702 if (!streq_ptr(*our_path
, path
))
1705 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m
)), !streq_ptr(result
, "done"));
1709 static int transfer_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
1714 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32
"\" to abort transfer.", PTR_TO_UINT32(userdata
));
1716 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
1720 static int transfer_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1721 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1722 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1723 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1724 _cleanup_event_unref_ sd_event
* event
= NULL
;
1725 const char *path
= NULL
;
1732 polkit_agent_open_if_enabled();
1734 r
= sd_event_default(&event
);
1736 return log_error_errno(r
, "Failed to get event loop: %m");
1738 r
= sd_bus_attach_event(bus
, event
, 0);
1740 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1742 r
= sd_bus_add_match(
1746 "sender='org.freedesktop.import1',"
1747 "interface='org.freedesktop.import1.Manager',"
1748 "member='TransferRemoved',"
1749 "path='/org/freedesktop/import1'",
1750 match_transfer_removed
, &path
);
1752 return log_error_errno(r
, "Failed to install match: %m");
1754 r
= sd_bus_add_match(
1758 "sender='org.freedesktop.import1',"
1759 "interface='org.freedesktop.import1.Transfer',"
1760 "member='LogMessage'",
1761 match_log_message
, &path
);
1763 return log_error_errno(r
, "Failed to install match: %m");
1765 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1767 log_error("Failed transfer image: %s", bus_error_message(&error
, -r
));
1771 r
= sd_bus_message_read(reply
, "uo", &id
, &path
);
1773 return bus_log_parse_error(r
);
1775 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
1778 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id
);
1780 sd_event_add_signal(event
, NULL
, SIGINT
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1781 sd_event_add_signal(event
, NULL
, SIGTERM
, transfer_signal_handler
, UINT32_TO_PTR(id
));
1783 r
= sd_event_loop(event
);
1785 return log_error_errno(r
, "Failed to run event loop: %m");
1790 static int import_tar(int argc
, char *argv
[], void *userdata
) {
1791 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1792 _cleanup_free_
char *ll
= NULL
;
1793 _cleanup_close_
int fd
= -1;
1794 const char *local
= NULL
, *path
= NULL
;
1795 sd_bus
*bus
= userdata
;
1802 if (isempty(path
) || streq(path
, "-"))
1808 local
= basename(path
);
1809 if (isempty(local
) || streq(local
, "-"))
1813 log_error("Need either path or local name.");
1817 r
= tar_strip_suffixes(local
, &ll
);
1823 if (!machine_name_is_valid(local
)) {
1824 log_error("Local name %s is not a suitable machine name.", local
);
1829 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1831 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1834 r
= sd_bus_message_new_method_call(
1837 "org.freedesktop.import1",
1838 "/org/freedesktop/import1",
1839 "org.freedesktop.import1.Manager",
1842 return bus_log_create_error(r
);
1844 r
= sd_bus_message_append(
1847 fd
>= 0 ? fd
: STDIN_FILENO
,
1852 return bus_log_create_error(r
);
1854 return transfer_image_common(bus
, m
);
1857 static int import_raw(int argc
, char *argv
[], void *userdata
) {
1858 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1859 _cleanup_free_
char *ll
= NULL
;
1860 _cleanup_close_
int fd
= -1;
1861 const char *local
= NULL
, *path
= NULL
;
1862 sd_bus
*bus
= userdata
;
1869 if (isempty(path
) || streq(path
, "-"))
1875 local
= basename(path
);
1876 if (isempty(local
) || streq(local
, "-"))
1880 log_error("Need either path or local name.");
1884 r
= raw_strip_suffixes(local
, &ll
);
1890 if (!machine_name_is_valid(local
)) {
1891 log_error("Local name %s is not a suitable machine name.", local
);
1896 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1898 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1901 r
= sd_bus_message_new_method_call(
1904 "org.freedesktop.import1",
1905 "/org/freedesktop/import1",
1906 "org.freedesktop.import1.Manager",
1909 return bus_log_create_error(r
);
1911 r
= sd_bus_message_append(
1914 fd
>= 0 ? fd
: STDIN_FILENO
,
1919 return bus_log_create_error(r
);
1921 return transfer_image_common(bus
, m
);
1924 static void determine_compression_from_filename(const char *p
) {
1931 if (endswith(p
, ".xz"))
1933 else if (endswith(p
, ".gz"))
1934 arg_format
= "gzip";
1935 else if (endswith(p
, ".bz2"))
1936 arg_format
= "bzip2";
1939 static int export_tar(int argc
, char *argv
[], void *userdata
) {
1940 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1941 _cleanup_close_
int fd
= -1;
1942 const char *local
= NULL
, *path
= NULL
;
1943 sd_bus
*bus
= userdata
;
1949 if (!machine_name_is_valid(local
)) {
1950 log_error("Machine name %s is not valid.", local
);
1956 if (isempty(path
) || streq(path
, "-"))
1960 determine_compression_from_filename(path
);
1962 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
1964 return log_error_errno(errno
, "Failed to open %s: %m", path
);
1967 r
= sd_bus_message_new_method_call(
1970 "org.freedesktop.import1",
1971 "/org/freedesktop/import1",
1972 "org.freedesktop.import1.Manager",
1975 return bus_log_create_error(r
);
1977 r
= sd_bus_message_append(
1981 fd
>= 0 ? fd
: STDOUT_FILENO
,
1984 return bus_log_create_error(r
);
1986 return transfer_image_common(bus
, m
);
1989 static int export_raw(int argc
, char *argv
[], void *userdata
) {
1990 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1991 _cleanup_close_
int fd
= -1;
1992 const char *local
= NULL
, *path
= NULL
;
1993 sd_bus
*bus
= userdata
;
1999 if (!machine_name_is_valid(local
)) {
2000 log_error("Machine name %s is not valid.", local
);
2006 if (isempty(path
) || streq(path
, "-"))
2010 determine_compression_from_filename(path
);
2012 fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
2014 return log_error_errno(errno
, "Failed to open %s: %m", path
);
2017 r
= sd_bus_message_new_method_call(
2020 "org.freedesktop.import1",
2021 "/org/freedesktop/import1",
2022 "org.freedesktop.import1.Manager",
2025 return bus_log_create_error(r
);
2027 r
= sd_bus_message_append(
2031 fd
>= 0 ? fd
: STDOUT_FILENO
,
2034 return bus_log_create_error(r
);
2036 return transfer_image_common(bus
, m
);
2039 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
2040 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2041 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2042 const char *local
, *remote
;
2043 sd_bus
*bus
= userdata
;
2049 if (!http_url_is_valid(remote
)) {
2050 log_error("URL '%s' is not valid.", remote
);
2057 r
= import_url_last_component(remote
, &l
);
2059 return log_error_errno(r
, "Failed to get final component of URL: %m");
2064 if (isempty(local
) || streq(local
, "-"))
2068 r
= tar_strip_suffixes(local
, &ll
);
2074 if (!machine_name_is_valid(local
)) {
2075 log_error("Local name %s is not a suitable machine name.", local
);
2080 r
= sd_bus_message_new_method_call(
2083 "org.freedesktop.import1",
2084 "/org/freedesktop/import1",
2085 "org.freedesktop.import1.Manager",
2088 return bus_log_create_error(r
);
2090 r
= sd_bus_message_append(
2095 import_verify_to_string(arg_verify
),
2098 return bus_log_create_error(r
);
2100 return transfer_image_common(bus
, m
);
2103 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
2104 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2105 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
2106 const char *local
, *remote
;
2107 sd_bus
*bus
= userdata
;
2113 if (!http_url_is_valid(remote
)) {
2114 log_error("URL '%s' is not valid.", remote
);
2121 r
= import_url_last_component(remote
, &l
);
2123 return log_error_errno(r
, "Failed to get final component of URL: %m");
2128 if (isempty(local
) || streq(local
, "-"))
2132 r
= raw_strip_suffixes(local
, &ll
);
2138 if (!machine_name_is_valid(local
)) {
2139 log_error("Local name %s is not a suitable machine name.", local
);
2144 r
= sd_bus_message_new_method_call(
2147 "org.freedesktop.import1",
2148 "/org/freedesktop/import1",
2149 "org.freedesktop.import1.Manager",
2152 return bus_log_create_error(r
);
2154 r
= sd_bus_message_append(
2159 import_verify_to_string(arg_verify
),
2162 return bus_log_create_error(r
);
2164 return transfer_image_common(bus
, m
);
2167 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
2168 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2169 const char *local
, *remote
, *tag
;
2170 sd_bus
*bus
= userdata
;
2173 if (arg_verify
!= IMPORT_VERIFY_NO
) {
2174 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2179 tag
= strchr(remote
, ':');
2181 remote
= strndupa(remote
, tag
- remote
);
2185 if (!dkr_name_is_valid(remote
)) {
2186 log_error("DKR name '%s' is invalid.", remote
);
2189 if (tag
&& !dkr_tag_is_valid(tag
)) {
2190 log_error("DKR tag '%s' is invalid.", remote
);
2197 local
= strchr(remote
, '/');
2204 if (isempty(local
) || streq(local
, "-"))
2208 if (!machine_name_is_valid(local
)) {
2209 log_error("Local name %s is not a suitable machine name.", local
);
2214 r
= sd_bus_message_new_method_call(
2217 "org.freedesktop.import1",
2218 "/org/freedesktop/import1",
2219 "org.freedesktop.import1.Manager",
2222 return bus_log_create_error(r
);
2224 r
= sd_bus_message_append(
2231 import_verify_to_string(arg_verify
),
2234 return bus_log_create_error(r
);
2236 return transfer_image_common(bus
, m
);
2239 typedef struct TransferInfo
{
2247 static int compare_transfer_info(const void *a
, const void *b
) {
2248 const TransferInfo
*x
= a
, *y
= b
;
2250 return strcmp(x
->local
, y
->local
);
2253 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2254 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2255 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2256 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2257 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2258 size_t n_transfers
= 0, n_allocated
= 0, j
;
2259 const char *type
, *remote
, *local
, *object
;
2260 sd_bus
*bus
= userdata
;
2261 uint32_t id
, max_id
= 0;
2265 pager_open_if_enabled();
2267 r
= sd_bus_call_method(
2269 "org.freedesktop.import1",
2270 "/org/freedesktop/import1",
2271 "org.freedesktop.import1.Manager",
2277 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2281 r
= sd_bus_message_enter_container(reply
, 'a', "(usssdo)");
2283 return bus_log_parse_error(r
);
2285 while ((r
= sd_bus_message_read(reply
, "(usssdo)", &id
, &type
, &remote
, &local
, &progress
, &object
)) > 0) {
2288 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2291 transfers
[n_transfers
].id
= id
;
2292 transfers
[n_transfers
].type
= type
;
2293 transfers
[n_transfers
].remote
= remote
;
2294 transfers
[n_transfers
].local
= local
;
2295 transfers
[n_transfers
].progress
= progress
;
2315 return bus_log_parse_error(r
);
2317 r
= sd_bus_message_exit_container(reply
);
2319 return bus_log_parse_error(r
);
2321 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2324 printf("%-*s %-*s %-*s %-*s %-*s\n",
2325 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2327 (int) max_type
, "TYPE",
2328 (int) max_local
, "LOCAL",
2329 (int) max_remote
, "REMOTE");
2331 for (j
= 0; j
< n_transfers
; j
++)
2332 printf("%*" PRIu32
" %*u%% %-*s %-*s %-*s\n",
2333 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2334 (int) 6, (unsigned) (transfers
[j
].progress
* 100),
2335 (int) max_type
, transfers
[j
].type
,
2336 (int) max_local
, transfers
[j
].local
,
2337 (int) max_remote
, transfers
[j
].remote
);
2340 printf("\n%zu transfers listed.\n", n_transfers
);
2345 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2346 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2347 sd_bus
*bus
= userdata
;
2352 polkit_agent_open_if_enabled();
2354 for (i
= 1; i
< argc
; i
++) {
2357 r
= safe_atou32(argv
[i
], &id
);
2359 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2361 r
= sd_bus_call_method(
2363 "org.freedesktop.import1",
2364 "/org/freedesktop/import1",
2365 "org.freedesktop.import1.Manager",
2371 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2379 static int set_limit(int argc
, char *argv
[], void *userdata
) {
2380 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2381 sd_bus
*bus
= userdata
;
2385 if (streq(argv
[argc
-1], "-"))
2386 limit
= (uint64_t) -1;
2388 r
= parse_size(argv
[argc
-1], 1024, &limit
);
2390 return log_error("Failed to parse size: %s", argv
[argc
-1]);
2394 /* With two arguments changes the quota limit of the
2395 * specified image */
2396 r
= sd_bus_call_method(
2398 "org.freedesktop.machine1",
2399 "/org/freedesktop/machine1",
2400 "org.freedesktop.machine1.Manager",
2404 "st", argv
[1], limit
);
2406 /* With one argument changes the pool quota limit */
2407 r
= sd_bus_call_method(
2409 "org.freedesktop.machine1",
2410 "/org/freedesktop/machine1",
2411 "org.freedesktop.machine1.Manager",
2418 log_error("Could not set limit: %s", bus_error_message(&error
, -r
));
2425 static int help(int argc
, char *argv
[], void *userdata
) {
2427 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2428 "Send control commands to or query the virtual machine and container\n"
2429 "registration manager.\n\n"
2430 " -h --help Show this help\n"
2431 " --version Show package version\n"
2432 " --no-pager Do not pipe output into a pager\n"
2433 " --no-legend Do not show the headers and footers\n"
2434 " --no-ask-password Do not ask for system passwords\n"
2435 " -H --host=[USER@]HOST Operate on remote host\n"
2436 " -M --machine=CONTAINER Operate on local container\n"
2437 " -p --property=NAME Show only properties by this name\n"
2438 " -q --quiet Suppress output\n"
2439 " -a --all Show all properties, including empty ones\n"
2440 " -l --full Do not ellipsize output\n"
2441 " --kill-who=WHO Who to send signal to\n"
2442 " -s --signal=SIGNAL Which signal to send\n"
2443 " --uid=USER Specify user ID to invoke shell as\n"
2444 " --setenv=VAR=VALUE Add an environment variable for shell\n"
2445 " --read-only Create read-only bind mount\n"
2446 " --mkdir Create directory before bind mounting, if missing\n"
2447 " -n --lines=INTEGER Number of journal entries to show\n"
2448 " -o --output=STRING Change journal output mode (short,\n"
2449 " short-monotonic, verbose, export, json,\n"
2450 " json-pretty, json-sse, cat)\n"
2451 " --verify=MODE Verification mode for downloaded images (no,\n"
2452 " checksum, signature)\n"
2453 " --force Download image even if already exists\n"
2454 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2456 "Machine Commands:\n"
2457 " list List running VMs and containers\n"
2458 " status NAME... Show VM/container details\n"
2459 " show [NAME...] Show properties of one or more VMs/containers\n"
2460 " start NAME... Start container as a service\n"
2461 " login [NAME] Get a login prompt in a container or on the\n"
2463 " shell [[USER@]NAME [COMMAND...]]\n"
2464 " Invoke a shell (or other command) in a container\n"
2465 " or on the local host\n"
2466 " enable NAME... Enable automatic container start at boot\n"
2467 " disable NAME... Disable automatic container start at boot\n"
2468 " poweroff NAME... Power off one or more containers\n"
2469 " reboot NAME... Reboot one or more containers\n"
2470 " terminate NAME... Terminate one or more VMs/containers\n"
2471 " kill NAME... Send signal to processes of a VM/container\n"
2472 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2473 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2474 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2476 " list-images Show available container and VM images\n"
2477 " image-status [NAME...] Show image details\n"
2478 " show-image [NAME...] Show properties of image\n"
2479 " clone NAME NAME Clone an image\n"
2480 " rename NAME NAME Rename an image\n"
2481 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2482 " remove NAME... Remove an image\n"
2483 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2484 "Image Transfer Commands:\n"
2485 " pull-tar URL [NAME] Download a TAR container image\n"
2486 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2487 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2488 " import-tar FILE [NAME] Import a local TAR container image\n"
2489 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2490 " export-tar NAME [FILE] Export a TAR container image locally\n"
2491 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2492 " list-transfers Show list of downloads in progress\n"
2493 " cancel-transfer Cancel a download\n"
2494 , program_invocation_short_name
);
2499 static int parse_argv(int argc
, char *argv
[]) {
2502 ARG_VERSION
= 0x100,
2508 ARG_NO_ASK_PASSWORD
,
2517 static const struct option options
[] = {
2518 { "help", no_argument
, NULL
, 'h' },
2519 { "version", no_argument
, NULL
, ARG_VERSION
},
2520 { "property", required_argument
, NULL
, 'p' },
2521 { "all", no_argument
, NULL
, 'a' },
2522 { "full", no_argument
, NULL
, 'l' },
2523 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2524 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2525 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2526 { "signal", required_argument
, NULL
, 's' },
2527 { "host", required_argument
, NULL
, 'H' },
2528 { "machine", required_argument
, NULL
, 'M' },
2529 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2530 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2531 { "quiet", no_argument
, NULL
, 'q' },
2532 { "lines", required_argument
, NULL
, 'n' },
2533 { "output", required_argument
, NULL
, 'o' },
2534 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2535 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2536 { "force", no_argument
, NULL
, ARG_FORCE
},
2537 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2538 { "format", required_argument
, NULL
, ARG_FORMAT
},
2539 { "uid", required_argument
, NULL
, ARG_UID
},
2540 { "setenv", required_argument
, NULL
, ARG_SETENV
},
2549 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2554 return help(0, NULL
, NULL
);
2557 puts(PACKAGE_STRING
);
2558 puts(SYSTEMD_FEATURES
);
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_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
2742 setlocale(LC_ALL
, "");
2743 log_parse_environment();
2746 r
= parse_argv(argc
, argv
);
2750 r
= bus_open_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
;