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>
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"
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 unsigned arg_lines = 10;
69 static OutputMode arg_output = OUTPUT_SHORT;
71 static void pager_open_if_enabled(void) {
73 /* Cache result before we open the pager */
80 static OutputFlags get_output_flags(void) {
82 arg_all * OUTPUT_SHOW_ALL |
83 arg_full * OUTPUT_FULL_WIDTH |
84 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
85 on_tty() * OUTPUT_COLOR |
86 !arg_quiet * OUTPUT_WARN_CUTOFF;
89 typedef struct MachineInfo {
95 static int compare_machine_info(const void *a, const void *b) {
96 const MachineInfo *x = a, *y = b;
98 return strcmp(x->name, y->name);
101 static int list_machines(int argc, char *argv[], void *userdata) {
103 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
104 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
105 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
106 _cleanup_free_ MachineInfo *machines = NULL;
107 const char *name, *class, *service, *object;
108 size_t n_machines = 0, n_allocated = 0, j;
109 sd_bus *bus = userdata;
114 pager_open_if_enabled();
116 r = sd_bus_call_method(
118 "org.freedesktop.machine1",
119 "/org/freedesktop/machine1",
120 "org.freedesktop.machine1.Manager",
126 log_error("Could not get machines: %s", bus_error_message(&error, -r));
130 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
132 return bus_log_parse_error(r);
134 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
137 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
140 machines[n_machines].name = name;
141 machines[n_machines].class = class;
142 machines[n_machines].service = service;
159 return bus_log_parse_error(r);
161 r = sd_bus_message_exit_container(reply);
163 return bus_log_parse_error(r);
165 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
168 printf("%-*s %-*s %-*s\n",
169 (int) max_name, "MACHINE",
170 (int) max_class, "CLASS",
171 (int) max_service, "SERVICE");
173 for (j = 0; j < n_machines; j++)
174 printf("%-*s %-*s %-*s\n",
175 (int) max_name, machines[j].name,
176 (int) max_class, machines[j].class,
177 (int) max_service, machines[j].service);
180 printf("\n%zu machines listed.\n", n_machines);
185 typedef struct ImageInfo {
194 static int compare_image_info(const void *a, const void *b) {
195 const ImageInfo *x = a, *y = b;
197 return strcmp(x->name, y->name);
200 static int list_images(int argc, char *argv[], void *userdata) {
202 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
203 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("SIZE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
204 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
205 _cleanup_free_ ImageInfo *images = NULL;
206 size_t n_images = 0, n_allocated = 0, j;
207 const char *name, *type, *object;
208 sd_bus *bus = userdata;
209 uint64_t crtime, mtime, size;
214 pager_open_if_enabled();
216 r = sd_bus_call_method(
218 "org.freedesktop.machine1",
219 "/org/freedesktop/machine1",
220 "org.freedesktop.machine1.Manager",
226 log_error("Could not get images: %s", bus_error_message(&error, -r));
230 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
232 return bus_log_parse_error(r);
234 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
235 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
238 if (name[0] == '.' && !arg_all)
241 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
244 images[n_images].name = name;
245 images[n_images].type = type;
246 images[n_images].read_only = read_only;
247 images[n_images].crtime = crtime;
248 images[n_images].mtime = mtime;
249 images[n_images].size = size;
260 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
266 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
271 if (size != (uint64_t) -1) {
272 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
280 return bus_log_parse_error(r);
282 r = sd_bus_message_exit_container(reply);
284 return bus_log_parse_error(r);
286 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
289 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
290 (int) max_name, "NAME",
291 (int) max_type, "TYPE",
293 (int) max_size, "SIZE",
294 (int) max_crtime, "CREATED",
295 (int) max_mtime, "MODIFIED");
297 for (j = 0; j < n_images; j++) {
298 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
300 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
301 (int) max_name, images[j].name,
302 (int) max_type, images[j].type,
303 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
304 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
305 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
306 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
310 printf("\n%zu images listed.\n", n_images);
315 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
316 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
317 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
318 _cleanup_free_ char *path = NULL;
326 if (arg_transport == BUS_TRANSPORT_REMOTE)
329 path = unit_dbus_path_from_name(unit);
333 r = sd_bus_get_property(
335 "org.freedesktop.systemd1",
337 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
343 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
347 r = sd_bus_message_read(reply, "s", &cgroup);
349 return bus_log_parse_error(r);
354 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
363 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
367 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
368 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
376 r = sd_bus_call_method(bus,
377 "org.freedesktop.machine1",
378 "/org/freedesktop/machine1",
379 "org.freedesktop.machine1.Manager",
380 "GetMachineAddresses",
387 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
389 return bus_log_parse_error(r);
391 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
395 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
397 r = sd_bus_message_read(reply, "i", &family);
399 return bus_log_parse_error(r);
401 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
403 return bus_log_parse_error(r);
405 fputs(prefix, stdout);
406 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
407 if (family == AF_INET6 && ifi > 0)
411 r = sd_bus_message_exit_container(reply);
413 return bus_log_parse_error(r);
415 if (prefix != prefix2)
419 return bus_log_parse_error(r);
421 r = sd_bus_message_exit_container(reply);
423 return bus_log_parse_error(r);
428 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
429 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
430 const char *k, *v, *pretty = NULL;
437 r = sd_bus_call_method(bus,
438 "org.freedesktop.machine1",
439 "/org/freedesktop/machine1",
440 "org.freedesktop.machine1.Manager",
441 "GetMachineOSRelease",
448 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
450 return bus_log_parse_error(r);
452 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
453 if (streq(k, "PRETTY_NAME"))
458 return bus_log_parse_error(r);
460 r = sd_bus_message_exit_container(reply);
462 return bus_log_parse_error(r);
465 printf("%s%s\n", prefix, pretty);
470 typedef struct MachineStatusInfo {
476 char *root_directory;
478 struct dual_timestamp timestamp;
483 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
484 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
485 char since2[FORMAT_TIMESTAMP_MAX], *s2;
491 fputs(strna(i->name), stdout);
493 if (!sd_id128_equal(i->id, SD_ID128_NULL))
494 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
498 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp.realtime);
499 s2 = format_timestamp(since2, sizeof(since2), i->timestamp.realtime);
502 printf("\t Since: %s; %s\n", s2, s1);
504 printf("\t Since: %s\n", s2);
507 _cleanup_free_ char *t = NULL;
509 printf("\t Leader: %u", (unsigned) i->leader);
511 get_process_comm(i->leader, &t);
519 printf("\t Service: %s", i->service);
522 printf("; class %s", i->class);
526 printf("\t Class: %s\n", i->class);
528 if (i->root_directory)
529 printf("\t Root: %s\n", i->root_directory);
531 if (i->n_netif > 0) {
534 fputs("\t Iface:", stdout);
536 for (c = 0; c < i->n_netif; c++) {
537 char name[IF_NAMESIZE+1] = "";
539 if (if_indextoname(i->netif[c], name)) {
548 printf(" %i", i->netif[c]);
554 print_addresses(bus, i->name, ifi,
558 print_os_release(bus, i->name, "\t OS: ");
561 printf("\t Unit: %s\n", i->unit);
562 show_unit_cgroup(bus, i->unit, i->leader);
564 if (arg_transport == BUS_TRANSPORT_LOCAL) {
566 show_journal_by_unit(
571 i->timestamp.monotonic,
574 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
575 SD_JOURNAL_LOCAL_ONLY,
582 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
583 MachineStatusInfo *i = userdata;
588 assert_cc(sizeof(int32_t) == sizeof(int));
589 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
595 i->n_netif = l / sizeof(int32_t);
596 i->netif = memdup(v, l);
603 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
605 static const struct bus_properties_map map[] = {
606 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
607 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
608 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
609 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
610 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
611 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
612 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp.realtime) },
613 { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) },
614 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
615 { "NetworkInterfaces", "ai", map_netif, 0 },
619 MachineStatusInfo info = {};
627 r = bus_map_all_properties(bus,
628 "org.freedesktop.machine1",
633 return log_error_errno(r, "Could not get properties: %m");
639 print_machine_status_info(bus, &info);
645 free(info.root_directory);
651 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
663 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
665 log_error_errno(r, "Could not get properties: %m");
670 static int show_machine(int argc, char *argv[], void *userdata) {
672 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
673 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
674 bool properties, new_line = false;
675 sd_bus *bus = userdata;
680 properties = !strstr(argv[0], "status");
682 pager_open_if_enabled();
684 if (properties && argc <= 1) {
686 /* If no argument is specified, inspect the manager
688 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
693 for (i = 1; i < argc; i++) {
694 const char *path = NULL;
696 r = sd_bus_call_method(
698 "org.freedesktop.machine1",
699 "/org/freedesktop/machine1",
700 "org.freedesktop.machine1.Manager",
706 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
710 r = sd_bus_message_read(reply, "o", &path);
712 return bus_log_parse_error(r);
715 r = show_machine_properties(bus, path, &new_line);
717 r = show_machine_info(argv[0], bus, path, &new_line);
723 typedef struct ImageStatusInfo {
732 uint64_t size_exclusive;
733 uint64_t limit_exclusive;
736 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
737 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
738 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
739 char bs[FORMAT_BYTES_MAX], *s3;
740 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
746 fputs(i->name, stdout);
751 printf("\t Type: %s\n", i->type);
754 printf("\t Path: %s\n", i->path);
756 printf("\t RO: %s%s%s\n",
757 i->read_only ? ansi_highlight_red() : "",
758 i->read_only ? "read-only" : "writable",
759 i->read_only ? ansi_highlight_off() : "");
761 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
762 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
764 printf("\t Created: %s; %s\n", s2, s1);
766 printf("\t Created: %s\n", s2);
768 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
769 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
771 printf("\tModified: %s; %s\n", s2, s1);
773 printf("\tModified: %s\n", s2);
775 s3 = format_bytes(bs, sizeof(bs), i->size);
776 s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
778 printf("\t Size: %s (exclusive: %s)\n", s3, s4);
780 printf("\t Size: %s\n", s3);
782 s3 = format_bytes(bs, sizeof(bs), i->limit);
783 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
785 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
787 printf("\t Limit: %s\n", s3);
790 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
792 static const struct bus_properties_map map[] = {
793 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
794 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
795 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
796 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
797 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
798 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
799 { "Size", "t", NULL, offsetof(ImageStatusInfo, size) },
800 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
801 { "SizeExclusive", "t", NULL, offsetof(ImageStatusInfo, size_exclusive) },
802 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
806 ImageStatusInfo info = {};
814 r = bus_map_all_properties(bus,
815 "org.freedesktop.machine1",
820 return log_error_errno(r, "Could not get properties: %m");
826 print_image_status_info(bus, &info);
835 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
847 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
849 log_error_errno(r, "Could not get properties: %m");
854 static int show_image(int argc, char *argv[], void *userdata) {
856 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
857 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
858 bool properties, new_line = false;
859 sd_bus *bus = userdata;
864 properties = !strstr(argv[0], "status");
866 pager_open_if_enabled();
868 if (properties && argc <= 1) {
870 /* If no argument is specified, inspect the manager
872 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
877 for (i = 1; i < argc; i++) {
878 const char *path = NULL;
880 r = sd_bus_call_method(
882 "org.freedesktop.machine1",
883 "/org/freedesktop/machine1",
884 "org.freedesktop.machine1.Manager",
890 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
894 r = sd_bus_message_read(reply, "o", &path);
896 return bus_log_parse_error(r);
899 r = show_image_properties(bus, path, &new_line);
901 r = show_image_info(argv[0], bus, path, &new_line);
907 static int kill_machine(int argc, char *argv[], void *userdata) {
908 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
909 sd_bus *bus = userdata;
915 arg_kill_who = "all";
917 for (i = 1; i < argc; i++) {
920 r = sd_bus_call_method(
922 "org.freedesktop.machine1",
923 "/org/freedesktop/machine1",
924 "org.freedesktop.machine1.Manager",
928 "ssi", argv[i], arg_kill_who, arg_signal);
930 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
938 static int reboot_machine(int argc, char *argv[], void *userdata) {
939 arg_kill_who = "leader";
940 arg_signal = SIGINT; /* sysvinit + systemd */
942 return kill_machine(argc, argv, userdata);
945 static int poweroff_machine(int argc, char *argv[], void *userdata) {
946 arg_kill_who = "leader";
947 arg_signal = SIGRTMIN+4; /* only systemd */
949 return kill_machine(argc, argv, userdata);
952 static int terminate_machine(int argc, char *argv[], void *userdata) {
953 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
954 sd_bus *bus = userdata;
959 for (i = 1; i < argc; i++) {
962 r = sd_bus_call_method(
964 "org.freedesktop.machine1",
965 "/org/freedesktop/machine1",
966 "org.freedesktop.machine1.Manager",
972 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
980 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
981 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
982 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
991 r = sd_bus_call_method(
993 "org.freedesktop.machine1",
994 "/org/freedesktop/machine1",
995 "org.freedesktop.machine1.Manager",
1001 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
1005 r = sd_bus_message_read(reply, "o", &object);
1007 return bus_log_parse_error(r);
1009 r = sd_bus_get_property(
1011 "org.freedesktop.machine1",
1013 "org.freedesktop.machine1.Machine",
1019 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
1021 r = sd_bus_message_read(reply2, "u", &leader);
1023 return bus_log_parse_error(r);
1029 static int copy_files(int argc, char *argv[], void *userdata) {
1030 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
1031 _cleanup_close_ int hostfd = -1;
1032 sd_bus *bus = userdata;
1033 pid_t child, leader;
1040 copy_from = streq(argv[0], "copy-from");
1041 dest = argv[3] ?: argv[2];
1042 host_path = strdupa(copy_from ? dest : argv[2]);
1043 container_path = strdupa(copy_from ? argv[2] : dest);
1045 if (!path_is_absolute(container_path)) {
1046 log_error("Container path not absolute.");
1050 t = strdup(host_path);
1051 host_basename = basename(t);
1052 host_dirname = dirname(host_path);
1054 t = strdup(container_path);
1055 container_basename = basename(t);
1056 container_dirname = dirname(container_path);
1058 r = machine_get_leader(bus, argv[1], &leader);
1062 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1064 return log_error_errno(errno, "Failed to open source directory: %m");
1068 return log_error_errno(errno, "Failed to fork(): %m");
1075 q = procfs_file_alloca(leader, "ns/mnt");
1076 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1078 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1079 _exit(EXIT_FAILURE);
1082 if (setns(mntfd, CLONE_NEWNS) < 0) {
1083 log_error_errno(errno, "Failed to join namespace of leader: %m");
1084 _exit(EXIT_FAILURE);
1087 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1088 if (containerfd < 0) {
1089 log_error_errno(errno, "Failed top open destination directory: %m");
1090 _exit(EXIT_FAILURE);
1094 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1096 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1098 log_error_errno(errno, "Failed to copy tree: %m");
1099 _exit(EXIT_FAILURE);
1102 _exit(EXIT_SUCCESS);
1105 r = wait_for_terminate(child, &si);
1107 return log_error_errno(r, "Failed to wait for client: %m");
1108 if (si.si_code != CLD_EXITED) {
1109 log_error("Client died abnormally.");
1112 if (si.si_status != EXIT_SUCCESS)
1118 static int bind_mount(int argc, char *argv[], void *userdata) {
1119 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1120 sd_bus *bus = userdata;
1121 pid_t child, leader;
1124 bool mount_slave_created = false, mount_slave_mounted = false,
1125 mount_tmp_created = false, mount_tmp_mounted = false,
1126 mount_outside_created = false, mount_outside_mounted = false;
1131 /* One day, when bind mounting /proc/self/fd/n works across
1132 * namespace boundaries we should rework this logic to make
1135 dest = argv[3] ?: argv[2];
1136 if (!path_is_absolute(dest)) {
1137 log_error("Destination path not absolute.");
1141 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1142 if (access(p, F_OK) < 0) {
1143 log_error("Container does not allow propagation of mount points.");
1147 r = machine_get_leader(bus, argv[1], &leader);
1151 /* Our goal is to install a new bind mount into the container,
1152 possibly read-only. This is irritatingly complex
1153 unfortunately, currently.
1155 First, we start by creating a private playground in /tmp,
1156 that we can mount MS_SLAVE. (Which is necessary, since
1157 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1160 if (!mkdtemp(mount_slave))
1161 return log_error_errno(errno, "Failed to create playground: %m");
1163 mount_slave_created = true;
1165 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1166 r = log_error_errno(errno, "Failed to make bind mount: %m");
1170 mount_slave_mounted = true;
1172 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1173 r = log_error_errno(errno, "Failed to remount slave: %m");
1177 /* Second, we mount the source directory to a directory inside
1178 of our MS_SLAVE playground. */
1179 mount_tmp = strappenda(mount_slave, "/mount");
1180 if (mkdir(mount_tmp, 0700) < 0) {
1181 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1185 mount_tmp_created = true;
1187 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1188 r = log_error_errno(errno, "Failed to overmount: %m");
1192 mount_tmp_mounted = true;
1194 /* Third, we remount the new bind mount read-only if requested. */
1196 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1197 r = log_error_errno(errno, "Failed to mark read-only: %m");
1201 /* Fourth, we move the new bind mount into the propagation
1202 * directory. This way it will appear there read-only
1205 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1206 if (!mkdtemp(mount_outside)) {
1207 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1211 mount_outside_created = true;
1213 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1214 r = log_error_errno(errno, "Failed to move: %m");
1218 mount_outside_mounted = true;
1219 mount_tmp_mounted = false;
1221 (void) rmdir(mount_tmp);
1222 mount_tmp_created = false;
1224 (void) umount(mount_slave);
1225 mount_slave_mounted = false;
1227 (void) rmdir(mount_slave);
1228 mount_slave_created = false;
1232 r = log_error_errno(errno, "Failed to fork(): %m");
1237 const char *mount_inside;
1241 q = procfs_file_alloca(leader, "ns/mnt");
1242 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1244 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1245 _exit(EXIT_FAILURE);
1248 if (setns(mntfd, CLONE_NEWNS) < 0) {
1249 log_error_errno(errno, "Failed to join namespace of leader: %m");
1250 _exit(EXIT_FAILURE);
1254 mkdir_p(dest, 0755);
1256 /* Fifth, move the mount to the right place inside */
1257 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1258 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1259 log_error_errno(errno, "Failed to mount: %m");
1260 _exit(EXIT_FAILURE);
1263 _exit(EXIT_SUCCESS);
1266 r = wait_for_terminate(child, &si);
1268 log_error_errno(r, "Failed to wait for client: %m");
1271 if (si.si_code != CLD_EXITED) {
1272 log_error("Client died abnormally.");
1276 if (si.si_status != EXIT_SUCCESS) {
1284 if (mount_outside_mounted)
1285 umount(mount_outside);
1286 if (mount_outside_created)
1287 rmdir(mount_outside);
1289 if (mount_tmp_mounted)
1291 if (mount_tmp_created)
1294 if (mount_slave_mounted)
1295 umount(mount_slave);
1296 if (mount_slave_created)
1297 umount(mount_slave);
1302 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1303 PTYForward ** forward = (PTYForward**) userdata;
1311 /* If the forwarder is already initialized, tell it to
1312 * exit on the next vhangup(), so that we still flush
1313 * out what might be queued and exit then. */
1315 r = pty_forward_set_ignore_vhangup(*forward, false);
1319 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1322 /* On error, or when the forwarder is not initialized yet, quit immediately */
1323 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1327 static int login_machine(int argc, char *argv[], void *userdata) {
1328 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1329 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1330 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1331 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1332 _cleanup_event_unref_ sd_event *event = NULL;
1333 int master = -1, r, ret = 0;
1334 sd_bus *bus = userdata;
1335 const char *pty, *match;
1342 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1343 arg_transport != BUS_TRANSPORT_MACHINE) {
1344 log_error("Login only supported on local machines.");
1348 r = sd_event_default(&event);
1350 return log_error_errno(r, "Failed to get event loop: %m");
1352 r = sd_bus_attach_event(bus, event, 0);
1354 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1356 match = strappenda("type='signal',"
1357 "sender='org.freedesktop.machine1',"
1358 "path='/org/freedesktop/machine1',",
1359 "interface='org.freedesktop.machine1.Manager',"
1360 "member='MachineRemoved',"
1365 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1367 return log_error_errno(r, "Failed to add machine removal match: %m");
1369 r = sd_bus_message_new_method_call(bus,
1371 "org.freedesktop.machine1",
1372 "/org/freedesktop/machine1",
1373 "org.freedesktop.machine1.Manager",
1374 "OpenMachineLogin");
1376 return bus_log_create_error(r);
1378 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1380 return bus_log_create_error(r);
1382 r = sd_bus_message_append(m, "s", argv[1]);
1384 return bus_log_create_error(r);
1386 r = sd_bus_call(bus, m, 0, &error, &reply);
1388 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1392 r = sd_bus_message_read(reply, "hs", &master, &pty);
1394 return bus_log_parse_error(r);
1396 assert_se(sigemptyset(&mask) == 0);
1397 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1398 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1400 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1402 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1403 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1405 r = pty_forward_new(event, master, true, &forward);
1407 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1409 r = sd_event_loop(event);
1411 return log_error_errno(r, "Failed to run event loop: %m");
1413 pty_forward_get_last_char(forward, &last_char);
1414 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1416 forward = pty_forward_free(forward);
1418 if (last_char != '\n')
1419 fputc('\n', stdout);
1422 log_info("Machine %s terminated.", argv[1]);
1424 log_info("Connection to machine %s terminated.", argv[1]);
1426 sd_event_get_exit_code(event, &ret);
1430 static int remove_image(int argc, char *argv[], void *userdata) {
1431 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1432 sd_bus *bus = userdata;
1437 for (i = 1; i < argc; i++) {
1438 r = sd_bus_call_method(
1440 "org.freedesktop.machine1",
1441 "/org/freedesktop/machine1",
1442 "org.freedesktop.machine1.Manager",
1448 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1456 static int rename_image(int argc, char *argv[], void *userdata) {
1457 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1458 sd_bus *bus = userdata;
1461 r = sd_bus_call_method(
1463 "org.freedesktop.machine1",
1464 "/org/freedesktop/machine1",
1465 "org.freedesktop.machine1.Manager",
1469 "ss", argv[1], argv[2]);
1471 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1478 static int clone_image(int argc, char *argv[], void *userdata) {
1479 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1480 sd_bus *bus = userdata;
1483 r = sd_bus_call_method(
1485 "org.freedesktop.machine1",
1486 "/org/freedesktop/machine1",
1487 "org.freedesktop.machine1.Manager",
1491 "ssb", argv[1], argv[2], arg_read_only);
1493 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1500 static int read_only_image(int argc, char *argv[], void *userdata) {
1501 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1502 sd_bus *bus = userdata;
1506 b = parse_boolean(argv[2]);
1508 log_error("Failed to parse boolean argument: %s", argv[2]);
1513 r = sd_bus_call_method(
1515 "org.freedesktop.machine1",
1516 "/org/freedesktop/machine1",
1517 "org.freedesktop.machine1.Manager",
1518 "MarkImageReadOnly",
1523 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1530 static int start_machine(int argc, char *argv[], void *userdata) {
1531 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1532 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1533 sd_bus *bus = userdata;
1538 r = bus_wait_for_jobs_new(bus, &w);
1542 for (i = 1; i < argc; i++) {
1543 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1544 _cleanup_free_ char *e = NULL, *unit = NULL;
1547 if (!machine_name_is_valid(argv[i])) {
1548 log_error("Invalid machine name %s.", argv[i]);
1552 e = unit_name_escape(argv[i]);
1556 unit = unit_name_build("systemd-nspawn", e, ".service");
1560 r = sd_bus_message_new_method_call(
1563 "org.freedesktop.systemd1",
1564 "/org/freedesktop/systemd1",
1565 "org.freedesktop.systemd1.Manager",
1568 return bus_log_create_error(r);
1570 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1572 return bus_log_create_error(r);
1574 r = sd_bus_message_append(m, "ss", unit, "fail");
1576 return bus_log_create_error(r);
1578 r = sd_bus_call(bus, m, 0, &error, &reply);
1580 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1584 r = sd_bus_message_read(reply, "o", &object);
1586 return bus_log_parse_error(r);
1588 r = bus_wait_for_jobs_add(w, object);
1593 r = bus_wait_for_jobs(w, arg_quiet);
1600 static int enable_machine(int argc, char *argv[], void *userdata) {
1601 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1602 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1603 int carries_install_info = 0;
1604 const char *method = NULL;
1605 sd_bus *bus = userdata;
1610 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1612 r = sd_bus_message_new_method_call(
1615 "org.freedesktop.systemd1",
1616 "/org/freedesktop/systemd1",
1617 "org.freedesktop.systemd1.Manager",
1620 return bus_log_create_error(r);
1622 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1624 return bus_log_create_error(r);
1626 r = sd_bus_message_open_container(m, 'a', "s");
1628 return bus_log_create_error(r);
1630 for (i = 1; i < argc; i++) {
1631 _cleanup_free_ char *e = NULL, *unit = NULL;
1633 if (!machine_name_is_valid(argv[i])) {
1634 log_error("Invalid machine name %s.", argv[i]);
1638 e = unit_name_escape(argv[i]);
1642 unit = unit_name_build("systemd-nspawn", e, ".service");
1646 r = sd_bus_message_append(m, "s", unit);
1648 return bus_log_create_error(r);
1651 r = sd_bus_message_close_container(m);
1653 return bus_log_create_error(r);
1655 if (streq(argv[0], "enable"))
1656 r = sd_bus_message_append(m, "bb", false, false);
1658 r = sd_bus_message_append(m, "b", false);
1660 return bus_log_create_error(r);
1662 r = sd_bus_call(bus, m, 0, &error, &reply);
1664 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1668 if (streq(argv[0], "enable")) {
1669 r = sd_bus_message_read(reply, "b", carries_install_info);
1671 return bus_log_parse_error(r);
1674 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1678 m = sd_bus_message_unref(m);
1680 r = sd_bus_message_new_method_call(
1683 "org.freedesktop.systemd1",
1684 "/org/freedesktop/systemd1",
1685 "org.freedesktop.systemd1.Manager",
1688 return bus_log_create_error(r);
1690 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1692 return bus_log_create_error(r);
1694 r = sd_bus_call(bus, m, 0, &error, NULL);
1696 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1703 static int help(int argc, char *argv[], void *userdata) {
1705 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1706 "Send control commands to or query the virtual machine and container\n"
1707 "registration manager.\n\n"
1708 " -h --help Show this help\n"
1709 " --version Show package version\n"
1710 " --no-pager Do not pipe output into a pager\n"
1711 " --no-legend Do not show the headers and footers\n"
1712 " -H --host=[USER@]HOST Operate on remote host\n"
1713 " -M --machine=CONTAINER Operate on local container\n"
1714 " -p --property=NAME Show only properties by this name\n"
1715 " -q --quiet Suppress output\n"
1716 " -a --all Show all properties, including empty ones\n"
1717 " -l --full Do not ellipsize output\n"
1718 " --kill-who=WHO Who to send signal to\n"
1719 " -s --signal=SIGNAL Which signal to send\n"
1720 " --read-only Create read-only bind mount\n"
1721 " --mkdir Create directory before bind mounting, if missing\n"
1722 " -n --lines=INTEGER Number of journal entries to show\n"
1723 " -o --output=STRING Change journal output mode (short,\n"
1724 " short-monotonic, verbose, export, json,\n"
1725 " json-pretty, json-sse, cat)\n\n"
1726 "Machine Commands:\n"
1727 " list List running VMs and containers\n"
1728 " status NAME... Show VM/container details\n"
1729 " show NAME... Show properties of one or more VMs/containers\n"
1730 " login NAME Get a login prompt on a container\n"
1731 " start NAME... Start container as a service\n"
1732 " enable NAME... Enable automatic container start at boot\n"
1733 " disable NAME... Disable automatic container start at boot\n"
1734 " poweroff NAME... Power off one or more containers\n"
1735 " reboot NAME... Reboot one or more containers\n"
1736 " terminate NAME... Terminate one or more VMs/containers\n"
1737 " kill NAME... Send signal to processes of a VM/container\n"
1738 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1739 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1740 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1742 " list-images Show available images\n"
1743 " image-status NAME... Show image details\n"
1744 " show-image NAME... Show properties of image\n"
1745 " clone NAME NAME Clone an image\n"
1746 " rename NAME NAME Rename an image\n"
1747 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1748 " remove NAME... Remove an image\n",
1749 program_invocation_short_name);
1754 static int parse_argv(int argc, char *argv[]) {
1757 ARG_VERSION = 0x100,
1765 static const struct option options[] = {
1766 { "help", no_argument, NULL, 'h' },
1767 { "version", no_argument, NULL, ARG_VERSION },
1768 { "property", required_argument, NULL, 'p' },
1769 { "all", no_argument, NULL, 'a' },
1770 { "full", no_argument, NULL, 'l' },
1771 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1772 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1773 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1774 { "signal", required_argument, NULL, 's' },
1775 { "host", required_argument, NULL, 'H' },
1776 { "machine", required_argument, NULL, 'M' },
1777 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1778 { "mkdir", no_argument, NULL, ARG_MKDIR },
1779 { "quiet", no_argument, NULL, 'q' },
1780 { "lines", required_argument, NULL, 'n' },
1781 { "output", required_argument, NULL, 'o' },
1790 while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0)
1795 return help(0, NULL, NULL);
1798 puts(PACKAGE_STRING);
1799 puts(SYSTEMD_FEATURES);
1803 r = strv_extend(&arg_property, optarg);
1807 /* If the user asked for a particular
1808 * property, show it to him, even if it is
1822 if (safe_atou(optarg, &arg_lines) < 0) {
1823 log_error("Failed to parse lines '%s'", optarg);
1829 arg_output = output_mode_from_string(optarg);
1830 if (arg_output < 0) {
1831 log_error("Unknown output '%s'.", optarg);
1837 arg_no_pager = true;
1845 arg_kill_who = optarg;
1849 arg_signal = signal_from_string_try_harder(optarg);
1850 if (arg_signal < 0) {
1851 log_error("Failed to parse signal string %s.", optarg);
1857 arg_transport = BUS_TRANSPORT_REMOTE;
1862 arg_transport = BUS_TRANSPORT_MACHINE;
1867 arg_read_only = true;
1882 assert_not_reached("Unhandled option");
1888 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1890 static const Verb verbs[] = {
1891 { "help", VERB_ANY, VERB_ANY, 0, help },
1892 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1893 { "list-images", VERB_ANY, 1, 0, list_images },
1894 { "status", 2, VERB_ANY, 0, show_machine },
1895 { "image-status",2, VERB_ANY, 0, show_image },
1896 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1897 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1898 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1899 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1900 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1901 { "kill", 2, VERB_ANY, 0, kill_machine },
1902 { "login", 2, 2, 0, login_machine },
1903 { "bind", 3, 4, 0, bind_mount },
1904 { "copy-to", 3, 4, 0, copy_files },
1905 { "copy-from", 3, 4, 0, copy_files },
1906 { "remove", 2, VERB_ANY, 0, remove_image },
1907 { "rename", 3, 3, 0, rename_image },
1908 { "clone", 3, 3, 0, clone_image },
1909 { "read-only", 2, 3, 0, read_only_image },
1910 { "start", 2, VERB_ANY, 0, start_machine },
1911 { "enable", 2, VERB_ANY, 0, enable_machine },
1912 { "disable", 2, VERB_ANY, 0, enable_machine },
1916 return dispatch_verb(argc, argv, verbs, bus);
1919 int main(int argc, char*argv[]) {
1920 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1923 setlocale(LC_ALL, "");
1924 log_parse_environment();
1927 r = parse_argv(argc, argv);
1931 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1933 log_error_errno(r, "Failed to create bus connection: %m");
1937 r = machinectl_main(argc, argv, bus);
1942 strv_free(arg_property);
1944 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;