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 ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
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;
1363 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1364 arg_transport != BUS_TRANSPORT_MACHINE) {
1365 log_error("Login only supported on local machines.");
1369 polkit_agent_open_if_enabled();
1371 r = sd_event_default(&event);
1373 return log_error_errno(r, "Failed to get event loop: %m");
1375 r = sd_bus_attach_event(bus, event, 0);
1377 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1379 match = strappenda("type='signal',"
1380 "sender='org.freedesktop.machine1',"
1381 "path='/org/freedesktop/machine1',",
1382 "interface='org.freedesktop.machine1.Manager',"
1383 "member='MachineRemoved',"
1388 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1390 return log_error_errno(r, "Failed to add machine removal match: %m");
1392 r = sd_bus_message_new_method_call(bus,
1394 "org.freedesktop.machine1",
1395 "/org/freedesktop/machine1",
1396 "org.freedesktop.machine1.Manager",
1397 "OpenMachineLogin");
1399 return bus_log_create_error(r);
1401 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1403 return bus_log_create_error(r);
1405 r = sd_bus_message_append(m, "s", argv[1]);
1407 return bus_log_create_error(r);
1409 r = sd_bus_call(bus, m, 0, &error, &reply);
1411 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1415 r = sd_bus_message_read(reply, "hs", &master, &pty);
1417 return bus_log_parse_error(r);
1419 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1421 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1423 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1424 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1426 r = pty_forward_new(event, master, true, &forward);
1428 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1430 r = sd_event_loop(event);
1432 return log_error_errno(r, "Failed to run event loop: %m");
1434 pty_forward_get_last_char(forward, &last_char);
1435 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1437 forward = pty_forward_free(forward);
1439 if (last_char != '\n')
1440 fputc('\n', stdout);
1443 log_info("Machine %s terminated.", argv[1]);
1445 log_info("Connection to machine %s terminated.", argv[1]);
1447 sd_event_get_exit_code(event, &ret);
1451 static int remove_image(int argc, char *argv[], void *userdata) {
1452 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1453 sd_bus *bus = userdata;
1458 polkit_agent_open_if_enabled();
1460 for (i = 1; i < argc; i++) {
1461 r = sd_bus_call_method(
1463 "org.freedesktop.machine1",
1464 "/org/freedesktop/machine1",
1465 "org.freedesktop.machine1.Manager",
1471 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1479 static int rename_image(int argc, char *argv[], void *userdata) {
1480 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1481 sd_bus *bus = userdata;
1484 polkit_agent_open_if_enabled();
1486 r = sd_bus_call_method(
1488 "org.freedesktop.machine1",
1489 "/org/freedesktop/machine1",
1490 "org.freedesktop.machine1.Manager",
1494 "ss", argv[1], argv[2]);
1496 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1503 static int clone_image(int argc, char *argv[], void *userdata) {
1504 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1505 sd_bus *bus = userdata;
1508 polkit_agent_open_if_enabled();
1510 r = sd_bus_call_method(
1512 "org.freedesktop.machine1",
1513 "/org/freedesktop/machine1",
1514 "org.freedesktop.machine1.Manager",
1518 "ssb", argv[1], argv[2], arg_read_only);
1520 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1527 static int read_only_image(int argc, char *argv[], void *userdata) {
1528 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1529 sd_bus *bus = userdata;
1533 b = parse_boolean(argv[2]);
1535 log_error("Failed to parse boolean argument: %s", argv[2]);
1540 polkit_agent_open_if_enabled();
1542 r = sd_bus_call_method(
1544 "org.freedesktop.machine1",
1545 "/org/freedesktop/machine1",
1546 "org.freedesktop.machine1.Manager",
1547 "MarkImageReadOnly",
1552 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1559 static int start_machine(int argc, char *argv[], void *userdata) {
1560 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1561 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1562 sd_bus *bus = userdata;
1567 polkit_agent_open_if_enabled();
1569 r = bus_wait_for_jobs_new(bus, &w);
1573 for (i = 1; i < argc; i++) {
1574 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1575 _cleanup_free_ char *e = NULL, *unit = NULL;
1578 if (!machine_name_is_valid(argv[i])) {
1579 log_error("Invalid machine name %s.", argv[i]);
1583 e = unit_name_escape(argv[i]);
1587 unit = unit_name_build("systemd-nspawn", e, ".service");
1591 r = sd_bus_message_new_method_call(
1594 "org.freedesktop.systemd1",
1595 "/org/freedesktop/systemd1",
1596 "org.freedesktop.systemd1.Manager",
1599 return bus_log_create_error(r);
1601 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1603 return bus_log_create_error(r);
1605 r = sd_bus_message_append(m, "ss", unit, "fail");
1607 return bus_log_create_error(r);
1609 r = sd_bus_call(bus, m, 0, &error, &reply);
1611 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1615 r = sd_bus_message_read(reply, "o", &object);
1617 return bus_log_parse_error(r);
1619 r = bus_wait_for_jobs_add(w, object);
1624 r = bus_wait_for_jobs(w, arg_quiet);
1631 static int enable_machine(int argc, char *argv[], void *userdata) {
1632 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1633 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1634 int carries_install_info = 0;
1635 const char *method = NULL;
1636 sd_bus *bus = userdata;
1641 polkit_agent_open_if_enabled();
1643 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1645 r = sd_bus_message_new_method_call(
1648 "org.freedesktop.systemd1",
1649 "/org/freedesktop/systemd1",
1650 "org.freedesktop.systemd1.Manager",
1653 return bus_log_create_error(r);
1655 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1657 return bus_log_create_error(r);
1659 r = sd_bus_message_open_container(m, 'a', "s");
1661 return bus_log_create_error(r);
1663 for (i = 1; i < argc; i++) {
1664 _cleanup_free_ char *e = NULL, *unit = NULL;
1666 if (!machine_name_is_valid(argv[i])) {
1667 log_error("Invalid machine name %s.", argv[i]);
1671 e = unit_name_escape(argv[i]);
1675 unit = unit_name_build("systemd-nspawn", e, ".service");
1679 r = sd_bus_message_append(m, "s", unit);
1681 return bus_log_create_error(r);
1684 r = sd_bus_message_close_container(m);
1686 return bus_log_create_error(r);
1688 if (streq(argv[0], "enable"))
1689 r = sd_bus_message_append(m, "bb", false, false);
1691 r = sd_bus_message_append(m, "b", false);
1693 return bus_log_create_error(r);
1695 r = sd_bus_call(bus, m, 0, &error, &reply);
1697 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1701 if (streq(argv[0], "enable")) {
1702 r = sd_bus_message_read(reply, "b", carries_install_info);
1704 return bus_log_parse_error(r);
1707 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1711 m = sd_bus_message_unref(m);
1713 r = sd_bus_message_new_method_call(
1716 "org.freedesktop.systemd1",
1717 "/org/freedesktop/systemd1",
1718 "org.freedesktop.systemd1.Manager",
1721 return bus_log_create_error(r);
1723 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1725 return bus_log_create_error(r);
1727 r = sd_bus_call(bus, m, 0, &error, NULL);
1729 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1736 typedef struct PullContext {
1741 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1742 PullContext *c = userdata;
1750 r = sd_bus_message_read(m, "us", &priority, &line);
1752 bus_log_parse_error(r);
1756 if (!streq_ptr(c->path, sd_bus_message_get_path(m)))
1759 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1762 log_full(priority, "%s", line);
1766 static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1767 PullContext *c = userdata;
1768 const char *path, *result;
1776 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1778 bus_log_parse_error(r);
1782 if (!streq_ptr(c->path, path))
1785 c->result = streq_ptr(result, "done");
1789 static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1790 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1791 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1792 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1802 polkit_agent_open_if_enabled();
1804 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1806 return bus_log_create_error(r);
1808 r = sd_bus_add_match(
1812 "sender='org.freedesktop.import1',"
1813 "interface='org.freedesktop.import1.Manager',"
1814 "member='TransferRemoved',"
1815 "path='/org/freedesktop/import1'",
1816 match_transfer_removed, &c);
1818 return log_error_errno(r, "Failed to install match: %m");
1820 r = sd_bus_add_match(
1824 "sender='org.freedesktop.import1',"
1825 "interface='org.freedesktop.import1.Transfer',"
1826 "member='LogMessage'",
1827 match_log_message, &c);
1829 return log_error_errno(r, "Failed to install match: %m");
1831 r = sd_bus_call(bus, m, 0, &error, &reply);
1833 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1837 r = sd_bus_message_read(reply, "uo", &id, &c.path);
1839 return bus_log_parse_error(r);
1842 r = sd_bus_process(bus, NULL);
1846 /* The match sets this to NULL when we are done */
1850 r = sd_bus_wait(bus, (uint64_t) -1);
1855 return c.result ? 0 : -EINVAL;
1858 static int pull_tar(int argc, char *argv[], void *userdata) {
1859 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1860 _cleanup_free_ char *l = NULL, *ll = NULL;
1861 const char *local, *remote;
1862 sd_bus *bus = userdata;
1868 if (!http_url_is_valid(remote)) {
1869 log_error("URL '%s' is not valid.", remote);
1876 r = import_url_last_component(remote, &l);
1878 return log_error_errno(r, "Failed to get final component of URL: %m");
1883 if (isempty(local) || streq(local, "-"))
1887 r = tar_strip_suffixes(local, &ll);
1889 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1893 if (!machine_name_is_valid(local)) {
1894 log_error("Local name %s is not a suitable machine name.", local);
1899 r = sd_bus_message_new_method_call(
1902 "org.freedesktop.import1",
1903 "/org/freedesktop/import1",
1904 "org.freedesktop.import1.Manager",
1907 return bus_log_create_error(r);
1909 r = sd_bus_message_append(
1914 import_verify_to_string(arg_verify),
1917 return bus_log_create_error(r);
1919 return pull_image_common(bus, m);
1922 static int pull_raw(int argc, char *argv[], void *userdata) {
1923 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1924 _cleanup_free_ char *l = NULL, *ll = NULL;
1925 const char *local, *remote;
1926 sd_bus *bus = userdata;
1932 if (!http_url_is_valid(remote)) {
1933 log_error("URL '%s' is not valid.", remote);
1940 r = import_url_last_component(remote, &l);
1942 return log_error_errno(r, "Failed to get final component of URL: %m");
1947 if (isempty(local) || streq(local, "-"))
1951 r = raw_strip_suffixes(local, &ll);
1953 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1957 if (!machine_name_is_valid(local)) {
1958 log_error("Local name %s is not a suitable machine name.", local);
1963 r = sd_bus_message_new_method_call(
1966 "org.freedesktop.import1",
1967 "/org/freedesktop/import1",
1968 "org.freedesktop.import1.Manager",
1971 return bus_log_create_error(r);
1973 r = sd_bus_message_append(
1978 import_verify_to_string(arg_verify),
1981 return bus_log_create_error(r);
1983 return pull_image_common(bus, m);
1986 static int pull_dkr(int argc, char *argv[], void *userdata) {
1987 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1988 _cleanup_free_ char *l = NULL, *ll = NULL;
1989 const char *local, *remote, *tag;
1990 sd_bus *bus = userdata;
1993 if (!streq_ptr(arg_dkr_index_url, "no")) {
1994 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
1999 tag = strchr(remote, ':');
2001 remote = strndupa(remote, tag - remote);
2005 if (!dkr_name_is_valid(remote)) {
2006 log_error("DKR name '%s' is invalid.", remote);
2009 if (tag && !dkr_tag_is_valid(tag)) {
2010 log_error("DKR tag '%s' is invalid.", remote);
2017 local = strchr(remote, '/');
2024 if (isempty(local) || streq(local, "-"))
2028 if (!machine_name_is_valid(local)) {
2029 log_error("Local name %s is not a suitable machine name.", local);
2034 r = sd_bus_message_new_method_call(
2037 "org.freedesktop.import1",
2038 "/org/freedesktop/import1",
2039 "org.freedesktop.import1.Manager",
2042 return bus_log_create_error(r);
2044 r = sd_bus_message_append(
2051 import_verify_to_string(arg_verify),
2054 return bus_log_create_error(r);
2056 return pull_image_common(bus, m);
2059 typedef struct TransferInfo {
2066 static int compare_transfer_info(const void *a, const void *b) {
2067 const TransferInfo *x = a, *y = b;
2069 return strcmp(x->local, y->local);
2072 static int list_transfers(int argc, char *argv[], void *userdata) {
2073 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2074 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2075 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2076 _cleanup_free_ TransferInfo *transfers = NULL;
2077 size_t n_transfers = 0, n_allocated = 0, j;
2078 const char *type, *remote, *local, *object;
2079 sd_bus *bus = userdata;
2080 uint32_t id, max_id = 0;
2083 pager_open_if_enabled();
2085 r = sd_bus_call_method(
2087 "org.freedesktop.import1",
2088 "/org/freedesktop/import1",
2089 "org.freedesktop.import1.Manager",
2095 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2099 r = sd_bus_message_enter_container(reply, 'a', "(ussso)");
2101 return bus_log_parse_error(r);
2103 while ((r = sd_bus_message_read(reply, "(ussso)", &id, &type, &remote, &local, &object)) > 0) {
2106 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2109 transfers[n_transfers].id = id;
2110 transfers[n_transfers].type = type;
2111 transfers[n_transfers].remote = remote;
2112 transfers[n_transfers].local = local;
2132 return bus_log_parse_error(r);
2134 r = sd_bus_message_exit_container(reply);
2136 return bus_log_parse_error(r);
2138 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2141 printf("%-*s %-*s %-*s %-*s\n",
2142 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2143 (int) max_type, "TYPE",
2144 (int) max_local, "LOCAL",
2145 (int) max_remote, "REMOTE");
2147 for (j = 0; j < n_transfers; j++)
2148 printf("%*" PRIu32 " %-*s %-*s %-*s\n",
2149 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2150 (int) max_type, transfers[j].type,
2151 (int) max_local, transfers[j].local,
2152 (int) max_remote, transfers[j].remote);
2155 printf("\n%zu transfers listed.\n", n_transfers);
2160 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2161 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2162 sd_bus *bus = userdata;
2167 polkit_agent_open_if_enabled();
2169 for (i = 1; i < argc; i++) {
2172 r = safe_atou32(argv[i], &id);
2174 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2176 r = sd_bus_call_method(
2178 "org.freedesktop.import1",
2179 "/org/freedesktop/import1",
2180 "org.freedesktop.import1.Manager",
2186 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2194 static int help(int argc, char *argv[], void *userdata) {
2196 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2197 "Send control commands to or query the virtual machine and container\n"
2198 "registration manager.\n\n"
2199 " -h --help Show this help\n"
2200 " --version Show package version\n"
2201 " --no-pager Do not pipe output into a pager\n"
2202 " --no-legend Do not show the headers and footers\n"
2203 " --no-ask-password Do not ask for system passwords\n"
2204 " -H --host=[USER@]HOST Operate on remote host\n"
2205 " -M --machine=CONTAINER Operate on local container\n"
2206 " -p --property=NAME Show only properties by this name\n"
2207 " -q --quiet Suppress output\n"
2208 " -a --all Show all properties, including empty ones\n"
2209 " -l --full Do not ellipsize output\n"
2210 " --kill-who=WHO Who to send signal to\n"
2211 " -s --signal=SIGNAL Which signal to send\n"
2212 " --read-only Create read-only bind mount\n"
2213 " --mkdir Create directory before bind mounting, if missing\n"
2214 " -n --lines=INTEGER Number of journal entries to show\n"
2215 " -o --output=STRING Change journal output mode (short,\n"
2216 " short-monotonic, verbose, export, json,\n"
2217 " json-pretty, json-sse, cat)\n"
2218 " --verify=MODE Verification mode for downloaded images (no,\n"
2219 " checksum, signature)\n"
2220 " --force Download image even if already exists\n"
2221 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2223 "Machine Commands:\n"
2224 " list List running VMs and containers\n"
2225 " status NAME... Show VM/container details\n"
2226 " show NAME... Show properties of one or more VMs/containers\n"
2227 " start NAME... Start container as a service\n"
2228 " login NAME Get a login prompt on a container\n"
2229 " enable NAME... Enable automatic container start at boot\n"
2230 " disable NAME... Disable automatic container start at boot\n"
2231 " poweroff NAME... Power off one or more containers\n"
2232 " reboot NAME... Reboot one or more containers\n"
2233 " terminate NAME... Terminate one or more VMs/containers\n"
2234 " kill NAME... Send signal to processes of a VM/container\n"
2235 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2236 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2237 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2239 " list-images Show available container annd VM images\n"
2240 " image-status NAME... Show image details\n"
2241 " show-image NAME... Show properties of image\n"
2242 " clone NAME NAME Clone an image\n"
2243 " rename NAME NAME Rename an image\n"
2244 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2245 " remove NAME... Remove an image\n\n"
2246 "Image Transfer Commands:\n"
2247 " pull-tar URL [NAME] Download a TAR container image\n"
2248 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2249 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2250 " list-transfers Show list of downloads in progress\n"
2251 " cancel-transfer Cancel a download\n"
2252 , program_invocation_short_name);
2257 static int parse_argv(int argc, char *argv[]) {
2260 ARG_VERSION = 0x100,
2266 ARG_NO_ASK_PASSWORD,
2272 static const struct option options[] = {
2273 { "help", no_argument, NULL, 'h' },
2274 { "version", no_argument, NULL, ARG_VERSION },
2275 { "property", required_argument, NULL, 'p' },
2276 { "all", no_argument, NULL, 'a' },
2277 { "full", no_argument, NULL, 'l' },
2278 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2279 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2280 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2281 { "signal", required_argument, NULL, 's' },
2282 { "host", required_argument, NULL, 'H' },
2283 { "machine", required_argument, NULL, 'M' },
2284 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2285 { "mkdir", no_argument, NULL, ARG_MKDIR },
2286 { "quiet", no_argument, NULL, 'q' },
2287 { "lines", required_argument, NULL, 'n' },
2288 { "output", required_argument, NULL, 'o' },
2289 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2290 { "verify", required_argument, NULL, ARG_VERIFY },
2291 { "force", no_argument, NULL, ARG_FORCE },
2292 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2301 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2306 return help(0, NULL, NULL);
2309 puts(PACKAGE_STRING);
2310 puts(SYSTEMD_FEATURES);
2314 r = strv_extend(&arg_property, optarg);
2318 /* If the user asked for a particular
2319 * property, show it to him, even if it is
2333 if (safe_atou(optarg, &arg_lines) < 0) {
2334 log_error("Failed to parse lines '%s'", optarg);
2340 arg_output = output_mode_from_string(optarg);
2341 if (arg_output < 0) {
2342 log_error("Unknown output '%s'.", optarg);
2348 arg_no_pager = true;
2356 arg_kill_who = optarg;
2360 arg_signal = signal_from_string_try_harder(optarg);
2361 if (arg_signal < 0) {
2362 log_error("Failed to parse signal string %s.", optarg);
2367 case ARG_NO_ASK_PASSWORD:
2368 arg_ask_password = false;
2372 arg_transport = BUS_TRANSPORT_REMOTE;
2377 arg_transport = BUS_TRANSPORT_MACHINE;
2382 arg_read_only = true;
2394 arg_verify = import_verify_from_string(optarg);
2395 if (arg_verify < 0) {
2396 log_error("Failed to parse --verify= setting: %s", optarg);
2405 case ARG_DKR_INDEX_URL:
2406 if (!http_url_is_valid(optarg)) {
2407 log_error("Index URL is invalid: %s", optarg);
2411 arg_dkr_index_url = optarg;
2418 assert_not_reached("Unhandled option");
2424 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2426 static const Verb verbs[] = {
2427 { "help", VERB_ANY, VERB_ANY, 0, help },
2428 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2429 { "list-images", VERB_ANY, 1, 0, list_images },
2430 { "status", 2, VERB_ANY, 0, show_machine },
2431 { "image-status", 2, VERB_ANY, 0, show_image },
2432 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2433 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2434 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2435 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2436 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2437 { "kill", 2, VERB_ANY, 0, kill_machine },
2438 { "login", 2, 2, 0, login_machine },
2439 { "bind", 3, 4, 0, bind_mount },
2440 { "copy-to", 3, 4, 0, copy_files },
2441 { "copy-from", 3, 4, 0, copy_files },
2442 { "remove", 2, VERB_ANY, 0, remove_image },
2443 { "rename", 3, 3, 0, rename_image },
2444 { "clone", 3, 3, 0, clone_image },
2445 { "read-only", 2, 3, 0, read_only_image },
2446 { "start", 2, VERB_ANY, 0, start_machine },
2447 { "enable", 2, VERB_ANY, 0, enable_machine },
2448 { "disable", 2, VERB_ANY, 0, enable_machine },
2449 { "pull-tar", 2, 3, 0, pull_tar },
2450 { "pull-raw", 2, 3, 0, pull_raw },
2451 { "pull-dkr", 2, 3, 0, pull_dkr },
2452 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2453 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2457 return dispatch_verb(argc, argv, verbs, bus);
2460 int main(int argc, char*argv[]) {
2461 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2464 setlocale(LC_ALL, "");
2465 log_parse_environment();
2468 r = parse_argv(argc, argv);
2472 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2474 log_error_errno(r, "Failed to create bus connection: %m");
2478 r = machinectl_main(argc, argv, bus);
2482 polkit_agent_close();
2484 strv_free(arg_property);
2486 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;