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 pull 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 pull_tar(int argc, char *argv[], void *userdata) {
1625 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1626 _cleanup_free_ char *l = NULL, *ll = NULL;
1627 const char *local, *remote;
1628 sd_bus *bus = userdata;
1634 if (!http_url_is_valid(remote)) {
1635 log_error("URL '%s' is not valid.", remote);
1642 r = import_url_last_component(remote, &l);
1644 return log_error_errno(r, "Failed to get final component of URL: %m");
1649 if (isempty(local) || streq(local, "-"))
1653 r = tar_strip_suffixes(local, &ll);
1655 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1659 if (!machine_name_is_valid(local)) {
1660 log_error("Local name %s is not a suitable machine name.", local);
1665 r = sd_bus_message_new_method_call(
1668 "org.freedesktop.import1",
1669 "/org/freedesktop/import1",
1670 "org.freedesktop.import1.Manager",
1673 return bus_log_create_error(r);
1675 r = sd_bus_message_append(
1680 import_verify_to_string(arg_verify),
1683 return bus_log_create_error(r);
1685 return pull_image_common(bus, m);
1688 static int pull_raw(int argc, char *argv[], void *userdata) {
1689 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1690 _cleanup_free_ char *l = NULL, *ll = NULL;
1691 const char *local, *remote;
1692 sd_bus *bus = userdata;
1698 if (!http_url_is_valid(remote)) {
1699 log_error("URL '%s' is not valid.", remote);
1706 r = import_url_last_component(remote, &l);
1708 return log_error_errno(r, "Failed to get final component of URL: %m");
1713 if (isempty(local) || streq(local, "-"))
1717 r = raw_strip_suffixes(local, &ll);
1719 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1723 if (!machine_name_is_valid(local)) {
1724 log_error("Local name %s is not a suitable machine name.", local);
1729 r = sd_bus_message_new_method_call(
1732 "org.freedesktop.import1",
1733 "/org/freedesktop/import1",
1734 "org.freedesktop.import1.Manager",
1737 return bus_log_create_error(r);
1739 r = sd_bus_message_append(
1744 import_verify_to_string(arg_verify),
1747 return bus_log_create_error(r);
1749 return pull_image_common(bus, m);
1752 static int pull_dkr(int argc, char *argv[], void *userdata) {
1753 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1754 const char *local, *remote, *tag;
1755 sd_bus *bus = userdata;
1758 if (arg_verify != IMPORT_VERIFY_NO) {
1759 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
1764 tag = strchr(remote, ':');
1766 remote = strndupa(remote, tag - remote);
1770 if (!dkr_name_is_valid(remote)) {
1771 log_error("DKR name '%s' is invalid.", remote);
1774 if (tag && !dkr_tag_is_valid(tag)) {
1775 log_error("DKR tag '%s' is invalid.", remote);
1782 local = strchr(remote, '/');
1789 if (isempty(local) || streq(local, "-"))
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(
1816 import_verify_to_string(arg_verify),
1819 return bus_log_create_error(r);
1821 return pull_image_common(bus, m);
1824 typedef struct TransferInfo {
1832 static int compare_transfer_info(const void *a, const void *b) {
1833 const TransferInfo *x = a, *y = b;
1835 return strcmp(x->local, y->local);
1838 static int list_transfers(int argc, char *argv[], void *userdata) {
1839 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
1840 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1841 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1842 _cleanup_free_ TransferInfo *transfers = NULL;
1843 size_t n_transfers = 0, n_allocated = 0, j;
1844 const char *type, *remote, *local, *object;
1845 sd_bus *bus = userdata;
1846 uint32_t id, max_id = 0;
1850 pager_open_if_enabled();
1852 r = sd_bus_call_method(
1854 "org.freedesktop.import1",
1855 "/org/freedesktop/import1",
1856 "org.freedesktop.import1.Manager",
1862 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
1866 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
1868 return bus_log_parse_error(r);
1870 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
1873 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
1876 transfers[n_transfers].id = id;
1877 transfers[n_transfers].type = type;
1878 transfers[n_transfers].remote = remote;
1879 transfers[n_transfers].local = local;
1880 transfers[n_transfers].progress = progress;
1900 return bus_log_parse_error(r);
1902 r = sd_bus_message_exit_container(reply);
1904 return bus_log_parse_error(r);
1906 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
1909 printf("%-*s %-*s %-*s %-*s %-*s\n",
1910 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
1912 (int) max_type, "TYPE",
1913 (int) max_local, "LOCAL",
1914 (int) max_remote, "REMOTE");
1916 for (j = 0; j < n_transfers; j++)
1917 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
1918 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
1919 (int) 6, (unsigned) (transfers[j].progress * 100),
1920 (int) max_type, transfers[j].type,
1921 (int) max_local, transfers[j].local,
1922 (int) max_remote, transfers[j].remote);
1925 printf("\n%zu transfers listed.\n", n_transfers);
1930 static int cancel_transfer(int argc, char *argv[], void *userdata) {
1931 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1932 sd_bus *bus = userdata;
1937 polkit_agent_open_if_enabled();
1939 for (i = 1; i < argc; i++) {
1942 r = safe_atou32(argv[i], &id);
1944 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
1946 r = sd_bus_call_method(
1948 "org.freedesktop.import1",
1949 "/org/freedesktop/import1",
1950 "org.freedesktop.import1.Manager",
1956 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
1964 static int help(int argc, char *argv[], void *userdata) {
1966 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1967 "Send control commands to or query the virtual machine and container\n"
1968 "registration manager.\n\n"
1969 " -h --help Show this help\n"
1970 " --version Show package version\n"
1971 " --no-pager Do not pipe output into a pager\n"
1972 " --no-legend Do not show the headers and footers\n"
1973 " --no-ask-password Do not ask for system passwords\n"
1974 " -H --host=[USER@]HOST Operate on remote host\n"
1975 " -M --machine=CONTAINER Operate on local container\n"
1976 " -p --property=NAME Show only properties by this name\n"
1977 " -q --quiet Suppress output\n"
1978 " -a --all Show all properties, including empty ones\n"
1979 " -l --full Do not ellipsize output\n"
1980 " --kill-who=WHO Who to send signal to\n"
1981 " -s --signal=SIGNAL Which signal to send\n"
1982 " --read-only Create read-only bind mount\n"
1983 " --mkdir Create directory before bind mounting, if missing\n"
1984 " -n --lines=INTEGER Number of journal entries to show\n"
1985 " -o --output=STRING Change journal output mode (short,\n"
1986 " short-monotonic, verbose, export, json,\n"
1987 " json-pretty, json-sse, cat)\n"
1988 " --verify=MODE Verification mode for downloaded images (no,\n"
1989 " checksum, signature)\n"
1990 " --force Download image even if already exists\n"
1991 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
1993 "Machine Commands:\n"
1994 " list List running VMs and containers\n"
1995 " status NAME... Show VM/container details\n"
1996 " show NAME... Show properties of one or more VMs/containers\n"
1997 " start NAME... Start container as a service\n"
1998 " login NAME Get a login prompt on a container\n"
1999 " enable NAME... Enable automatic container start at boot\n"
2000 " disable NAME... Disable automatic container start at boot\n"
2001 " poweroff NAME... Power off one or more containers\n"
2002 " reboot NAME... Reboot one or more containers\n"
2003 " terminate NAME... Terminate one or more VMs/containers\n"
2004 " kill NAME... Send signal to processes of a VM/container\n"
2005 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
2006 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
2007 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
2009 " list-images Show available container and VM images\n"
2010 " image-status NAME... Show image details\n"
2011 " show-image NAME... Show properties of image\n"
2012 " clone NAME NAME Clone an image\n"
2013 " rename NAME NAME Rename an image\n"
2014 " read-only NAME [BOOL] Mark or unmark image read-only\n"
2015 " remove NAME... Remove an image\n\n"
2016 "Image Transfer Commands:\n"
2017 " pull-tar URL [NAME] Download a TAR container image\n"
2018 " pull-raw URL [NAME] Download a RAW container or VM image\n"
2019 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
2020 " list-transfers Show list of downloads in progress\n"
2021 " cancel-transfer Cancel a download\n"
2022 , program_invocation_short_name);
2027 static int parse_argv(int argc, char *argv[]) {
2030 ARG_VERSION = 0x100,
2036 ARG_NO_ASK_PASSWORD,
2042 static const struct option options[] = {
2043 { "help", no_argument, NULL, 'h' },
2044 { "version", no_argument, NULL, ARG_VERSION },
2045 { "property", required_argument, NULL, 'p' },
2046 { "all", no_argument, NULL, 'a' },
2047 { "full", no_argument, NULL, 'l' },
2048 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2049 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2050 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2051 { "signal", required_argument, NULL, 's' },
2052 { "host", required_argument, NULL, 'H' },
2053 { "machine", required_argument, NULL, 'M' },
2054 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2055 { "mkdir", no_argument, NULL, ARG_MKDIR },
2056 { "quiet", no_argument, NULL, 'q' },
2057 { "lines", required_argument, NULL, 'n' },
2058 { "output", required_argument, NULL, 'o' },
2059 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2060 { "verify", required_argument, NULL, ARG_VERIFY },
2061 { "force", no_argument, NULL, ARG_FORCE },
2062 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2071 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2076 return help(0, NULL, NULL);
2079 puts(PACKAGE_STRING);
2080 puts(SYSTEMD_FEATURES);
2084 r = strv_extend(&arg_property, optarg);
2088 /* If the user asked for a particular
2089 * property, show it to him, even if it is
2103 if (safe_atou(optarg, &arg_lines) < 0) {
2104 log_error("Failed to parse lines '%s'", optarg);
2110 arg_output = output_mode_from_string(optarg);
2111 if (arg_output < 0) {
2112 log_error("Unknown output '%s'.", optarg);
2118 arg_no_pager = true;
2126 arg_kill_who = optarg;
2130 arg_signal = signal_from_string_try_harder(optarg);
2131 if (arg_signal < 0) {
2132 log_error("Failed to parse signal string %s.", optarg);
2137 case ARG_NO_ASK_PASSWORD:
2138 arg_ask_password = false;
2142 arg_transport = BUS_TRANSPORT_REMOTE;
2147 arg_transport = BUS_TRANSPORT_MACHINE;
2152 arg_read_only = true;
2164 arg_verify = import_verify_from_string(optarg);
2165 if (arg_verify < 0) {
2166 log_error("Failed to parse --verify= setting: %s", optarg);
2175 case ARG_DKR_INDEX_URL:
2176 if (!http_url_is_valid(optarg)) {
2177 log_error("Index URL is invalid: %s", optarg);
2181 arg_dkr_index_url = optarg;
2188 assert_not_reached("Unhandled option");
2194 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2196 static const Verb verbs[] = {
2197 { "help", VERB_ANY, VERB_ANY, 0, help },
2198 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2199 { "list-images", VERB_ANY, 1, 0, list_images },
2200 { "status", 2, VERB_ANY, 0, show_machine },
2201 { "image-status", VERB_ANY, VERB_ANY, 0, show_image },
2202 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2203 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2204 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2205 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2206 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2207 { "kill", 2, VERB_ANY, 0, kill_machine },
2208 { "login", 2, 2, 0, login_machine },
2209 { "bind", 3, 4, 0, bind_mount },
2210 { "copy-to", 3, 4, 0, copy_files },
2211 { "copy-from", 3, 4, 0, copy_files },
2212 { "remove", 2, VERB_ANY, 0, remove_image },
2213 { "rename", 3, 3, 0, rename_image },
2214 { "clone", 3, 3, 0, clone_image },
2215 { "read-only", 2, 3, 0, read_only_image },
2216 { "start", 2, VERB_ANY, 0, start_machine },
2217 { "enable", 2, VERB_ANY, 0, enable_machine },
2218 { "disable", 2, VERB_ANY, 0, enable_machine },
2219 { "pull-tar", 2, 3, 0, pull_tar },
2220 { "pull-raw", 2, 3, 0, pull_raw },
2221 { "pull-dkr", 2, 3, 0, pull_dkr },
2222 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2223 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2227 return dispatch_verb(argc, argv, verbs, bus);
2230 int main(int argc, char*argv[]) {
2231 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2234 setlocale(LC_ALL, "");
2235 log_parse_environment();
2238 r = parse_argv(argc, argv);
2242 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2244 log_error_errno(r, "Failed to create bus connection: %m");
2248 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
2250 r = machinectl_main(argc, argv, bus);
2254 polkit_agent_close();
2256 strv_free(arg_property);
2258 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;