1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
32 #include <sys/mount.h>
39 #include "spawn-polkit-agent.h"
41 #include "bus-error.h"
44 #include "unit-name.h"
45 #include "cgroup-show.h"
46 #include "logs-show.h"
47 #include "cgroup-util.h"
49 #include "event-util.h"
50 #include "path-util.h"
54 #include "import-util.h"
56 static char **arg_property = NULL;
57 static bool arg_all = false;
58 static bool arg_full = false;
59 static bool arg_no_pager = false;
60 static bool arg_legend = true;
61 static const char *arg_kill_who = NULL;
62 static int arg_signal = SIGTERM;
63 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
64 static char *arg_host = NULL;
65 static bool arg_read_only = false;
66 static bool arg_mkdir = false;
67 static bool arg_quiet = false;
68 static bool arg_ask_password = true;
69 static unsigned arg_lines = 10;
70 static OutputMode arg_output = OUTPUT_SHORT;
71 static bool arg_force = false;
72 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
73 static const char* arg_dkr_index_url = NULL;
74 static const char* arg_format = NULL;
76 static void pager_open_if_enabled(void) {
84 static void polkit_agent_open_if_enabled(void) {
86 /* Open the polkit agent as a child process if necessary */
88 if (!arg_ask_password)
91 if (arg_transport != BUS_TRANSPORT_LOCAL)
97 static OutputFlags get_output_flags(void) {
99 arg_all * OUTPUT_SHOW_ALL |
100 arg_full * OUTPUT_FULL_WIDTH |
101 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
102 on_tty() * OUTPUT_COLOR |
103 !arg_quiet * OUTPUT_WARN_CUTOFF;
106 typedef struct MachineInfo {
112 static int compare_machine_info(const void *a, const void *b) {
113 const MachineInfo *x = a, *y = b;
115 return strcmp(x->name, y->name);
118 static int list_machines(int argc, char *argv[], void *userdata) {
120 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
121 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
122 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
123 _cleanup_free_ MachineInfo *machines = NULL;
124 const char *name, *class, *service, *object;
125 size_t n_machines = 0, n_allocated = 0, j;
126 sd_bus *bus = userdata;
131 pager_open_if_enabled();
133 r = sd_bus_call_method(
135 "org.freedesktop.machine1",
136 "/org/freedesktop/machine1",
137 "org.freedesktop.machine1.Manager",
143 log_error("Could not get machines: %s", bus_error_message(&error, -r));
147 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
149 return bus_log_parse_error(r);
151 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
154 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
157 machines[n_machines].name = name;
158 machines[n_machines].class = class;
159 machines[n_machines].service = service;
176 return bus_log_parse_error(r);
178 r = sd_bus_message_exit_container(reply);
180 return bus_log_parse_error(r);
182 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
185 printf("%-*s %-*s %-*s\n",
186 (int) max_name, "MACHINE",
187 (int) max_class, "CLASS",
188 (int) max_service, "SERVICE");
190 for (j = 0; j < n_machines; j++)
191 printf("%-*s %-*s %-*s\n",
192 (int) max_name, machines[j].name,
193 (int) max_class, machines[j].class,
194 (int) max_service, machines[j].service);
197 printf("\n%zu machines listed.\n", n_machines);
202 typedef struct ImageInfo {
211 static int compare_image_info(const void *a, const void *b) {
212 const ImageInfo *x = a, *y = b;
214 return strcmp(x->name, y->name);
217 static int list_images(int argc, char *argv[], void *userdata) {
219 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
220 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
221 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
222 _cleanup_free_ ImageInfo *images = NULL;
223 size_t n_images = 0, n_allocated = 0, j;
224 const char *name, *type, *object;
225 sd_bus *bus = userdata;
226 uint64_t crtime, mtime, size;
231 pager_open_if_enabled();
233 r = sd_bus_call_method(
235 "org.freedesktop.machine1",
236 "/org/freedesktop/machine1",
237 "org.freedesktop.machine1.Manager",
243 log_error("Could not get images: %s", bus_error_message(&error, -r));
247 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
249 return bus_log_parse_error(r);
251 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
252 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
255 if (name[0] == '.' && !arg_all)
258 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
261 images[n_images].name = name;
262 images[n_images].type = type;
263 images[n_images].read_only = read_only;
264 images[n_images].crtime = crtime;
265 images[n_images].mtime = mtime;
266 images[n_images].size = size;
277 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
283 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
288 if (size != (uint64_t) -1) {
289 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
297 return bus_log_parse_error(r);
299 r = sd_bus_message_exit_container(reply);
301 return bus_log_parse_error(r);
303 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
306 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
307 (int) max_name, "NAME",
308 (int) max_type, "TYPE",
310 (int) max_size, "USAGE",
311 (int) max_crtime, "CREATED",
312 (int) max_mtime, "MODIFIED");
314 for (j = 0; j < n_images; j++) {
315 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
317 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
318 (int) max_name, images[j].name,
319 (int) max_type, images[j].type,
320 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
321 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
322 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
323 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
327 printf("\n%zu images listed.\n", n_images);
332 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
333 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
335 _cleanup_free_ char *path = NULL;
343 if (arg_transport == BUS_TRANSPORT_REMOTE)
346 path = unit_dbus_path_from_name(unit);
350 r = sd_bus_get_property(
352 "org.freedesktop.systemd1",
354 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
360 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
364 r = sd_bus_message_read(reply, "s", &cgroup);
366 return bus_log_parse_error(r);
371 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
380 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
384 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
385 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
393 r = sd_bus_call_method(bus,
394 "org.freedesktop.machine1",
395 "/org/freedesktop/machine1",
396 "org.freedesktop.machine1.Manager",
397 "GetMachineAddresses",
404 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
406 return bus_log_parse_error(r);
408 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
412 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
414 r = sd_bus_message_read(reply, "i", &family);
416 return bus_log_parse_error(r);
418 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
420 return bus_log_parse_error(r);
422 fputs(prefix, stdout);
423 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
424 if (family == AF_INET6 && ifi > 0)
428 r = sd_bus_message_exit_container(reply);
430 return bus_log_parse_error(r);
432 if (prefix != prefix2)
436 return bus_log_parse_error(r);
438 r = sd_bus_message_exit_container(reply);
440 return bus_log_parse_error(r);
445 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
446 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
447 const char *k, *v, *pretty = NULL;
454 r = sd_bus_call_method(bus,
455 "org.freedesktop.machine1",
456 "/org/freedesktop/machine1",
457 "org.freedesktop.machine1.Manager",
458 "GetMachineOSRelease",
465 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
467 return bus_log_parse_error(r);
469 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
470 if (streq(k, "PRETTY_NAME"))
475 return bus_log_parse_error(r);
477 r = sd_bus_message_exit_container(reply);
479 return bus_log_parse_error(r);
482 printf("%s%s\n", prefix, pretty);
487 typedef struct MachineStatusInfo {
493 char *root_directory;
495 struct dual_timestamp timestamp;
500 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
501 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
502 char since2[FORMAT_TIMESTAMP_MAX], *s2;
508 fputs(strna(i->name), stdout);
510 if (!sd_id128_equal(i->id, SD_ID128_NULL))
511 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
515 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
516 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
519 printf("\t Since: %s; %s\n", s2, s1);
521 printf("\t Since: %s\n", s2);
524 _cleanup_free_ char *t = NULL;
526 printf("\t Leader: %u", (unsigned) i->leader);
528 get_process_comm(i->leader, &t);
536 printf("\t Service: %s", i->service);
539 printf("; class %s", i->class);
543 printf("\t Class: %s\n", i->class);
545 if (i->root_directory)
546 printf("\t Root: %s\n", i->root_directory);
548 if (i->n_netif > 0) {
551 fputs("\t Iface:", stdout);
553 for (c = 0; c < i->n_netif; c++) {
554 char name[IF_NAMESIZE+1] = "";
556 if (if_indextoname(i->netif[c], name)) {
565 printf(" %i", i->netif[c]);
571 print_addresses(bus, i->name, ifi,
575 print_os_release(bus, i->name, "\t OS: ");
578 printf("\t Unit: %s\n", i->unit);
579 show_unit_cgroup(bus, i->unit, i->leader);
581 if (arg_transport == BUS_TRANSPORT_LOCAL) {
583 show_journal_by_unit(
588 i->timestamp.monotonic,
591 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
592 SD_JOURNAL_LOCAL_ONLY,
599 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
600 MachineStatusInfo *i = userdata;
605 assert_cc(sizeof(int32_t) == sizeof(int));
606 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
612 i->n_netif = l / sizeof(int32_t);
613 i->netif = memdup(v, l);
620 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
622 static const struct bus_properties_map map[] = {
623 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
624 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
625 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
626 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
627 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
628 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
629 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
630 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
631 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
632 { "NetworkInterfaces", "ai", map_netif, 0 },
636 MachineStatusInfo info = {};
644 r = bus_map_all_properties(bus,
645 "org.freedesktop.machine1",
650 return log_error_errno(r, "Could not get properties: %m");
656 print_machine_status_info(bus, &info);
662 free(info.root_directory);
668 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
680 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
682 log_error_errno(r, "Could not get properties: %m");
687 static int show_machine(int argc, char *argv[], void *userdata) {
689 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
690 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
691 bool properties, new_line = false;
692 sd_bus *bus = userdata;
697 properties = !strstr(argv[0], "status");
699 pager_open_if_enabled();
701 if (properties && argc <= 1) {
703 /* If no argument is specified, inspect the manager
705 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
710 for (i = 1; i < argc; i++) {
711 const char *path = NULL;
713 r = sd_bus_call_method(
715 "org.freedesktop.machine1",
716 "/org/freedesktop/machine1",
717 "org.freedesktop.machine1.Manager",
723 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
727 r = sd_bus_message_read(reply, "o", &path);
729 return bus_log_parse_error(r);
732 r = show_machine_properties(bus, path, &new_line);
734 r = show_machine_info(argv[0], bus, path, &new_line);
740 typedef struct ImageStatusInfo {
749 uint64_t usage_exclusive;
750 uint64_t limit_exclusive;
753 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
754 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
755 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
756 char bs[FORMAT_BYTES_MAX], *s3;
757 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
763 fputs(i->name, stdout);
768 printf("\t Type: %s\n", i->type);
771 printf("\t Path: %s\n", i->path);
773 printf("\t RO: %s%s%s\n",
774 i->read_only ? ansi_highlight_red() : "",
775 i->read_only ? "read-only" : "writable",
776 i->read_only ? ansi_highlight_off() : "");
778 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
779 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
781 printf("\t Created: %s; %s\n", s2, s1);
783 printf("\t Created: %s\n", s2);
785 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
786 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
788 printf("\tModified: %s; %s\n", s2, s1);
790 printf("\tModified: %s\n", s2);
792 s3 = format_bytes(bs, sizeof(bs), i->usage);
793 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
795 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
797 printf("\t Usage: %s\n", s3);
799 s3 = format_bytes(bs, sizeof(bs), i->limit);
800 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
802 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
804 printf("\t Limit: %s\n", s3);
807 static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
809 static const struct bus_properties_map map[] = {
810 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
811 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
812 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
813 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
814 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
815 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
816 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
817 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
818 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
819 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
823 ImageStatusInfo info = {};
830 r = bus_map_all_properties(bus,
831 "org.freedesktop.machine1",
836 return log_error_errno(r, "Could not get properties: %m");
842 print_image_status_info(bus, &info);
851 typedef struct PoolStatusInfo {
857 static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
858 char bs[FORMAT_BYTES_MAX], *s;
861 printf("\t Path: %s\n", i->path);
863 s = format_bytes(bs, sizeof(bs), i->usage);
865 printf("\t Usage: %s\n", s);
867 s = format_bytes(bs, sizeof(bs), i->limit);
869 printf("\t Limit: %s\n", s);
872 static int show_pool_info(sd_bus *bus) {
874 static const struct bus_properties_map map[] = {
875 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
876 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
877 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
881 PoolStatusInfo info = {
882 .usage = (uint64_t) -1,
883 .limit = (uint64_t) -1,
889 r = bus_map_all_properties(bus,
890 "org.freedesktop.machine1",
891 "/org/freedesktop/machine1",
895 return log_error_errno(r, "Could not get properties: %m");
897 print_pool_status_info(bus, &info);
904 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
916 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
918 log_error_errno(r, "Could not get properties: %m");
923 static int show_image(int argc, char *argv[], void *userdata) {
925 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
926 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
927 bool properties, new_line = false;
928 sd_bus *bus = userdata;
933 properties = !strstr(argv[0], "status");
935 pager_open_if_enabled();
939 /* If no argument is specified, inspect the manager
943 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
945 r = show_pool_info(bus);
950 for (i = 1; i < argc; i++) {
951 const char *path = NULL;
953 r = sd_bus_call_method(
955 "org.freedesktop.machine1",
956 "/org/freedesktop/machine1",
957 "org.freedesktop.machine1.Manager",
963 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
967 r = sd_bus_message_read(reply, "o", &path);
969 return bus_log_parse_error(r);
972 r = show_image_properties(bus, path, &new_line);
974 r = show_image_info(bus, path, &new_line);
980 static int kill_machine(int argc, char *argv[], void *userdata) {
981 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
982 sd_bus *bus = userdata;
987 polkit_agent_open_if_enabled();
990 arg_kill_who = "all";
992 for (i = 1; i < argc; i++) {
993 r = sd_bus_call_method(
995 "org.freedesktop.machine1",
996 "/org/freedesktop/machine1",
997 "org.freedesktop.machine1.Manager",
1001 "ssi", argv[i], arg_kill_who, arg_signal);
1003 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1011 static int reboot_machine(int argc, char *argv[], void *userdata) {
1012 arg_kill_who = "leader";
1013 arg_signal = SIGINT; /* sysvinit + systemd */
1015 return kill_machine(argc, argv, userdata);
1018 static int poweroff_machine(int argc, char *argv[], void *userdata) {
1019 arg_kill_who = "leader";
1020 arg_signal = SIGRTMIN+4; /* only systemd */
1022 return kill_machine(argc, argv, userdata);
1025 static int terminate_machine(int argc, char *argv[], void *userdata) {
1026 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1027 sd_bus *bus = userdata;
1032 polkit_agent_open_if_enabled();
1034 for (i = 1; i < argc; i++) {
1035 r = sd_bus_call_method(
1037 "org.freedesktop.machine1",
1038 "/org/freedesktop/machine1",
1039 "org.freedesktop.machine1.Manager",
1045 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
1053 static int copy_files(int argc, char *argv[], void *userdata) {
1054 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1055 sd_bus *bus = userdata;
1061 polkit_agent_open_if_enabled();
1063 copy_from = streq(argv[0], "copy-from");
1065 r = sd_bus_call_method(
1067 "org.freedesktop.machine1",
1068 "/org/freedesktop/machine1",
1069 "org.freedesktop.machine1.Manager",
1070 copy_from ? "CopyFromMachine" : "CopyToMachine",
1078 log_error("Failed to copy: %s", bus_error_message(&error, -r));
1085 static int bind_mount(int argc, char *argv[], void *userdata) {
1086 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1087 sd_bus *bus = userdata;
1092 polkit_agent_open_if_enabled();
1094 r = sd_bus_call_method(
1096 "org.freedesktop.machine1",
1097 "/org/freedesktop/machine1",
1098 "org.freedesktop.machine1.Manager",
1109 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1116 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1117 PTYForward ** forward = (PTYForward**) userdata;
1125 /* If the forwarder is already initialized, tell it to
1126 * exit on the next vhangup(), so that we still flush
1127 * out what might be queued and exit then. */
1129 r = pty_forward_set_ignore_vhangup(*forward, false);
1133 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1136 /* On error, or when the forwarder is not initialized yet, quit immediately */
1137 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1141 static int login_machine(int argc, char *argv[], void *userdata) {
1142 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1143 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1144 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1145 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1146 _cleanup_event_unref_ sd_event *event = NULL;
1147 int master = -1, r, ret = 0;
1148 sd_bus *bus = userdata;
1149 const char *pty, *match;
1155 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1156 arg_transport != BUS_TRANSPORT_MACHINE) {
1157 log_error("Login only supported on local machines.");
1161 polkit_agent_open_if_enabled();
1163 r = sd_event_default(&event);
1165 return log_error_errno(r, "Failed to get event loop: %m");
1167 r = sd_bus_attach_event(bus, event, 0);
1169 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1171 match = strjoina("type='signal',"
1172 "sender='org.freedesktop.machine1',"
1173 "path='/org/freedesktop/machine1',",
1174 "interface='org.freedesktop.machine1.Manager',"
1175 "member='MachineRemoved',"
1180 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1182 return log_error_errno(r, "Failed to add machine removal match: %m");
1184 r = sd_bus_call_method(
1186 "org.freedesktop.machine1",
1187 "/org/freedesktop/machine1",
1188 "org.freedesktop.machine1.Manager",
1194 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1198 r = sd_bus_message_read(reply, "hs", &master, &pty);
1200 return bus_log_parse_error(r);
1202 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1204 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1206 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1207 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1209 r = pty_forward_new(event, master, true, false, &forward);
1211 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1213 r = sd_event_loop(event);
1215 return log_error_errno(r, "Failed to run event loop: %m");
1217 pty_forward_get_last_char(forward, &last_char);
1218 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1220 forward = pty_forward_free(forward);
1222 if (last_char != '\n')
1223 fputc('\n', stdout);
1226 log_info("Machine %s terminated.", argv[1]);
1228 log_info("Connection to machine %s terminated.", argv[1]);
1230 sd_event_get_exit_code(event, &ret);
1234 static int remove_image(int argc, char *argv[], void *userdata) {
1235 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1236 sd_bus *bus = userdata;
1241 polkit_agent_open_if_enabled();
1243 for (i = 1; i < argc; i++) {
1244 r = sd_bus_call_method(
1246 "org.freedesktop.machine1",
1247 "/org/freedesktop/machine1",
1248 "org.freedesktop.machine1.Manager",
1254 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1262 static int rename_image(int argc, char *argv[], void *userdata) {
1263 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1264 sd_bus *bus = userdata;
1267 polkit_agent_open_if_enabled();
1269 r = sd_bus_call_method(
1271 "org.freedesktop.machine1",
1272 "/org/freedesktop/machine1",
1273 "org.freedesktop.machine1.Manager",
1277 "ss", argv[1], argv[2]);
1279 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1286 static int clone_image(int argc, char *argv[], void *userdata) {
1287 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1288 sd_bus *bus = userdata;
1291 polkit_agent_open_if_enabled();
1293 r = sd_bus_call_method(
1295 "org.freedesktop.machine1",
1296 "/org/freedesktop/machine1",
1297 "org.freedesktop.machine1.Manager",
1301 "ssb", argv[1], argv[2], arg_read_only);
1303 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1310 static int read_only_image(int argc, char *argv[], void *userdata) {
1311 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1312 sd_bus *bus = userdata;
1316 b = parse_boolean(argv[2]);
1318 log_error("Failed to parse boolean argument: %s", argv[2]);
1323 polkit_agent_open_if_enabled();
1325 r = sd_bus_call_method(
1327 "org.freedesktop.machine1",
1328 "/org/freedesktop/machine1",
1329 "org.freedesktop.machine1.Manager",
1330 "MarkImageReadOnly",
1335 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1342 static int start_machine(int argc, char *argv[], void *userdata) {
1343 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1344 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1345 sd_bus *bus = userdata;
1350 polkit_agent_open_if_enabled();
1352 r = bus_wait_for_jobs_new(bus, &w);
1356 for (i = 1; i < argc; i++) {
1357 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1358 _cleanup_free_ char *e = NULL, *unit = NULL;
1361 if (!machine_name_is_valid(argv[i])) {
1362 log_error("Invalid machine name %s.", argv[i]);
1366 e = unit_name_escape(argv[i]);
1370 unit = unit_name_build("systemd-nspawn", e, ".service");
1374 r = sd_bus_call_method(
1376 "org.freedesktop.systemd1",
1377 "/org/freedesktop/systemd1",
1378 "org.freedesktop.systemd1.Manager",
1382 "ss", unit, "fail");
1384 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1388 r = sd_bus_message_read(reply, "o", &object);
1390 return bus_log_parse_error(r);
1392 r = bus_wait_for_jobs_add(w, object);
1397 r = bus_wait_for_jobs(w, arg_quiet);
1404 static int enable_machine(int argc, char *argv[], void *userdata) {
1405 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1406 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1407 int carries_install_info = 0;
1408 const char *method = NULL;
1409 sd_bus *bus = userdata;
1414 polkit_agent_open_if_enabled();
1416 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1418 r = sd_bus_message_new_method_call(
1421 "org.freedesktop.systemd1",
1422 "/org/freedesktop/systemd1",
1423 "org.freedesktop.systemd1.Manager",
1426 return bus_log_create_error(r);
1428 r = sd_bus_message_open_container(m, 'a', "s");
1430 return bus_log_create_error(r);
1432 for (i = 1; i < argc; i++) {
1433 _cleanup_free_ char *e = NULL, *unit = NULL;
1435 if (!machine_name_is_valid(argv[i])) {
1436 log_error("Invalid machine name %s.", argv[i]);
1440 e = unit_name_escape(argv[i]);
1444 unit = unit_name_build("systemd-nspawn", e, ".service");
1448 r = sd_bus_message_append(m, "s", unit);
1450 return bus_log_create_error(r);
1453 r = sd_bus_message_close_container(m);
1455 return bus_log_create_error(r);
1457 if (streq(argv[0], "enable"))
1458 r = sd_bus_message_append(m, "bb", false, false);
1460 r = sd_bus_message_append(m, "b", false);
1462 return bus_log_create_error(r);
1464 r = sd_bus_call(bus, m, 0, &error, &reply);
1466 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1470 if (streq(argv[0], "enable")) {
1471 r = sd_bus_message_read(reply, "b", carries_install_info);
1473 return bus_log_parse_error(r);
1476 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1480 r = sd_bus_call_method(
1482 "org.freedesktop.systemd1",
1483 "/org/freedesktop/systemd1",
1484 "org.freedesktop.systemd1.Manager",
1490 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1497 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1498 const char **our_path = userdata, *line;
1506 r = sd_bus_message_read(m, "us", &priority, &line);
1508 bus_log_parse_error(r);
1512 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1515 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1518 log_full(priority, "%s", line);
1522 static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1523 const char **our_path = userdata, *path, *result;
1531 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1533 bus_log_parse_error(r);
1537 if (!streq_ptr(*our_path, path))
1540 sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1544 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1549 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1551 sd_event_exit(sd_event_source_get_event(s), EINTR);
1555 static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
1556 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1557 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1558 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1559 _cleanup_event_unref_ sd_event* event = NULL;
1560 const char *path = NULL;
1567 polkit_agent_open_if_enabled();
1569 r = sd_event_default(&event);
1571 return log_error_errno(r, "Failed to get event loop: %m");
1573 r = sd_bus_attach_event(bus, event, 0);
1575 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1577 r = sd_bus_add_match(
1581 "sender='org.freedesktop.import1',"
1582 "interface='org.freedesktop.import1.Manager',"
1583 "member='TransferRemoved',"
1584 "path='/org/freedesktop/import1'",
1585 match_transfer_removed, &path);
1587 return log_error_errno(r, "Failed to install match: %m");
1589 r = sd_bus_add_match(
1593 "sender='org.freedesktop.import1',"
1594 "interface='org.freedesktop.import1.Transfer',"
1595 "member='LogMessage'",
1596 match_log_message, &path);
1598 return log_error_errno(r, "Failed to install match: %m");
1600 r = sd_bus_call(bus, m, 0, &error, &reply);
1602 log_error("Failed transfer image: %s", bus_error_message(&error, -r));
1606 r = sd_bus_message_read(reply, "uo", &id, &path);
1608 return bus_log_parse_error(r);
1610 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1613 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1615 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1616 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1618 r = sd_event_loop(event);
1620 return log_error_errno(r, "Failed to run event loop: %m");
1625 static int import_tar(int argc, char *argv[], void *userdata) {
1626 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1627 _cleanup_free_ char *ll = NULL;
1628 _cleanup_close_ int fd = -1;
1629 const char *local = NULL, *path = NULL;
1630 sd_bus *bus = userdata;
1637 if (isempty(path) || streq(path, "-"))
1643 local = basename(path);
1644 if (isempty(local) || streq(local, "-"))
1648 log_error("Need either path or local name.");
1652 r = tar_strip_suffixes(local, &ll);
1658 if (!machine_name_is_valid(local)) {
1659 log_error("Local name %s is not a suitable machine name.", local);
1664 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1666 return log_error_errno(errno, "Failed to open %s: %m", path);
1669 r = sd_bus_message_new_method_call(
1672 "org.freedesktop.import1",
1673 "/org/freedesktop/import1",
1674 "org.freedesktop.import1.Manager",
1677 return bus_log_create_error(r);
1679 r = sd_bus_message_append(
1682 fd >= 0 ? fd : STDIN_FILENO,
1687 return bus_log_create_error(r);
1689 return transfer_image_common(bus, m);
1692 static int import_raw(int argc, char *argv[], void *userdata) {
1693 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1694 _cleanup_free_ char *ll = NULL;
1695 _cleanup_close_ int fd = -1;
1696 const char *local = NULL, *path = NULL;
1697 sd_bus *bus = userdata;
1704 if (isempty(path) || streq(path, "-"))
1710 local = basename(path);
1711 if (isempty(local) || streq(local, "-"))
1715 log_error("Need either path or local name.");
1719 r = raw_strip_suffixes(local, &ll);
1725 if (!machine_name_is_valid(local)) {
1726 log_error("Local name %s is not a suitable machine name.", local);
1731 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1733 return log_error_errno(errno, "Failed to open %s: %m", path);
1736 r = sd_bus_message_new_method_call(
1739 "org.freedesktop.import1",
1740 "/org/freedesktop/import1",
1741 "org.freedesktop.import1.Manager",
1744 return bus_log_create_error(r);
1746 r = sd_bus_message_append(
1749 fd >= 0 ? fd : STDIN_FILENO,
1754 return bus_log_create_error(r);
1756 return transfer_image_common(bus, m);
1759 static void determine_compression_from_filename(const char *p) {
1766 if (endswith(p, ".xz"))
1768 else if (endswith(p, ".gz"))
1769 arg_format = "gzip";
1770 else if (endswith(p, ".bz2"))
1771 arg_format = "bzip2";
1774 static int export_tar(int argc, char *argv[], void *userdata) {
1775 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1776 _cleanup_close_ int fd = -1;
1777 const char *local = NULL, *path = NULL;
1778 sd_bus *bus = userdata;
1784 if (!machine_name_is_valid(local)) {
1785 log_error("Machine name %s is not valid.", local);
1791 if (isempty(path) || streq(path, "-"))
1795 determine_compression_from_filename(path);
1797 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1799 return log_error_errno(errno, "Failed to open %s: %m", path);
1802 r = sd_bus_message_new_method_call(
1805 "org.freedesktop.import1",
1806 "/org/freedesktop/import1",
1807 "org.freedesktop.import1.Manager",
1810 return bus_log_create_error(r);
1812 r = sd_bus_message_append(
1816 fd >= 0 ? fd : STDOUT_FILENO,
1819 return bus_log_create_error(r);
1821 return transfer_image_common(bus, m);
1824 static int export_raw(int argc, char *argv[], void *userdata) {
1825 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1826 _cleanup_close_ int fd = -1;
1827 const char *local = NULL, *path = NULL;
1828 sd_bus *bus = userdata;
1834 if (!machine_name_is_valid(local)) {
1835 log_error("Machine name %s is not valid.", local);
1841 if (isempty(path) || streq(path, "-"))
1845 determine_compression_from_filename(path);
1847 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1849 return log_error_errno(errno, "Failed to open %s: %m", path);
1852 r = sd_bus_message_new_method_call(
1855 "org.freedesktop.import1",
1856 "/org/freedesktop/import1",
1857 "org.freedesktop.import1.Manager",
1860 return bus_log_create_error(r);
1862 r = sd_bus_message_append(
1866 fd >= 0 ? fd : STDOUT_FILENO,
1869 return bus_log_create_error(r);
1871 return transfer_image_common(bus, m);
1874 static int pull_tar(int argc, char *argv[], void *userdata) {
1875 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1876 _cleanup_free_ char *l = NULL, *ll = NULL;
1877 const char *local, *remote;
1878 sd_bus *bus = userdata;
1884 if (!http_url_is_valid(remote)) {
1885 log_error("URL '%s' is not valid.", remote);
1892 r = import_url_last_component(remote, &l);
1894 return log_error_errno(r, "Failed to get final component of URL: %m");
1899 if (isempty(local) || streq(local, "-"))
1903 r = tar_strip_suffixes(local, &ll);
1909 if (!machine_name_is_valid(local)) {
1910 log_error("Local name %s is not a suitable machine name.", local);
1915 r = sd_bus_message_new_method_call(
1918 "org.freedesktop.import1",
1919 "/org/freedesktop/import1",
1920 "org.freedesktop.import1.Manager",
1923 return bus_log_create_error(r);
1925 r = sd_bus_message_append(
1930 import_verify_to_string(arg_verify),
1933 return bus_log_create_error(r);
1935 return transfer_image_common(bus, m);
1938 static int pull_raw(int argc, char *argv[], void *userdata) {
1939 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1940 _cleanup_free_ char *l = NULL, *ll = NULL;
1941 const char *local, *remote;
1942 sd_bus *bus = userdata;
1948 if (!http_url_is_valid(remote)) {
1949 log_error("URL '%s' is not valid.", remote);
1956 r = import_url_last_component(remote, &l);
1958 return log_error_errno(r, "Failed to get final component of URL: %m");
1963 if (isempty(local) || streq(local, "-"))
1967 r = raw_strip_suffixes(local, &ll);
1973 if (!machine_name_is_valid(local)) {
1974 log_error("Local name %s is not a suitable machine name.", local);
1979 r = sd_bus_message_new_method_call(
1982 "org.freedesktop.import1",
1983 "/org/freedesktop/import1",
1984 "org.freedesktop.import1.Manager",
1987 return bus_log_create_error(r);
1989 r = sd_bus_message_append(
1994 import_verify_to_string(arg_verify),
1997 return bus_log_create_error(r);
1999 return transfer_image_common(bus, m);
2002 static int pull_dkr(int argc, char *argv[], void *userdata) {
2003 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2004 const char *local, *remote, *tag;
2005 sd_bus *bus = userdata;
2008 if (arg_verify != IMPORT_VERIFY_NO) {
2009 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2014 tag = strchr(remote, ':');
2016 remote = strndupa(remote, tag - remote);
2020 if (!dkr_name_is_valid(remote)) {
2021 log_error("DKR name '%s' is invalid.", remote);
2024 if (tag && !dkr_tag_is_valid(tag)) {
2025 log_error("DKR tag '%s' is invalid.", remote);
2032 local = strchr(remote, '/');
2039 if (isempty(local) || streq(local, "-"))
2043 if (!machine_name_is_valid(local)) {
2044 log_error("Local name %s is not a suitable machine name.", local);
2049 r = sd_bus_message_new_method_call(
2052 "org.freedesktop.import1",
2053 "/org/freedesktop/import1",
2054 "org.freedesktop.import1.Manager",
2057 return bus_log_create_error(r);
2059 r = sd_bus_message_append(
2066 import_verify_to_string(arg_verify),
2069 return bus_log_create_error(r);
2071 return transfer_image_common(bus, m);
2074 typedef struct TransferInfo {
2082 static int compare_transfer_info(const void *a, const void *b) {
2083 const TransferInfo *x = a, *y = b;
2085 return strcmp(x->local, y->local);
2088 static int list_transfers(int argc, char *argv[], void *userdata) {
2089 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2090 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2091 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2092 _cleanup_free_ TransferInfo *transfers = NULL;
2093 size_t n_transfers = 0, n_allocated = 0, j;
2094 const char *type, *remote, *local, *object;
2095 sd_bus *bus = userdata;
2096 uint32_t id, max_id = 0;
2100 pager_open_if_enabled();
2102 r = sd_bus_call_method(
2104 "org.freedesktop.import1",
2105 "/org/freedesktop/import1",
2106 "org.freedesktop.import1.Manager",
2112 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2116 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2118 return bus_log_parse_error(r);
2120 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2123 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2126 transfers[n_transfers].id = id;
2127 transfers[n_transfers].type = type;
2128 transfers[n_transfers].remote = remote;
2129 transfers[n_transfers].local = local;
2130 transfers[n_transfers].progress = progress;
2150 return bus_log_parse_error(r);
2152 r = sd_bus_message_exit_container(reply);
2154 return bus_log_parse_error(r);
2156 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2159 printf("%-*s %-*s %-*s %-*s %-*s\n",
2160 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2162 (int) max_type, "TYPE",
2163 (int) max_local, "LOCAL",
2164 (int) max_remote, "REMOTE");
2166 for (j = 0; j < n_transfers; j++)
2167 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2168 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2169 (int) 6, (unsigned) (transfers[j].progress * 100),
2170 (int) max_type, transfers[j].type,
2171 (int) max_local, transfers[j].local,
2172 (int) max_remote, transfers[j].remote);
2175 printf("\n%zu transfers listed.\n", n_transfers);
2180 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2181 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2182 sd_bus *bus = userdata;
2187 polkit_agent_open_if_enabled();
2189 for (i = 1; i < argc; i++) {
2192 r = safe_atou32(argv[i], &id);
2194 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2196 r = sd_bus_call_method(
2198 "org.freedesktop.import1",
2199 "/org/freedesktop/import1",
2200 "org.freedesktop.import1.Manager",
2206 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2214 static int set_limit(int argc, char *argv[], void *userdata) {
2215 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2216 sd_bus *bus = userdata;
2220 if (streq(argv[argc-1], "-"))
2221 limit = (uint64_t) -1;
2225 r = parse_size(argv[argc-1], 1024, &off);
2227 return log_error("Failed to parse size: %s", argv[argc-1]);
2229 limit = (uint64_t) off;
2233 /* With two arguments changes the quota limit of the
2234 * specified image */
2235 r = sd_bus_call_method(
2237 "org.freedesktop.machine1",
2238 "/org/freedesktop/machine1",
2239 "org.freedesktop.machine1.Manager",
2243 "st", argv[1], limit);
2245 /* With one argument changes the pool quota limit */
2246 r = sd_bus_call_method(
2248 "org.freedesktop.machine1",
2249 "/org/freedesktop/machine1",
2250 "org.freedesktop.machine1.Manager",
2257 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2264 static int help(int argc, char *argv[], void *userdata) {
2266 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2267 "Send control commands to or query the virtual machine and container\n"
2268 "registration manager.\n\n"
2269 " -h --help Show this help\n"
2270 " --version Show package version\n"
2271 " --no-pager Do not pipe output into a pager\n"
2272 " --no-legend Do not show the headers and footers\n"
2273 " --no-ask-password Do not ask for system passwords\n"
2274 " -H --host=[USER@]HOST Operate on remote host\n"
2275 " -M --machine=CONTAINER Operate on local container\n"
2276 " -p --property=NAME Show only properties by this name\n"
2277 " -q --quiet Suppress output\n"
2278 " -a --all Show all properties, including empty ones\n"
2279 " -l --full Do not ellipsize output\n"
2280 " --kill-who=WHO Who to send signal to\n"
2281 " -s --signal=SIGNAL Which signal to send\n"
2282 " --read-only Create read-only bind mount\n"
2283 " --mkdir Create directory before bind mounting, if missing\n"
2284 " -n --lines=INTEGER Number of journal entries to show\n"
2285 " -o --output=STRING Change journal output mode (short,\n"
2286 " short-monotonic, verbose, export, json,\n"
2287 " json-pretty, json-sse, cat)\n"
2288 " --verify=MODE Verification mode for downloaded images (no,\n"
2289 " checksum, signature)\n"
2290 " --force Download image even if already exists\n"
2291 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2293 "Machine Commands:\n"
2294 " list List running VMs and containers\n"
2295 " status NAME... Show VM/container details\n"
2296 " show NAME... Show properties of one or more VMs/containers\n"
2297 " start NAME... Start container as a service\n"
2298 " login NAME Get a login prompt on a container\n"
2299 " enable NAME... Enable automatic container start at boot\n"
2300 " disable NAME... Disable automatic container start at boot\n"
2301 " poweroff NAME... Power off one or more containers\n"
2302 " reboot NAME... Reboot one or more containers\n"
2303 " terminate NAME... Terminate one or more VMs/containers\n"
2304 " kill NAME... Send signal to processes of a VM/container\n"
2305 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2306 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2307 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2309 " list-images Show available container and VM images\n"
2310 " image-status NAME... Show image details\n"
2311 " show-image NAME... Show properties of image\n"
2312 " clone NAME NAME Clone an image\n"
2313 " rename NAME NAME Rename an image\n"
2314 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2315 " remove NAME... Remove an image\n"
2316 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2317 "Image Transfer Commands:\n"
2318 " pull-tar URL [NAME] Download a TAR container image\n"
2319 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2320 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2321 " import-tar FILE [NAME] Import a local TAR container image\n"
2322 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2323 " export-tar NAME [FILE] Export a TAR container image locally\n"
2324 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
2325 " list-transfers Show list of downloads in progress\n"
2326 " cancel-transfer Cancel a download\n"
2327 , program_invocation_short_name);
2332 static int parse_argv(int argc, char *argv[]) {
2335 ARG_VERSION = 0x100,
2341 ARG_NO_ASK_PASSWORD,
2348 static const struct option options[] = {
2349 { "help", no_argument, NULL, 'h' },
2350 { "version", no_argument, NULL, ARG_VERSION },
2351 { "property", required_argument, NULL, 'p' },
2352 { "all", no_argument, NULL, 'a' },
2353 { "full", no_argument, NULL, 'l' },
2354 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2355 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2356 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2357 { "signal", required_argument, NULL, 's' },
2358 { "host", required_argument, NULL, 'H' },
2359 { "machine", required_argument, NULL, 'M' },
2360 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2361 { "mkdir", no_argument, NULL, ARG_MKDIR },
2362 { "quiet", no_argument, NULL, 'q' },
2363 { "lines", required_argument, NULL, 'n' },
2364 { "output", required_argument, NULL, 'o' },
2365 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2366 { "verify", required_argument, NULL, ARG_VERIFY },
2367 { "force", no_argument, NULL, ARG_FORCE },
2368 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2369 { "format", required_argument, NULL, ARG_FORMAT },
2378 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2383 return help(0, NULL, NULL);
2386 puts(PACKAGE_STRING);
2387 puts(SYSTEMD_FEATURES);
2391 r = strv_extend(&arg_property, optarg);
2395 /* If the user asked for a particular
2396 * property, show it to him, even if it is
2410 if (safe_atou(optarg, &arg_lines) < 0) {
2411 log_error("Failed to parse lines '%s'", optarg);
2417 arg_output = output_mode_from_string(optarg);
2418 if (arg_output < 0) {
2419 log_error("Unknown output '%s'.", optarg);
2425 arg_no_pager = true;
2433 arg_kill_who = optarg;
2437 arg_signal = signal_from_string_try_harder(optarg);
2438 if (arg_signal < 0) {
2439 log_error("Failed to parse signal string %s.", optarg);
2444 case ARG_NO_ASK_PASSWORD:
2445 arg_ask_password = false;
2449 arg_transport = BUS_TRANSPORT_REMOTE;
2454 arg_transport = BUS_TRANSPORT_MACHINE;
2459 arg_read_only = true;
2471 arg_verify = import_verify_from_string(optarg);
2472 if (arg_verify < 0) {
2473 log_error("Failed to parse --verify= setting: %s", optarg);
2482 case ARG_DKR_INDEX_URL:
2483 if (!http_url_is_valid(optarg)) {
2484 log_error("Index URL is invalid: %s", optarg);
2488 arg_dkr_index_url = optarg;
2492 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
2493 log_error("Unknown format: %s", optarg);
2497 arg_format = optarg;
2504 assert_not_reached("Unhandled option");
2510 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2512 static const Verb verbs[] = {
2513 { "help", VERB_ANY, VERB_ANY, 0, help },
2514 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2515 { "list-images", VERB_ANY, 1, 0, list_images },
2516 { "status", 2, VERB_ANY, 0, show_machine },
2517 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
2518 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2519 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2520 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2521 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2522 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2523 { "kill", 2, VERB_ANY, 0, kill_machine },
2524 { "login", 2, 2, 0, login_machine },
2525 { "bind", 3, 4, 0, bind_mount },
2526 { "copy-to", 3, 4, 0, copy_files },
2527 { "copy-from", 3, 4, 0, copy_files },
2528 { "remove", 2, VERB_ANY, 0, remove_image },
2529 { "rename", 3, 3, 0, rename_image },
2530 { "clone", 3, 3, 0, clone_image },
2531 { "read-only", 2, 3, 0, read_only_image },
2532 { "start", 2, VERB_ANY, 0, start_machine },
2533 { "enable", 2, VERB_ANY, 0, enable_machine },
2534 { "disable", 2, VERB_ANY, 0, enable_machine },
2535 { "import-tar", 2, 3, 0, import_tar },
2536 { "import-raw", 2, 3, 0, import_raw },
2537 { "export-tar", 2, 3, 0, export_tar },
2538 { "export-raw", 2, 3, 0, export_raw },
2539 { "pull-tar", 2, 3, 0, pull_tar },
2540 { "pull-raw", 2, 3, 0, pull_raw },
2541 { "pull-dkr", 2, 3, 0, pull_dkr },
2542 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2543 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2544 { "set-limit", 2, 3, 0, set_limit },
2548 return dispatch_verb(argc, argv, verbs, bus);
2551 int main(int argc, char*argv[]) {
2552 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2555 setlocale(LC_ALL, "");
2556 log_parse_environment();
2559 r = parse_argv(argc, argv);
2563 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2565 log_error_errno(r, "Failed to create bus connection: %m");
2569 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2571 r = machinectl_main(argc, argv, bus);
2575 polkit_agent_close();
2577 strv_free(arg_property);
2579 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;