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 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1737 const char **our_path = userdata, *line;
1745 r = sd_bus_message_read(m, "us", &priority, &line);
1747 bus_log_parse_error(r);
1751 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1754 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1757 log_full(priority, "%s", line);
1761 static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1762 const char **our_path = userdata, *path, *result;
1770 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1772 bus_log_parse_error(r);
1776 if (!streq_ptr(*our_path, path))
1779 sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1783 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1788 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1790 sd_event_exit(sd_event_source_get_event(s), EINTR);
1794 static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1795 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1796 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1797 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1798 _cleanup_event_unref_ sd_event* event = NULL;
1799 const char *path = NULL;
1806 polkit_agent_open_if_enabled();
1808 r = sd_event_default(&event);
1810 return log_error_errno(r, "Failed to get event loop: %m");
1812 r = sd_bus_attach_event(bus, event, 0);
1814 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1816 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1818 return bus_log_create_error(r);
1820 r = sd_bus_add_match(
1824 "sender='org.freedesktop.import1',"
1825 "interface='org.freedesktop.import1.Manager',"
1826 "member='TransferRemoved',"
1827 "path='/org/freedesktop/import1'",
1828 match_transfer_removed, &path);
1830 return log_error_errno(r, "Failed to install match: %m");
1832 r = sd_bus_add_match(
1836 "sender='org.freedesktop.import1',"
1837 "interface='org.freedesktop.import1.Transfer',"
1838 "member='LogMessage'",
1839 match_log_message, &path);
1841 return log_error_errno(r, "Failed to install match: %m");
1843 r = sd_bus_call(bus, m, 0, &error, &reply);
1845 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1849 r = sd_bus_message_read(reply, "uo", &id, &path);
1851 return bus_log_parse_error(r);
1853 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1856 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1858 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1859 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1861 r = sd_event_loop(event);
1863 return log_error_errno(r, "Failed to run event loop: %m");
1868 static int pull_tar(int argc, char *argv[], void *userdata) {
1869 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1870 _cleanup_free_ char *l = NULL, *ll = NULL;
1871 const char *local, *remote;
1872 sd_bus *bus = userdata;
1878 if (!http_url_is_valid(remote)) {
1879 log_error("URL '%s' is not valid.", remote);
1886 r = import_url_last_component(remote, &l);
1888 return log_error_errno(r, "Failed to get final component of URL: %m");
1893 if (isempty(local) || streq(local, "-"))
1897 r = tar_strip_suffixes(local, &ll);
1899 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1903 if (!machine_name_is_valid(local)) {
1904 log_error("Local name %s is not a suitable machine name.", local);
1909 r = sd_bus_message_new_method_call(
1912 "org.freedesktop.import1",
1913 "/org/freedesktop/import1",
1914 "org.freedesktop.import1.Manager",
1917 return bus_log_create_error(r);
1919 r = sd_bus_message_append(
1924 import_verify_to_string(arg_verify),
1927 return bus_log_create_error(r);
1929 return pull_image_common(bus, m);
1932 static int pull_raw(int argc, char *argv[], void *userdata) {
1933 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1934 _cleanup_free_ char *l = NULL, *ll = NULL;
1935 const char *local, *remote;
1936 sd_bus *bus = userdata;
1942 if (!http_url_is_valid(remote)) {
1943 log_error("URL '%s' is not valid.", remote);
1950 r = import_url_last_component(remote, &l);
1952 return log_error_errno(r, "Failed to get final component of URL: %m");
1957 if (isempty(local) || streq(local, "-"))
1961 r = raw_strip_suffixes(local, &ll);
1963 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1967 if (!machine_name_is_valid(local)) {
1968 log_error("Local name %s is not a suitable machine name.", local);
1973 r = sd_bus_message_new_method_call(
1976 "org.freedesktop.import1",
1977 "/org/freedesktop/import1",
1978 "org.freedesktop.import1.Manager",
1981 return bus_log_create_error(r);
1983 r = sd_bus_message_append(
1988 import_verify_to_string(arg_verify),
1991 return bus_log_create_error(r);
1993 return pull_image_common(bus, m);
1996 static int pull_dkr(int argc, char *argv[], void *userdata) {
1997 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1998 _cleanup_free_ char *l = NULL, *ll = NULL;
1999 const char *local, *remote, *tag;
2000 sd_bus *bus = userdata;
2003 if (arg_verify != IMPORT_VERIFY_NO) {
2004 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2009 tag = strchr(remote, ':');
2011 remote = strndupa(remote, tag - remote);
2015 if (!dkr_name_is_valid(remote)) {
2016 log_error("DKR name '%s' is invalid.", remote);
2019 if (tag && !dkr_tag_is_valid(tag)) {
2020 log_error("DKR tag '%s' is invalid.", remote);
2027 local = strchr(remote, '/');
2034 if (isempty(local) || streq(local, "-"))
2038 if (!machine_name_is_valid(local)) {
2039 log_error("Local name %s is not a suitable machine name.", local);
2044 r = sd_bus_message_new_method_call(
2047 "org.freedesktop.import1",
2048 "/org/freedesktop/import1",
2049 "org.freedesktop.import1.Manager",
2052 return bus_log_create_error(r);
2054 r = sd_bus_message_append(
2061 import_verify_to_string(arg_verify),
2064 return bus_log_create_error(r);
2066 return pull_image_common(bus, m);
2069 typedef struct TransferInfo {
2077 static int compare_transfer_info(const void *a, const void *b) {
2078 const TransferInfo *x = a, *y = b;
2080 return strcmp(x->local, y->local);
2083 static int list_transfers(int argc, char *argv[], void *userdata) {
2084 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2085 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2086 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2087 _cleanup_free_ TransferInfo *transfers = NULL;
2088 size_t n_transfers = 0, n_allocated = 0, j;
2089 const char *type, *remote, *local, *object;
2090 sd_bus *bus = userdata;
2091 uint32_t id, max_id = 0;
2095 pager_open_if_enabled();
2097 r = sd_bus_call_method(
2099 "org.freedesktop.import1",
2100 "/org/freedesktop/import1",
2101 "org.freedesktop.import1.Manager",
2107 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2111 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2113 return bus_log_parse_error(r);
2115 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2118 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2121 transfers[n_transfers].id = id;
2122 transfers[n_transfers].type = type;
2123 transfers[n_transfers].remote = remote;
2124 transfers[n_transfers].local = local;
2125 transfers[n_transfers].progress = progress;
2145 return bus_log_parse_error(r);
2147 r = sd_bus_message_exit_container(reply);
2149 return bus_log_parse_error(r);
2151 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2154 printf("%-*s %-*s %-*s %-*s %-*s\n",
2155 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2157 (int) max_type, "TYPE",
2158 (int) max_local, "LOCAL",
2159 (int) max_remote, "REMOTE");
2161 for (j = 0; j < n_transfers; j++)
2162 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2163 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2164 (int) 6, (unsigned) (transfers[j].progress * 100),
2165 (int) max_type, transfers[j].type,
2166 (int) max_local, transfers[j].local,
2167 (int) max_remote, transfers[j].remote);
2170 printf("\n%zu transfers listed.\n", n_transfers);
2175 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2176 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2177 sd_bus *bus = userdata;
2182 polkit_agent_open_if_enabled();
2184 for (i = 1; i < argc; i++) {
2187 r = safe_atou32(argv[i], &id);
2189 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2191 r = sd_bus_call_method(
2193 "org.freedesktop.import1",
2194 "/org/freedesktop/import1",
2195 "org.freedesktop.import1.Manager",
2201 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2209 static int help(int argc, char *argv[], void *userdata) {
2211 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2212 "Send control commands to or query the virtual machine and container\n"
2213 "registration manager.\n\n"
2214 " -h --help Show this help\n"
2215 " --version Show package version\n"
2216 " --no-pager Do not pipe output into a pager\n"
2217 " --no-legend Do not show the headers and footers\n"
2218 " --no-ask-password Do not ask for system passwords\n"
2219 " -H --host=[USER@]HOST Operate on remote host\n"
2220 " -M --machine=CONTAINER Operate on local container\n"
2221 " -p --property=NAME Show only properties by this name\n"
2222 " -q --quiet Suppress output\n"
2223 " -a --all Show all properties, including empty ones\n"
2224 " -l --full Do not ellipsize output\n"
2225 " --kill-who=WHO Who to send signal to\n"
2226 " -s --signal=SIGNAL Which signal to send\n"
2227 " --read-only Create read-only bind mount\n"
2228 " --mkdir Create directory before bind mounting, if missing\n"
2229 " -n --lines=INTEGER Number of journal entries to show\n"
2230 " -o --output=STRING Change journal output mode (short,\n"
2231 " short-monotonic, verbose, export, json,\n"
2232 " json-pretty, json-sse, cat)\n"
2233 " --verify=MODE Verification mode for downloaded images (no,\n"
2234 " checksum, signature)\n"
2235 " --force Download image even if already exists\n"
2236 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2238 "Machine Commands:\n"
2239 " list List running VMs and containers\n"
2240 " status NAME... Show VM/container details\n"
2241 " show NAME... Show properties of one or more VMs/containers\n"
2242 " start NAME... Start container as a service\n"
2243 " login NAME Get a login prompt on a container\n"
2244 " enable NAME... Enable automatic container start at boot\n"
2245 " disable NAME... Disable automatic container start at boot\n"
2246 " poweroff NAME... Power off one or more containers\n"
2247 " reboot NAME... Reboot one or more containers\n"
2248 " terminate NAME... Terminate one or more VMs/containers\n"
2249 " kill NAME... Send signal to processes of a VM/container\n"
2250 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2251 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2252 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2254 " list-images Show available container and VM images\n"
2255 " image-status NAME... Show image details\n"
2256 " show-image NAME... Show properties of image\n"
2257 " clone NAME NAME Clone an image\n"
2258 " rename NAME NAME Rename an image\n"
2259 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2260 " remove NAME... Remove an image\n\n"
2261 "Image Transfer Commands:\n"
2262 " pull-tar URL [NAME] Download a TAR container image\n"
2263 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2264 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2265 " list-transfers Show list of downloads in progress\n"
2266 " cancel-transfer Cancel a download\n"
2267 , program_invocation_short_name);
2272 static int parse_argv(int argc, char *argv[]) {
2275 ARG_VERSION = 0x100,
2281 ARG_NO_ASK_PASSWORD,
2287 static const struct option options[] = {
2288 { "help", no_argument, NULL, 'h' },
2289 { "version", no_argument, NULL, ARG_VERSION },
2290 { "property", required_argument, NULL, 'p' },
2291 { "all", no_argument, NULL, 'a' },
2292 { "full", no_argument, NULL, 'l' },
2293 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2294 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2295 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2296 { "signal", required_argument, NULL, 's' },
2297 { "host", required_argument, NULL, 'H' },
2298 { "machine", required_argument, NULL, 'M' },
2299 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2300 { "mkdir", no_argument, NULL, ARG_MKDIR },
2301 { "quiet", no_argument, NULL, 'q' },
2302 { "lines", required_argument, NULL, 'n' },
2303 { "output", required_argument, NULL, 'o' },
2304 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2305 { "verify", required_argument, NULL, ARG_VERIFY },
2306 { "force", no_argument, NULL, ARG_FORCE },
2307 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2316 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2321 return help(0, NULL, NULL);
2324 puts(PACKAGE_STRING);
2325 puts(SYSTEMD_FEATURES);
2329 r = strv_extend(&arg_property, optarg);
2333 /* If the user asked for a particular
2334 * property, show it to him, even if it is
2348 if (safe_atou(optarg, &arg_lines) < 0) {
2349 log_error("Failed to parse lines '%s'", optarg);
2355 arg_output = output_mode_from_string(optarg);
2356 if (arg_output < 0) {
2357 log_error("Unknown output '%s'.", optarg);
2363 arg_no_pager = true;
2371 arg_kill_who = optarg;
2375 arg_signal = signal_from_string_try_harder(optarg);
2376 if (arg_signal < 0) {
2377 log_error("Failed to parse signal string %s.", optarg);
2382 case ARG_NO_ASK_PASSWORD:
2383 arg_ask_password = false;
2387 arg_transport = BUS_TRANSPORT_REMOTE;
2392 arg_transport = BUS_TRANSPORT_MACHINE;
2397 arg_read_only = true;
2409 arg_verify = import_verify_from_string(optarg);
2410 if (arg_verify < 0) {
2411 log_error("Failed to parse --verify= setting: %s", optarg);
2420 case ARG_DKR_INDEX_URL:
2421 if (!http_url_is_valid(optarg)) {
2422 log_error("Index URL is invalid: %s", optarg);
2426 arg_dkr_index_url = optarg;
2433 assert_not_reached("Unhandled option");
2439 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2441 static const Verb verbs[] = {
2442 { "help", VERB_ANY, VERB_ANY, 0, help },
2443 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2444 { "list-images", VERB_ANY, 1, 0, list_images },
2445 { "status", 2, VERB_ANY, 0, show_machine },
2446 { "image-status", 2, VERB_ANY, 0, show_image },
2447 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2448 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2449 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2450 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2451 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2452 { "kill", 2, VERB_ANY, 0, kill_machine },
2453 { "login", 2, 2, 0, login_machine },
2454 { "bind", 3, 4, 0, bind_mount },
2455 { "copy-to", 3, 4, 0, copy_files },
2456 { "copy-from", 3, 4, 0, copy_files },
2457 { "remove", 2, VERB_ANY, 0, remove_image },
2458 { "rename", 3, 3, 0, rename_image },
2459 { "clone", 3, 3, 0, clone_image },
2460 { "read-only", 2, 3, 0, read_only_image },
2461 { "start", 2, VERB_ANY, 0, start_machine },
2462 { "enable", 2, VERB_ANY, 0, enable_machine },
2463 { "disable", 2, VERB_ANY, 0, enable_machine },
2464 { "pull-tar", 2, 3, 0, pull_tar },
2465 { "pull-raw", 2, 3, 0, pull_raw },
2466 { "pull-dkr", 2, 3, 0, pull_dkr },
2467 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2468 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2472 return dispatch_verb(argc, argv, verbs, bus);
2475 int main(int argc, char*argv[]) {
2476 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2479 setlocale(LC_ALL, "");
2480 log_parse_environment();
2483 r = parse_argv(argc, argv);
2487 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2489 log_error_errno(r, "Failed to create bus connection: %m");
2493 r = machinectl_main(argc, argv, bus);
2497 polkit_agent_close();
2499 strv_free(arg_property);
2501 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;