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>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <sys/mount.h>
41 #include "spawn-polkit-agent.h"
43 #include "bus-error.h"
46 #include "unit-name.h"
47 #include "cgroup-show.h"
48 #include "logs-show.h"
49 #include "cgroup-util.h"
51 #include "event-util.h"
52 #include "path-util.h"
56 #include "import-util.h"
58 static char **arg_property
= NULL
;
59 static bool arg_all
= false;
60 static bool arg_full
= false;
61 static bool arg_no_pager
= false;
62 static bool arg_legend
= true;
63 static const char *arg_kill_who
= NULL
;
64 static int arg_signal
= SIGTERM
;
65 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
66 static char *arg_host
= NULL
;
67 static bool arg_read_only
= false;
68 static bool arg_mkdir
= false;
69 static bool arg_quiet
= false;
70 static bool arg_ask_password
= true;
71 static unsigned arg_lines
= 10;
72 static OutputMode arg_output
= OUTPUT_SHORT
;
73 static bool arg_force
= false;
74 static const char* arg_verify
= NULL
;
75 static const char* arg_dkr_index_url
= NULL
;
77 static void pager_open_if_enabled(void) {
85 static void polkit_agent_open_if_enabled(void) {
87 /* Open the polkit agent as a child process if necessary */
89 if (!arg_ask_password
)
92 if (arg_transport
!= BUS_TRANSPORT_LOCAL
)
98 static OutputFlags
get_output_flags(void) {
100 arg_all
* OUTPUT_SHOW_ALL
|
101 arg_full
* OUTPUT_FULL_WIDTH
|
102 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH
|
103 on_tty() * OUTPUT_COLOR
|
104 !arg_quiet
* OUTPUT_WARN_CUTOFF
;
107 typedef struct MachineInfo
{
113 static int compare_machine_info(const void *a
, const void *b
) {
114 const MachineInfo
*x
= a
, *y
= b
;
116 return strcmp(x
->name
, y
->name
);
119 static int list_machines(int argc
, char *argv
[], void *userdata
) {
121 size_t max_name
= strlen("MACHINE"), max_class
= strlen("CLASS"), max_service
= strlen("SERVICE");
122 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
123 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
124 _cleanup_free_ MachineInfo
*machines
= NULL
;
125 const char *name
, *class, *service
, *object
;
126 size_t n_machines
= 0, n_allocated
= 0, j
;
127 sd_bus
*bus
= userdata
;
132 pager_open_if_enabled();
134 r
= sd_bus_call_method(
136 "org.freedesktop.machine1",
137 "/org/freedesktop/machine1",
138 "org.freedesktop.machine1.Manager",
144 log_error("Could not get machines: %s", bus_error_message(&error
, -r
));
148 r
= sd_bus_message_enter_container(reply
, 'a', "(ssso)");
150 return bus_log_parse_error(r
);
152 while ((r
= sd_bus_message_read(reply
, "(ssso)", &name
, &class, &service
, &object
)) > 0) {
155 if (!GREEDY_REALLOC(machines
, n_allocated
, n_machines
+ 1))
158 machines
[n_machines
].name
= name
;
159 machines
[n_machines
].class = class;
160 machines
[n_machines
].service
= service
;
177 return bus_log_parse_error(r
);
179 r
= sd_bus_message_exit_container(reply
);
181 return bus_log_parse_error(r
);
183 qsort_safe(machines
, n_machines
, sizeof(MachineInfo
), compare_machine_info
);
186 printf("%-*s %-*s %-*s\n",
187 (int) max_name
, "MACHINE",
188 (int) max_class
, "CLASS",
189 (int) max_service
, "SERVICE");
191 for (j
= 0; j
< n_machines
; j
++)
192 printf("%-*s %-*s %-*s\n",
193 (int) max_name
, machines
[j
].name
,
194 (int) max_class
, machines
[j
].class,
195 (int) max_service
, machines
[j
].service
);
198 printf("\n%zu machines listed.\n", n_machines
);
203 typedef struct ImageInfo
{
212 static int compare_image_info(const void *a
, const void *b
) {
213 const ImageInfo
*x
= a
, *y
= b
;
215 return strcmp(x
->name
, y
->name
);
218 static int list_images(int argc
, char *argv
[], void *userdata
) {
220 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
221 size_t max_name
= strlen("NAME"), max_type
= strlen("TYPE"), max_size
= strlen("USAGE"), max_crtime
= strlen("CREATED"), max_mtime
= strlen("MODIFIED");
222 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
223 _cleanup_free_ ImageInfo
*images
= NULL
;
224 size_t n_images
= 0, n_allocated
= 0, j
;
225 const char *name
, *type
, *object
;
226 sd_bus
*bus
= userdata
;
227 uint64_t crtime
, mtime
, size
;
232 pager_open_if_enabled();
234 r
= sd_bus_call_method(
236 "org.freedesktop.machine1",
237 "/org/freedesktop/machine1",
238 "org.freedesktop.machine1.Manager",
244 log_error("Could not get images: %s", bus_error_message(&error
, -r
));
248 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssbttto)");
250 return bus_log_parse_error(r
);
252 while ((r
= sd_bus_message_read(reply
, "(ssbttto)", &name
, &type
, &read_only
, &crtime
, &mtime
, &size
, &object
)) > 0) {
253 char buf
[MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_BYTES_MAX
)];
256 if (name
[0] == '.' && !arg_all
)
259 if (!GREEDY_REALLOC(images
, n_allocated
, n_images
+ 1))
262 images
[n_images
].name
= name
;
263 images
[n_images
].type
= type
;
264 images
[n_images
].read_only
= read_only
;
265 images
[n_images
].crtime
= crtime
;
266 images
[n_images
].mtime
= mtime
;
267 images
[n_images
].size
= size
;
278 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), crtime
)));
284 l
= strlen(strna(format_timestamp(buf
, sizeof(buf
), mtime
)));
289 if (size
!= (uint64_t) -1) {
290 l
= strlen(strna(format_bytes(buf
, sizeof(buf
), size
)));
298 return bus_log_parse_error(r
);
300 r
= sd_bus_message_exit_container(reply
);
302 return bus_log_parse_error(r
);
304 qsort_safe(images
, n_images
, sizeof(ImageInfo
), compare_image_info
);
307 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
308 (int) max_name
, "NAME",
309 (int) max_type
, "TYPE",
311 (int) max_size
, "USAGE",
312 (int) max_crtime
, "CREATED",
313 (int) max_mtime
, "MODIFIED");
315 for (j
= 0; j
< n_images
; j
++) {
316 char crtime_buf
[FORMAT_TIMESTAMP_MAX
], mtime_buf
[FORMAT_TIMESTAMP_MAX
], size_buf
[FORMAT_BYTES_MAX
];
318 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
319 (int) max_name
, images
[j
].name
,
320 (int) max_type
, images
[j
].type
,
321 images
[j
].read_only
? ansi_highlight_red() : "", yes_no(images
[j
].read_only
), images
[j
].read_only
? ansi_highlight_off() : "",
322 (int) max_size
, strna(format_bytes(size_buf
, sizeof(size_buf
), images
[j
].size
)),
323 (int) max_crtime
, strna(format_timestamp(crtime_buf
, sizeof(crtime_buf
), images
[j
].crtime
)),
324 (int) max_mtime
, strna(format_timestamp(mtime_buf
, sizeof(mtime_buf
), images
[j
].mtime
)));
328 printf("\n%zu images listed.\n", n_images
);
333 static int show_unit_cgroup(sd_bus
*bus
, const char *unit
, pid_t leader
) {
334 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
335 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
336 _cleanup_free_
char *path
= NULL
;
344 if (arg_transport
== BUS_TRANSPORT_REMOTE
)
347 path
= unit_dbus_path_from_name(unit
);
351 r
= sd_bus_get_property(
353 "org.freedesktop.systemd1",
355 endswith(unit
, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
361 log_error("Failed to query ControlGroup: %s", bus_error_message(&error
, -r
));
365 r
= sd_bus_message_read(reply
, "s", &cgroup
);
367 return bus_log_parse_error(r
);
372 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, false) != 0 && leader
<= 0)
381 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER
, cgroup
, "\t\t ", c
, false, &leader
, leader
> 0, get_output_flags());
385 static int print_addresses(sd_bus
*bus
, const char *name
, int ifi
, const char *prefix
, const char *prefix2
) {
386 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
394 r
= sd_bus_call_method(bus
,
395 "org.freedesktop.machine1",
396 "/org/freedesktop/machine1",
397 "org.freedesktop.machine1.Manager",
398 "GetMachineAddresses",
405 r
= sd_bus_message_enter_container(reply
, 'a', "(iay)");
407 return bus_log_parse_error(r
);
409 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iay")) > 0) {
413 char buffer
[MAX(INET6_ADDRSTRLEN
, INET_ADDRSTRLEN
)];
415 r
= sd_bus_message_read(reply
, "i", &family
);
417 return bus_log_parse_error(r
);
419 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
421 return bus_log_parse_error(r
);
423 fputs(prefix
, stdout
);
424 fputs(inet_ntop(family
, a
, buffer
, sizeof(buffer
)), stdout
);
425 if (family
== AF_INET6
&& ifi
> 0)
429 r
= sd_bus_message_exit_container(reply
);
431 return bus_log_parse_error(r
);
433 if (prefix
!= prefix2
)
437 return bus_log_parse_error(r
);
439 r
= sd_bus_message_exit_container(reply
);
441 return bus_log_parse_error(r
);
446 static int print_os_release(sd_bus
*bus
, const char *name
, const char *prefix
) {
447 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
448 const char *k
, *v
, *pretty
= NULL
;
455 r
= sd_bus_call_method(bus
,
456 "org.freedesktop.machine1",
457 "/org/freedesktop/machine1",
458 "org.freedesktop.machine1.Manager",
459 "GetMachineOSRelease",
466 r
= sd_bus_message_enter_container(reply
, 'a', "{ss}");
468 return bus_log_parse_error(r
);
470 while ((r
= sd_bus_message_read(reply
, "{ss}", &k
, &v
)) > 0) {
471 if (streq(k
, "PRETTY_NAME"))
476 return bus_log_parse_error(r
);
478 r
= sd_bus_message_exit_container(reply
);
480 return bus_log_parse_error(r
);
483 printf("%s%s\n", prefix
, pretty
);
488 typedef struct MachineStatusInfo
{
494 char *root_directory
;
496 struct dual_timestamp timestamp
;
501 static void print_machine_status_info(sd_bus
*bus
, MachineStatusInfo
*i
) {
502 char since1
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
503 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
509 fputs(strna(i
->name
), stdout
);
511 if (!sd_id128_equal(i
->id
, SD_ID128_NULL
))
512 printf("(" SD_ID128_FORMAT_STR
")\n", SD_ID128_FORMAT_VAL(i
->id
));
516 s1
= format_timestamp_relative(since1
, sizeof(since1
), i
->timestamp
.realtime
);
517 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
.realtime
);
520 printf("\t Since: %s; %s\n", s2
, s1
);
522 printf("\t Since: %s\n", s2
);
525 _cleanup_free_
char *t
= NULL
;
527 printf("\t Leader: %u", (unsigned) i
->leader
);
529 get_process_comm(i
->leader
, &t
);
537 printf("\t Service: %s", i
->service
);
540 printf("; class %s", i
->class);
544 printf("\t Class: %s\n", i
->class);
546 if (i
->root_directory
)
547 printf("\t Root: %s\n", i
->root_directory
);
549 if (i
->n_netif
> 0) {
552 fputs("\t Iface:", stdout
);
554 for (c
= 0; c
< i
->n_netif
; c
++) {
555 char name
[IF_NAMESIZE
+1] = "";
557 if (if_indextoname(i
->netif
[c
], name
)) {
566 printf(" %i", i
->netif
[c
]);
572 print_addresses(bus
, i
->name
, ifi
,
576 print_os_release(bus
, i
->name
, "\t OS: ");
579 printf("\t Unit: %s\n", i
->unit
);
580 show_unit_cgroup(bus
, i
->unit
, i
->leader
);
582 if (arg_transport
== BUS_TRANSPORT_LOCAL
) {
584 show_journal_by_unit(
589 i
->timestamp
.monotonic
,
592 get_output_flags() | OUTPUT_BEGIN_NEWLINE
,
593 SD_JOURNAL_LOCAL_ONLY
,
600 static int map_netif(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
601 MachineStatusInfo
*i
= userdata
;
606 assert_cc(sizeof(int32_t) == sizeof(int));
607 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_INT32
, &v
, &l
);
613 i
->n_netif
= l
/ sizeof(int32_t);
614 i
->netif
= memdup(v
, l
);
621 static int show_machine_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
623 static const struct bus_properties_map map
[] = {
624 { "Name", "s", NULL
, offsetof(MachineStatusInfo
, name
) },
625 { "Class", "s", NULL
, offsetof(MachineStatusInfo
, class) },
626 { "Service", "s", NULL
, offsetof(MachineStatusInfo
, service
) },
627 { "Unit", "s", NULL
, offsetof(MachineStatusInfo
, unit
) },
628 { "RootDirectory", "s", NULL
, offsetof(MachineStatusInfo
, root_directory
) },
629 { "Leader", "u", NULL
, offsetof(MachineStatusInfo
, leader
) },
630 { "Timestamp", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.realtime
) },
631 { "TimestampMonotonic", "t", NULL
, offsetof(MachineStatusInfo
, timestamp
.monotonic
) },
632 { "Id", "ay", bus_map_id128
, offsetof(MachineStatusInfo
, id
) },
633 { "NetworkInterfaces", "ai", map_netif
, 0 },
637 MachineStatusInfo info
= {};
645 r
= bus_map_all_properties(bus
,
646 "org.freedesktop.machine1",
651 return log_error_errno(r
, "Could not get properties: %m");
657 print_machine_status_info(bus
, &info
);
663 free(info
.root_directory
);
669 static int show_machine_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
681 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
683 log_error_errno(r
, "Could not get properties: %m");
688 static int show_machine(int argc
, char *argv
[], void *userdata
) {
690 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
691 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
692 bool properties
, new_line
= false;
693 sd_bus
*bus
= userdata
;
698 properties
= !strstr(argv
[0], "status");
700 pager_open_if_enabled();
702 if (properties
&& argc
<= 1) {
704 /* If no argument is specified, inspect the manager
706 r
= show_machine_properties(bus
, "/org/freedesktop/machine1", &new_line
);
711 for (i
= 1; i
< argc
; i
++) {
712 const char *path
= NULL
;
714 r
= sd_bus_call_method(
716 "org.freedesktop.machine1",
717 "/org/freedesktop/machine1",
718 "org.freedesktop.machine1.Manager",
724 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
728 r
= sd_bus_message_read(reply
, "o", &path
);
730 return bus_log_parse_error(r
);
733 r
= show_machine_properties(bus
, path
, &new_line
);
735 r
= show_machine_info(argv
[0], bus
, path
, &new_line
);
741 typedef struct ImageStatusInfo
{
750 uint64_t usage_exclusive
;
751 uint64_t limit_exclusive
;
754 static void print_image_status_info(sd_bus
*bus
, ImageStatusInfo
*i
) {
755 char ts_relative
[FORMAT_TIMESTAMP_RELATIVE_MAX
], *s1
;
756 char ts_absolute
[FORMAT_TIMESTAMP_MAX
], *s2
;
757 char bs
[FORMAT_BYTES_MAX
], *s3
;
758 char bs_exclusive
[FORMAT_BYTES_MAX
], *s4
;
764 fputs(i
->name
, stdout
);
769 printf("\t Type: %s\n", i
->type
);
772 printf("\t Path: %s\n", i
->path
);
774 printf("\t RO: %s%s%s\n",
775 i
->read_only
? ansi_highlight_red() : "",
776 i
->read_only
? "read-only" : "writable",
777 i
->read_only
? ansi_highlight_off() : "");
779 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->crtime
);
780 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->crtime
);
782 printf("\t Created: %s; %s\n", s2
, s1
);
784 printf("\t Created: %s\n", s2
);
786 s1
= format_timestamp_relative(ts_relative
, sizeof(ts_relative
), i
->mtime
);
787 s2
= format_timestamp(ts_absolute
, sizeof(ts_absolute
), i
->mtime
);
789 printf("\tModified: %s; %s\n", s2
, s1
);
791 printf("\tModified: %s\n", s2
);
793 s3
= format_bytes(bs
, sizeof(bs
), i
->usage
);
794 s4
= i
->usage_exclusive
!= i
->usage
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->usage_exclusive
) : NULL
;
796 printf("\t Usage: %s (exclusive: %s)\n", s3
, s4
);
798 printf("\t Usage: %s\n", s3
);
800 s3
= format_bytes(bs
, sizeof(bs
), i
->limit
);
801 s4
= i
->limit_exclusive
!= i
->limit
? format_bytes(bs_exclusive
, sizeof(bs_exclusive
), i
->limit_exclusive
) : NULL
;
803 printf("\t Limit: %s (exclusive: %s)\n", s3
, s4
);
805 printf("\t Limit: %s\n", s3
);
808 static int show_image_info(const char *verb
, sd_bus
*bus
, const char *path
, bool *new_line
) {
810 static const struct bus_properties_map map
[] = {
811 { "Name", "s", NULL
, offsetof(ImageStatusInfo
, name
) },
812 { "Path", "s", NULL
, offsetof(ImageStatusInfo
, path
) },
813 { "Type", "s", NULL
, offsetof(ImageStatusInfo
, type
) },
814 { "ReadOnly", "b", NULL
, offsetof(ImageStatusInfo
, read_only
) },
815 { "CreationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, crtime
) },
816 { "ModificationTimestamp", "t", NULL
, offsetof(ImageStatusInfo
, mtime
) },
817 { "Usage", "t", NULL
, offsetof(ImageStatusInfo
, usage
) },
818 { "Limit", "t", NULL
, offsetof(ImageStatusInfo
, limit
) },
819 { "UsageExclusive", "t", NULL
, offsetof(ImageStatusInfo
, usage_exclusive
) },
820 { "LimitExclusive", "t", NULL
, offsetof(ImageStatusInfo
, limit_exclusive
) },
824 ImageStatusInfo info
= {};
832 r
= bus_map_all_properties(bus
,
833 "org.freedesktop.machine1",
838 return log_error_errno(r
, "Could not get properties: %m");
844 print_image_status_info(bus
, &info
);
853 static int show_image_properties(sd_bus
*bus
, const char *path
, bool *new_line
) {
865 r
= bus_print_all_properties(bus
, "org.freedesktop.machine1", path
, arg_property
, arg_all
);
867 log_error_errno(r
, "Could not get properties: %m");
872 static int show_image(int argc
, char *argv
[], void *userdata
) {
874 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
875 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
876 bool properties
, new_line
= false;
877 sd_bus
*bus
= userdata
;
882 properties
= !strstr(argv
[0], "status");
884 pager_open_if_enabled();
886 if (properties
&& argc
<= 1) {
888 /* If no argument is specified, inspect the manager
890 r
= show_image_properties(bus
, "/org/freedesktop/machine1", &new_line
);
895 for (i
= 1; i
< argc
; i
++) {
896 const char *path
= NULL
;
898 r
= sd_bus_call_method(
900 "org.freedesktop.machine1",
901 "/org/freedesktop/machine1",
902 "org.freedesktop.machine1.Manager",
908 log_error("Could not get path to image: %s", bus_error_message(&error
, -r
));
912 r
= sd_bus_message_read(reply
, "o", &path
);
914 return bus_log_parse_error(r
);
917 r
= show_image_properties(bus
, path
, &new_line
);
919 r
= show_image_info(argv
[0], bus
, path
, &new_line
);
925 static int kill_machine(int argc
, char *argv
[], void *userdata
) {
926 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
927 sd_bus
*bus
= userdata
;
932 polkit_agent_open_if_enabled();
935 arg_kill_who
= "all";
937 for (i
= 1; i
< argc
; i
++) {
940 r
= sd_bus_call_method(
942 "org.freedesktop.machine1",
943 "/org/freedesktop/machine1",
944 "org.freedesktop.machine1.Manager",
948 "ssi", argv
[i
], arg_kill_who
, arg_signal
);
950 log_error("Could not kill machine: %s", bus_error_message(&error
, -r
));
958 static int reboot_machine(int argc
, char *argv
[], void *userdata
) {
959 arg_kill_who
= "leader";
960 arg_signal
= SIGINT
; /* sysvinit + systemd */
962 return kill_machine(argc
, argv
, userdata
);
965 static int poweroff_machine(int argc
, char *argv
[], void *userdata
) {
966 arg_kill_who
= "leader";
967 arg_signal
= SIGRTMIN
+4; /* only systemd */
969 return kill_machine(argc
, argv
, userdata
);
972 static int terminate_machine(int argc
, char *argv
[], void *userdata
) {
973 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
974 sd_bus
*bus
= userdata
;
979 polkit_agent_open_if_enabled();
981 for (i
= 1; i
< argc
; i
++) {
984 r
= sd_bus_call_method(
986 "org.freedesktop.machine1",
987 "/org/freedesktop/machine1",
988 "org.freedesktop.machine1.Manager",
994 log_error("Could not terminate machine: %s", bus_error_message(&error
, -r
));
1002 static int machine_get_leader(sd_bus
*bus
, const char *name
, pid_t
*ret
) {
1003 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1004 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *reply2
= NULL
;
1013 r
= sd_bus_call_method(
1015 "org.freedesktop.machine1",
1016 "/org/freedesktop/machine1",
1017 "org.freedesktop.machine1.Manager",
1023 log_error("Could not get path to machine: %s", bus_error_message(&error
, -r
));
1027 r
= sd_bus_message_read(reply
, "o", &object
);
1029 return bus_log_parse_error(r
);
1031 r
= sd_bus_get_property(
1033 "org.freedesktop.machine1",
1035 "org.freedesktop.machine1.Machine",
1041 return log_error_errno(r
, "Failed to retrieve PID of leader: %m");
1043 r
= sd_bus_message_read(reply2
, "u", &leader
);
1045 return bus_log_parse_error(r
);
1051 static int copy_files(int argc
, char *argv
[], void *userdata
) {
1052 char *dest
, *host_path
, *container_path
, *host_dirname
, *host_basename
, *container_dirname
, *container_basename
, *t
;
1053 _cleanup_close_
int hostfd
= -1;
1054 sd_bus
*bus
= userdata
;
1055 pid_t child
, leader
;
1062 copy_from
= streq(argv
[0], "copy-from");
1063 dest
= argv
[3] ?: argv
[2];
1064 host_path
= strdupa(copy_from
? dest
: argv
[2]);
1065 container_path
= strdupa(copy_from
? argv
[2] : dest
);
1067 if (!path_is_absolute(container_path
)) {
1068 log_error("Container path not absolute.");
1072 t
= strdupa(host_path
);
1073 host_basename
= basename(t
);
1074 host_dirname
= dirname(host_path
);
1076 t
= strdupa(container_path
);
1077 container_basename
= basename(t
);
1078 container_dirname
= dirname(container_path
);
1080 r
= machine_get_leader(bus
, argv
[1], &leader
);
1084 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1086 return log_error_errno(errno
, "Failed to open source directory: %m");
1090 return log_error_errno(errno
, "Failed to fork(): %m");
1097 q
= procfs_file_alloca(leader
, "ns/mnt");
1098 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1100 log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1101 _exit(EXIT_FAILURE
);
1104 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1105 log_error_errno(errno
, "Failed to join namespace of leader: %m");
1106 _exit(EXIT_FAILURE
);
1109 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1110 if (containerfd
< 0) {
1111 log_error_errno(errno
, "Failed top open destination directory: %m");
1112 _exit(EXIT_FAILURE
);
1116 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, true);
1118 r
= copy_tree_at(hostfd
, host_basename
, containerfd
, container_basename
, true);
1120 log_error_errno(errno
, "Failed to copy tree: %m");
1121 _exit(EXIT_FAILURE
);
1124 _exit(EXIT_SUCCESS
);
1127 r
= wait_for_terminate(child
, &si
);
1129 return log_error_errno(r
, "Failed to wait for client: %m");
1130 if (si
.si_code
!= CLD_EXITED
) {
1131 log_error("Client died abnormally.");
1134 if (si
.si_status
!= EXIT_SUCCESS
)
1140 static int bind_mount(int argc
, char *argv
[], void *userdata
) {
1141 char mount_slave
[] = "/tmp/propagate.XXXXXX", *mount_tmp
, *mount_outside
, *p
;
1142 sd_bus
*bus
= userdata
;
1143 pid_t child
, leader
;
1146 bool mount_slave_created
= false, mount_slave_mounted
= false,
1147 mount_tmp_created
= false, mount_tmp_mounted
= false,
1148 mount_outside_created
= false, mount_outside_mounted
= false;
1153 /* One day, when bind mounting /proc/self/fd/n works across
1154 * namespace boundaries we should rework this logic to make
1157 dest
= argv
[3] ?: argv
[2];
1158 if (!path_is_absolute(dest
)) {
1159 log_error("Destination path not absolute.");
1163 p
= strappenda("/run/systemd/nspawn/propagate/", argv
[1], "/");
1164 if (access(p
, F_OK
) < 0) {
1165 log_error("Container does not allow propagation of mount points.");
1169 r
= machine_get_leader(bus
, argv
[1], &leader
);
1173 /* Our goal is to install a new bind mount into the container,
1174 possibly read-only. This is irritatingly complex
1175 unfortunately, currently.
1177 First, we start by creating a private playground in /tmp,
1178 that we can mount MS_SLAVE. (Which is necessary, since
1179 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1182 if (!mkdtemp(mount_slave
))
1183 return log_error_errno(errno
, "Failed to create playground: %m");
1185 mount_slave_created
= true;
1187 if (mount(mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
) < 0) {
1188 r
= log_error_errno(errno
, "Failed to make bind mount: %m");
1192 mount_slave_mounted
= true;
1194 if (mount(NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
) < 0) {
1195 r
= log_error_errno(errno
, "Failed to remount slave: %m");
1199 /* Second, we mount the source directory to a directory inside
1200 of our MS_SLAVE playground. */
1201 mount_tmp
= strappenda(mount_slave
, "/mount");
1202 if (mkdir(mount_tmp
, 0700) < 0) {
1203 r
= log_error_errno(errno
, "Failed to create temporary mount: %m");
1207 mount_tmp_created
= true;
1209 if (mount(argv
[2], mount_tmp
, NULL
, MS_BIND
, NULL
) < 0) {
1210 r
= log_error_errno(errno
, "Failed to overmount: %m");
1214 mount_tmp_mounted
= true;
1216 /* Third, we remount the new bind mount read-only if requested. */
1218 if (mount(NULL
, mount_tmp
, NULL
, MS_BIND
|MS_REMOUNT
|MS_RDONLY
, NULL
) < 0) {
1219 r
= log_error_errno(errno
, "Failed to mark read-only: %m");
1223 /* Fourth, we move the new bind mount into the propagation
1224 * directory. This way it will appear there read-only
1227 mount_outside
= strappenda("/run/systemd/nspawn/propagate/", argv
[1], "/XXXXXX");
1228 if (!mkdtemp(mount_outside
)) {
1229 r
= log_error_errno(errno
, "Cannot create propagation directory: %m");
1233 mount_outside_created
= true;
1235 if (mount(mount_tmp
, mount_outside
, NULL
, MS_MOVE
, NULL
) < 0) {
1236 r
= log_error_errno(errno
, "Failed to move: %m");
1240 mount_outside_mounted
= true;
1241 mount_tmp_mounted
= false;
1243 (void) rmdir(mount_tmp
);
1244 mount_tmp_created
= false;
1246 (void) umount(mount_slave
);
1247 mount_slave_mounted
= false;
1249 (void) rmdir(mount_slave
);
1250 mount_slave_created
= false;
1254 r
= log_error_errno(errno
, "Failed to fork(): %m");
1259 const char *mount_inside
;
1263 q
= procfs_file_alloca(leader
, "ns/mnt");
1264 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1266 log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1267 _exit(EXIT_FAILURE
);
1270 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1271 log_error_errno(errno
, "Failed to join namespace of leader: %m");
1272 _exit(EXIT_FAILURE
);
1276 mkdir_p(dest
, 0755);
1278 /* Fifth, move the mount to the right place inside */
1279 mount_inside
= strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside
));
1280 if (mount(mount_inside
, dest
, NULL
, MS_MOVE
, NULL
) < 0) {
1281 log_error_errno(errno
, "Failed to mount: %m");
1282 _exit(EXIT_FAILURE
);
1285 _exit(EXIT_SUCCESS
);
1288 r
= wait_for_terminate(child
, &si
);
1290 log_error_errno(r
, "Failed to wait for client: %m");
1293 if (si
.si_code
!= CLD_EXITED
) {
1294 log_error("Client died abnormally.");
1298 if (si
.si_status
!= EXIT_SUCCESS
) {
1306 if (mount_outside_mounted
)
1307 umount(mount_outside
);
1308 if (mount_outside_created
)
1309 rmdir(mount_outside
);
1311 if (mount_tmp_mounted
)
1313 if (mount_tmp_created
)
1316 if (mount_slave_mounted
)
1317 umount(mount_slave
);
1318 if (mount_slave_created
)
1319 umount(mount_slave
);
1324 static int on_machine_removed(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
1325 PTYForward
** forward
= (PTYForward
**) userdata
;
1333 /* If the forwarder is already initialized, tell it to
1334 * exit on the next vhangup(), so that we still flush
1335 * out what might be queued and exit then. */
1337 r
= pty_forward_set_ignore_vhangup(*forward
, false);
1341 log_error_errno(r
, "Failed to set ignore_vhangup flag: %m");
1344 /* On error, or when the forwarder is not initialized yet, quit immediately */
1345 sd_event_exit(sd_bus_get_event(bus
), EXIT_FAILURE
);
1349 static int login_machine(int argc
, char *argv
[], void *userdata
) {
1350 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1351 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1352 _cleanup_bus_slot_unref_ sd_bus_slot
*slot
= NULL
;
1353 _cleanup_(pty_forward_freep
) PTYForward
*forward
= NULL
;
1354 _cleanup_event_unref_ sd_event
*event
= NULL
;
1355 int master
= -1, r
, ret
= 0;
1356 sd_bus
*bus
= userdata
;
1357 const char *pty
, *match
;
1364 if (arg_transport
!= BUS_TRANSPORT_LOCAL
&&
1365 arg_transport
!= BUS_TRANSPORT_MACHINE
) {
1366 log_error("Login only supported on local machines.");
1370 polkit_agent_open_if_enabled();
1372 r
= sd_event_default(&event
);
1374 return log_error_errno(r
, "Failed to get event loop: %m");
1376 r
= sd_bus_attach_event(bus
, event
, 0);
1378 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1380 match
= strappenda("type='signal',"
1381 "sender='org.freedesktop.machine1',"
1382 "path='/org/freedesktop/machine1',",
1383 "interface='org.freedesktop.machine1.Manager',"
1384 "member='MachineRemoved',"
1389 r
= sd_bus_add_match(bus
, &slot
, match
, on_machine_removed
, &forward
);
1391 return log_error_errno(r
, "Failed to add machine removal match: %m");
1393 r
= sd_bus_message_new_method_call(bus
,
1395 "org.freedesktop.machine1",
1396 "/org/freedesktop/machine1",
1397 "org.freedesktop.machine1.Manager",
1398 "OpenMachineLogin");
1400 return bus_log_create_error(r
);
1402 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1404 return bus_log_create_error(r
);
1406 r
= sd_bus_message_append(m
, "s", argv
[1]);
1408 return bus_log_create_error(r
);
1410 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1412 log_error("Failed to get machine PTY: %s", bus_error_message(&error
, -r
));
1416 r
= sd_bus_message_read(reply
, "hs", &master
, &pty
);
1418 return bus_log_parse_error(r
);
1420 assert_se(sigemptyset(&mask
) == 0);
1421 sigset_add_many(&mask
, SIGWINCH
, SIGTERM
, SIGINT
, -1);
1422 assert_se(sigprocmask(SIG_BLOCK
, &mask
, NULL
) == 0);
1424 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv
[1]);
1426 sd_event_add_signal(event
, NULL
, SIGINT
, NULL
, NULL
);
1427 sd_event_add_signal(event
, NULL
, SIGTERM
, NULL
, NULL
);
1429 r
= pty_forward_new(event
, master
, true, &forward
);
1431 return log_error_errno(r
, "Failed to create PTY forwarder: %m");
1433 r
= sd_event_loop(event
);
1435 return log_error_errno(r
, "Failed to run event loop: %m");
1437 pty_forward_get_last_char(forward
, &last_char
);
1438 machine_died
= pty_forward_get_ignore_vhangup(forward
) == 0;
1440 forward
= pty_forward_free(forward
);
1442 if (last_char
!= '\n')
1443 fputc('\n', stdout
);
1446 log_info("Machine %s terminated.", argv
[1]);
1448 log_info("Connection to machine %s terminated.", argv
[1]);
1450 sd_event_get_exit_code(event
, &ret
);
1454 static int remove_image(int argc
, char *argv
[], void *userdata
) {
1455 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1456 sd_bus
*bus
= userdata
;
1461 polkit_agent_open_if_enabled();
1463 for (i
= 1; i
< argc
; i
++) {
1464 r
= sd_bus_call_method(
1466 "org.freedesktop.machine1",
1467 "/org/freedesktop/machine1",
1468 "org.freedesktop.machine1.Manager",
1474 log_error("Could not remove image: %s", bus_error_message(&error
, -r
));
1482 static int rename_image(int argc
, char *argv
[], void *userdata
) {
1483 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1484 sd_bus
*bus
= userdata
;
1487 polkit_agent_open_if_enabled();
1489 r
= sd_bus_call_method(
1491 "org.freedesktop.machine1",
1492 "/org/freedesktop/machine1",
1493 "org.freedesktop.machine1.Manager",
1497 "ss", argv
[1], argv
[2]);
1499 log_error("Could not rename image: %s", bus_error_message(&error
, -r
));
1506 static int clone_image(int argc
, char *argv
[], void *userdata
) {
1507 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1508 sd_bus
*bus
= userdata
;
1511 polkit_agent_open_if_enabled();
1513 r
= sd_bus_call_method(
1515 "org.freedesktop.machine1",
1516 "/org/freedesktop/machine1",
1517 "org.freedesktop.machine1.Manager",
1521 "ssb", argv
[1], argv
[2], arg_read_only
);
1523 log_error("Could not clone image: %s", bus_error_message(&error
, -r
));
1530 static int read_only_image(int argc
, char *argv
[], void *userdata
) {
1531 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1532 sd_bus
*bus
= userdata
;
1536 b
= parse_boolean(argv
[2]);
1538 log_error("Failed to parse boolean argument: %s", argv
[2]);
1543 polkit_agent_open_if_enabled();
1545 r
= sd_bus_call_method(
1547 "org.freedesktop.machine1",
1548 "/org/freedesktop/machine1",
1549 "org.freedesktop.machine1.Manager",
1550 "MarkImageReadOnly",
1555 log_error("Could not mark image read-only: %s", bus_error_message(&error
, -r
));
1562 static int start_machine(int argc
, char *argv
[], void *userdata
) {
1563 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1564 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
1565 sd_bus
*bus
= userdata
;
1570 polkit_agent_open_if_enabled();
1572 r
= bus_wait_for_jobs_new(bus
, &w
);
1576 for (i
= 1; i
< argc
; i
++) {
1577 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1578 _cleanup_free_
char *e
= NULL
, *unit
= NULL
;
1581 if (!machine_name_is_valid(argv
[i
])) {
1582 log_error("Invalid machine name %s.", argv
[i
]);
1586 e
= unit_name_escape(argv
[i
]);
1590 unit
= unit_name_build("systemd-nspawn", e
, ".service");
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_set_allow_interactive_authorization(m
, arg_ask_password
);
1606 return bus_log_create_error(r
);
1608 r
= sd_bus_message_append(m
, "ss", unit
, "fail");
1610 return bus_log_create_error(r
);
1612 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1614 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
1618 r
= sd_bus_message_read(reply
, "o", &object
);
1620 return bus_log_parse_error(r
);
1622 r
= bus_wait_for_jobs_add(w
, object
);
1627 r
= bus_wait_for_jobs(w
, arg_quiet
);
1634 static int enable_machine(int argc
, char *argv
[], void *userdata
) {
1635 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
, *reply
= NULL
;
1636 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1637 int carries_install_info
= 0;
1638 const char *method
= NULL
;
1639 sd_bus
*bus
= userdata
;
1644 polkit_agent_open_if_enabled();
1646 method
= streq(argv
[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1648 r
= sd_bus_message_new_method_call(
1651 "org.freedesktop.systemd1",
1652 "/org/freedesktop/systemd1",
1653 "org.freedesktop.systemd1.Manager",
1656 return bus_log_create_error(r
);
1658 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1660 return bus_log_create_error(r
);
1662 r
= sd_bus_message_open_container(m
, 'a', "s");
1664 return bus_log_create_error(r
);
1666 for (i
= 1; i
< argc
; i
++) {
1667 _cleanup_free_
char *e
= NULL
, *unit
= NULL
;
1669 if (!machine_name_is_valid(argv
[i
])) {
1670 log_error("Invalid machine name %s.", argv
[i
]);
1674 e
= unit_name_escape(argv
[i
]);
1678 unit
= unit_name_build("systemd-nspawn", e
, ".service");
1682 r
= sd_bus_message_append(m
, "s", unit
);
1684 return bus_log_create_error(r
);
1687 r
= sd_bus_message_close_container(m
);
1689 return bus_log_create_error(r
);
1691 if (streq(argv
[0], "enable"))
1692 r
= sd_bus_message_append(m
, "bb", false, false);
1694 r
= sd_bus_message_append(m
, "b", false);
1696 return bus_log_create_error(r
);
1698 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1700 log_error("Failed to enable or disable unit: %s", bus_error_message(&error
, -r
));
1704 if (streq(argv
[0], "enable")) {
1705 r
= sd_bus_message_read(reply
, "b", carries_install_info
);
1707 return bus_log_parse_error(r
);
1710 r
= bus_deserialize_and_dump_unit_file_changes(reply
, arg_quiet
);
1714 m
= sd_bus_message_unref(m
);
1716 r
= sd_bus_message_new_method_call(
1719 "org.freedesktop.systemd1",
1720 "/org/freedesktop/systemd1",
1721 "org.freedesktop.systemd1.Manager",
1724 return bus_log_create_error(r
);
1726 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1728 return bus_log_create_error(r
);
1730 r
= sd_bus_call(bus
, m
, 0, &error
, NULL
);
1732 log_error("Failed to reload daemon: %s", bus_error_message(&error
, -r
));
1739 typedef struct PullContext
{
1744 static int match_log_message(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1745 PullContext
*c
= userdata
;
1753 r
= sd_bus_message_read(m
, "us", &priority
, &line
);
1755 bus_log_parse_error(r
);
1759 if (!streq_ptr(c
->path
, sd_bus_message_get_path(m
)))
1762 if (arg_quiet
&& LOG_PRI(priority
) >= LOG_INFO
)
1765 log_full(priority
, "%s", line
);
1769 static int match_transfer_removed(sd_bus
*bus
, sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1770 PullContext
*c
= userdata
;
1771 const char *path
, *result
;
1779 r
= sd_bus_message_read(m
, "uos", &id
, &path
, &result
);
1781 bus_log_parse_error(r
);
1785 if (!streq_ptr(c
->path
, path
))
1788 c
->result
= streq_ptr(result
, "done");
1792 static int pull_image_common(sd_bus
*bus
, sd_bus_message
*m
) {
1793 _cleanup_bus_slot_unref_ sd_bus_slot
*slot_job_removed
= NULL
, *slot_log_message
= NULL
;
1794 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1795 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1805 polkit_agent_open_if_enabled();
1807 r
= sd_bus_message_set_allow_interactive_authorization(m
, arg_ask_password
);
1809 return bus_log_create_error(r
);
1811 r
= sd_bus_add_match(
1815 "sender='org.freedesktop.import1',"
1816 "interface='org.freedesktop.import1.Manager',"
1817 "member='TransferRemoved',"
1818 "path='/org/freedesktop/import1'",
1819 match_transfer_removed
, &c
);
1821 return log_error_errno(r
, "Failed to install match: %m");
1823 r
= sd_bus_add_match(
1827 "sender='org.freedesktop.import1',"
1828 "interface='org.freedesktop.import1.Transfer',"
1829 "member='LogMessage'",
1830 match_log_message
, &c
);
1832 return log_error_errno(r
, "Failed to install match: %m");
1834 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
1836 log_error("Failed pull image: %s", bus_error_message(&error
, -r
));
1840 r
= sd_bus_message_read(reply
, "uo", &id
, &c
.path
);
1842 return bus_log_parse_error(r
);
1845 r
= sd_bus_process(bus
, NULL
);
1849 /* The match sets this to NULL when we are done */
1853 r
= sd_bus_wait(bus
, (uint64_t) -1);
1858 return c
.result
? 0 : -EINVAL
;
1861 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
1862 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1863 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1864 const char *local
, *remote
;
1865 sd_bus
*bus
= userdata
;
1871 if (!http_url_is_valid(remote
)) {
1872 log_error("URL '%s' is not valid.", remote
);
1879 r
= import_url_last_component(remote
, &l
);
1881 return log_error_errno(r
, "Failed to get final component of URL: %m");
1886 if (isempty(local
) || streq(local
, "-"))
1890 r
= tar_strip_suffixes(local
, &ll
);
1892 return log_error_errno(r
, "Failed to strip tar suffixes: %m");
1896 if (!machine_name_is_valid(local
)) {
1897 log_error("Local name %s is not a suitable machine name.", local
);
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(
1920 return bus_log_create_error(r
);
1922 return pull_image_common(bus
, m
);
1925 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
1926 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1927 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1928 const char *local
, *remote
;
1929 sd_bus
*bus
= userdata
;
1935 if (!http_url_is_valid(remote
)) {
1936 log_error("URL '%s' is not valid.", remote
);
1943 r
= import_url_last_component(remote
, &l
);
1945 return log_error_errno(r
, "Failed to get final component of URL: %m");
1950 if (isempty(local
) || streq(local
, "-"))
1954 r
= raw_strip_suffixes(local
, &ll
);
1956 return log_error_errno(r
, "Failed to strip tar suffixes: %m");
1960 if (!machine_name_is_valid(local
)) {
1961 log_error("Local name %s is not a suitable machine name.", local
);
1966 r
= sd_bus_message_new_method_call(
1969 "org.freedesktop.import1",
1970 "/org/freedesktop/import1",
1971 "org.freedesktop.import1.Manager",
1974 return bus_log_create_error(r
);
1976 r
= sd_bus_message_append(
1984 return bus_log_create_error(r
);
1986 return pull_image_common(bus
, m
);
1989 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
1990 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1991 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
1992 const char *local
, *remote
, *tag
;
1993 sd_bus
*bus
= userdata
;
1996 if (!streq_ptr(arg_dkr_index_url
, "no")) {
1997 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2002 tag
= strchr(remote
, ':');
2004 remote
= strndupa(remote
, tag
- remote
);
2008 if (!dkr_name_is_valid(remote
)) {
2009 log_error("DKR name '%s' is invalid.", remote
);
2012 if (tag
&& !dkr_tag_is_valid(tag
)) {
2013 log_error("DKR tag '%s' is invalid.", remote
);
2020 local
= strchr(remote
, '/');
2027 if (isempty(local
) || streq(local
, "-"))
2031 if (!machine_name_is_valid(local
)) {
2032 log_error("Local name %s is not a suitable machine name.", local
);
2037 r
= sd_bus_message_new_method_call(
2040 "org.freedesktop.import1",
2041 "/org/freedesktop/import1",
2042 "org.freedesktop.import1.Manager",
2045 return bus_log_create_error(r
);
2047 r
= sd_bus_message_append(
2057 return bus_log_create_error(r
);
2059 return pull_image_common(bus
, m
);
2062 typedef struct TransferInfo
{
2069 static int compare_transfer_info(const void *a
, const void *b
) {
2070 const TransferInfo
*x
= a
, *y
= b
;
2072 return strcmp(x
->local
, y
->local
);
2075 static int list_transfers(int argc
, char *argv
[], void *userdata
) {
2076 size_t max_type
= strlen("TYPE"), max_local
= strlen("LOCAL"), max_remote
= strlen("REMOTE");
2077 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
2078 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2079 _cleanup_free_ TransferInfo
*transfers
= NULL
;
2080 size_t n_transfers
= 0, n_allocated
= 0, j
;
2081 const char *type
, *remote
, *local
, *object
;
2082 sd_bus
*bus
= userdata
;
2083 uint32_t id
, max_id
= 0;
2086 pager_open_if_enabled();
2088 r
= sd_bus_call_method(
2090 "org.freedesktop.import1",
2091 "/org/freedesktop/import1",
2092 "org.freedesktop.import1.Manager",
2098 log_error("Could not get transfers: %s", bus_error_message(&error
, -r
));
2102 r
= sd_bus_message_enter_container(reply
, 'a', "(ussso)");
2104 return bus_log_parse_error(r
);
2106 while ((r
= sd_bus_message_read(reply
, "(ussso)", &id
, &type
, &remote
, &local
, &object
)) > 0) {
2109 if (!GREEDY_REALLOC(transfers
, n_allocated
, n_transfers
+ 1))
2112 transfers
[n_transfers
].id
= id
;
2113 transfers
[n_transfers
].type
= type
;
2114 transfers
[n_transfers
].remote
= remote
;
2115 transfers
[n_transfers
].local
= local
;
2135 return bus_log_parse_error(r
);
2137 r
= sd_bus_message_exit_container(reply
);
2139 return bus_log_parse_error(r
);
2141 qsort_safe(transfers
, n_transfers
, sizeof(TransferInfo
), compare_transfer_info
);
2144 printf("%-*s %-*s %-*s %-*s\n",
2145 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), "ID",
2146 (int) max_type
, "TYPE",
2147 (int) max_local
, "LOCAL",
2148 (int) max_remote
, "REMOTE");
2150 for (j
= 0; j
< n_transfers
; j
++)
2151 printf("%*" PRIu32
" %-*s %-*s %-*s\n",
2152 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id
)), transfers
[j
].id
,
2153 (int) max_type
, transfers
[j
].type
,
2154 (int) max_local
, transfers
[j
].local
,
2155 (int) max_remote
, transfers
[j
].remote
);
2158 printf("\n%zu transfers listed.\n", n_transfers
);
2163 static int cancel_transfer(int argc
, char *argv
[], void *userdata
) {
2164 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2165 sd_bus
*bus
= userdata
;
2170 polkit_agent_open_if_enabled();
2172 for (i
= 1; i
< argc
; i
++) {
2175 r
= safe_atou32(argv
[i
], &id
);
2177 return log_error_errno(r
, "Failed to parse transfer id: %s", argv
[i
]);
2179 r
= sd_bus_call_method(
2181 "org.freedesktop.import1",
2182 "/org/freedesktop/import1",
2183 "org.freedesktop.import1.Manager",
2189 log_error("Could not cancel transfer: %s", bus_error_message(&error
, -r
));
2197 static int help(int argc
, char *argv
[], void *userdata
) {
2199 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2200 "Send control commands to or query the virtual machine and container\n"
2201 "registration manager.\n\n"
2202 " -h --help Show this help\n"
2203 " --version Show package version\n"
2204 " --no-pager Do not pipe output into a pager\n"
2205 " --no-legend Do not show the headers and footers\n"
2206 " --no-ask-password Do not ask for system passwords\n"
2207 " -H --host=[USER@]HOST Operate on remote host\n"
2208 " -M --machine=CONTAINER Operate on local container\n"
2209 " -p --property=NAME Show only properties by this name\n"
2210 " -q --quiet Suppress output\n"
2211 " -a --all Show all properties, including empty ones\n"
2212 " -l --full Do not ellipsize output\n"
2213 " --kill-who=WHO Who to send signal to\n"
2214 " -s --signal=SIGNAL Which signal to send\n"
2215 " --read-only Create read-only bind mount\n"
2216 " --mkdir Create directory before bind mounting, if missing\n"
2217 " -n --lines=INTEGER Number of journal entries to show\n"
2218 " -o --output=STRING Change journal output mode (short,\n"
2219 " short-monotonic, verbose, export, json,\n"
2220 " json-pretty, json-sse, cat)\n"
2221 " --verify=MODE Verification mode for downloaded images (no,\n"
2222 " checksum, signature)\n"
2223 " --force Download image even if already exists\n"
2224 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2226 "Machine Commands:\n"
2227 " list List running VMs and containers\n"
2228 " status NAME... Show VM/container details\n"
2229 " show NAME... Show properties of one or more VMs/containers\n"
2230 " start NAME... Start container as a service\n"
2231 " login NAME Get a login prompt on a container\n"
2232 " enable NAME... Enable automatic container start at boot\n"
2233 " disable NAME... Disable automatic container start at boot\n"
2234 " poweroff NAME... Power off one or more containers\n"
2235 " reboot NAME... Reboot one or more containers\n"
2236 " terminate NAME... Terminate one or more VMs/containers\n"
2237 " kill NAME... Send signal to processes of a VM/container\n"
2238 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2239 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2240 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2242 " list-images Show available container annd VM images\n"
2243 " image-status NAME... Show image details\n"
2244 " show-image NAME... Show properties of image\n"
2245 " clone NAME NAME Clone an image\n"
2246 " rename NAME NAME Rename an image\n"
2247 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2248 " remove NAME... Remove an image\n\n"
2249 "Image Transfer Commands:\n"
2250 " pull-tar URL [NAME] Download a TAR container image\n"
2251 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2252 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2253 " list-transfers Show list of downloads in progress\n"
2254 " cancel-transfer Cancel a download\n"
2255 , program_invocation_short_name
);
2260 static int parse_argv(int argc
, char *argv
[]) {
2263 ARG_VERSION
= 0x100,
2269 ARG_NO_ASK_PASSWORD
,
2275 static const struct option options
[] = {
2276 { "help", no_argument
, NULL
, 'h' },
2277 { "version", no_argument
, NULL
, ARG_VERSION
},
2278 { "property", required_argument
, NULL
, 'p' },
2279 { "all", no_argument
, NULL
, 'a' },
2280 { "full", no_argument
, NULL
, 'l' },
2281 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2282 { "no-legend", no_argument
, NULL
, ARG_NO_LEGEND
},
2283 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
2284 { "signal", required_argument
, NULL
, 's' },
2285 { "host", required_argument
, NULL
, 'H' },
2286 { "machine", required_argument
, NULL
, 'M' },
2287 { "read-only", no_argument
, NULL
, ARG_READ_ONLY
},
2288 { "mkdir", no_argument
, NULL
, ARG_MKDIR
},
2289 { "quiet", no_argument
, NULL
, 'q' },
2290 { "lines", required_argument
, NULL
, 'n' },
2291 { "output", required_argument
, NULL
, 'o' },
2292 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
2293 { "verify", required_argument
, NULL
, ARG_VERIFY
},
2294 { "force", no_argument
, NULL
, ARG_FORCE
},
2295 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
2304 while ((c
= getopt_long(argc
, argv
, "hp:als:H:M:qn:o:", options
, NULL
)) >= 0)
2309 return help(0, NULL
, NULL
);
2312 puts(PACKAGE_STRING
);
2313 puts(SYSTEMD_FEATURES
);
2317 r
= strv_extend(&arg_property
, optarg
);
2321 /* If the user asked for a particular
2322 * property, show it to him, even if it is
2336 if (safe_atou(optarg
, &arg_lines
) < 0) {
2337 log_error("Failed to parse lines '%s'", optarg
);
2343 arg_output
= output_mode_from_string(optarg
);
2344 if (arg_output
< 0) {
2345 log_error("Unknown output '%s'.", optarg
);
2351 arg_no_pager
= true;
2359 arg_kill_who
= optarg
;
2363 arg_signal
= signal_from_string_try_harder(optarg
);
2364 if (arg_signal
< 0) {
2365 log_error("Failed to parse signal string %s.", optarg
);
2370 case ARG_NO_ASK_PASSWORD
:
2371 arg_ask_password
= false;
2375 arg_transport
= BUS_TRANSPORT_REMOTE
;
2380 arg_transport
= BUS_TRANSPORT_MACHINE
;
2385 arg_read_only
= true;
2397 arg_verify
= optarg
;
2404 case ARG_DKR_INDEX_URL
:
2405 if (!http_url_is_valid(optarg
)) {
2406 log_error("Index URL is invalid: %s", optarg
);
2410 arg_dkr_index_url
= optarg
;
2417 assert_not_reached("Unhandled option");
2423 static int machinectl_main(int argc
, char *argv
[], sd_bus
*bus
) {
2425 static const Verb verbs
[] = {
2426 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2427 { "list", VERB_ANY
, 1, VERB_DEFAULT
, list_machines
},
2428 { "list-images", VERB_ANY
, 1, 0, list_images
},
2429 { "status", 2, VERB_ANY
, 0, show_machine
},
2430 { "image-status", 2, VERB_ANY
, 0, show_image
},
2431 { "show", VERB_ANY
, VERB_ANY
, 0, show_machine
},
2432 { "show-image", VERB_ANY
, VERB_ANY
, 0, show_image
},
2433 { "terminate", 2, VERB_ANY
, 0, terminate_machine
},
2434 { "reboot", 2, VERB_ANY
, 0, reboot_machine
},
2435 { "poweroff", 2, VERB_ANY
, 0, poweroff_machine
},
2436 { "kill", 2, VERB_ANY
, 0, kill_machine
},
2437 { "login", 2, 2, 0, login_machine
},
2438 { "bind", 3, 4, 0, bind_mount
},
2439 { "copy-to", 3, 4, 0, copy_files
},
2440 { "copy-from", 3, 4, 0, copy_files
},
2441 { "remove", 2, VERB_ANY
, 0, remove_image
},
2442 { "rename", 3, 3, 0, rename_image
},
2443 { "clone", 3, 3, 0, clone_image
},
2444 { "read-only", 2, 3, 0, read_only_image
},
2445 { "start", 2, VERB_ANY
, 0, start_machine
},
2446 { "enable", 2, VERB_ANY
, 0, enable_machine
},
2447 { "disable", 2, VERB_ANY
, 0, enable_machine
},
2448 { "pull-tar", 2, 3, 0, pull_tar
},
2449 { "pull-raw", 2, 3, 0, pull_raw
},
2450 { "pull-dkr", 2, 3, 0, pull_dkr
},
2451 { "list-transfers", VERB_ANY
, 1, 0, list_transfers
},
2452 { "cancel-transfer", 2, VERB_ANY
, 0, cancel_transfer
},
2456 return dispatch_verb(argc
, argv
, verbs
, bus
);
2459 int main(int argc
, char*argv
[]) {
2460 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
2463 setlocale(LC_ALL
, "");
2464 log_parse_environment();
2467 r
= parse_argv(argc
, argv
);
2471 r
= bus_open_transport(arg_transport
, arg_host
, false, &bus
);
2473 log_error_errno(r
, "Failed to create bus connection: %m");
2477 r
= machinectl_main(argc
, argv
, bus
);
2481 polkit_agent_close();
2483 strv_free(arg_property
);
2485 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;