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;
75 static void pager_open_if_enabled(void) {
83 static void polkit_agent_open_if_enabled(void) {
85 /* Open the polkit agent as a child process if necessary */
87 if (!arg_ask_password)
90 if (arg_transport != BUS_TRANSPORT_LOCAL)
96 static OutputFlags get_output_flags(void) {
98 arg_all * OUTPUT_SHOW_ALL |
99 arg_full * OUTPUT_FULL_WIDTH |
100 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
101 on_tty() * OUTPUT_COLOR |
102 !arg_quiet * OUTPUT_WARN_CUTOFF;
105 typedef struct MachineInfo {
111 static int compare_machine_info(const void *a, const void *b) {
112 const MachineInfo *x = a, *y = b;
114 return strcmp(x->name, y->name);
117 static int list_machines(int argc, char *argv[], void *userdata) {
119 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
120 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
121 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
122 _cleanup_free_ MachineInfo *machines = NULL;
123 const char *name, *class, *service, *object;
124 size_t n_machines = 0, n_allocated = 0, j;
125 sd_bus *bus = userdata;
130 pager_open_if_enabled();
132 r = sd_bus_call_method(
134 "org.freedesktop.machine1",
135 "/org/freedesktop/machine1",
136 "org.freedesktop.machine1.Manager",
142 log_error("Could not get machines: %s", bus_error_message(&error, -r));
146 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
148 return bus_log_parse_error(r);
150 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
153 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
156 machines[n_machines].name = name;
157 machines[n_machines].class = class;
158 machines[n_machines].service = service;
175 return bus_log_parse_error(r);
177 r = sd_bus_message_exit_container(reply);
179 return bus_log_parse_error(r);
181 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
184 printf("%-*s %-*s %-*s\n",
185 (int) max_name, "MACHINE",
186 (int) max_class, "CLASS",
187 (int) max_service, "SERVICE");
189 for (j = 0; j < n_machines; j++)
190 printf("%-*s %-*s %-*s\n",
191 (int) max_name, machines[j].name,
192 (int) max_class, machines[j].class,
193 (int) max_service, machines[j].service);
196 printf("\n%zu machines listed.\n", n_machines);
201 typedef struct ImageInfo {
210 static int compare_image_info(const void *a, const void *b) {
211 const ImageInfo *x = a, *y = b;
213 return strcmp(x->name, y->name);
216 static int list_images(int argc, char *argv[], void *userdata) {
218 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
219 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
220 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
221 _cleanup_free_ ImageInfo *images = NULL;
222 size_t n_images = 0, n_allocated = 0, j;
223 const char *name, *type, *object;
224 sd_bus *bus = userdata;
225 uint64_t crtime, mtime, size;
230 pager_open_if_enabled();
232 r = sd_bus_call_method(
234 "org.freedesktop.machine1",
235 "/org/freedesktop/machine1",
236 "org.freedesktop.machine1.Manager",
242 log_error("Could not get images: %s", bus_error_message(&error, -r));
246 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
248 return bus_log_parse_error(r);
250 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
251 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
254 if (name[0] == '.' && !arg_all)
257 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
260 images[n_images].name = name;
261 images[n_images].type = type;
262 images[n_images].read_only = read_only;
263 images[n_images].crtime = crtime;
264 images[n_images].mtime = mtime;
265 images[n_images].size = size;
276 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
282 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
287 if (size != (uint64_t) -1) {
288 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
296 return bus_log_parse_error(r);
298 r = sd_bus_message_exit_container(reply);
300 return bus_log_parse_error(r);
302 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
305 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
306 (int) max_name, "NAME",
307 (int) max_type, "TYPE",
309 (int) max_size, "USAGE",
310 (int) max_crtime, "CREATED",
311 (int) max_mtime, "MODIFIED");
313 for (j = 0; j < n_images; j++) {
314 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
316 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
317 (int) max_name, images[j].name,
318 (int) max_type, images[j].type,
319 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
320 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
321 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
322 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
326 printf("\n%zu images listed.\n", n_images);
331 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
332 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
333 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
334 _cleanup_free_ char *path = NULL;
342 if (arg_transport == BUS_TRANSPORT_REMOTE)
345 path = unit_dbus_path_from_name(unit);
349 r = sd_bus_get_property(
351 "org.freedesktop.systemd1",
353 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
359 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
363 r = sd_bus_message_read(reply, "s", &cgroup);
365 return bus_log_parse_error(r);
370 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
379 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
383 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
384 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
392 r = sd_bus_call_method(bus,
393 "org.freedesktop.machine1",
394 "/org/freedesktop/machine1",
395 "org.freedesktop.machine1.Manager",
396 "GetMachineAddresses",
403 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
405 return bus_log_parse_error(r);
407 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
411 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
413 r = sd_bus_message_read(reply, "i", &family);
415 return bus_log_parse_error(r);
417 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
419 return bus_log_parse_error(r);
421 fputs(prefix, stdout);
422 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
423 if (family == AF_INET6 && ifi > 0)
427 r = sd_bus_message_exit_container(reply);
429 return bus_log_parse_error(r);
431 if (prefix != prefix2)
435 return bus_log_parse_error(r);
437 r = sd_bus_message_exit_container(reply);
439 return bus_log_parse_error(r);
444 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
445 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
446 const char *k, *v, *pretty = NULL;
453 r = sd_bus_call_method(bus,
454 "org.freedesktop.machine1",
455 "/org/freedesktop/machine1",
456 "org.freedesktop.machine1.Manager",
457 "GetMachineOSRelease",
464 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
466 return bus_log_parse_error(r);
468 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
469 if (streq(k, "PRETTY_NAME"))
474 return bus_log_parse_error(r);
476 r = sd_bus_message_exit_container(reply);
478 return bus_log_parse_error(r);
481 printf("%s%s\n", prefix, pretty);
486 typedef struct MachineStatusInfo {
492 char *root_directory;
494 struct dual_timestamp timestamp;
499 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
500 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
501 char since2[FORMAT_TIMESTAMP_MAX], *s2;
507 fputs(strna(i->name), stdout);
509 if (!sd_id128_equal(i->id, SD_ID128_NULL))
510 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
514 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
515 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
518 printf("\t Since: %s; %s\n", s2, s1);
520 printf("\t Since: %s\n", s2);
523 _cleanup_free_ char *t = NULL;
525 printf("\t Leader: %u", (unsigned) i->leader);
527 get_process_comm(i->leader, &t);
535 printf("\t Service: %s", i->service);
538 printf("; class %s", i->class);
542 printf("\t Class: %s\n", i->class);
544 if (i->root_directory)
545 printf("\t Root: %s\n", i->root_directory);
547 if (i->n_netif > 0) {
550 fputs("\t Iface:", stdout);
552 for (c = 0; c < i->n_netif; c++) {
553 char name[IF_NAMESIZE+1] = "";
555 if (if_indextoname(i->netif[c], name)) {
564 printf(" %i", i->netif[c]);
570 print_addresses(bus, i->name, ifi,
574 print_os_release(bus, i->name, "\t OS: ");
577 printf("\t Unit: %s\n", i->unit);
578 show_unit_cgroup(bus, i->unit, i->leader);
580 if (arg_transport == BUS_TRANSPORT_LOCAL) {
582 show_journal_by_unit(
587 i->timestamp.monotonic,
590 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
591 SD_JOURNAL_LOCAL_ONLY,
598 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
599 MachineStatusInfo *i = userdata;
604 assert_cc(sizeof(int32_t) == sizeof(int));
605 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
611 i->n_netif = l / sizeof(int32_t);
612 i->netif = memdup(v, l);
619 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
621 static const struct bus_properties_map map[] = {
622 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
623 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
624 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
625 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
626 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
627 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
628 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
629 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
630 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
631 { "NetworkInterfaces", "ai", map_netif, 0 },
635 MachineStatusInfo info = {};
643 r = bus_map_all_properties(bus,
644 "org.freedesktop.machine1",
649 return log_error_errno(r, "Could not get properties: %m");
655 print_machine_status_info(bus, &info);
661 free(info.root_directory);
667 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
679 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
681 log_error_errno(r, "Could not get properties: %m");
686 static int show_machine(int argc, char *argv[], void *userdata) {
688 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
689 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
690 bool properties, new_line = false;
691 sd_bus *bus = userdata;
696 properties = !strstr(argv[0], "status");
698 pager_open_if_enabled();
700 if (properties && argc <= 1) {
702 /* If no argument is specified, inspect the manager
704 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
709 for (i = 1; i < argc; i++) {
710 const char *path = NULL;
712 r = sd_bus_call_method(
714 "org.freedesktop.machine1",
715 "/org/freedesktop/machine1",
716 "org.freedesktop.machine1.Manager",
722 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
726 r = sd_bus_message_read(reply, "o", &path);
728 return bus_log_parse_error(r);
731 r = show_machine_properties(bus, path, &new_line);
733 r = show_machine_info(argv[0], bus, path, &new_line);
739 typedef struct ImageStatusInfo {
748 uint64_t usage_exclusive;
749 uint64_t limit_exclusive;
752 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
753 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
754 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
755 char bs[FORMAT_BYTES_MAX], *s3;
756 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
762 fputs(i->name, stdout);
767 printf("\t Type: %s\n", i->type);
770 printf("\t Path: %s\n", i->path);
772 printf("\t RO: %s%s%s\n",
773 i->read_only ? ansi_highlight_red() : "",
774 i->read_only ? "read-only" : "writable",
775 i->read_only ? ansi_highlight_off() : "");
777 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
778 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
780 printf("\t Created: %s; %s\n", s2, s1);
782 printf("\t Created: %s\n", s2);
784 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
785 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
787 printf("\tModified: %s; %s\n", s2, s1);
789 printf("\tModified: %s\n", s2);
791 s3 = format_bytes(bs, sizeof(bs), i->usage);
792 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
794 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
796 printf("\t Usage: %s\n", s3);
798 s3 = format_bytes(bs, sizeof(bs), i->limit);
799 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
801 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
803 printf("\t Limit: %s\n", s3);
806 static int show_image_info(sd_bus *bus, const char *path, bool *new_line) {
808 static const struct bus_properties_map map[] = {
809 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
810 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
811 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
812 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
813 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
814 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
815 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
816 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
817 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
818 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
822 ImageStatusInfo info = {};
829 r = bus_map_all_properties(bus,
830 "org.freedesktop.machine1",
835 return log_error_errno(r, "Could not get properties: %m");
841 print_image_status_info(bus, &info);
850 typedef struct PoolStatusInfo {
856 static void print_pool_status_info(sd_bus *bus, PoolStatusInfo *i) {
857 char bs[FORMAT_BYTES_MAX], *s;
860 printf("\t Path: %s\n", i->path);
862 s = format_bytes(bs, sizeof(bs), i->usage);
864 printf("\t Usage: %s\n", s);
866 s = format_bytes(bs, sizeof(bs), i->limit);
868 printf("\t Limit: %s\n", s);
871 static int show_pool_info(sd_bus *bus) {
873 static const struct bus_properties_map map[] = {
874 { "PoolPath", "s", NULL, offsetof(PoolStatusInfo, path) },
875 { "PoolUsage", "t", NULL, offsetof(PoolStatusInfo, usage) },
876 { "PoolLimit", "t", NULL, offsetof(PoolStatusInfo, limit) },
880 PoolStatusInfo info = {
881 .usage = (uint64_t) -1,
882 .limit = (uint64_t) -1,
888 r = bus_map_all_properties(bus,
889 "org.freedesktop.machine1",
890 "/org/freedesktop/machine1",
894 return log_error_errno(r, "Could not get properties: %m");
896 print_pool_status_info(bus, &info);
903 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
915 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
917 log_error_errno(r, "Could not get properties: %m");
922 static int show_image(int argc, char *argv[], void *userdata) {
924 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
925 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
926 bool properties, new_line = false;
927 sd_bus *bus = userdata;
932 properties = !strstr(argv[0], "status");
934 pager_open_if_enabled();
938 /* If no argument is specified, inspect the manager
942 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
944 r = show_pool_info(bus);
949 for (i = 1; i < argc; i++) {
950 const char *path = NULL;
952 r = sd_bus_call_method(
954 "org.freedesktop.machine1",
955 "/org/freedesktop/machine1",
956 "org.freedesktop.machine1.Manager",
962 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
966 r = sd_bus_message_read(reply, "o", &path);
968 return bus_log_parse_error(r);
971 r = show_image_properties(bus, path, &new_line);
973 r = show_image_info(bus, path, &new_line);
979 static int kill_machine(int argc, char *argv[], void *userdata) {
980 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
981 sd_bus *bus = userdata;
986 polkit_agent_open_if_enabled();
989 arg_kill_who = "all";
991 for (i = 1; i < argc; i++) {
992 r = sd_bus_call_method(
994 "org.freedesktop.machine1",
995 "/org/freedesktop/machine1",
996 "org.freedesktop.machine1.Manager",
1000 "ssi", argv[i], arg_kill_who, arg_signal);
1002 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
1010 static int reboot_machine(int argc, char *argv[], void *userdata) {
1011 arg_kill_who = "leader";
1012 arg_signal = SIGINT; /* sysvinit + systemd */
1014 return kill_machine(argc, argv, userdata);
1017 static int poweroff_machine(int argc, char *argv[], void *userdata) {
1018 arg_kill_who = "leader";
1019 arg_signal = SIGRTMIN+4; /* only systemd */
1021 return kill_machine(argc, argv, userdata);
1024 static int terminate_machine(int argc, char *argv[], void *userdata) {
1025 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1026 sd_bus *bus = userdata;
1031 polkit_agent_open_if_enabled();
1033 for (i = 1; i < argc; i++) {
1034 r = sd_bus_call_method(
1036 "org.freedesktop.machine1",
1037 "/org/freedesktop/machine1",
1038 "org.freedesktop.machine1.Manager",
1044 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
1052 static int copy_files(int argc, char *argv[], void *userdata) {
1053 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1054 sd_bus *bus = userdata;
1060 polkit_agent_open_if_enabled();
1062 copy_from = streq(argv[0], "copy-from");
1064 r = sd_bus_call_method(
1066 "org.freedesktop.machine1",
1067 "/org/freedesktop/machine1",
1068 "org.freedesktop.machine1.Manager",
1069 copy_from ? "CopyFromMachine" : "CopyToMachine",
1077 log_error("Failed to copy: %s", bus_error_message(&error, -r));
1084 static int bind_mount(int argc, char *argv[], void *userdata) {
1085 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1086 sd_bus *bus = userdata;
1091 polkit_agent_open_if_enabled();
1093 r = sd_bus_call_method(
1095 "org.freedesktop.machine1",
1096 "/org/freedesktop/machine1",
1097 "org.freedesktop.machine1.Manager",
1108 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1115 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1116 PTYForward ** forward = (PTYForward**) userdata;
1124 /* If the forwarder is already initialized, tell it to
1125 * exit on the next vhangup(), so that we still flush
1126 * out what might be queued and exit then. */
1128 r = pty_forward_set_ignore_vhangup(*forward, false);
1132 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1135 /* On error, or when the forwarder is not initialized yet, quit immediately */
1136 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1140 static int login_machine(int argc, char *argv[], void *userdata) {
1141 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1142 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1143 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1144 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1145 _cleanup_event_unref_ sd_event *event = NULL;
1146 int master = -1, r, ret = 0;
1147 sd_bus *bus = userdata;
1148 const char *pty, *match;
1154 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1155 arg_transport != BUS_TRANSPORT_MACHINE) {
1156 log_error("Login only supported on local machines.");
1160 polkit_agent_open_if_enabled();
1162 r = sd_event_default(&event);
1164 return log_error_errno(r, "Failed to get event loop: %m");
1166 r = sd_bus_attach_event(bus, event, 0);
1168 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1170 match = strjoina("type='signal',"
1171 "sender='org.freedesktop.machine1',"
1172 "path='/org/freedesktop/machine1',",
1173 "interface='org.freedesktop.machine1.Manager',"
1174 "member='MachineRemoved',"
1179 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1181 return log_error_errno(r, "Failed to add machine removal match: %m");
1183 r = sd_bus_call_method(
1185 "org.freedesktop.machine1",
1186 "/org/freedesktop/machine1",
1187 "org.freedesktop.machine1.Manager",
1193 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1197 r = sd_bus_message_read(reply, "hs", &master, &pty);
1199 return bus_log_parse_error(r);
1201 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1203 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1205 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1206 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1208 r = pty_forward_new(event, master, true, false, &forward);
1210 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1212 r = sd_event_loop(event);
1214 return log_error_errno(r, "Failed to run event loop: %m");
1216 pty_forward_get_last_char(forward, &last_char);
1217 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1219 forward = pty_forward_free(forward);
1221 if (last_char != '\n')
1222 fputc('\n', stdout);
1225 log_info("Machine %s terminated.", argv[1]);
1227 log_info("Connection to machine %s terminated.", argv[1]);
1229 sd_event_get_exit_code(event, &ret);
1233 static int remove_image(int argc, char *argv[], void *userdata) {
1234 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1235 sd_bus *bus = userdata;
1240 polkit_agent_open_if_enabled();
1242 for (i = 1; i < argc; i++) {
1243 r = sd_bus_call_method(
1245 "org.freedesktop.machine1",
1246 "/org/freedesktop/machine1",
1247 "org.freedesktop.machine1.Manager",
1253 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1261 static int rename_image(int argc, char *argv[], void *userdata) {
1262 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1263 sd_bus *bus = userdata;
1266 polkit_agent_open_if_enabled();
1268 r = sd_bus_call_method(
1270 "org.freedesktop.machine1",
1271 "/org/freedesktop/machine1",
1272 "org.freedesktop.machine1.Manager",
1276 "ss", argv[1], argv[2]);
1278 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1285 static int clone_image(int argc, char *argv[], void *userdata) {
1286 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1287 sd_bus *bus = userdata;
1290 polkit_agent_open_if_enabled();
1292 r = sd_bus_call_method(
1294 "org.freedesktop.machine1",
1295 "/org/freedesktop/machine1",
1296 "org.freedesktop.machine1.Manager",
1300 "ssb", argv[1], argv[2], arg_read_only);
1302 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1309 static int read_only_image(int argc, char *argv[], void *userdata) {
1310 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1311 sd_bus *bus = userdata;
1315 b = parse_boolean(argv[2]);
1317 log_error("Failed to parse boolean argument: %s", argv[2]);
1322 polkit_agent_open_if_enabled();
1324 r = sd_bus_call_method(
1326 "org.freedesktop.machine1",
1327 "/org/freedesktop/machine1",
1328 "org.freedesktop.machine1.Manager",
1329 "MarkImageReadOnly",
1334 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1341 static int start_machine(int argc, char *argv[], void *userdata) {
1342 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1343 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1344 sd_bus *bus = userdata;
1349 polkit_agent_open_if_enabled();
1351 r = bus_wait_for_jobs_new(bus, &w);
1355 for (i = 1; i < argc; i++) {
1356 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1357 _cleanup_free_ char *e = NULL, *unit = NULL;
1360 if (!machine_name_is_valid(argv[i])) {
1361 log_error("Invalid machine name %s.", argv[i]);
1365 e = unit_name_escape(argv[i]);
1369 unit = unit_name_build("systemd-nspawn", e, ".service");
1373 r = sd_bus_call_method(
1375 "org.freedesktop.systemd1",
1376 "/org/freedesktop/systemd1",
1377 "org.freedesktop.systemd1.Manager",
1381 "ss", unit, "fail");
1383 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1387 r = sd_bus_message_read(reply, "o", &object);
1389 return bus_log_parse_error(r);
1391 r = bus_wait_for_jobs_add(w, object);
1396 r = bus_wait_for_jobs(w, arg_quiet);
1403 static int enable_machine(int argc, char *argv[], void *userdata) {
1404 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1405 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1406 int carries_install_info = 0;
1407 const char *method = NULL;
1408 sd_bus *bus = userdata;
1413 polkit_agent_open_if_enabled();
1415 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1417 r = sd_bus_message_new_method_call(
1420 "org.freedesktop.systemd1",
1421 "/org/freedesktop/systemd1",
1422 "org.freedesktop.systemd1.Manager",
1425 return bus_log_create_error(r);
1427 r = sd_bus_message_open_container(m, 'a', "s");
1429 return bus_log_create_error(r);
1431 for (i = 1; i < argc; i++) {
1432 _cleanup_free_ char *e = NULL, *unit = NULL;
1434 if (!machine_name_is_valid(argv[i])) {
1435 log_error("Invalid machine name %s.", argv[i]);
1439 e = unit_name_escape(argv[i]);
1443 unit = unit_name_build("systemd-nspawn", e, ".service");
1447 r = sd_bus_message_append(m, "s", unit);
1449 return bus_log_create_error(r);
1452 r = sd_bus_message_close_container(m);
1454 return bus_log_create_error(r);
1456 if (streq(argv[0], "enable"))
1457 r = sd_bus_message_append(m, "bb", false, false);
1459 r = sd_bus_message_append(m, "b", false);
1461 return bus_log_create_error(r);
1463 r = sd_bus_call(bus, m, 0, &error, &reply);
1465 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1469 if (streq(argv[0], "enable")) {
1470 r = sd_bus_message_read(reply, "b", carries_install_info);
1472 return bus_log_parse_error(r);
1475 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1479 r = sd_bus_call_method(
1481 "org.freedesktop.systemd1",
1482 "/org/freedesktop/systemd1",
1483 "org.freedesktop.systemd1.Manager",
1489 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1496 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1497 const char **our_path = userdata, *line;
1505 r = sd_bus_message_read(m, "us", &priority, &line);
1507 bus_log_parse_error(r);
1511 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1514 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1517 log_full(priority, "%s", line);
1521 static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1522 const char **our_path = userdata, *path, *result;
1530 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1532 bus_log_parse_error(r);
1536 if (!streq_ptr(*our_path, path))
1539 sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1543 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1548 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1550 sd_event_exit(sd_event_source_get_event(s), EINTR);
1554 static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1555 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1556 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1557 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1558 _cleanup_event_unref_ sd_event* event = NULL;
1559 const char *path = NULL;
1566 polkit_agent_open_if_enabled();
1568 r = sd_event_default(&event);
1570 return log_error_errno(r, "Failed to get event loop: %m");
1572 r = sd_bus_attach_event(bus, event, 0);
1574 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1576 r = sd_bus_add_match(
1580 "sender='org.freedesktop.import1',"
1581 "interface='org.freedesktop.import1.Manager',"
1582 "member='TransferRemoved',"
1583 "path='/org/freedesktop/import1'",
1584 match_transfer_removed, &path);
1586 return log_error_errno(r, "Failed to install match: %m");
1588 r = sd_bus_add_match(
1592 "sender='org.freedesktop.import1',"
1593 "interface='org.freedesktop.import1.Transfer',"
1594 "member='LogMessage'",
1595 match_log_message, &path);
1597 return log_error_errno(r, "Failed to install match: %m");
1599 r = sd_bus_call(bus, m, 0, &error, &reply);
1601 log_error("Failed acquire image: %s", bus_error_message(&error, -r));
1605 r = sd_bus_message_read(reply, "uo", &id, &path);
1607 return bus_log_parse_error(r);
1609 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1612 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1614 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1615 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1617 r = sd_event_loop(event);
1619 return log_error_errno(r, "Failed to run event loop: %m");
1624 static int import_tar(int argc, char *argv[], void *userdata) {
1625 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1626 _cleanup_free_ char *ll = NULL;
1627 _cleanup_close_ int fd = -1;
1628 const char *local = NULL, *path = NULL;
1629 sd_bus *bus = userdata;
1636 if (isempty(path) || streq(path, "-"))
1642 local = basename(path);
1643 if (isempty(local) || streq(local, "-"))
1647 log_error("Need either path or local name.");
1651 r = tar_strip_suffixes(local, &ll);
1657 if (!machine_name_is_valid(local)) {
1658 log_error("Local name %s is not a suitable machine name.", local);
1663 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1665 return log_error_errno(errno, "Failed to open %s: %m", path);
1668 r = sd_bus_message_new_method_call(
1671 "org.freedesktop.import1",
1672 "/org/freedesktop/import1",
1673 "org.freedesktop.import1.Manager",
1676 return bus_log_create_error(r);
1678 r = sd_bus_message_append(
1681 fd >= 0 ? fd : STDIN_FILENO,
1686 return bus_log_create_error(r);
1688 return pull_image_common(bus, m);
1691 static int import_raw(int argc, char *argv[], void *userdata) {
1692 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1693 _cleanup_free_ char *ll = NULL;
1694 _cleanup_close_ int fd = -1;
1695 const char *local = NULL, *path = NULL;
1696 sd_bus *bus = userdata;
1703 if (isempty(path) || streq(path, "-"))
1709 local = basename(path);
1710 if (isempty(local) || streq(local, "-"))
1714 log_error("Need either path or local name.");
1718 r = raw_strip_suffixes(local, &ll);
1724 if (!machine_name_is_valid(local)) {
1725 log_error("Local name %s is not a suitable machine name.", local);
1730 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1732 return log_error_errno(errno, "Failed to open %s: %m", path);
1735 r = sd_bus_message_new_method_call(
1738 "org.freedesktop.import1",
1739 "/org/freedesktop/import1",
1740 "org.freedesktop.import1.Manager",
1743 return bus_log_create_error(r);
1745 r = sd_bus_message_append(
1748 fd >= 0 ? fd : STDIN_FILENO,
1753 return bus_log_create_error(r);
1755 return pull_image_common(bus, m);
1758 static int pull_tar(int argc, char *argv[], void *userdata) {
1759 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1760 _cleanup_free_ char *l = NULL, *ll = NULL;
1761 const char *local, *remote;
1762 sd_bus *bus = userdata;
1768 if (!http_url_is_valid(remote)) {
1769 log_error("URL '%s' is not valid.", remote);
1776 r = import_url_last_component(remote, &l);
1778 return log_error_errno(r, "Failed to get final component of URL: %m");
1783 if (isempty(local) || streq(local, "-"))
1787 r = tar_strip_suffixes(local, &ll);
1793 if (!machine_name_is_valid(local)) {
1794 log_error("Local name %s is not a suitable machine name.", local);
1799 r = sd_bus_message_new_method_call(
1802 "org.freedesktop.import1",
1803 "/org/freedesktop/import1",
1804 "org.freedesktop.import1.Manager",
1807 return bus_log_create_error(r);
1809 r = sd_bus_message_append(
1814 import_verify_to_string(arg_verify),
1817 return bus_log_create_error(r);
1819 return pull_image_common(bus, m);
1822 static int pull_raw(int argc, char *argv[], void *userdata) {
1823 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1824 _cleanup_free_ char *l = NULL, *ll = NULL;
1825 const char *local, *remote;
1826 sd_bus *bus = userdata;
1832 if (!http_url_is_valid(remote)) {
1833 log_error("URL '%s' is not valid.", remote);
1840 r = import_url_last_component(remote, &l);
1842 return log_error_errno(r, "Failed to get final component of URL: %m");
1847 if (isempty(local) || streq(local, "-"))
1851 r = raw_strip_suffixes(local, &ll);
1857 if (!machine_name_is_valid(local)) {
1858 log_error("Local name %s is not a suitable machine name.", local);
1863 r = sd_bus_message_new_method_call(
1866 "org.freedesktop.import1",
1867 "/org/freedesktop/import1",
1868 "org.freedesktop.import1.Manager",
1871 return bus_log_create_error(r);
1873 r = sd_bus_message_append(
1878 import_verify_to_string(arg_verify),
1881 return bus_log_create_error(r);
1883 return pull_image_common(bus, m);
1886 static int pull_dkr(int argc, char *argv[], void *userdata) {
1887 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1888 const char *local, *remote, *tag;
1889 sd_bus *bus = userdata;
1892 if (arg_verify != IMPORT_VERIFY_NO) {
1893 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
1898 tag = strchr(remote, ':');
1900 remote = strndupa(remote, tag - remote);
1904 if (!dkr_name_is_valid(remote)) {
1905 log_error("DKR name '%s' is invalid.", remote);
1908 if (tag && !dkr_tag_is_valid(tag)) {
1909 log_error("DKR tag '%s' is invalid.", remote);
1916 local = strchr(remote, '/');
1923 if (isempty(local) || streq(local, "-"))
1927 if (!machine_name_is_valid(local)) {
1928 log_error("Local name %s is not a suitable machine name.", local);
1933 r = sd_bus_message_new_method_call(
1936 "org.freedesktop.import1",
1937 "/org/freedesktop/import1",
1938 "org.freedesktop.import1.Manager",
1941 return bus_log_create_error(r);
1943 r = sd_bus_message_append(
1950 import_verify_to_string(arg_verify),
1953 return bus_log_create_error(r);
1955 return pull_image_common(bus, m);
1958 typedef struct TransferInfo {
1966 static int compare_transfer_info(const void *a, const void *b) {
1967 const TransferInfo *x = a, *y = b;
1969 return strcmp(x->local, y->local);
1972 static int list_transfers(int argc, char *argv[], void *userdata) {
1973 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
1974 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1975 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1976 _cleanup_free_ TransferInfo *transfers = NULL;
1977 size_t n_transfers = 0, n_allocated = 0, j;
1978 const char *type, *remote, *local, *object;
1979 sd_bus *bus = userdata;
1980 uint32_t id, max_id = 0;
1984 pager_open_if_enabled();
1986 r = sd_bus_call_method(
1988 "org.freedesktop.import1",
1989 "/org/freedesktop/import1",
1990 "org.freedesktop.import1.Manager",
1996 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
2000 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
2002 return bus_log_parse_error(r);
2004 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
2007 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
2010 transfers[n_transfers].id = id;
2011 transfers[n_transfers].type = type;
2012 transfers[n_transfers].remote = remote;
2013 transfers[n_transfers].local = local;
2014 transfers[n_transfers].progress = progress;
2034 return bus_log_parse_error(r);
2036 r = sd_bus_message_exit_container(reply);
2038 return bus_log_parse_error(r);
2040 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
2043 printf("%-*s %-*s %-*s %-*s %-*s\n",
2044 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
2046 (int) max_type, "TYPE",
2047 (int) max_local, "LOCAL",
2048 (int) max_remote, "REMOTE");
2050 for (j = 0; j < n_transfers; j++)
2051 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
2052 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
2053 (int) 6, (unsigned) (transfers[j].progress * 100),
2054 (int) max_type, transfers[j].type,
2055 (int) max_local, transfers[j].local,
2056 (int) max_remote, transfers[j].remote);
2059 printf("\n%zu transfers listed.\n", n_transfers);
2064 static int cancel_transfer(int argc, char *argv[], void *userdata) {
2065 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2066 sd_bus *bus = userdata;
2071 polkit_agent_open_if_enabled();
2073 for (i = 1; i < argc; i++) {
2076 r = safe_atou32(argv[i], &id);
2078 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
2080 r = sd_bus_call_method(
2082 "org.freedesktop.import1",
2083 "/org/freedesktop/import1",
2084 "org.freedesktop.import1.Manager",
2090 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
2098 static int set_limit(int argc, char *argv[], void *userdata) {
2099 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2100 sd_bus *bus = userdata;
2104 if (streq(argv[argc-1], "-"))
2105 limit = (uint64_t) -1;
2109 r = parse_size(argv[argc-1], 1024, &off);
2111 return log_error("Failed to parse size: %s", argv[argc-1]);
2113 limit = (uint64_t) off;
2117 /* With two arguments changes the quota limit of the
2118 * specified image */
2119 r = sd_bus_call_method(
2121 "org.freedesktop.machine1",
2122 "/org/freedesktop/machine1",
2123 "org.freedesktop.machine1.Manager",
2127 "st", argv[1], limit);
2129 /* With one argument changes the pool quota limit */
2130 r = sd_bus_call_method(
2132 "org.freedesktop.machine1",
2133 "/org/freedesktop/machine1",
2134 "org.freedesktop.machine1.Manager",
2141 log_error("Could not set limit: %s", bus_error_message(&error, -r));
2148 static int help(int argc, char *argv[], void *userdata) {
2150 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2151 "Send control commands to or query the virtual machine and container\n"
2152 "registration manager.\n\n"
2153 " -h --help Show this help\n"
2154 " --version Show package version\n"
2155 " --no-pager Do not pipe output into a pager\n"
2156 " --no-legend Do not show the headers and footers\n"
2157 " --no-ask-password Do not ask for system passwords\n"
2158 " -H --host=[USER@]HOST Operate on remote host\n"
2159 " -M --machine=CONTAINER Operate on local container\n"
2160 " -p --property=NAME Show only properties by this name\n"
2161 " -q --quiet Suppress output\n"
2162 " -a --all Show all properties, including empty ones\n"
2163 " -l --full Do not ellipsize output\n"
2164 " --kill-who=WHO Who to send signal to\n"
2165 " -s --signal=SIGNAL Which signal to send\n"
2166 " --read-only Create read-only bind mount\n"
2167 " --mkdir Create directory before bind mounting, if missing\n"
2168 " -n --lines=INTEGER Number of journal entries to show\n"
2169 " -o --output=STRING Change journal output mode (short,\n"
2170 " short-monotonic, verbose, export, json,\n"
2171 " json-pretty, json-sse, cat)\n"
2172 " --verify=MODE Verification mode for downloaded images (no,\n"
2173 " checksum, signature)\n"
2174 " --force Download image even if already exists\n"
2175 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
2177 "Machine Commands:\n"
2178 " list List running VMs and containers\n"
2179 " status NAME... Show VM/container details\n"
2180 " show NAME... Show properties of one or more VMs/containers\n"
2181 " start NAME... Start container as a service\n"
2182 " login NAME Get a login prompt on a container\n"
2183 " enable NAME... Enable automatic container start at boot\n"
2184 " disable NAME... Disable automatic container start at boot\n"
2185 " poweroff NAME... Power off one or more containers\n"
2186 " reboot NAME... Reboot one or more containers\n"
2187 " terminate NAME... Terminate one or more VMs/containers\n"
2188 " kill NAME... Send signal to processes of a VM/container\n"
2189 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2190 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2191 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2193 " list-images Show available container and VM images\n"
2194 " image-status NAME... Show image details\n"
2195 " show-image NAME... Show properties of image\n"
2196 " clone NAME NAME Clone an image\n"
2197 " rename NAME NAME Rename an image\n"
2198 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2199 " remove NAME... Remove an image\n"
2200 " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
2201 "Image Transfer Commands:\n"
2202 " import-tar FILE [NAME] Import a local TAR container image\n"
2203 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
2204 " pull-tar URL [NAME] Download a TAR container image\n"
2205 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2206 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2207 " list-transfers Show list of downloads in progress\n"
2208 " cancel-transfer Cancel a download\n"
2209 , program_invocation_short_name);
2214 static int parse_argv(int argc, char *argv[]) {
2217 ARG_VERSION = 0x100,
2223 ARG_NO_ASK_PASSWORD,
2229 static const struct option options[] = {
2230 { "help", no_argument, NULL, 'h' },
2231 { "version", no_argument, NULL, ARG_VERSION },
2232 { "property", required_argument, NULL, 'p' },
2233 { "all", no_argument, NULL, 'a' },
2234 { "full", no_argument, NULL, 'l' },
2235 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2236 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2237 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2238 { "signal", required_argument, NULL, 's' },
2239 { "host", required_argument, NULL, 'H' },
2240 { "machine", required_argument, NULL, 'M' },
2241 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2242 { "mkdir", no_argument, NULL, ARG_MKDIR },
2243 { "quiet", no_argument, NULL, 'q' },
2244 { "lines", required_argument, NULL, 'n' },
2245 { "output", required_argument, NULL, 'o' },
2246 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2247 { "verify", required_argument, NULL, ARG_VERIFY },
2248 { "force", no_argument, NULL, ARG_FORCE },
2249 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2258 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2263 return help(0, NULL, NULL);
2266 puts(PACKAGE_STRING);
2267 puts(SYSTEMD_FEATURES);
2271 r = strv_extend(&arg_property, optarg);
2275 /* If the user asked for a particular
2276 * property, show it to him, even if it is
2290 if (safe_atou(optarg, &arg_lines) < 0) {
2291 log_error("Failed to parse lines '%s'", optarg);
2297 arg_output = output_mode_from_string(optarg);
2298 if (arg_output < 0) {
2299 log_error("Unknown output '%s'.", optarg);
2305 arg_no_pager = true;
2313 arg_kill_who = optarg;
2317 arg_signal = signal_from_string_try_harder(optarg);
2318 if (arg_signal < 0) {
2319 log_error("Failed to parse signal string %s.", optarg);
2324 case ARG_NO_ASK_PASSWORD:
2325 arg_ask_password = false;
2329 arg_transport = BUS_TRANSPORT_REMOTE;
2334 arg_transport = BUS_TRANSPORT_MACHINE;
2339 arg_read_only = true;
2351 arg_verify = import_verify_from_string(optarg);
2352 if (arg_verify < 0) {
2353 log_error("Failed to parse --verify= setting: %s", optarg);
2362 case ARG_DKR_INDEX_URL:
2363 if (!http_url_is_valid(optarg)) {
2364 log_error("Index URL is invalid: %s", optarg);
2368 arg_dkr_index_url = optarg;
2375 assert_not_reached("Unhandled option");
2381 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2383 static const Verb verbs[] = {
2384 { "help", VERB_ANY, VERB_ANY, 0, help },
2385 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2386 { "list-images", VERB_ANY, 1, 0, list_images },
2387 { "status", 2, VERB_ANY, 0, show_machine },
2388 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
2389 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2390 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2391 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2392 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2393 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2394 { "kill", 2, VERB_ANY, 0, kill_machine },
2395 { "login", 2, 2, 0, login_machine },
2396 { "bind", 3, 4, 0, bind_mount },
2397 { "copy-to", 3, 4, 0, copy_files },
2398 { "copy-from", 3, 4, 0, copy_files },
2399 { "remove", 2, VERB_ANY, 0, remove_image },
2400 { "rename", 3, 3, 0, rename_image },
2401 { "clone", 3, 3, 0, clone_image },
2402 { "read-only", 2, 3, 0, read_only_image },
2403 { "start", 2, VERB_ANY, 0, start_machine },
2404 { "enable", 2, VERB_ANY, 0, enable_machine },
2405 { "disable", 2, VERB_ANY, 0, enable_machine },
2406 { "import-tar", 2, 3, 0, import_tar },
2407 { "import-raw", 2, 3, 0, import_raw },
2408 { "pull-tar", 2, 3, 0, pull_tar },
2409 { "pull-raw", 2, 3, 0, pull_raw },
2410 { "pull-dkr", 2, 3, 0, pull_dkr },
2411 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2412 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2413 { "set-limit", 2, 3, 0, set_limit },
2417 return dispatch_verb(argc, argv, verbs, bus);
2420 int main(int argc, char*argv[]) {
2421 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2424 setlocale(LC_ALL, "");
2425 log_parse_environment();
2428 r = parse_argv(argc, argv);
2432 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2434 log_error_errno(r, "Failed to create bus connection: %m");
2438 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2440 r = machinectl_main(argc, argv, bus);
2444 polkit_agent_close();
2446 strv_free(arg_property);
2448 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;