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>
35 /* When we include libgen.h because we need dirname() we immediately
36 * undefine basename() since libgen.h defines it as a macro to the XDG
37 * version which is really broken. */
46 #include "spawn-polkit-agent.h"
48 #include "bus-error.h"
51 #include "unit-name.h"
52 #include "cgroup-show.h"
53 #include "logs-show.h"
54 #include "cgroup-util.h"
56 #include "event-util.h"
57 #include "path-util.h"
61 #include "import-util.h"
63 static char **arg_property = NULL;
64 static bool arg_all = false;
65 static bool arg_full = false;
66 static bool arg_no_pager = false;
67 static bool arg_legend = true;
68 static const char *arg_kill_who = NULL;
69 static int arg_signal = SIGTERM;
70 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
71 static char *arg_host = NULL;
72 static bool arg_read_only = false;
73 static bool arg_mkdir = false;
74 static bool arg_quiet = false;
75 static bool arg_ask_password = true;
76 static unsigned arg_lines = 10;
77 static OutputMode arg_output = OUTPUT_SHORT;
78 static bool arg_force = false;
79 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
80 static const char* arg_dkr_index_url = NULL;
82 static void pager_open_if_enabled(void) {
90 static void polkit_agent_open_if_enabled(void) {
92 /* Open the polkit agent as a child process if necessary */
94 if (!arg_ask_password)
97 if (arg_transport != BUS_TRANSPORT_LOCAL)
103 static OutputFlags get_output_flags(void) {
105 arg_all * OUTPUT_SHOW_ALL |
106 arg_full * OUTPUT_FULL_WIDTH |
107 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
108 on_tty() * OUTPUT_COLOR |
109 !arg_quiet * OUTPUT_WARN_CUTOFF;
112 typedef struct MachineInfo {
118 static int compare_machine_info(const void *a, const void *b) {
119 const MachineInfo *x = a, *y = b;
121 return strcmp(x->name, y->name);
124 static int list_machines(int argc, char *argv[], void *userdata) {
126 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
127 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
128 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
129 _cleanup_free_ MachineInfo *machines = NULL;
130 const char *name, *class, *service, *object;
131 size_t n_machines = 0, n_allocated = 0, j;
132 sd_bus *bus = userdata;
137 pager_open_if_enabled();
139 r = sd_bus_call_method(
141 "org.freedesktop.machine1",
142 "/org/freedesktop/machine1",
143 "org.freedesktop.machine1.Manager",
149 log_error("Could not get machines: %s", bus_error_message(&error, -r));
153 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
155 return bus_log_parse_error(r);
157 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
160 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
163 machines[n_machines].name = name;
164 machines[n_machines].class = class;
165 machines[n_machines].service = service;
182 return bus_log_parse_error(r);
184 r = sd_bus_message_exit_container(reply);
186 return bus_log_parse_error(r);
188 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
191 printf("%-*s %-*s %-*s\n",
192 (int) max_name, "MACHINE",
193 (int) max_class, "CLASS",
194 (int) max_service, "SERVICE");
196 for (j = 0; j < n_machines; j++)
197 printf("%-*s %-*s %-*s\n",
198 (int) max_name, machines[j].name,
199 (int) max_class, machines[j].class,
200 (int) max_service, machines[j].service);
203 printf("\n%zu machines listed.\n", n_machines);
208 typedef struct ImageInfo {
217 static int compare_image_info(const void *a, const void *b) {
218 const ImageInfo *x = a, *y = b;
220 return strcmp(x->name, y->name);
223 static int list_images(int argc, char *argv[], void *userdata) {
225 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
226 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
227 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
228 _cleanup_free_ ImageInfo *images = NULL;
229 size_t n_images = 0, n_allocated = 0, j;
230 const char *name, *type, *object;
231 sd_bus *bus = userdata;
232 uint64_t crtime, mtime, size;
237 pager_open_if_enabled();
239 r = sd_bus_call_method(
241 "org.freedesktop.machine1",
242 "/org/freedesktop/machine1",
243 "org.freedesktop.machine1.Manager",
249 log_error("Could not get images: %s", bus_error_message(&error, -r));
253 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
255 return bus_log_parse_error(r);
257 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
258 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
261 if (name[0] == '.' && !arg_all)
264 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
267 images[n_images].name = name;
268 images[n_images].type = type;
269 images[n_images].read_only = read_only;
270 images[n_images].crtime = crtime;
271 images[n_images].mtime = mtime;
272 images[n_images].size = size;
283 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
289 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
294 if (size != (uint64_t) -1) {
295 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
303 return bus_log_parse_error(r);
305 r = sd_bus_message_exit_container(reply);
307 return bus_log_parse_error(r);
309 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
312 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
313 (int) max_name, "NAME",
314 (int) max_type, "TYPE",
316 (int) max_size, "USAGE",
317 (int) max_crtime, "CREATED",
318 (int) max_mtime, "MODIFIED");
320 for (j = 0; j < n_images; j++) {
321 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
323 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
324 (int) max_name, images[j].name,
325 (int) max_type, images[j].type,
326 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
327 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
328 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
329 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
333 printf("\n%zu images listed.\n", n_images);
338 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
339 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
340 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
341 _cleanup_free_ char *path = NULL;
349 if (arg_transport == BUS_TRANSPORT_REMOTE)
352 path = unit_dbus_path_from_name(unit);
356 r = sd_bus_get_property(
358 "org.freedesktop.systemd1",
360 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
366 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
370 r = sd_bus_message_read(reply, "s", &cgroup);
372 return bus_log_parse_error(r);
377 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
386 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
390 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
391 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
399 r = sd_bus_call_method(bus,
400 "org.freedesktop.machine1",
401 "/org/freedesktop/machine1",
402 "org.freedesktop.machine1.Manager",
403 "GetMachineAddresses",
410 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
412 return bus_log_parse_error(r);
414 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
418 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
420 r = sd_bus_message_read(reply, "i", &family);
422 return bus_log_parse_error(r);
424 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
426 return bus_log_parse_error(r);
428 fputs(prefix, stdout);
429 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
430 if (family == AF_INET6 && ifi > 0)
434 r = sd_bus_message_exit_container(reply);
436 return bus_log_parse_error(r);
438 if (prefix != prefix2)
442 return bus_log_parse_error(r);
444 r = sd_bus_message_exit_container(reply);
446 return bus_log_parse_error(r);
451 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
452 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
453 const char *k, *v, *pretty = NULL;
460 r = sd_bus_call_method(bus,
461 "org.freedesktop.machine1",
462 "/org/freedesktop/machine1",
463 "org.freedesktop.machine1.Manager",
464 "GetMachineOSRelease",
471 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
473 return bus_log_parse_error(r);
475 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
476 if (streq(k, "PRETTY_NAME"))
481 return bus_log_parse_error(r);
483 r = sd_bus_message_exit_container(reply);
485 return bus_log_parse_error(r);
488 printf("%s%s\n", prefix, pretty);
493 typedef struct MachineStatusInfo {
499 char *root_directory;
501 struct dual_timestamp timestamp;
506 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
507 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
508 char since2[FORMAT_TIMESTAMP_MAX], *s2;
514 fputs(strna(i->name), stdout);
516 if (!sd_id128_equal(i->id, SD_ID128_NULL))
517 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
521 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
522 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
525 printf("\t Since: %s; %s\n", s2, s1);
527 printf("\t Since: %s\n", s2);
530 _cleanup_free_ char *t = NULL;
532 printf("\t Leader: %u", (unsigned) i->leader);
534 get_process_comm(i->leader, &t);
542 printf("\t Service: %s", i->service);
545 printf("; class %s", i->class);
549 printf("\t Class: %s\n", i->class);
551 if (i->root_directory)
552 printf("\t Root: %s\n", i->root_directory);
554 if (i->n_netif > 0) {
557 fputs("\t Iface:", stdout);
559 for (c = 0; c < i->n_netif; c++) {
560 char name[IF_NAMESIZE+1] = "";
562 if (if_indextoname(i->netif[c], name)) {
571 printf(" %i", i->netif[c]);
577 print_addresses(bus, i->name, ifi,
581 print_os_release(bus, i->name, "\t OS: ");
584 printf("\t Unit: %s\n", i->unit);
585 show_unit_cgroup(bus, i->unit, i->leader);
587 if (arg_transport == BUS_TRANSPORT_LOCAL) {
589 show_journal_by_unit(
594 i->timestamp.monotonic,
597 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
598 SD_JOURNAL_LOCAL_ONLY,
605 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
606 MachineStatusInfo *i = userdata;
611 assert_cc(sizeof(int32_t) == sizeof(int));
612 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
618 i->n_netif = l / sizeof(int32_t);
619 i->netif = memdup(v, l);
626 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
628 static const struct bus_properties_map map[] = {
629 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
630 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
631 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
632 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
633 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
634 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
635 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
636 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
637 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
638 { "NetworkInterfaces", "ai", map_netif, 0 },
642 MachineStatusInfo info = {};
650 r = bus_map_all_properties(bus,
651 "org.freedesktop.machine1",
656 return log_error_errno(r, "Could not get properties: %m");
662 print_machine_status_info(bus, &info);
668 free(info.root_directory);
674 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
686 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
688 log_error_errno(r, "Could not get properties: %m");
693 static int show_machine(int argc, char *argv[], void *userdata) {
695 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
696 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
697 bool properties, new_line = false;
698 sd_bus *bus = userdata;
703 properties = !strstr(argv[0], "status");
705 pager_open_if_enabled();
707 if (properties && argc <= 1) {
709 /* If no argument is specified, inspect the manager
711 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
716 for (i = 1; i < argc; i++) {
717 const char *path = NULL;
719 r = sd_bus_call_method(
721 "org.freedesktop.machine1",
722 "/org/freedesktop/machine1",
723 "org.freedesktop.machine1.Manager",
729 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
733 r = sd_bus_message_read(reply, "o", &path);
735 return bus_log_parse_error(r);
738 r = show_machine_properties(bus, path, &new_line);
740 r = show_machine_info(argv[0], bus, path, &new_line);
746 typedef struct ImageStatusInfo {
755 uint64_t usage_exclusive;
756 uint64_t limit_exclusive;
759 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
760 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
761 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
762 char bs[FORMAT_BYTES_MAX], *s3;
763 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
769 fputs(i->name, stdout);
774 printf("\t Type: %s\n", i->type);
777 printf("\t Path: %s\n", i->path);
779 printf("\t RO: %s%s%s\n",
780 i->read_only ? ansi_highlight_red() : "",
781 i->read_only ? "read-only" : "writable",
782 i->read_only ? ansi_highlight_off() : "");
784 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
785 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
787 printf("\t Created: %s; %s\n", s2, s1);
789 printf("\t Created: %s\n", s2);
791 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
792 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
794 printf("\tModified: %s; %s\n", s2, s1);
796 printf("\tModified: %s\n", s2);
798 s3 = format_bytes(bs, sizeof(bs), i->usage);
799 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
801 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
803 printf("\t Usage: %s\n", s3);
805 s3 = format_bytes(bs, sizeof(bs), i->limit);
806 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
808 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
810 printf("\t Limit: %s\n", s3);
813 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
815 static const struct bus_properties_map map[] = {
816 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
817 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
818 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
819 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
820 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
821 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
822 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
823 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
824 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
825 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
829 ImageStatusInfo info = {};
837 r = bus_map_all_properties(bus,
838 "org.freedesktop.machine1",
843 return log_error_errno(r, "Could not get properties: %m");
849 print_image_status_info(bus, &info);
858 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
870 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
872 log_error_errno(r, "Could not get properties: %m");
877 static int show_image(int argc, char *argv[], void *userdata) {
879 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
880 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
881 bool properties, new_line = false;
882 sd_bus *bus = userdata;
887 properties = !strstr(argv[0], "status");
889 pager_open_if_enabled();
891 if (properties && argc <= 1) {
893 /* If no argument is specified, inspect the manager
895 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
900 for (i = 1; i < argc; i++) {
901 const char *path = NULL;
903 r = sd_bus_call_method(
905 "org.freedesktop.machine1",
906 "/org/freedesktop/machine1",
907 "org.freedesktop.machine1.Manager",
913 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
917 r = sd_bus_message_read(reply, "o", &path);
919 return bus_log_parse_error(r);
922 r = show_image_properties(bus, path, &new_line);
924 r = show_image_info(argv[0], bus, path, &new_line);
930 static int kill_machine(int argc, char *argv[], void *userdata) {
931 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
932 sd_bus *bus = userdata;
937 polkit_agent_open_if_enabled();
940 arg_kill_who = "all";
942 for (i = 1; i < argc; i++) {
945 r = sd_bus_call_method(
947 "org.freedesktop.machine1",
948 "/org/freedesktop/machine1",
949 "org.freedesktop.machine1.Manager",
953 "ssi", argv[i], arg_kill_who, arg_signal);
955 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
963 static int reboot_machine(int argc, char *argv[], void *userdata) {
964 arg_kill_who = "leader";
965 arg_signal = SIGINT; /* sysvinit + systemd */
967 return kill_machine(argc, argv, userdata);
970 static int poweroff_machine(int argc, char *argv[], void *userdata) {
971 arg_kill_who = "leader";
972 arg_signal = SIGRTMIN+4; /* only systemd */
974 return kill_machine(argc, argv, userdata);
977 static int terminate_machine(int argc, char *argv[], void *userdata) {
978 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
979 sd_bus *bus = userdata;
984 polkit_agent_open_if_enabled();
986 for (i = 1; i < argc; i++) {
989 r = sd_bus_call_method(
991 "org.freedesktop.machine1",
992 "/org/freedesktop/machine1",
993 "org.freedesktop.machine1.Manager",
999 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
1007 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
1008 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1009 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
1018 r = sd_bus_call_method(
1020 "org.freedesktop.machine1",
1021 "/org/freedesktop/machine1",
1022 "org.freedesktop.machine1.Manager",
1028 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
1032 r = sd_bus_message_read(reply, "o", &object);
1034 return bus_log_parse_error(r);
1036 r = sd_bus_get_property(
1038 "org.freedesktop.machine1",
1040 "org.freedesktop.machine1.Machine",
1046 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
1048 r = sd_bus_message_read(reply2, "u", &leader);
1050 return bus_log_parse_error(r);
1056 static int copy_files(int argc, char *argv[], void *userdata) {
1057 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
1058 _cleanup_close_ int hostfd = -1;
1059 sd_bus *bus = userdata;
1060 pid_t child, leader;
1067 copy_from = streq(argv[0], "copy-from");
1068 dest = argv[3] ?: argv[2];
1069 host_path = strdupa(copy_from ? dest : argv[2]);
1070 container_path = strdupa(copy_from ? argv[2] : dest);
1072 if (!path_is_absolute(container_path)) {
1073 log_error("Container path not absolute.");
1077 t = strdupa(host_path);
1078 host_basename = basename(t);
1079 host_dirname = dirname(host_path);
1081 t = strdupa(container_path);
1082 container_basename = basename(t);
1083 container_dirname = dirname(container_path);
1085 r = machine_get_leader(bus, argv[1], &leader);
1089 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1091 return log_error_errno(errno, "Failed to open source directory: %m");
1095 return log_error_errno(errno, "Failed to fork(): %m");
1102 q = procfs_file_alloca(leader, "ns/mnt");
1103 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1105 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1106 _exit(EXIT_FAILURE);
1109 if (setns(mntfd, CLONE_NEWNS) < 0) {
1110 log_error_errno(errno, "Failed to join namespace of leader: %m");
1111 _exit(EXIT_FAILURE);
1114 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1115 if (containerfd < 0) {
1116 log_error_errno(errno, "Failed top open destination directory: %m");
1117 _exit(EXIT_FAILURE);
1121 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1123 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1125 log_error_errno(errno, "Failed to copy tree: %m");
1126 _exit(EXIT_FAILURE);
1129 _exit(EXIT_SUCCESS);
1132 r = wait_for_terminate(child, &si);
1134 return log_error_errno(r, "Failed to wait for client: %m");
1135 if (si.si_code != CLD_EXITED) {
1136 log_error("Client died abnormally.");
1139 if (si.si_status != EXIT_SUCCESS)
1145 static int bind_mount(int argc, char *argv[], void *userdata) {
1146 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1147 sd_bus *bus = userdata;
1148 pid_t child, leader;
1151 bool mount_slave_created = false, mount_slave_mounted = false,
1152 mount_tmp_created = false, mount_tmp_mounted = false,
1153 mount_outside_created = false, mount_outside_mounted = false;
1158 /* One day, when bind mounting /proc/self/fd/n works across
1159 * namespace boundaries we should rework this logic to make
1162 dest = argv[3] ?: argv[2];
1163 if (!path_is_absolute(dest)) {
1164 log_error("Destination path not absolute.");
1168 p = strjoina("/run/systemd/nspawn/propagate/", argv[1], "/");
1169 if (access(p, F_OK) < 0) {
1170 log_error("Container does not allow propagation of mount points.");
1174 r = machine_get_leader(bus, argv[1], &leader);
1178 /* Our goal is to install a new bind mount into the container,
1179 possibly read-only. This is irritatingly complex
1180 unfortunately, currently.
1182 First, we start by creating a private playground in /tmp,
1183 that we can mount MS_SLAVE. (Which is necessary, since
1184 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1187 if (!mkdtemp(mount_slave))
1188 return log_error_errno(errno, "Failed to create playground: %m");
1190 mount_slave_created = true;
1192 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1193 r = log_error_errno(errno, "Failed to make bind mount: %m");
1197 mount_slave_mounted = true;
1199 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1200 r = log_error_errno(errno, "Failed to remount slave: %m");
1204 /* Second, we mount the source directory to a directory inside
1205 of our MS_SLAVE playground. */
1206 mount_tmp = strjoina(mount_slave, "/mount");
1207 if (mkdir(mount_tmp, 0700) < 0) {
1208 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1212 mount_tmp_created = true;
1214 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1215 r = log_error_errno(errno, "Failed to overmount: %m");
1219 mount_tmp_mounted = true;
1221 /* Third, we remount the new bind mount read-only if requested. */
1223 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1224 r = log_error_errno(errno, "Failed to mark read-only: %m");
1228 /* Fourth, we move the new bind mount into the propagation
1229 * directory. This way it will appear there read-only
1232 mount_outside = strjoina("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1233 if (!mkdtemp(mount_outside)) {
1234 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1238 mount_outside_created = true;
1240 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1241 r = log_error_errno(errno, "Failed to move: %m");
1245 mount_outside_mounted = true;
1246 mount_tmp_mounted = false;
1248 (void) rmdir(mount_tmp);
1249 mount_tmp_created = false;
1251 (void) umount(mount_slave);
1252 mount_slave_mounted = false;
1254 (void) rmdir(mount_slave);
1255 mount_slave_created = false;
1259 r = log_error_errno(errno, "Failed to fork(): %m");
1264 const char *mount_inside;
1268 q = procfs_file_alloca(leader, "ns/mnt");
1269 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1271 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1272 _exit(EXIT_FAILURE);
1275 if (setns(mntfd, CLONE_NEWNS) < 0) {
1276 log_error_errno(errno, "Failed to join namespace of leader: %m");
1277 _exit(EXIT_FAILURE);
1281 mkdir_p(dest, 0755);
1283 /* Fifth, move the mount to the right place inside */
1284 mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside));
1285 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1286 log_error_errno(errno, "Failed to mount: %m");
1287 _exit(EXIT_FAILURE);
1290 _exit(EXIT_SUCCESS);
1293 r = wait_for_terminate(child, &si);
1295 log_error_errno(r, "Failed to wait for client: %m");
1298 if (si.si_code != CLD_EXITED) {
1299 log_error("Client died abnormally.");
1303 if (si.si_status != EXIT_SUCCESS) {
1311 if (mount_outside_mounted)
1312 umount(mount_outside);
1313 if (mount_outside_created)
1314 rmdir(mount_outside);
1316 if (mount_tmp_mounted)
1318 if (mount_tmp_created)
1321 if (mount_slave_mounted)
1322 umount(mount_slave);
1323 if (mount_slave_created)
1324 umount(mount_slave);
1329 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1330 PTYForward ** forward = (PTYForward**) userdata;
1338 /* If the forwarder is already initialized, tell it to
1339 * exit on the next vhangup(), so that we still flush
1340 * out what might be queued and exit then. */
1342 r = pty_forward_set_ignore_vhangup(*forward, false);
1346 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1349 /* On error, or when the forwarder is not initialized yet, quit immediately */
1350 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1354 static int login_machine(int argc, char *argv[], void *userdata) {
1355 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1356 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1357 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1358 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1359 _cleanup_event_unref_ sd_event *event = NULL;
1360 int master = -1, r, ret = 0;
1361 sd_bus *bus = userdata;
1362 const char *pty, *match;
1368 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1369 arg_transport != BUS_TRANSPORT_MACHINE) {
1370 log_error("Login only supported on local machines.");
1374 polkit_agent_open_if_enabled();
1376 r = sd_event_default(&event);
1378 return log_error_errno(r, "Failed to get event loop: %m");
1380 r = sd_bus_attach_event(bus, event, 0);
1382 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1384 match = strjoina("type='signal',"
1385 "sender='org.freedesktop.machine1',"
1386 "path='/org/freedesktop/machine1',",
1387 "interface='org.freedesktop.machine1.Manager',"
1388 "member='MachineRemoved',"
1393 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1395 return log_error_errno(r, "Failed to add machine removal match: %m");
1397 r = sd_bus_message_new_method_call(bus,
1399 "org.freedesktop.machine1",
1400 "/org/freedesktop/machine1",
1401 "org.freedesktop.machine1.Manager",
1402 "OpenMachineLogin");
1404 return bus_log_create_error(r);
1406 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1408 return bus_log_create_error(r);
1410 r = sd_bus_message_append(m, "s", argv[1]);
1412 return bus_log_create_error(r);
1414 r = sd_bus_call(bus, m, 0, &error, &reply);
1416 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1420 r = sd_bus_message_read(reply, "hs", &master, &pty);
1422 return bus_log_parse_error(r);
1424 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1426 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1428 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1429 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1431 r = pty_forward_new(event, master, true, &forward);
1433 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1435 r = sd_event_loop(event);
1437 return log_error_errno(r, "Failed to run event loop: %m");
1439 pty_forward_get_last_char(forward, &last_char);
1440 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1442 forward = pty_forward_free(forward);
1444 if (last_char != '\n')
1445 fputc('\n', stdout);
1448 log_info("Machine %s terminated.", argv[1]);
1450 log_info("Connection to machine %s terminated.", argv[1]);
1452 sd_event_get_exit_code(event, &ret);
1456 static int remove_image(int argc, char *argv[], void *userdata) {
1457 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1458 sd_bus *bus = userdata;
1463 polkit_agent_open_if_enabled();
1465 for (i = 1; i < argc; i++) {
1466 r = sd_bus_call_method(
1468 "org.freedesktop.machine1",
1469 "/org/freedesktop/machine1",
1470 "org.freedesktop.machine1.Manager",
1476 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1484 static int rename_image(int argc, char *argv[], void *userdata) {
1485 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1486 sd_bus *bus = userdata;
1489 polkit_agent_open_if_enabled();
1491 r = sd_bus_call_method(
1493 "org.freedesktop.machine1",
1494 "/org/freedesktop/machine1",
1495 "org.freedesktop.machine1.Manager",
1499 "ss", argv[1], argv[2]);
1501 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1508 static int clone_image(int argc, char *argv[], void *userdata) {
1509 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1510 sd_bus *bus = userdata;
1513 polkit_agent_open_if_enabled();
1515 r = sd_bus_call_method(
1517 "org.freedesktop.machine1",
1518 "/org/freedesktop/machine1",
1519 "org.freedesktop.machine1.Manager",
1523 "ssb", argv[1], argv[2], arg_read_only);
1525 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1532 static int read_only_image(int argc, char *argv[], void *userdata) {
1533 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1534 sd_bus *bus = userdata;
1538 b = parse_boolean(argv[2]);
1540 log_error("Failed to parse boolean argument: %s", argv[2]);
1545 polkit_agent_open_if_enabled();
1547 r = sd_bus_call_method(
1549 "org.freedesktop.machine1",
1550 "/org/freedesktop/machine1",
1551 "org.freedesktop.machine1.Manager",
1552 "MarkImageReadOnly",
1557 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1564 static int start_machine(int argc, char *argv[], void *userdata) {
1565 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1566 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1567 sd_bus *bus = userdata;
1572 polkit_agent_open_if_enabled();
1574 r = bus_wait_for_jobs_new(bus, &w);
1578 for (i = 1; i < argc; i++) {
1579 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1580 _cleanup_free_ char *e = NULL, *unit = NULL;
1583 if (!machine_name_is_valid(argv[i])) {
1584 log_error("Invalid machine name %s.", argv[i]);
1588 e = unit_name_escape(argv[i]);
1592 unit = unit_name_build("systemd-nspawn", e, ".service");
1596 r = sd_bus_message_new_method_call(
1599 "org.freedesktop.systemd1",
1600 "/org/freedesktop/systemd1",
1601 "org.freedesktop.systemd1.Manager",
1604 return bus_log_create_error(r);
1606 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1608 return bus_log_create_error(r);
1610 r = sd_bus_message_append(m, "ss", unit, "fail");
1612 return bus_log_create_error(r);
1614 r = sd_bus_call(bus, m, 0, &error, &reply);
1616 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1620 r = sd_bus_message_read(reply, "o", &object);
1622 return bus_log_parse_error(r);
1624 r = bus_wait_for_jobs_add(w, object);
1629 r = bus_wait_for_jobs(w, arg_quiet);
1636 static int enable_machine(int argc, char *argv[], void *userdata) {
1637 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1638 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1639 int carries_install_info = 0;
1640 const char *method = NULL;
1641 sd_bus *bus = userdata;
1646 polkit_agent_open_if_enabled();
1648 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1650 r = sd_bus_message_new_method_call(
1653 "org.freedesktop.systemd1",
1654 "/org/freedesktop/systemd1",
1655 "org.freedesktop.systemd1.Manager",
1658 return bus_log_create_error(r);
1660 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1662 return bus_log_create_error(r);
1664 r = sd_bus_message_open_container(m, 'a', "s");
1666 return bus_log_create_error(r);
1668 for (i = 1; i < argc; i++) {
1669 _cleanup_free_ char *e = NULL, *unit = NULL;
1671 if (!machine_name_is_valid(argv[i])) {
1672 log_error("Invalid machine name %s.", argv[i]);
1676 e = unit_name_escape(argv[i]);
1680 unit = unit_name_build("systemd-nspawn", e, ".service");
1684 r = sd_bus_message_append(m, "s", unit);
1686 return bus_log_create_error(r);
1689 r = sd_bus_message_close_container(m);
1691 return bus_log_create_error(r);
1693 if (streq(argv[0], "enable"))
1694 r = sd_bus_message_append(m, "bb", false, false);
1696 r = sd_bus_message_append(m, "b", false);
1698 return bus_log_create_error(r);
1700 r = sd_bus_call(bus, m, 0, &error, &reply);
1702 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1706 if (streq(argv[0], "enable")) {
1707 r = sd_bus_message_read(reply, "b", carries_install_info);
1709 return bus_log_parse_error(r);
1712 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1716 m = sd_bus_message_unref(m);
1718 r = sd_bus_message_new_method_call(
1721 "org.freedesktop.systemd1",
1722 "/org/freedesktop/systemd1",
1723 "org.freedesktop.systemd1.Manager",
1726 return bus_log_create_error(r);
1728 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1730 return bus_log_create_error(r);
1732 r = sd_bus_call(bus, m, 0, &error, NULL);
1734 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1741 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1742 const char **our_path = userdata, *line;
1750 r = sd_bus_message_read(m, "us", &priority, &line);
1752 bus_log_parse_error(r);
1756 if (!streq_ptr(*our_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 const char **our_path = userdata, *path, *result;
1775 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1777 bus_log_parse_error(r);
1781 if (!streq_ptr(*our_path, path))
1784 sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1788 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1793 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1795 sd_event_exit(sd_event_source_get_event(s), EINTR);
1799 static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1800 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1801 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1802 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1803 _cleanup_event_unref_ sd_event* event = NULL;
1804 const char *path = NULL;
1811 polkit_agent_open_if_enabled();
1813 r = sd_event_default(&event);
1815 return log_error_errno(r, "Failed to get event loop: %m");
1817 r = sd_bus_attach_event(bus, event, 0);
1819 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1821 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1823 return bus_log_create_error(r);
1825 r = sd_bus_add_match(
1829 "sender='org.freedesktop.import1',"
1830 "interface='org.freedesktop.import1.Manager',"
1831 "member='TransferRemoved',"
1832 "path='/org/freedesktop/import1'",
1833 match_transfer_removed, &path);
1835 return log_error_errno(r, "Failed to install match: %m");
1837 r = sd_bus_add_match(
1841 "sender='org.freedesktop.import1',"
1842 "interface='org.freedesktop.import1.Transfer',"
1843 "member='LogMessage'",
1844 match_log_message, &path);
1846 return log_error_errno(r, "Failed to install match: %m");
1848 r = sd_bus_call(bus, m, 0, &error, &reply);
1850 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1854 r = sd_bus_message_read(reply, "uo", &id, &path);
1856 return bus_log_parse_error(r);
1858 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1861 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1863 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1864 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1866 r = sd_event_loop(event);
1868 return log_error_errno(r, "Failed to run event loop: %m");
1873 static int pull_tar(int argc, char *argv[], void *userdata) {
1874 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1875 _cleanup_free_ char *l = NULL, *ll = NULL;
1876 const char *local, *remote;
1877 sd_bus *bus = userdata;
1883 if (!http_url_is_valid(remote)) {
1884 log_error("URL '%s' is not valid.", remote);
1891 r = import_url_last_component(remote, &l);
1893 return log_error_errno(r, "Failed to get final component of URL: %m");
1898 if (isempty(local) || streq(local, "-"))
1902 r = tar_strip_suffixes(local, &ll);
1904 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1908 if (!machine_name_is_valid(local)) {
1909 log_error("Local name %s is not a suitable machine name.", local);
1914 r = sd_bus_message_new_method_call(
1917 "org.freedesktop.import1",
1918 "/org/freedesktop/import1",
1919 "org.freedesktop.import1.Manager",
1922 return bus_log_create_error(r);
1924 r = sd_bus_message_append(
1929 import_verify_to_string(arg_verify),
1932 return bus_log_create_error(r);
1934 return pull_image_common(bus, m);
1937 static int pull_raw(int argc, char *argv[], void *userdata) {
1938 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1939 _cleanup_free_ char *l = NULL, *ll = NULL;
1940 const char *local, *remote;
1941 sd_bus *bus = userdata;
1947 if (!http_url_is_valid(remote)) {
1948 log_error("URL '%s' is not valid.", remote);
1955 r = import_url_last_component(remote, &l);
1957 return log_error_errno(r, "Failed to get final component of URL: %m");
1962 if (isempty(local) || streq(local, "-"))
1966 r = raw_strip_suffixes(local, &ll);
1968 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1972 if (!machine_name_is_valid(local)) {
1973 log_error("Local name %s is not a suitable machine name.", local);
1978 r = sd_bus_message_new_method_call(
1981 "org.freedesktop.import1",
1982 "/org/freedesktop/import1",
1983 "org.freedesktop.import1.Manager",
1986 return bus_log_create_error(r);
1988 r = sd_bus_message_append(
1993 import_verify_to_string(arg_verify),
1996 return bus_log_create_error(r);
1998 return pull_image_common(bus, m);
2001 static int pull_dkr(int argc, char *argv[], void *userdata) {
2002 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2003 const char *local, *remote, *tag;
2004 sd_bus *bus = userdata;
2007 if (arg_verify != IMPORT_VERIFY_NO) {
2008 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2013 tag = strchr(remote, ':');
2015 remote = strndupa(remote, tag - remote);
2019 if (!dkr_name_is_valid(remote)) {
2020 log_error("DKR name '%s' is invalid.", remote);
2023 if (tag && !dkr_tag_is_valid(tag)) {
2024 log_error("DKR tag '%s' is invalid.", remote);
2031 local = strchr(remote, '/');
2038 if (isempty(local) || streq(local, "-"))
2042 if (!machine_name_is_valid(local)) {
2043 log_error("Local name %s is not a suitable machine name.", local);
2048 r = sd_bus_message_new_method_call(
2051 "org.freedesktop.import1",
2052 "/org/freedesktop/import1",
2053 "org.freedesktop.import1.Manager",
2056 return bus_log_create_error(r);
2058 r = sd_bus_message_append(
2065 import_verify_to_string(arg_verify),
2068 return bus_log_create_error(r);
2070 return pull_image_common(bus, m);
2073 typedef struct TransferInfo {
2081 static int compare_transfer_info(const void *a, const void *b) {
2082 const TransferInfo *x = a, *y = b;
2084 return strcmp(x->local, y->local);
2087 static int list_transfers(int argc, char *argv[], void *userdata) {
2088 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2089 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2090 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2091 _cleanup_free_ TransferInfo *transfers = NULL;
2092 size_t n_transfers = 0, n_allocated = 0, j;
2093 const char *type, *remote, *local, *object;
2094 sd_bus *bus = userdata;
2095 uint32_t id, max_id = 0;
2099 pager_open_if_enabled();
2101 r = sd_bus_call_method(
2103 "org.freedesktop.import1",
2104 "/org/freedesktop/import1",
2105 "org.freedesktop.import1.Manager",
2111 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2115 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2117 return bus_log_parse_error(r);
2119 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2122 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2125 transfers[n_transfers].id = id;
2126 transfers[n_transfers].type = type;
2127 transfers[n_transfers].remote = remote;
2128 transfers[n_transfers].local = local;
2129 transfers[n_transfers].progress = progress;
2149 return bus_log_parse_error(r);
2151 r = sd_bus_message_exit_container(reply);
2153 return bus_log_parse_error(r);
2155 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2158 printf("%-*s %-*s %-*s %-*s %-*s\n",
2159 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2161 (int) max_type, "TYPE",
2162 (int) max_local, "LOCAL",
2163 (int) max_remote, "REMOTE");
2165 for (j = 0; j < n_transfers; j++)
2166 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2167 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2168 (int) 6, (unsigned) (transfers[j].progress * 100),
2169 (int) max_type, transfers[j].type,
2170 (int) max_local, transfers[j].local,
2171 (int) max_remote, transfers[j].remote);
2174 printf("\n%zu transfers listed.\n", n_transfers);
2179 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2180 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2181 sd_bus *bus = userdata;
2186 polkit_agent_open_if_enabled();
2188 for (i = 1; i < argc; i++) {
2191 r = safe_atou32(argv[i], &id);
2193 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2195 r = sd_bus_call_method(
2197 "org.freedesktop.import1",
2198 "/org/freedesktop/import1",
2199 "org.freedesktop.import1.Manager",
2205 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2213 static int help(int argc, char *argv[], void *userdata) {
2215 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2216 "Send control commands to or query the virtual machine and container\n"
2217 "registration manager.\n\n"
2218 " -h --help Show this help\n"
2219 " --version Show package version\n"
2220 " --no-pager Do not pipe output into a pager\n"
2221 " --no-legend Do not show the headers and footers\n"
2222 " --no-ask-password Do not ask for system passwords\n"
2223 " -H --host=[USER@]HOST Operate on remote host\n"
2224 " -M --machine=CONTAINER Operate on local container\n"
2225 " -p --property=NAME Show only properties by this name\n"
2226 " -q --quiet Suppress output\n"
2227 " -a --all Show all properties, including empty ones\n"
2228 " -l --full Do not ellipsize output\n"
2229 " --kill-who=WHO Who to send signal to\n"
2230 " -s --signal=SIGNAL Which signal to send\n"
2231 " --read-only Create read-only bind mount\n"
2232 " --mkdir Create directory before bind mounting, if missing\n"
2233 " -n --lines=INTEGER Number of journal entries to show\n"
2234 " -o --output=STRING Change journal output mode (short,\n"
2235 " short-monotonic, verbose, export, json,\n"
2236 " json-pretty, json-sse, cat)\n"
2237 " --verify=MODE Verification mode for downloaded images (no,\n"
2238 " checksum, signature)\n"
2239 " --force Download image even if already exists\n"
2240 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2242 "Machine Commands:\n"
2243 " list List running VMs and containers\n"
2244 " status NAME... Show VM/container details\n"
2245 " show NAME... Show properties of one or more VMs/containers\n"
2246 " start NAME... Start container as a service\n"
2247 " login NAME Get a login prompt on a container\n"
2248 " enable NAME... Enable automatic container start at boot\n"
2249 " disable NAME... Disable automatic container start at boot\n"
2250 " poweroff NAME... Power off one or more containers\n"
2251 " reboot NAME... Reboot one or more containers\n"
2252 " terminate NAME... Terminate one or more VMs/containers\n"
2253 " kill NAME... Send signal to processes of a VM/container\n"
2254 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2255 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2256 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2258 " list-images Show available container and VM images\n"
2259 " image-status NAME... Show image details\n"
2260 " show-image NAME... Show properties of image\n"
2261 " clone NAME NAME Clone an image\n"
2262 " rename NAME NAME Rename an image\n"
2263 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2264 " remove NAME... Remove an image\n\n"
2265 "Image Transfer Commands:\n"
2266 " pull-tar URL [NAME] Download a TAR container image\n"
2267 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2268 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2269 " list-transfers Show list of downloads in progress\n"
2270 " cancel-transfer Cancel a download\n"
2271 , program_invocation_short_name);
2276 static int parse_argv(int argc, char *argv[]) {
2279 ARG_VERSION = 0x100,
2285 ARG_NO_ASK_PASSWORD,
2291 static const struct option options[] = {
2292 { "help", no_argument, NULL, 'h' },
2293 { "version", no_argument, NULL, ARG_VERSION },
2294 { "property", required_argument, NULL, 'p' },
2295 { "all", no_argument, NULL, 'a' },
2296 { "full", no_argument, NULL, 'l' },
2297 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2298 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2299 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2300 { "signal", required_argument, NULL, 's' },
2301 { "host", required_argument, NULL, 'H' },
2302 { "machine", required_argument, NULL, 'M' },
2303 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2304 { "mkdir", no_argument, NULL, ARG_MKDIR },
2305 { "quiet", no_argument, NULL, 'q' },
2306 { "lines", required_argument, NULL, 'n' },
2307 { "output", required_argument, NULL, 'o' },
2308 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2309 { "verify", required_argument, NULL, ARG_VERIFY },
2310 { "force", no_argument, NULL, ARG_FORCE },
2311 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2320 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2325 return help(0, NULL, NULL);
2328 puts(PACKAGE_STRING);
2329 puts(SYSTEMD_FEATURES);
2333 r = strv_extend(&arg_property, optarg);
2337 /* If the user asked for a particular
2338 * property, show it to him, even if it is
2352 if (safe_atou(optarg, &arg_lines) < 0) {
2353 log_error("Failed to parse lines '%s'", optarg);
2359 arg_output = output_mode_from_string(optarg);
2360 if (arg_output < 0) {
2361 log_error("Unknown output '%s'.", optarg);
2367 arg_no_pager = true;
2375 arg_kill_who = optarg;
2379 arg_signal = signal_from_string_try_harder(optarg);
2380 if (arg_signal < 0) {
2381 log_error("Failed to parse signal string %s.", optarg);
2386 case ARG_NO_ASK_PASSWORD:
2387 arg_ask_password = false;
2391 arg_transport = BUS_TRANSPORT_REMOTE;
2396 arg_transport = BUS_TRANSPORT_MACHINE;
2401 arg_read_only = true;
2413 arg_verify = import_verify_from_string(optarg);
2414 if (arg_verify < 0) {
2415 log_error("Failed to parse --verify= setting: %s", optarg);
2424 case ARG_DKR_INDEX_URL:
2425 if (!http_url_is_valid(optarg)) {
2426 log_error("Index URL is invalid: %s", optarg);
2430 arg_dkr_index_url = optarg;
2437 assert_not_reached("Unhandled option");
2443 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2445 static const Verb verbs[] = {
2446 { "help", VERB_ANY, VERB_ANY, 0, help },
2447 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2448 { "list-images", VERB_ANY, 1, 0, list_images },
2449 { "status", 2, VERB_ANY, 0, show_machine },
2450 { "image-status", 2, VERB_ANY, 0, show_image },
2451 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2452 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2453 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2454 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2455 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2456 { "kill", 2, VERB_ANY, 0, kill_machine },
2457 { "login", 2, 2, 0, login_machine },
2458 { "bind", 3, 4, 0, bind_mount },
2459 { "copy-to", 3, 4, 0, copy_files },
2460 { "copy-from", 3, 4, 0, copy_files },
2461 { "remove", 2, VERB_ANY, 0, remove_image },
2462 { "rename", 3, 3, 0, rename_image },
2463 { "clone", 3, 3, 0, clone_image },
2464 { "read-only", 2, 3, 0, read_only_image },
2465 { "start", 2, VERB_ANY, 0, start_machine },
2466 { "enable", 2, VERB_ANY, 0, enable_machine },
2467 { "disable", 2, VERB_ANY, 0, enable_machine },
2468 { "pull-tar", 2, 3, 0, pull_tar },
2469 { "pull-raw", 2, 3, 0, pull_raw },
2470 { "pull-dkr", 2, 3, 0, pull_dkr },
2471 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2472 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2476 return dispatch_verb(argc, argv, verbs, bus);
2479 int main(int argc, char*argv[]) {
2480 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2483 setlocale(LC_ALL, "");
2484 log_parse_environment();
2487 r = parse_argv(argc, argv);
2491 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2493 log_error_errno(r, "Failed to create bus connection: %m");
2497 r = machinectl_main(argc, argv, bus);
2501 polkit_agent_close();
2503 strv_free(arg_property);
2505 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;