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_free_ char *ll = NULL;
1777 _cleanup_close_ int fd = -1;
1778 const char *local = NULL, *path = NULL;
1779 sd_bus *bus = userdata;
1785 if (!machine_name_is_valid(local)) {
1786 log_error("Machine name %s is not valid.", local);
1792 if (isempty(path) || streq(path, "-"))
1796 determine_compression_from_filename(path);
1798 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1800 return log_error_errno(errno, "Failed to open %s: %m", path);
1803 r = sd_bus_message_new_method_call(
1806 "org.freedesktop.import1",
1807 "/org/freedesktop/import1",
1808 "org.freedesktop.import1.Manager",
1811 return bus_log_create_error(r);
1813 r = sd_bus_message_append(
1817 fd >= 0 ? fd : STDOUT_FILENO,
1820 return bus_log_create_error(r);
1822 return transfer_image_common(bus, m);
1825 static int export_raw(int argc, char *argv[], void *userdata) {
1826 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1827 _cleanup_free_ char *ll = NULL;
1828 _cleanup_close_ int fd = -1;
1829 const char *local = NULL, *path = NULL;
1830 sd_bus *bus = userdata;
1836 if (!machine_name_is_valid(local)) {
1837 log_error("Machine name %s is not valid.", local);
1843 if (isempty(path) || streq(path, "-"))
1847 determine_compression_from_filename(path);
1849 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
1851 return log_error_errno(errno, "Failed to open %s: %m", path);
1854 r = sd_bus_message_new_method_call(
1857 "org.freedesktop.import1",
1858 "/org/freedesktop/import1",
1859 "org.freedesktop.import1.Manager",
1862 return bus_log_create_error(r);
1864 r = sd_bus_message_append(
1868 fd >= 0 ? fd : STDOUT_FILENO,
1871 return bus_log_create_error(r);
1873 return transfer_image_common(bus, m);
1876 static int pull_tar(int argc, char *argv[], void *userdata) {
1877 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1878 _cleanup_free_ char *l = NULL, *ll = NULL;
1879 const char *local, *remote;
1880 sd_bus *bus = userdata;
1886 if (!http_url_is_valid(remote)) {
1887 log_error("URL '%s' is not valid.", remote);
1894 r = import_url_last_component(remote, &l);
1896 return log_error_errno(r, "Failed to get final component of URL: %m");
1901 if (isempty(local) || streq(local, "-"))
1905 r = tar_strip_suffixes(local, &ll);
1911 if (!machine_name_is_valid(local)) {
1912 log_error("Local name %s is not a suitable machine name.", local);
1917 r = sd_bus_message_new_method_call(
1920 "org.freedesktop.import1",
1921 "/org/freedesktop/import1",
1922 "org.freedesktop.import1.Manager",
1925 return bus_log_create_error(r);
1927 r = sd_bus_message_append(
1932 import_verify_to_string(arg_verify),
1935 return bus_log_create_error(r);
1937 return transfer_image_common(bus, m);
1940 static int pull_raw(int argc, char *argv[], void *userdata) {
1941 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1942 _cleanup_free_ char *l = NULL, *ll = NULL;
1943 const char *local, *remote;
1944 sd_bus *bus = userdata;
1950 if (!http_url_is_valid(remote)) {
1951 log_error("URL '%s' is not valid.", remote);
1958 r = import_url_last_component(remote, &l);
1960 return log_error_errno(r, "Failed to get final component of URL: %m");
1965 if (isempty(local) || streq(local, "-"))
1969 r = raw_strip_suffixes(local, &ll);
1975 if (!machine_name_is_valid(local)) {
1976 log_error("Local name %s is not a suitable machine name.", local);
1981 r = sd_bus_message_new_method_call(
1984 "org.freedesktop.import1",
1985 "/org/freedesktop/import1",
1986 "org.freedesktop.import1.Manager",
1989 return bus_log_create_error(r);
1991 r = sd_bus_message_append(
1996 import_verify_to_string(arg_verify),
1999 return bus_log_create_error(r);
2001 return transfer_image_common(bus, m);
2004 static int pull_dkr(int argc, char *argv[], void *userdata) {
2005 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2006 const char *local, *remote, *tag;
2007 sd_bus *bus = userdata;
2010 if (arg_verify != IMPORT_VERIFY_NO) {
2011 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
2016 tag = strchr(remote, ':');
2018 remote = strndupa(remote, tag - remote);
2022 if (!dkr_name_is_valid(remote)) {
2023 log_error("DKR name '%s' is invalid.", remote);
2026 if (tag && !dkr_tag_is_valid(tag)) {
2027 log_error("DKR tag '%s' is invalid.", remote);
2034 local = strchr(remote, '/');
2041 if (isempty(local) || streq(local, "-"))
2045 if (!machine_name_is_valid(local)) {
2046 log_error("Local name %s is not a suitable machine name.", local);
2051 r = sd_bus_message_new_method_call(
2054 "org.freedesktop.import1",
2055 "/org/freedesktop/import1",
2056 "org.freedesktop.import1.Manager",
2059 return bus_log_create_error(r);
2061 r = sd_bus_message_append(
2068 import_verify_to_string(arg_verify),
2071 return bus_log_create_error(r);
2073 return transfer_image_common(bus, m);
2076 typedef struct TransferInfo {
2084 static int compare_transfer_info(const void *a, const void *b) {
2085 const TransferInfo *x = a, *y = b;
2087 return strcmp(x->local, y->local);
2090 static int list_transfers(int argc, char *argv[], void *userdata) {
2091 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
2092 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2093 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2094 _cleanup_free_ TransferInfo *transfers = NULL;
2095 size_t n_transfers = 0, n_allocated = 0, j;
2096 const char *type, *remote, *local, *object;
2097 sd_bus *bus = userdata;
2098 uint32_t id, max_id = 0;
2102 pager_open_if_enabled();
2104 r = sd_bus_call_method(
2106 "org.freedesktop.import1",
2107 "/org/freedesktop/import1",
2108 "org.freedesktop.import1.Manager",
2114 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2118 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2120 return bus_log_parse_error(r);
2122 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2125 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2128 transfers[n_transfers].id = id;
2129 transfers[n_transfers].type = type;
2130 transfers[n_transfers].remote = remote;
2131 transfers[n_transfers].local = local;
2132 transfers[n_transfers].progress = progress;
2152 return bus_log_parse_error(r);
2154 r = sd_bus_message_exit_container(reply);
2156 return bus_log_parse_error(r);
2158 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2161 printf("%-*s %-*s %-*s %-*s %-*s\n",
2162 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2164 (int) max_type, "TYPE",
2165 (int) max_local, "LOCAL",
2166 (int) max_remote, "REMOTE");
2168 for (j = 0; j < n_transfers; j++)
2169 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2170 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2171 (int) 6, (unsigned) (transfers[j].progress * 100),
2172 (int) max_type, transfers[j].type,
2173 (int) max_local, transfers[j].local,
2174 (int) max_remote, transfers[j].remote);
2177 printf("\n%zu transfers listed.\n", n_transfers);
2182 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2183 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2184 sd_bus *bus = userdata;
2189 polkit_agent_open_if_enabled();
2191 for (i = 1; i < argc; i++) {
2194 r = safe_atou32(argv[i], &id);
2196 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2198 r = sd_bus_call_method(
2200 "org.freedesktop.import1",
2201 "/org/freedesktop/import1",
2202 "org.freedesktop.import1.Manager",
2208 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2216 static int set_limit(int argc, char *argv[], void *userdata) {
2217 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2218 sd_bus *bus = userdata;
2222 if (streq(argv[argc-1], "-"))
2223 limit = (uint64_t) -1;
2227 r = parse_size(argv[argc-1], 1024, &off);
2229 return log_error("Failed to parse size: %s", argv[argc-1]);
2231 limit = (uint64_t) off;
2235 /* With two arguments changes the quota limit of the
2236 * specified image */
2237 r = sd_bus_call_method(
2239 "org.freedesktop.machine1",
2240 "/org/freedesktop/machine1",
2241 "org.freedesktop.machine1.Manager",
2245 "st", argv[1], limit);
2247 /* With one argument changes the pool quota limit */
2248 r = sd_bus_call_method(
2250 "org.freedesktop.machine1",
2251 "/org/freedesktop/machine1",
2252 "org.freedesktop.machine1.Manager",
2259 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2266 static int help(int argc, char *argv[], void *userdata) {
2268 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2269 "Send control commands to or query the virtual machine and container\n"
2270 "registration manager.\n\n"
2271 " -h --help Show this help\n"
2272 " --version Show package version\n"
2273 " --no-pager Do not pipe output into a pager\n"
2274 " --no-legend Do not show the headers and footers\n"
2275 " --no-ask-password Do not ask for system passwords\n"
2276 " -H --host=[USER@]HOST Operate on remote host\n"
2277 " -M --machine=CONTAINER Operate on local container\n"
2278 " -p --property=NAME Show only properties by this name\n"
2279 " -q --quiet Suppress output\n"
2280 " -a --all Show all properties, including empty ones\n"
2281 " -l --full Do not ellipsize output\n"
2282 " --kill-who=WHO Who to send signal to\n"
2283 " -s --signal=SIGNAL Which signal to send\n"
2284 " --read-only Create read-only bind mount\n"
2285 " --mkdir Create directory before bind mounting, if missing\n"
2286 " -n --lines=INTEGER Number of journal entries to show\n"
2287 " -o --output=STRING Change journal output mode (short,\n"
2288 " short-monotonic, verbose, export, json,\n"
2289 " json-pretty, json-sse, cat)\n"
2290 " --verify=MODE Verification mode for downloaded images (no,\n"
2291 " checksum, signature)\n"
2292 " --force Download image even if already exists\n"
2293 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2295 "Machine Commands:\n"
2296 " list List running VMs and containers\n"
2297 " status NAME... Show VM/container details\n"
2298 " show NAME... Show properties of one or more VMs/containers\n"
2299 " start NAME... Start container as a service\n"
2300 " login NAME Get a login prompt on a container\n"
2301 " enable NAME... Enable automatic container start at boot\n"
2302 " disable NAME... Disable automatic container start at boot\n"
2303 " poweroff NAME... Power off one or more containers\n"
2304 " reboot NAME... Reboot one or more containers\n"
2305 " terminate NAME... Terminate one or more VMs/containers\n"
2306 " kill NAME... Send signal to processes of a VM/container\n"
2307 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2308 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2309 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2311 " list-images Show available container and VM images\n"
2312 " image-status NAME... Show image details\n"
2313 " show-image NAME... Show properties of image\n"
2314 " clone NAME NAME Clone an image\n"
2315 " rename NAME NAME Rename an image\n"
2316 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2317 " remove NAME... Remove an image\n"
2318 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2319 "Image Transfer Commands:\n"
2320 " pull-tar URL [NAME] Download a TAR container image\n"
2321 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2322 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2323 " import-tar FILE [NAME] Import a local TAR container image\n"
2324 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2325 " export-tar FILE [NAME] Export a TAR container image locally\n"
2326 " export-raw FILE [NAME] Export a RAW container or VM image locally\n"
2327 " list-transfers Show list of downloads in progress\n"
2328 " cancel-transfer Cancel a download\n"
2329 , program_invocation_short_name);
2334 static int parse_argv(int argc, char *argv[]) {
2337 ARG_VERSION = 0x100,
2343 ARG_NO_ASK_PASSWORD,
2350 static const struct option options[] = {
2351 { "help", no_argument, NULL, 'h' },
2352 { "version", no_argument, NULL, ARG_VERSION },
2353 { "property", required_argument, NULL, 'p' },
2354 { "all", no_argument, NULL, 'a' },
2355 { "full", no_argument, NULL, 'l' },
2356 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2357 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2358 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2359 { "signal", required_argument, NULL, 's' },
2360 { "host", required_argument, NULL, 'H' },
2361 { "machine", required_argument, NULL, 'M' },
2362 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2363 { "mkdir", no_argument, NULL, ARG_MKDIR },
2364 { "quiet", no_argument, NULL, 'q' },
2365 { "lines", required_argument, NULL, 'n' },
2366 { "output", required_argument, NULL, 'o' },
2367 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2368 { "verify", required_argument, NULL, ARG_VERIFY },
2369 { "force", no_argument, NULL, ARG_FORCE },
2370 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2371 { "format", required_argument, NULL, ARG_FORMAT },
2380 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2385 return help(0, NULL, NULL);
2388 puts(PACKAGE_STRING);
2389 puts(SYSTEMD_FEATURES);
2393 r = strv_extend(&arg_property, optarg);
2397 /* If the user asked for a particular
2398 * property, show it to him, even if it is
2412 if (safe_atou(optarg, &arg_lines) < 0) {
2413 log_error("Failed to parse lines '%s'", optarg);
2419 arg_output = output_mode_from_string(optarg);
2420 if (arg_output < 0) {
2421 log_error("Unknown output '%s'.", optarg);
2427 arg_no_pager = true;
2435 arg_kill_who = optarg;
2439 arg_signal = signal_from_string_try_harder(optarg);
2440 if (arg_signal < 0) {
2441 log_error("Failed to parse signal string %s.", optarg);
2446 case ARG_NO_ASK_PASSWORD:
2447 arg_ask_password = false;
2451 arg_transport = BUS_TRANSPORT_REMOTE;
2456 arg_transport = BUS_TRANSPORT_MACHINE;
2461 arg_read_only = true;
2473 arg_verify = import_verify_from_string(optarg);
2474 if (arg_verify < 0) {
2475 log_error("Failed to parse --verify= setting: %s", optarg);
2484 case ARG_DKR_INDEX_URL:
2485 if (!http_url_is_valid(optarg)) {
2486 log_error("Index URL is invalid: %s", optarg);
2490 arg_dkr_index_url = optarg;
2494 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2")) {
2495 log_error("Unknown format: %s", optarg);
2499 arg_format = optarg;
2506 assert_not_reached("Unhandled option");
2512 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2514 static const Verb verbs[] = {
2515 { "help", VERB_ANY, VERB_ANY, 0, help },
2516 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2517 { "list-images", VERB_ANY, 1, 0, list_images },
2518 { "status", 2, VERB_ANY, 0, show_machine },
2519 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
2520 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2521 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2522 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2523 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2524 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2525 { "kill", 2, VERB_ANY, 0, kill_machine },
2526 { "login", 2, 2, 0, login_machine },
2527 { "bind", 3, 4, 0, bind_mount },
2528 { "copy-to", 3, 4, 0, copy_files },
2529 { "copy-from", 3, 4, 0, copy_files },
2530 { "remove", 2, VERB_ANY, 0, remove_image },
2531 { "rename", 3, 3, 0, rename_image },
2532 { "clone", 3, 3, 0, clone_image },
2533 { "read-only", 2, 3, 0, read_only_image },
2534 { "start", 2, VERB_ANY, 0, start_machine },
2535 { "enable", 2, VERB_ANY, 0, enable_machine },
2536 { "disable", 2, VERB_ANY, 0, enable_machine },
2537 { "import-tar", 2, 3, 0, import_tar },
2538 { "import-raw", 2, 3, 0, import_raw },
2539 { "export-tar", 2, 3, 0, export_tar },
2540 { "export-raw", 2, 3, 0, export_raw },
2541 { "pull-tar", 2, 3, 0, pull_tar },
2542 { "pull-raw", 2, 3, 0, pull_raw },
2543 { "pull-dkr", 2, 3, 0, pull_dkr },
2544 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2545 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2546 { "set-limit", 2, 3, 0, set_limit },
2550 return dispatch_verb(argc, argv, verbs, bus);
2553 int main(int argc, char*argv[]) {
2554 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2557 setlocale(LC_ALL, "");
2558 log_parse_environment();
2561 r = parse_argv(argc, argv);
2565 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2567 log_error_errno(r, "Failed to create bus connection: %m");
2571 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2573 r = machinectl_main(argc, argv, bus);
2577 polkit_agent_close();
2579 strv_free(arg_property);
2581 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;