1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <sys/mount.h>
40 #include "spawn-polkit-agent.h"
42 #include "bus-error.h"
45 #include "unit-name.h"
46 #include "cgroup-show.h"
47 #include "logs-show.h"
48 #include "cgroup-util.h"
50 #include "event-util.h"
51 #include "path-util.h"
55 #include "import-util.h"
57 static char **arg_property = NULL;
58 static bool arg_all = false;
59 static bool arg_full = false;
60 static bool arg_no_pager = false;
61 static bool arg_legend = true;
62 static const char *arg_kill_who = NULL;
63 static int arg_signal = SIGTERM;
64 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
65 static char *arg_host = NULL;
66 static bool arg_read_only = false;
67 static bool arg_mkdir = false;
68 static bool arg_quiet = false;
69 static bool arg_ask_password = true;
70 static unsigned arg_lines = 10;
71 static OutputMode arg_output = OUTPUT_SHORT;
72 static bool arg_force = false;
73 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
74 static const char* arg_dkr_index_url = NULL;
76 static void pager_open_if_enabled(void) {
84 static void polkit_agent_open_if_enabled(void) {
86 /* Open the polkit agent as a child process if necessary */
88 if (!arg_ask_password)
91 if (arg_transport != BUS_TRANSPORT_LOCAL)
97 static OutputFlags get_output_flags(void) {
99 arg_all * OUTPUT_SHOW_ALL |
100 arg_full * OUTPUT_FULL_WIDTH |
101 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
102 on_tty() * OUTPUT_COLOR |
103 !arg_quiet * OUTPUT_WARN_CUTOFF;
106 typedef struct MachineInfo {
112 static int compare_machine_info(const void *a, const void *b) {
113 const MachineInfo *x = a, *y = b;
115 return strcmp(x->name, y->name);
118 static int list_machines(int argc, char *argv[], void *userdata) {
120 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
121 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
122 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
123 _cleanup_free_ MachineInfo *machines = NULL;
124 const char *name, *class, *service, *object;
125 size_t n_machines = 0, n_allocated = 0, j;
126 sd_bus *bus = userdata;
131 pager_open_if_enabled();
133 r = sd_bus_call_method(
135 "org.freedesktop.machine1",
136 "/org/freedesktop/machine1",
137 "org.freedesktop.machine1.Manager",
143 log_error("Could not get machines: %s", bus_error_message(&error, -r));
147 r = sd_bus_message_enter_container(reply, 'a', "(ssso)");
149 return bus_log_parse_error(r);
151 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
154 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
157 machines[n_machines].name = name;
158 machines[n_machines].class = class;
159 machines[n_machines].service = service;
176 return bus_log_parse_error(r);
178 r = sd_bus_message_exit_container(reply);
180 return bus_log_parse_error(r);
182 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
185 printf("%-*s %-*s %-*s\n",
186 (int) max_name, "MACHINE",
187 (int) max_class, "CLASS",
188 (int) max_service, "SERVICE");
190 for (j = 0; j < n_machines; j++)
191 printf("%-*s %-*s %-*s\n",
192 (int) max_name, machines[j].name,
193 (int) max_class, machines[j].class,
194 (int) max_service, machines[j].service);
197 printf("\n%zu machines listed.\n", n_machines);
202 typedef struct ImageInfo {
211 static int compare_image_info(const void *a, const void *b) {
212 const ImageInfo *x = a, *y = b;
214 return strcmp(x->name, y->name);
217 static int list_images(int argc, char *argv[], void *userdata) {
219 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
220 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("USAGE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
221 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
222 _cleanup_free_ ImageInfo *images = NULL;
223 size_t n_images = 0, n_allocated = 0, j;
224 const char *name, *type, *object;
225 sd_bus *bus = userdata;
226 uint64_t crtime, mtime, size;
231 pager_open_if_enabled();
233 r = sd_bus_call_method(
235 "org.freedesktop.machine1",
236 "/org/freedesktop/machine1",
237 "org.freedesktop.machine1.Manager",
243 log_error("Could not get images: %s", bus_error_message(&error, -r));
247 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
249 return bus_log_parse_error(r);
251 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
252 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
255 if (name[0] == '.' && !arg_all)
258 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
261 images[n_images].name = name;
262 images[n_images].type = type;
263 images[n_images].read_only = read_only;
264 images[n_images].crtime = crtime;
265 images[n_images].mtime = mtime;
266 images[n_images].size = size;
277 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
283 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
288 if (size != (uint64_t) -1) {
289 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
297 return bus_log_parse_error(r);
299 r = sd_bus_message_exit_container(reply);
301 return bus_log_parse_error(r);
303 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
306 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
307 (int) max_name, "NAME",
308 (int) max_type, "TYPE",
310 (int) max_size, "USAGE",
311 (int) max_crtime, "CREATED",
312 (int) max_mtime, "MODIFIED");
314 for (j = 0; j < n_images; j++) {
315 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
317 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
318 (int) max_name, images[j].name,
319 (int) max_type, images[j].type,
320 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
321 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
322 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
323 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
327 printf("\n%zu images listed.\n", n_images);
332 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
333 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
335 _cleanup_free_ char *path = NULL;
343 if (arg_transport == BUS_TRANSPORT_REMOTE)
346 path = unit_dbus_path_from_name(unit);
350 r = sd_bus_get_property(
352 "org.freedesktop.systemd1",
354 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
360 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
364 r = sd_bus_message_read(reply, "s", &cgroup);
366 return bus_log_parse_error(r);
371 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
380 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
384 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
385 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
393 r = sd_bus_call_method(bus,
394 "org.freedesktop.machine1",
395 "/org/freedesktop/machine1",
396 "org.freedesktop.machine1.Manager",
397 "GetMachineAddresses",
404 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
406 return bus_log_parse_error(r);
408 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
412 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
414 r = sd_bus_message_read(reply, "i", &family);
416 return bus_log_parse_error(r);
418 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
420 return bus_log_parse_error(r);
422 fputs(prefix, stdout);
423 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
424 if (family == AF_INET6 && ifi > 0)
428 r = sd_bus_message_exit_container(reply);
430 return bus_log_parse_error(r);
432 if (prefix != prefix2)
436 return bus_log_parse_error(r);
438 r = sd_bus_message_exit_container(reply);
440 return bus_log_parse_error(r);
445 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
446 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
447 const char *k, *v, *pretty = NULL;
454 r = sd_bus_call_method(bus,
455 "org.freedesktop.machine1",
456 "/org/freedesktop/machine1",
457 "org.freedesktop.machine1.Manager",
458 "GetMachineOSRelease",
465 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
467 return bus_log_parse_error(r);
469 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
470 if (streq(k, "PRETTY_NAME"))
475 return bus_log_parse_error(r);
477 r = sd_bus_message_exit_container(reply);
479 return bus_log_parse_error(r);
482 printf("%s%s\n", prefix, pretty);
487 typedef struct MachineStatusInfo {
493 char *root_directory;
495 struct dual_timestamp timestamp;
500 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
501 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
502 char since2[FORMAT_TIMESTAMP_MAX], *s2;
508 fputs(strna(i->name), stdout);
510 if (!sd_id128_equal(i->id, SD_ID128_NULL))
511 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
515 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
516 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
519 printf("\t Since: %s; %s\n", s2, s1);
521 printf("\t Since: %s\n", s2);
524 _cleanup_free_ char *t = NULL;
526 printf("\t Leader: %u", (unsigned) i->leader);
528 get_process_comm(i->leader, &t);
536 printf("\t Service: %s", i->service);
539 printf("; class %s", i->class);
543 printf("\t Class: %s\n", i->class);
545 if (i->root_directory)
546 printf("\t Root: %s\n", i->root_directory);
548 if (i->n_netif > 0) {
551 fputs("\t Iface:", stdout);
553 for (c = 0; c < i->n_netif; c++) {
554 char name[IF_NAMESIZE+1] = "";
556 if (if_indextoname(i->netif[c], name)) {
565 printf(" %i", i->netif[c]);
571 print_addresses(bus, i->name, ifi,
575 print_os_release(bus, i->name, "\t OS: ");
578 printf("\t Unit: %s\n", i->unit);
579 show_unit_cgroup(bus, i->unit, i->leader);
581 if (arg_transport == BUS_TRANSPORT_LOCAL) {
583 show_journal_by_unit(
588 i->timestamp.monotonic,
591 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
592 SD_JOURNAL_LOCAL_ONLY,
599 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
600 MachineStatusInfo *i = userdata;
605 assert_cc(sizeof(int32_t) == sizeof(int));
606 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
612 i->n_netif = l / sizeof(int32_t);
613 i->netif = memdup(v, l);
620 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
622 static const struct bus_properties_map map[] = {
623 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
624 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
625 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
626 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
627 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
628 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
629 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
630 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
631 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
632 { "NetworkInterfaces", "ai", map_netif, 0 },
636 MachineStatusInfo info = {};
644 r = bus_map_all_properties(bus,
645 "org.freedesktop.machine1",
650 return log_error_errno(r, "Could not get properties: %m");
656 print_machine_status_info(bus, &info);
662 free(info.root_directory);
668 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
680 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
682 log_error_errno(r, "Could not get properties: %m");
687 static int show_machine(int argc, char *argv[], void *userdata) {
689 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
690 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
691 bool properties, new_line = false;
692 sd_bus *bus = userdata;
697 properties = !strstr(argv[0], "status");
699 pager_open_if_enabled();
701 if (properties && argc <= 1) {
703 /* If no argument is specified, inspect the manager
705 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
710 for (i = 1; i < argc; i++) {
711 const char *path = NULL;
713 r = sd_bus_call_method(
715 "org.freedesktop.machine1",
716 "/org/freedesktop/machine1",
717 "org.freedesktop.machine1.Manager",
723 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
727 r = sd_bus_message_read(reply, "o", &path);
729 return bus_log_parse_error(r);
732 r = show_machine_properties(bus, path, &new_line);
734 r = show_machine_info(argv[0], bus, path, &new_line);
740 typedef struct ImageStatusInfo {
749 uint64_t usage_exclusive;
750 uint64_t limit_exclusive;
753 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
754 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
755 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
756 char bs[FORMAT_BYTES_MAX], *s3;
757 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
763 fputs(i->name, stdout);
768 printf("\t Type: %s\n", i->type);
771 printf("\t Path: %s\n", i->path);
773 printf("\t RO: %s%s%s\n",
774 i->read_only ? ansi_highlight_red() : "",
775 i->read_only ? "read-only" : "writable",
776 i->read_only ? ansi_highlight_off() : "");
778 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
779 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
781 printf("\t Created: %s; %s\n", s2, s1);
783 printf("\t Created: %s\n", s2);
785 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
786 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
788 printf("\tModified: %s; %s\n", s2, s1);
790 printf("\tModified: %s\n", s2);
792 s3 = format_bytes(bs, sizeof(bs), i->usage);
793 s4 = i->usage_exclusive != i->usage ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->usage_exclusive) : NULL;
795 printf("\t Usage: %s (exclusive: %s)\n", s3, s4);
797 printf("\t Usage: %s\n", s3);
799 s3 = format_bytes(bs, sizeof(bs), i->limit);
800 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
802 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
804 printf("\t Limit: %s\n", s3);
807 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
809 static const struct bus_properties_map map[] = {
810 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
811 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
812 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
813 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
814 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
815 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
816 { "Usage", "t", NULL, offsetof(ImageStatusInfo, usage) },
817 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
818 { "UsageExclusive", "t", NULL, offsetof(ImageStatusInfo, usage_exclusive) },
819 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
823 ImageStatusInfo info = {};
831 r = bus_map_all_properties(bus,
832 "org.freedesktop.machine1",
837 return log_error_errno(r, "Could not get properties: %m");
843 print_image_status_info(bus, &info);
852 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
864 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
866 log_error_errno(r, "Could not get properties: %m");
871 static int show_image(int argc, char *argv[], void *userdata) {
873 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
874 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
875 bool properties, new_line = false;
876 sd_bus *bus = userdata;
881 properties = !strstr(argv[0], "status");
883 pager_open_if_enabled();
885 if (properties && argc <= 1) {
887 /* If no argument is specified, inspect the manager
889 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
894 for (i = 1; i < argc; i++) {
895 const char *path = NULL;
897 r = sd_bus_call_method(
899 "org.freedesktop.machine1",
900 "/org/freedesktop/machine1",
901 "org.freedesktop.machine1.Manager",
907 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
911 r = sd_bus_message_read(reply, "o", &path);
913 return bus_log_parse_error(r);
916 r = show_image_properties(bus, path, &new_line);
918 r = show_image_info(argv[0], bus, path, &new_line);
924 static int kill_machine(int argc, char *argv[], void *userdata) {
925 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
926 sd_bus *bus = userdata;
931 polkit_agent_open_if_enabled();
934 arg_kill_who = "all";
936 for (i = 1; i < argc; i++) {
937 r = sd_bus_call_method(
939 "org.freedesktop.machine1",
940 "/org/freedesktop/machine1",
941 "org.freedesktop.machine1.Manager",
945 "ssi", argv[i], arg_kill_who, arg_signal);
947 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
955 static int reboot_machine(int argc, char *argv[], void *userdata) {
956 arg_kill_who = "leader";
957 arg_signal = SIGINT; /* sysvinit + systemd */
959 return kill_machine(argc, argv, userdata);
962 static int poweroff_machine(int argc, char *argv[], void *userdata) {
963 arg_kill_who = "leader";
964 arg_signal = SIGRTMIN+4; /* only systemd */
966 return kill_machine(argc, argv, userdata);
969 static int terminate_machine(int argc, char *argv[], void *userdata) {
970 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
971 sd_bus *bus = userdata;
976 polkit_agent_open_if_enabled();
978 for (i = 1; i < argc; i++) {
981 r = sd_bus_call_method(
983 "org.freedesktop.machine1",
984 "/org/freedesktop/machine1",
985 "org.freedesktop.machine1.Manager",
991 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
999 static int copy_files(int argc, char *argv[], void *userdata) {
1000 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1001 sd_bus *bus = userdata;
1007 copy_from = streq(argv[0], "copy-from");
1009 r = sd_bus_call_method(
1011 "org.freedesktop.machine1",
1012 "/org/freedesktop/machine1",
1013 "org.freedesktop.machine1.Manager",
1014 copy_from ? "CopyFromMachine" : "CopyToMachine",
1022 log_error("Failed to copy: %s", bus_error_message(&error, -r));
1029 static int bind_mount(int argc, char *argv[], void *userdata) {
1030 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1031 sd_bus *bus = userdata;
1036 r = sd_bus_call_method(
1038 "org.freedesktop.machine1",
1039 "/org/freedesktop/machine1",
1040 "org.freedesktop.machine1.Manager",
1051 log_error("Failed to bind mount: %s", bus_error_message(&error, -r));
1058 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1059 PTYForward ** forward = (PTYForward**) userdata;
1067 /* If the forwarder is already initialized, tell it to
1068 * exit on the next vhangup(), so that we still flush
1069 * out what might be queued and exit then. */
1071 r = pty_forward_set_ignore_vhangup(*forward, false);
1075 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1078 /* On error, or when the forwarder is not initialized yet, quit immediately */
1079 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1083 static int login_machine(int argc, char *argv[], void *userdata) {
1084 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1085 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1086 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1087 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1088 _cleanup_event_unref_ sd_event *event = NULL;
1089 int master = -1, r, ret = 0;
1090 sd_bus *bus = userdata;
1091 const char *pty, *match;
1097 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1098 arg_transport != BUS_TRANSPORT_MACHINE) {
1099 log_error("Login only supported on local machines.");
1103 polkit_agent_open_if_enabled();
1105 r = sd_event_default(&event);
1107 return log_error_errno(r, "Failed to get event loop: %m");
1109 r = sd_bus_attach_event(bus, event, 0);
1111 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1113 match = strjoina("type='signal',"
1114 "sender='org.freedesktop.machine1',"
1115 "path='/org/freedesktop/machine1',",
1116 "interface='org.freedesktop.machine1.Manager',"
1117 "member='MachineRemoved',"
1122 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1124 return log_error_errno(r, "Failed to add machine removal match: %m");
1126 r = sd_bus_message_new_method_call(bus,
1128 "org.freedesktop.machine1",
1129 "/org/freedesktop/machine1",
1130 "org.freedesktop.machine1.Manager",
1131 "OpenMachineLogin");
1133 return bus_log_create_error(r);
1135 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1137 return bus_log_create_error(r);
1139 r = sd_bus_message_append(m, "s", argv[1]);
1141 return bus_log_create_error(r);
1143 r = sd_bus_call(bus, m, 0, &error, &reply);
1145 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1149 r = sd_bus_message_read(reply, "hs", &master, &pty);
1151 return bus_log_parse_error(r);
1153 sigprocmask_many(SIG_BLOCK, SIGWINCH, SIGTERM, SIGINT, -1);
1155 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1157 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1158 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1160 r = pty_forward_new(event, master, true, &forward);
1162 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1164 r = sd_event_loop(event);
1166 return log_error_errno(r, "Failed to run event loop: %m");
1168 pty_forward_get_last_char(forward, &last_char);
1169 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1171 forward = pty_forward_free(forward);
1173 if (last_char != '\n')
1174 fputc('\n', stdout);
1177 log_info("Machine %s terminated.", argv[1]);
1179 log_info("Connection to machine %s terminated.", argv[1]);
1181 sd_event_get_exit_code(event, &ret);
1185 static int remove_image(int argc, char *argv[], void *userdata) {
1186 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1187 sd_bus *bus = userdata;
1192 polkit_agent_open_if_enabled();
1194 for (i = 1; i < argc; i++) {
1195 r = sd_bus_call_method(
1197 "org.freedesktop.machine1",
1198 "/org/freedesktop/machine1",
1199 "org.freedesktop.machine1.Manager",
1205 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1213 static int rename_image(int argc, char *argv[], void *userdata) {
1214 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1215 sd_bus *bus = userdata;
1218 polkit_agent_open_if_enabled();
1220 r = sd_bus_call_method(
1222 "org.freedesktop.machine1",
1223 "/org/freedesktop/machine1",
1224 "org.freedesktop.machine1.Manager",
1228 "ss", argv[1], argv[2]);
1230 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1237 static int clone_image(int argc, char *argv[], void *userdata) {
1238 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1239 sd_bus *bus = userdata;
1242 polkit_agent_open_if_enabled();
1244 r = sd_bus_call_method(
1246 "org.freedesktop.machine1",
1247 "/org/freedesktop/machine1",
1248 "org.freedesktop.machine1.Manager",
1252 "ssb", argv[1], argv[2], arg_read_only);
1254 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1261 static int read_only_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;
1267 b = parse_boolean(argv[2]);
1269 log_error("Failed to parse boolean argument: %s", argv[2]);
1274 polkit_agent_open_if_enabled();
1276 r = sd_bus_call_method(
1278 "org.freedesktop.machine1",
1279 "/org/freedesktop/machine1",
1280 "org.freedesktop.machine1.Manager",
1281 "MarkImageReadOnly",
1286 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1293 static int start_machine(int argc, char *argv[], void *userdata) {
1294 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1295 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1296 sd_bus *bus = userdata;
1301 polkit_agent_open_if_enabled();
1303 r = bus_wait_for_jobs_new(bus, &w);
1307 for (i = 1; i < argc; i++) {
1308 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1309 _cleanup_free_ char *e = NULL, *unit = NULL;
1312 if (!machine_name_is_valid(argv[i])) {
1313 log_error("Invalid machine name %s.", argv[i]);
1317 e = unit_name_escape(argv[i]);
1321 unit = unit_name_build("systemd-nspawn", e, ".service");
1325 r = sd_bus_message_new_method_call(
1328 "org.freedesktop.systemd1",
1329 "/org/freedesktop/systemd1",
1330 "org.freedesktop.systemd1.Manager",
1333 return bus_log_create_error(r);
1335 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1337 return bus_log_create_error(r);
1339 r = sd_bus_message_append(m, "ss", unit, "fail");
1341 return bus_log_create_error(r);
1343 r = sd_bus_call(bus, m, 0, &error, &reply);
1345 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1349 r = sd_bus_message_read(reply, "o", &object);
1351 return bus_log_parse_error(r);
1353 r = bus_wait_for_jobs_add(w, object);
1358 r = bus_wait_for_jobs(w, arg_quiet);
1365 static int enable_machine(int argc, char *argv[], void *userdata) {
1366 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1367 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1368 int carries_install_info = 0;
1369 const char *method = NULL;
1370 sd_bus *bus = userdata;
1375 polkit_agent_open_if_enabled();
1377 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1379 r = sd_bus_message_new_method_call(
1382 "org.freedesktop.systemd1",
1383 "/org/freedesktop/systemd1",
1384 "org.freedesktop.systemd1.Manager",
1387 return bus_log_create_error(r);
1389 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1391 return bus_log_create_error(r);
1393 r = sd_bus_message_open_container(m, 'a', "s");
1395 return bus_log_create_error(r);
1397 for (i = 1; i < argc; i++) {
1398 _cleanup_free_ char *e = NULL, *unit = NULL;
1400 if (!machine_name_is_valid(argv[i])) {
1401 log_error("Invalid machine name %s.", argv[i]);
1405 e = unit_name_escape(argv[i]);
1409 unit = unit_name_build("systemd-nspawn", e, ".service");
1413 r = sd_bus_message_append(m, "s", unit);
1415 return bus_log_create_error(r);
1418 r = sd_bus_message_close_container(m);
1420 return bus_log_create_error(r);
1422 if (streq(argv[0], "enable"))
1423 r = sd_bus_message_append(m, "bb", false, false);
1425 r = sd_bus_message_append(m, "b", false);
1427 return bus_log_create_error(r);
1429 r = sd_bus_call(bus, m, 0, &error, &reply);
1431 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1435 if (streq(argv[0], "enable")) {
1436 r = sd_bus_message_read(reply, "b", carries_install_info);
1438 return bus_log_parse_error(r);
1441 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1445 m = sd_bus_message_unref(m);
1447 r = sd_bus_message_new_method_call(
1450 "org.freedesktop.systemd1",
1451 "/org/freedesktop/systemd1",
1452 "org.freedesktop.systemd1.Manager",
1455 return bus_log_create_error(r);
1457 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1459 return bus_log_create_error(r);
1461 r = sd_bus_call(bus, m, 0, &error, NULL);
1463 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1470 static int match_log_message(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1471 const char **our_path = userdata, *line;
1479 r = sd_bus_message_read(m, "us", &priority, &line);
1481 bus_log_parse_error(r);
1485 if (!streq_ptr(*our_path, sd_bus_message_get_path(m)))
1488 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
1491 log_full(priority, "%s", line);
1495 static int match_transfer_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
1496 const char **our_path = userdata, *path, *result;
1504 r = sd_bus_message_read(m, "uos", &id, &path, &result);
1506 bus_log_parse_error(r);
1510 if (!streq_ptr(*our_path, path))
1513 sd_event_exit(sd_bus_get_event(bus), !streq_ptr(result, "done"));
1517 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
1522 log_info("Continuing download in the background. Use \"machinectl cancel-transfer %" PRIu32 "\" to abort transfer.", PTR_TO_UINT32(userdata));
1524 sd_event_exit(sd_event_source_get_event(s), EINTR);
1528 static int pull_image_common(sd_bus *bus, sd_bus_message *m) {
1529 _cleanup_bus_slot_unref_ sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL;
1530 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1531 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1532 _cleanup_event_unref_ sd_event* event = NULL;
1533 const char *path = NULL;
1540 polkit_agent_open_if_enabled();
1542 r = sd_event_default(&event);
1544 return log_error_errno(r, "Failed to get event loop: %m");
1546 r = sd_bus_attach_event(bus, event, 0);
1548 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1550 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
1552 return bus_log_create_error(r);
1554 r = sd_bus_add_match(
1558 "sender='org.freedesktop.import1',"
1559 "interface='org.freedesktop.import1.Manager',"
1560 "member='TransferRemoved',"
1561 "path='/org/freedesktop/import1'",
1562 match_transfer_removed, &path);
1564 return log_error_errno(r, "Failed to install match: %m");
1566 r = sd_bus_add_match(
1570 "sender='org.freedesktop.import1',"
1571 "interface='org.freedesktop.import1.Transfer',"
1572 "member='LogMessage'",
1573 match_log_message, &path);
1575 return log_error_errno(r, "Failed to install match: %m");
1577 r = sd_bus_call(bus, m, 0, &error, &reply);
1579 log_error("Failed pull image: %s", bus_error_message(&error, -r));
1583 r = sd_bus_message_read(reply, "uo", &id, &path);
1585 return bus_log_parse_error(r);
1587 sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
1590 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
1592 sd_event_add_signal(event, NULL, SIGINT, transfer_signal_handler, UINT32_TO_PTR(id));
1593 sd_event_add_signal(event, NULL, SIGTERM, transfer_signal_handler, UINT32_TO_PTR(id));
1595 r = sd_event_loop(event);
1597 return log_error_errno(r, "Failed to run event loop: %m");
1602 static int pull_tar(int argc, char *argv[], void *userdata) {
1603 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1604 _cleanup_free_ char *l = NULL, *ll = NULL;
1605 const char *local, *remote;
1606 sd_bus *bus = userdata;
1612 if (!http_url_is_valid(remote)) {
1613 log_error("URL '%s' is not valid.", remote);
1620 r = import_url_last_component(remote, &l);
1622 return log_error_errno(r, "Failed to get final component of URL: %m");
1627 if (isempty(local) || streq(local, "-"))
1631 r = tar_strip_suffixes(local, &ll);
1633 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1637 if (!machine_name_is_valid(local)) {
1638 log_error("Local name %s is not a suitable machine name.", local);
1643 r = sd_bus_message_new_method_call(
1646 "org.freedesktop.import1",
1647 "/org/freedesktop/import1",
1648 "org.freedesktop.import1.Manager",
1651 return bus_log_create_error(r);
1653 r = sd_bus_message_append(
1658 import_verify_to_string(arg_verify),
1661 return bus_log_create_error(r);
1663 return pull_image_common(bus, m);
1666 static int pull_raw(int argc, char *argv[], void *userdata) {
1667 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1668 _cleanup_free_ char *l = NULL, *ll = NULL;
1669 const char *local, *remote;
1670 sd_bus *bus = userdata;
1676 if (!http_url_is_valid(remote)) {
1677 log_error("URL '%s' is not valid.", remote);
1684 r = import_url_last_component(remote, &l);
1686 return log_error_errno(r, "Failed to get final component of URL: %m");
1691 if (isempty(local) || streq(local, "-"))
1695 r = raw_strip_suffixes(local, &ll);
1697 return log_error_errno(r, "Failed to strip tar suffixes: %m");
1701 if (!machine_name_is_valid(local)) {
1702 log_error("Local name %s is not a suitable machine name.", local);
1707 r = sd_bus_message_new_method_call(
1710 "org.freedesktop.import1",
1711 "/org/freedesktop/import1",
1712 "org.freedesktop.import1.Manager",
1715 return bus_log_create_error(r);
1717 r = sd_bus_message_append(
1722 import_verify_to_string(arg_verify),
1725 return bus_log_create_error(r);
1727 return pull_image_common(bus, m);
1730 static int pull_dkr(int argc, char *argv[], void *userdata) {
1731 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1732 const char *local, *remote, *tag;
1733 sd_bus *bus = userdata;
1736 if (arg_verify != IMPORT_VERIFY_NO) {
1737 log_error("Imports from DKR do not support image verification, please pass --verify=no.");
1742 tag = strchr(remote, ':');
1744 remote = strndupa(remote, tag - remote);
1748 if (!dkr_name_is_valid(remote)) {
1749 log_error("DKR name '%s' is invalid.", remote);
1752 if (tag && !dkr_tag_is_valid(tag)) {
1753 log_error("DKR tag '%s' is invalid.", remote);
1760 local = strchr(remote, '/');
1767 if (isempty(local) || streq(local, "-"))
1771 if (!machine_name_is_valid(local)) {
1772 log_error("Local name %s is not a suitable machine name.", local);
1777 r = sd_bus_message_new_method_call(
1780 "org.freedesktop.import1",
1781 "/org/freedesktop/import1",
1782 "org.freedesktop.import1.Manager",
1785 return bus_log_create_error(r);
1787 r = sd_bus_message_append(
1794 import_verify_to_string(arg_verify),
1797 return bus_log_create_error(r);
1799 return pull_image_common(bus, m);
1802 typedef struct TransferInfo {
1810 static int compare_transfer_info(const void *a, const void *b) {
1811 const TransferInfo *x = a, *y = b;
1813 return strcmp(x->local, y->local);
1816 static int list_transfers(int argc, char *argv[], void *userdata) {
1817 size_t max_type = strlen("TYPE"), max_local = strlen("LOCAL"), max_remote = strlen("REMOTE");
1818 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1819 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1820 _cleanup_free_ TransferInfo *transfers = NULL;
1821 size_t n_transfers = 0, n_allocated = 0, j;
1822 const char *type, *remote, *local, *object;
1823 sd_bus *bus = userdata;
1824 uint32_t id, max_id = 0;
1828 pager_open_if_enabled();
1830 r = sd_bus_call_method(
1832 "org.freedesktop.import1",
1833 "/org/freedesktop/import1",
1834 "org.freedesktop.import1.Manager",
1840 log_error("Could not get transfers: %s", bus_error_message(&error, -r));
1844 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
1846 return bus_log_parse_error(r);
1848 while ((r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, &object)) > 0) {
1851 if (!GREEDY_REALLOC(transfers, n_allocated, n_transfers + 1))
1854 transfers[n_transfers].id = id;
1855 transfers[n_transfers].type = type;
1856 transfers[n_transfers].remote = remote;
1857 transfers[n_transfers].local = local;
1858 transfers[n_transfers].progress = progress;
1878 return bus_log_parse_error(r);
1880 r = sd_bus_message_exit_container(reply);
1882 return bus_log_parse_error(r);
1884 qsort_safe(transfers, n_transfers, sizeof(TransferInfo), compare_transfer_info);
1887 printf("%-*s %-*s %-*s %-*s %-*s\n",
1888 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), "ID",
1890 (int) max_type, "TYPE",
1891 (int) max_local, "LOCAL",
1892 (int) max_remote, "REMOTE");
1894 for (j = 0; j < n_transfers; j++)
1895 printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
1896 (int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
1897 (int) 6, (unsigned) (transfers[j].progress * 100),
1898 (int) max_type, transfers[j].type,
1899 (int) max_local, transfers[j].local,
1900 (int) max_remote, transfers[j].remote);
1903 printf("\n%zu transfers listed.\n", n_transfers);
1908 static int cancel_transfer(int argc, char *argv[], void *userdata) {
1909 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1910 sd_bus *bus = userdata;
1915 polkit_agent_open_if_enabled();
1917 for (i = 1; i < argc; i++) {
1920 r = safe_atou32(argv[i], &id);
1922 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
1924 r = sd_bus_call_method(
1926 "org.freedesktop.import1",
1927 "/org/freedesktop/import1",
1928 "org.freedesktop.import1.Manager",
1934 log_error("Could not cancel transfer: %s", bus_error_message(&error, -r));
1942 static int help(int argc, char *argv[], void *userdata) {
1944 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1945 "Send control commands to or query the virtual machine and container\n"
1946 "registration manager.\n\n"
1947 " -h --help Show this help\n"
1948 " --version Show package version\n"
1949 " --no-pager Do not pipe output into a pager\n"
1950 " --no-legend Do not show the headers and footers\n"
1951 " --no-ask-password Do not ask for system passwords\n"
1952 " -H --host=[USER@]HOST Operate on remote host\n"
1953 " -M --machine=CONTAINER Operate on local container\n"
1954 " -p --property=NAME Show only properties by this name\n"
1955 " -q --quiet Suppress output\n"
1956 " -a --all Show all properties, including empty ones\n"
1957 " -l --full Do not ellipsize output\n"
1958 " --kill-who=WHO Who to send signal to\n"
1959 " -s --signal=SIGNAL Which signal to send\n"
1960 " --read-only Create read-only bind mount\n"
1961 " --mkdir Create directory before bind mounting, if missing\n"
1962 " -n --lines=INTEGER Number of journal entries to show\n"
1963 " -o --output=STRING Change journal output mode (short,\n"
1964 " short-monotonic, verbose, export, json,\n"
1965 " json-pretty, json-sse, cat)\n"
1966 " --verify=MODE Verification mode for downloaded images (no,\n"
1967 " checksum, signature)\n"
1968 " --force Download image even if already exists\n"
1969 " --dkr-index-url=URL Specify the index URL to use for DKR image\n"
1971 "Machine Commands:\n"
1972 " list List running VMs and containers\n"
1973 " status NAME... Show VM/container details\n"
1974 " show NAME... Show properties of one or more VMs/containers\n"
1975 " start NAME... Start container as a service\n"
1976 " login NAME Get a login prompt on a container\n"
1977 " enable NAME... Enable automatic container start at boot\n"
1978 " disable NAME... Disable automatic container start at boot\n"
1979 " poweroff NAME... Power off one or more containers\n"
1980 " reboot NAME... Reboot one or more containers\n"
1981 " terminate NAME... Terminate one or more VMs/containers\n"
1982 " kill NAME... Send signal to processes of a VM/container\n"
1983 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1984 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1985 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1987 " list-images Show available container and VM images\n"
1988 " image-status NAME... Show image details\n"
1989 " show-image NAME... Show properties of image\n"
1990 " clone NAME NAME Clone an image\n"
1991 " rename NAME NAME Rename an image\n"
1992 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1993 " remove NAME... Remove an image\n\n"
1994 "Image Transfer Commands:\n"
1995 " pull-tar URL [NAME] Download a TAR container image\n"
1996 " pull-raw URL [NAME] Download a RAW container or VM image\n"
1997 " pull-dkr REMOTE [NAME] Download a DKR container image\n"
1998 " list-transfers Show list of downloads in progress\n"
1999 " cancel-transfer Cancel a download\n"
2000 , program_invocation_short_name);
2005 static int parse_argv(int argc, char *argv[]) {
2008 ARG_VERSION = 0x100,
2014 ARG_NO_ASK_PASSWORD,
2020 static const struct option options[] = {
2021 { "help", no_argument, NULL, 'h' },
2022 { "version", no_argument, NULL, ARG_VERSION },
2023 { "property", required_argument, NULL, 'p' },
2024 { "all", no_argument, NULL, 'a' },
2025 { "full", no_argument, NULL, 'l' },
2026 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2027 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2028 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
2029 { "signal", required_argument, NULL, 's' },
2030 { "host", required_argument, NULL, 'H' },
2031 { "machine", required_argument, NULL, 'M' },
2032 { "read-only", no_argument, NULL, ARG_READ_ONLY },
2033 { "mkdir", no_argument, NULL, ARG_MKDIR },
2034 { "quiet", no_argument, NULL, 'q' },
2035 { "lines", required_argument, NULL, 'n' },
2036 { "output", required_argument, NULL, 'o' },
2037 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
2038 { "verify", required_argument, NULL, ARG_VERIFY },
2039 { "force", no_argument, NULL, ARG_FORCE },
2040 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
2049 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
2054 return help(0, NULL, NULL);
2057 puts(PACKAGE_STRING);
2058 puts(SYSTEMD_FEATURES);
2062 r = strv_extend(&arg_property, optarg);
2066 /* If the user asked for a particular
2067 * property, show it to him, even if it is
2081 if (safe_atou(optarg, &arg_lines) < 0) {
2082 log_error("Failed to parse lines '%s'", optarg);
2088 arg_output = output_mode_from_string(optarg);
2089 if (arg_output < 0) {
2090 log_error("Unknown output '%s'.", optarg);
2096 arg_no_pager = true;
2104 arg_kill_who = optarg;
2108 arg_signal = signal_from_string_try_harder(optarg);
2109 if (arg_signal < 0) {
2110 log_error("Failed to parse signal string %s.", optarg);
2115 case ARG_NO_ASK_PASSWORD:
2116 arg_ask_password = false;
2120 arg_transport = BUS_TRANSPORT_REMOTE;
2125 arg_transport = BUS_TRANSPORT_MACHINE;
2130 arg_read_only = true;
2142 arg_verify = import_verify_from_string(optarg);
2143 if (arg_verify < 0) {
2144 log_error("Failed to parse --verify= setting: %s", optarg);
2153 case ARG_DKR_INDEX_URL:
2154 if (!http_url_is_valid(optarg)) {
2155 log_error("Index URL is invalid: %s", optarg);
2159 arg_dkr_index_url = optarg;
2166 assert_not_reached("Unhandled option");
2172 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
2174 static const Verb verbs[] = {
2175 { "help", VERB_ANY, VERB_ANY, 0, help },
2176 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
2177 { "list-images", VERB_ANY, 1, 0, list_images },
2178 { "status", 2, VERB_ANY, 0, show_machine },
2179 { "image-status", 2, VERB_ANY, 0, show_image },
2180 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
2181 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
2182 { "terminate", 2, VERB_ANY, 0, terminate_machine },
2183 { "reboot", 2, VERB_ANY, 0, reboot_machine },
2184 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
2185 { "kill", 2, VERB_ANY, 0, kill_machine },
2186 { "login", 2, 2, 0, login_machine },
2187 { "bind", 3, 4, 0, bind_mount },
2188 { "copy-to", 3, 4, 0, copy_files },
2189 { "copy-from", 3, 4, 0, copy_files },
2190 { "remove", 2, VERB_ANY, 0, remove_image },
2191 { "rename", 3, 3, 0, rename_image },
2192 { "clone", 3, 3, 0, clone_image },
2193 { "read-only", 2, 3, 0, read_only_image },
2194 { "start", 2, VERB_ANY, 0, start_machine },
2195 { "enable", 2, VERB_ANY, 0, enable_machine },
2196 { "disable", 2, VERB_ANY, 0, enable_machine },
2197 { "pull-tar", 2, 3, 0, pull_tar },
2198 { "pull-raw", 2, 3, 0, pull_raw },
2199 { "pull-dkr", 2, 3, 0, pull_dkr },
2200 { "list-transfers", VERB_ANY, 1, 0, list_transfers },
2201 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
2205 return dispatch_verb(argc, argv, verbs, bus);
2208 int main(int argc, char*argv[]) {
2209 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
2212 setlocale(LC_ALL, "");
2213 log_parse_environment();
2216 r = parse_argv(argc, argv);
2220 r = bus_open_transport(arg_transport, arg_host, false, &bus);
2222 log_error_errno(r, "Failed to create bus connection: %m");
2226 r = machinectl_main(argc, argv, bus);
2230 polkit_agent_close();
2232 strv_free(arg_property);
2234 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;