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 "cgroup-util.h"
49 #include "event-util.h"
50 #include "path-util.h"
55 static char **arg_property = NULL;
56 static bool arg_all = false;
57 static bool arg_full = false;
58 static bool arg_no_pager = false;
59 static bool arg_legend = true;
60 static const char *arg_kill_who = NULL;
61 static int arg_signal = SIGTERM;
62 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
63 static char *arg_host = NULL;
64 static bool arg_read_only = false;
65 static bool arg_mkdir = false;
66 static bool arg_quiet = false;
68 static void pager_open_if_enabled(void) {
70 /* Cache result before we open the pager */
77 typedef struct MachineInfo {
83 static int compare_machine_info(const void *a, const void *b) {
84 const MachineInfo *x = a, *y = b;
86 return strcmp(x->name, y->name);
89 static int list_machines(int argc, char *argv[], void *userdata) {
91 size_t max_name = strlen("MACHINE"), max_class = strlen("CLASS"), max_service = strlen("SERVICE");
92 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
93 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
94 _cleanup_free_ MachineInfo *machines = NULL;
95 const char *name, *class, *service, *object;
96 size_t n_machines = 0, n_allocated = 0, j;
97 sd_bus *bus = userdata;
102 pager_open_if_enabled();
104 r = sd_bus_call_method(
106 "org.freedesktop.machine1",
107 "/org/freedesktop/machine1",
108 "org.freedesktop.machine1.Manager",
114 log_error("Could not get machines: %s", bus_error_message(&error, -r));
118 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
120 return bus_log_parse_error(r);
122 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
125 if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1))
128 machines[n_machines].name = name;
129 machines[n_machines].class = class;
130 machines[n_machines].service = service;
147 return bus_log_parse_error(r);
149 r = sd_bus_message_exit_container(reply);
151 return bus_log_parse_error(r);
153 qsort_safe(machines, n_machines, sizeof(MachineInfo), compare_machine_info);
156 printf("%-*s %-*s %-*s\n",
157 (int) max_name, "MACHINE",
158 (int) max_class, "CLASS",
159 (int) max_service, "SERVICE");
161 for (j = 0; j < n_machines; j++)
162 printf("%-*s %-*s %-*s\n",
163 (int) max_name, machines[j].name,
164 (int) max_class, machines[j].class,
165 (int) max_service, machines[j].service);
168 printf("\n%zu machines listed.\n", n_machines);
173 typedef struct ImageInfo {
182 static int compare_image_info(const void *a, const void *b) {
183 const ImageInfo *x = a, *y = b;
185 return strcmp(x->name, y->name);
188 static int list_images(int argc, char *argv[], void *userdata) {
190 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
191 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("SIZE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
192 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
193 _cleanup_free_ ImageInfo *images = NULL;
194 size_t n_images = 0, n_allocated = 0, j;
195 const char *name, *type, *object;
196 sd_bus *bus = userdata;
197 uint64_t crtime, mtime, size;
202 pager_open_if_enabled();
204 r = sd_bus_call_method(
206 "org.freedesktop.machine1",
207 "/org/freedesktop/machine1",
208 "org.freedesktop.machine1.Manager",
214 log_error("Could not get images: %s", bus_error_message(&error, -r));
218 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
220 return bus_log_parse_error(r);
222 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
223 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
226 if (name[0] == '.' && !arg_all)
229 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
232 images[n_images].name = name;
233 images[n_images].type = type;
234 images[n_images].read_only = read_only;
235 images[n_images].crtime = crtime;
236 images[n_images].mtime = mtime;
237 images[n_images].size = size;
248 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
254 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
259 if (size != (uint64_t) -1) {
260 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
268 return bus_log_parse_error(r);
270 r = sd_bus_message_exit_container(reply);
272 return bus_log_parse_error(r);
274 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
277 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
278 (int) max_name, "NAME",
279 (int) max_type, "TYPE",
281 (int) max_size, "SIZE",
282 (int) max_crtime, "CREATED",
283 (int) max_mtime, "MODIFIED");
285 for (j = 0; j < n_images; j++) {
286 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
288 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
289 (int) max_name, images[j].name,
290 (int) max_type, images[j].type,
291 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
292 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
293 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
294 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
298 printf("\n%zu images listed.\n", n_images);
303 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
304 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
305 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
306 _cleanup_free_ char *path = NULL;
314 if (arg_transport == BUS_TRANSPORT_REMOTE)
317 path = unit_dbus_path_from_name(unit);
321 r = sd_bus_get_property(
323 "org.freedesktop.systemd1",
325 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
331 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
335 r = sd_bus_message_read(reply, "s", &cgroup);
337 return bus_log_parse_error(r);
342 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
346 arg_all * OUTPUT_SHOW_ALL |
347 arg_full * OUTPUT_FULL_WIDTH;
355 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
359 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
360 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
368 r = sd_bus_call_method(bus,
369 "org.freedesktop.machine1",
370 "/org/freedesktop/machine1",
371 "org.freedesktop.machine1.Manager",
372 "GetMachineAddresses",
379 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
381 return bus_log_parse_error(r);
383 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
387 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
389 r = sd_bus_message_read(reply, "i", &family);
391 return bus_log_parse_error(r);
393 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
395 return bus_log_parse_error(r);
397 fputs(prefix, stdout);
398 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
399 if (family == AF_INET6 && ifi > 0)
403 r = sd_bus_message_exit_container(reply);
405 return bus_log_parse_error(r);
407 if (prefix != prefix2)
411 return bus_log_parse_error(r);
413 r = sd_bus_message_exit_container(reply);
415 return bus_log_parse_error(r);
420 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
421 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
422 const char *k, *v, *pretty = NULL;
429 r = sd_bus_call_method(bus,
430 "org.freedesktop.machine1",
431 "/org/freedesktop/machine1",
432 "org.freedesktop.machine1.Manager",
433 "GetMachineOSRelease",
440 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
442 return bus_log_parse_error(r);
444 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
445 if (streq(k, "PRETTY_NAME"))
450 return bus_log_parse_error(r);
452 r = sd_bus_message_exit_container(reply);
454 return bus_log_parse_error(r);
457 printf("%s%s\n", prefix, pretty);
462 typedef struct MachineStatusInfo {
468 char *root_directory;
475 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
476 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
477 char since2[FORMAT_TIMESTAMP_MAX], *s2;
483 fputs(strna(i->name), stdout);
485 if (!sd_id128_equal(i->id, SD_ID128_NULL))
486 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
490 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
491 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
494 printf("\t Since: %s; %s\n", s2, s1);
496 printf("\t Since: %s\n", s2);
499 _cleanup_free_ char *t = NULL;
501 printf("\t Leader: %u", (unsigned) i->leader);
503 get_process_comm(i->leader, &t);
511 printf("\t Service: %s", i->service);
514 printf("; class %s", i->class);
518 printf("\t Class: %s\n", i->class);
520 if (i->root_directory)
521 printf("\t Root: %s\n", i->root_directory);
523 if (i->n_netif > 0) {
526 fputs("\t Iface:", stdout);
528 for (c = 0; c < i->n_netif; c++) {
529 char name[IF_NAMESIZE+1] = "";
531 if (if_indextoname(i->netif[c], name)) {
540 printf(" %i", i->netif[c]);
546 print_addresses(bus, i->name, ifi,
550 print_os_release(bus, i->name, "\t OS: ");
553 printf("\t Unit: %s\n", i->unit);
554 show_unit_cgroup(bus, i->unit, i->leader);
558 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
559 MachineStatusInfo *i = userdata;
564 assert_cc(sizeof(int32_t) == sizeof(int));
565 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
571 i->n_netif = l / sizeof(int32_t);
572 i->netif = memdup(v, l);
579 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
581 static const struct bus_properties_map map[] = {
582 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
583 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
584 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
585 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
586 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
587 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
588 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
589 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
590 { "NetworkInterfaces", "ai", map_netif, 0 },
594 MachineStatusInfo info = {};
602 r = bus_map_all_properties(bus,
603 "org.freedesktop.machine1",
608 return log_error_errno(r, "Could not get properties: %m");
614 print_machine_status_info(bus, &info);
620 free(info.root_directory);
626 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
638 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
640 log_error_errno(r, "Could not get properties: %m");
645 static int show_machine(int argc, char *argv[], void *userdata) {
647 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
648 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
649 bool properties, new_line = false;
650 sd_bus *bus = userdata;
655 properties = !strstr(argv[0], "status");
657 pager_open_if_enabled();
659 if (properties && argc <= 1) {
661 /* If no argument is specified, inspect the manager
663 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
668 for (i = 1; i < argc; i++) {
669 const char *path = NULL;
671 r = sd_bus_call_method(
673 "org.freedesktop.machine1",
674 "/org/freedesktop/machine1",
675 "org.freedesktop.machine1.Manager",
681 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
685 r = sd_bus_message_read(reply, "o", &path);
687 return bus_log_parse_error(r);
690 r = show_machine_properties(bus, path, &new_line);
692 r = show_machine_info(argv[0], bus, path, &new_line);
698 typedef struct ImageStatusInfo {
707 uint64_t size_exclusive;
708 uint64_t limit_exclusive;
711 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
712 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
713 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
714 char bs[FORMAT_BYTES_MAX], *s3;
715 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
721 fputs(i->name, stdout);
726 printf("\t Type: %s\n", i->type);
729 printf("\t Path: %s\n", i->path);
731 printf("\t RO: %s%s%s\n",
732 i->read_only ? ansi_highlight_red() : "",
733 i->read_only ? "read-only" : "writable",
734 i->read_only ? ansi_highlight_off() : "");
736 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
737 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
739 printf("\t Created: %s; %s\n", s2, s1);
741 printf("\t Created: %s\n", s2);
743 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
744 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
746 printf("\tModified: %s; %s\n", s2, s1);
748 printf("\tModified: %s\n", s2);
750 s3 = format_bytes(bs, sizeof(bs), i->size);
751 s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
753 printf("\t Size: %s (exclusive: %s)\n", s3, s4);
755 printf("\t Size: %s\n", s3);
757 s3 = format_bytes(bs, sizeof(bs), i->limit);
758 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
760 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
762 printf("\t Limit: %s\n", s3);
765 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
767 static const struct bus_properties_map map[] = {
768 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
769 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
770 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
771 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
772 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
773 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
774 { "Size", "t", NULL, offsetof(ImageStatusInfo, size) },
775 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
776 { "SizeExclusive", "t", NULL, offsetof(ImageStatusInfo, size_exclusive) },
777 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
781 ImageStatusInfo info = {};
789 r = bus_map_all_properties(bus,
790 "org.freedesktop.machine1",
795 return log_error_errno(r, "Could not get properties: %m");
801 print_image_status_info(bus, &info);
810 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
822 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
824 log_error_errno(r, "Could not get properties: %m");
829 static int show_image(int argc, char *argv[], void *userdata) {
831 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
832 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
833 bool properties, new_line = false;
834 sd_bus *bus = userdata;
839 properties = !strstr(argv[0], "status");
841 pager_open_if_enabled();
843 if (properties && argc <= 1) {
845 /* If no argument is specified, inspect the manager
847 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
852 for (i = 1; i < argc; i++) {
853 const char *path = NULL;
855 r = sd_bus_call_method(
857 "org.freedesktop.machine1",
858 "/org/freedesktop/machine1",
859 "org.freedesktop.machine1.Manager",
865 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
869 r = sd_bus_message_read(reply, "o", &path);
871 return bus_log_parse_error(r);
874 r = show_image_properties(bus, path, &new_line);
876 r = show_image_info(argv[0], bus, path, &new_line);
882 static int kill_machine(int argc, char *argv[], void *userdata) {
883 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
884 sd_bus *bus = userdata;
890 arg_kill_who = "all";
892 for (i = 1; i < argc; i++) {
895 r = sd_bus_call_method(
897 "org.freedesktop.machine1",
898 "/org/freedesktop/machine1",
899 "org.freedesktop.machine1.Manager",
903 "ssi", argv[i], arg_kill_who, arg_signal);
905 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
913 static int reboot_machine(int argc, char *argv[], void *userdata) {
914 arg_kill_who = "leader";
915 arg_signal = SIGINT; /* sysvinit + systemd */
917 return kill_machine(argc, argv, userdata);
920 static int poweroff_machine(int argc, char *argv[], void *userdata) {
921 arg_kill_who = "leader";
922 arg_signal = SIGRTMIN+4; /* only systemd */
924 return kill_machine(argc, argv, userdata);
927 static int terminate_machine(int argc, char *argv[], void *userdata) {
928 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
929 sd_bus *bus = userdata;
934 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",
947 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
955 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
956 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
957 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
966 r = sd_bus_call_method(
968 "org.freedesktop.machine1",
969 "/org/freedesktop/machine1",
970 "org.freedesktop.machine1.Manager",
976 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
980 r = sd_bus_message_read(reply, "o", &object);
982 return bus_log_parse_error(r);
984 r = sd_bus_get_property(
986 "org.freedesktop.machine1",
988 "org.freedesktop.machine1.Machine",
994 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
996 r = sd_bus_message_read(reply2, "u", &leader);
998 return bus_log_parse_error(r);
1004 static int copy_files(int argc, char *argv[], void *userdata) {
1005 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
1006 _cleanup_close_ int hostfd = -1;
1007 sd_bus *bus = userdata;
1008 pid_t child, leader;
1015 copy_from = streq(argv[0], "copy-from");
1016 dest = argv[3] ?: argv[2];
1017 host_path = strdupa(copy_from ? dest : argv[2]);
1018 container_path = strdupa(copy_from ? argv[2] : dest);
1020 if (!path_is_absolute(container_path)) {
1021 log_error("Container path not absolute.");
1025 t = strdup(host_path);
1026 host_basename = basename(t);
1027 host_dirname = dirname(host_path);
1029 t = strdup(container_path);
1030 container_basename = basename(t);
1031 container_dirname = dirname(container_path);
1033 r = machine_get_leader(bus, argv[1], &leader);
1037 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1039 return log_error_errno(errno, "Failed to open source directory: %m");
1043 return log_error_errno(errno, "Failed to fork(): %m");
1050 q = procfs_file_alloca(leader, "ns/mnt");
1051 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1053 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1054 _exit(EXIT_FAILURE);
1057 if (setns(mntfd, CLONE_NEWNS) < 0) {
1058 log_error_errno(errno, "Failed to join namespace of leader: %m");
1059 _exit(EXIT_FAILURE);
1062 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1063 if (containerfd < 0) {
1064 log_error_errno(errno, "Failed top open destination directory: %m");
1065 _exit(EXIT_FAILURE);
1069 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1071 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1073 log_error_errno(errno, "Failed to copy tree: %m");
1074 _exit(EXIT_FAILURE);
1077 _exit(EXIT_SUCCESS);
1080 r = wait_for_terminate(child, &si);
1082 return log_error_errno(r, "Failed to wait for client: %m");
1083 if (si.si_code != CLD_EXITED) {
1084 log_error("Client died abnormally.");
1087 if (si.si_status != EXIT_SUCCESS)
1093 static int bind_mount(int argc, char *argv[], void *userdata) {
1094 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1095 sd_bus *bus = userdata;
1096 pid_t child, leader;
1099 bool mount_slave_created = false, mount_slave_mounted = false,
1100 mount_tmp_created = false, mount_tmp_mounted = false,
1101 mount_outside_created = false, mount_outside_mounted = false;
1106 /* One day, when bind mounting /proc/self/fd/n works across
1107 * namespace boundaries we should rework this logic to make
1110 dest = argv[3] ?: argv[2];
1111 if (!path_is_absolute(dest)) {
1112 log_error("Destination path not absolute.");
1116 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1117 if (access(p, F_OK) < 0) {
1118 log_error("Container does not allow propagation of mount points.");
1122 r = machine_get_leader(bus, argv[1], &leader);
1126 /* Our goal is to install a new bind mount into the container,
1127 possibly read-only. This is irritatingly complex
1128 unfortunately, currently.
1130 First, we start by creating a private playground in /tmp,
1131 that we can mount MS_SLAVE. (Which is necessary, since
1132 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1135 if (!mkdtemp(mount_slave))
1136 return log_error_errno(errno, "Failed to create playground: %m");
1138 mount_slave_created = true;
1140 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1141 r = log_error_errno(errno, "Failed to make bind mount: %m");
1145 mount_slave_mounted = true;
1147 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1148 r = log_error_errno(errno, "Failed to remount slave: %m");
1152 /* Second, we mount the source directory to a directory inside
1153 of our MS_SLAVE playground. */
1154 mount_tmp = strappenda(mount_slave, "/mount");
1155 if (mkdir(mount_tmp, 0700) < 0) {
1156 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1160 mount_tmp_created = true;
1162 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1163 r = log_error_errno(errno, "Failed to overmount: %m");
1167 mount_tmp_mounted = true;
1169 /* Third, we remount the new bind mount read-only if requested. */
1171 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1172 r = log_error_errno(errno, "Failed to mark read-only: %m");
1176 /* Fourth, we move the new bind mount into the propagation
1177 * directory. This way it will appear there read-only
1180 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1181 if (!mkdtemp(mount_outside)) {
1182 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1186 mount_outside_created = true;
1188 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1189 r = log_error_errno(errno, "Failed to move: %m");
1193 mount_outside_mounted = true;
1194 mount_tmp_mounted = false;
1196 (void) rmdir(mount_tmp);
1197 mount_tmp_created = false;
1199 (void) umount(mount_slave);
1200 mount_slave_mounted = false;
1202 (void) rmdir(mount_slave);
1203 mount_slave_created = false;
1207 r = log_error_errno(errno, "Failed to fork(): %m");
1212 const char *mount_inside;
1216 q = procfs_file_alloca(leader, "ns/mnt");
1217 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1219 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1220 _exit(EXIT_FAILURE);
1223 if (setns(mntfd, CLONE_NEWNS) < 0) {
1224 log_error_errno(errno, "Failed to join namespace of leader: %m");
1225 _exit(EXIT_FAILURE);
1229 mkdir_p(dest, 0755);
1231 /* Fifth, move the mount to the right place inside */
1232 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1233 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1234 log_error_errno(errno, "Failed to mount: %m");
1235 _exit(EXIT_FAILURE);
1238 _exit(EXIT_SUCCESS);
1241 r = wait_for_terminate(child, &si);
1243 log_error_errno(r, "Failed to wait for client: %m");
1246 if (si.si_code != CLD_EXITED) {
1247 log_error("Client died abnormally.");
1251 if (si.si_status != EXIT_SUCCESS) {
1259 if (mount_outside_mounted)
1260 umount(mount_outside);
1261 if (mount_outside_created)
1262 rmdir(mount_outside);
1264 if (mount_tmp_mounted)
1266 if (mount_tmp_created)
1269 if (mount_slave_mounted)
1270 umount(mount_slave);
1271 if (mount_slave_created)
1272 umount(mount_slave);
1277 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1278 PTYForward ** forward = (PTYForward**) userdata;
1286 /* If the forwarder is already initialized, tell it to
1287 * exit on the next vhangup(), so that we still flush
1288 * out what might be queued and exit then. */
1290 r = pty_forward_set_ignore_vhangup(*forward, false);
1294 log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
1297 /* On error, or when the forwarder is not initialized yet, quit immediately */
1298 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1302 static int login_machine(int argc, char *argv[], void *userdata) {
1303 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1304 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1305 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1306 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1307 _cleanup_event_unref_ sd_event *event = NULL;
1308 int master = -1, r, ret = 0;
1309 sd_bus *bus = userdata;
1310 const char *pty, *match;
1317 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1318 arg_transport != BUS_TRANSPORT_MACHINE) {
1319 log_error("Login only supported on local machines.");
1323 r = sd_event_default(&event);
1325 return log_error_errno(r, "Failed to get event loop: %m");
1327 r = sd_bus_attach_event(bus, event, 0);
1329 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1331 match = strappenda("type='signal',"
1332 "sender='org.freedesktop.machine1',"
1333 "path='/org/freedesktop/machine1',",
1334 "interface='org.freedesktop.machine1.Manager',"
1335 "member='MachineRemoved',"
1340 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1342 return log_error_errno(r, "Failed to add machine removal match: %m");
1344 r = sd_bus_message_new_method_call(bus,
1346 "org.freedesktop.machine1",
1347 "/org/freedesktop/machine1",
1348 "org.freedesktop.machine1.Manager",
1349 "OpenMachineLogin");
1351 return bus_log_create_error(r);
1353 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1355 return bus_log_create_error(r);
1357 r = sd_bus_message_append(m, "s", argv[1]);
1359 return bus_log_create_error(r);
1361 r = sd_bus_call(bus, m, 0, &error, &reply);
1363 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1367 r = sd_bus_message_read(reply, "hs", &master, &pty);
1369 return bus_log_parse_error(r);
1371 assert_se(sigemptyset(&mask) == 0);
1372 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1373 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1375 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1377 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1378 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1380 r = pty_forward_new(event, master, true, &forward);
1382 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1384 r = sd_event_loop(event);
1386 return log_error_errno(r, "Failed to run event loop: %m");
1388 pty_forward_get_last_char(forward, &last_char);
1389 machine_died = pty_forward_get_ignore_vhangup(forward) == 0;
1391 forward = pty_forward_free(forward);
1393 if (last_char != '\n')
1394 fputc('\n', stdout);
1397 log_info("Machine %s terminated.", argv[1]);
1399 log_info("Connection to machine %s terminated.", argv[1]);
1401 sd_event_get_exit_code(event, &ret);
1405 static int remove_image(int argc, char *argv[], void *userdata) {
1406 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1407 sd_bus *bus = userdata;
1412 for (i = 1; i < argc; i++) {
1413 r = sd_bus_call_method(
1415 "org.freedesktop.machine1",
1416 "/org/freedesktop/machine1",
1417 "org.freedesktop.machine1.Manager",
1423 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1431 static int rename_image(int argc, char *argv[], void *userdata) {
1432 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1433 sd_bus *bus = userdata;
1436 r = sd_bus_call_method(
1438 "org.freedesktop.machine1",
1439 "/org/freedesktop/machine1",
1440 "org.freedesktop.machine1.Manager",
1444 "ss", argv[1], argv[2]);
1446 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1453 static int clone_image(int argc, char *argv[], void *userdata) {
1454 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1455 sd_bus *bus = userdata;
1458 r = sd_bus_call_method(
1460 "org.freedesktop.machine1",
1461 "/org/freedesktop/machine1",
1462 "org.freedesktop.machine1.Manager",
1466 "ssb", argv[1], argv[2], arg_read_only);
1468 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1475 static int read_only_image(int argc, char *argv[], void *userdata) {
1476 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1477 sd_bus *bus = userdata;
1481 b = parse_boolean(argv[2]);
1483 log_error("Failed to parse boolean argument: %s", argv[2]);
1488 r = sd_bus_call_method(
1490 "org.freedesktop.machine1",
1491 "/org/freedesktop/machine1",
1492 "org.freedesktop.machine1.Manager",
1493 "MarkImageReadOnly",
1498 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1505 static int start_machine(int argc, char *argv[], void *userdata) {
1506 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1507 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1508 sd_bus *bus = userdata;
1513 r = bus_wait_for_jobs_new(bus, &w);
1517 for (i = 1; i < argc; i++) {
1518 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1519 _cleanup_free_ char *e = NULL, *unit = NULL;
1522 if (!machine_name_is_valid(argv[i])) {
1523 log_error("Invalid machine name %s.", argv[i]);
1527 e = unit_name_escape(argv[i]);
1531 unit = unit_name_build("systemd-nspawn", e, ".service");
1535 r = sd_bus_message_new_method_call(
1538 "org.freedesktop.systemd1",
1539 "/org/freedesktop/systemd1",
1540 "org.freedesktop.systemd1.Manager",
1543 return bus_log_create_error(r);
1545 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1547 return bus_log_create_error(r);
1549 r = sd_bus_message_append(m, "ss", unit, "fail");
1551 return bus_log_create_error(r);
1553 r = sd_bus_call(bus, m, 0, &error, &reply);
1555 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1559 r = sd_bus_message_read(reply, "o", &object);
1561 return bus_log_parse_error(r);
1563 r = bus_wait_for_jobs_add(w, object);
1568 r = bus_wait_for_jobs(w, arg_quiet);
1575 static int enable_machine(int argc, char *argv[], void *userdata) {
1576 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1577 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1578 int carries_install_info = 0;
1579 const char *method = NULL;
1580 sd_bus *bus = userdata;
1585 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1587 r = sd_bus_message_new_method_call(
1590 "org.freedesktop.systemd1",
1591 "/org/freedesktop/systemd1",
1592 "org.freedesktop.systemd1.Manager",
1595 return bus_log_create_error(r);
1597 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1599 return bus_log_create_error(r);
1601 r = sd_bus_message_open_container(m, 'a', "s");
1603 return bus_log_create_error(r);
1605 for (i = 1; i < argc; i++) {
1606 _cleanup_free_ char *e = NULL, *unit = NULL;
1608 if (!machine_name_is_valid(argv[i])) {
1609 log_error("Invalid machine name %s.", argv[i]);
1613 e = unit_name_escape(argv[i]);
1617 unit = unit_name_build("systemd-nspawn", e, ".service");
1621 r = sd_bus_message_append(m, "s", unit);
1623 return bus_log_create_error(r);
1626 r = sd_bus_message_close_container(m);
1628 return bus_log_create_error(r);
1630 if (streq(argv[0], "enable"))
1631 r = sd_bus_message_append(m, "bb", false, false);
1633 r = sd_bus_message_append(m, "b", false);
1635 return bus_log_create_error(r);
1637 r = sd_bus_call(bus, m, 0, &error, &reply);
1639 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1643 if (streq(argv[0], "enable")) {
1644 r = sd_bus_message_read(reply, "b", carries_install_info);
1646 return bus_log_parse_error(r);
1649 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1653 m = sd_bus_message_unref(m);
1655 r = sd_bus_message_new_method_call(
1658 "org.freedesktop.systemd1",
1659 "/org/freedesktop/systemd1",
1660 "org.freedesktop.systemd1.Manager",
1663 return bus_log_create_error(r);
1665 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1667 return bus_log_create_error(r);
1669 r = sd_bus_call(bus, m, 0, &error, NULL);
1671 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1678 static int help(int argc, char *argv[], void *userdata) {
1680 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1681 "Send control commands to or query the virtual machine and container\n"
1682 "registration manager.\n\n"
1683 " -h --help Show this help\n"
1684 " --version Show package version\n"
1685 " --no-pager Do not pipe output into a pager\n"
1686 " --no-legend Do not show the headers and footers\n"
1687 " -H --host=[USER@]HOST Operate on remote host\n"
1688 " -M --machine=CONTAINER Operate on local container\n"
1689 " -p --property=NAME Show only properties by this name\n"
1690 " -q --quiet Suppress output\n"
1691 " -a --all Show all properties, including empty ones\n"
1692 " -l --full Do not ellipsize output\n"
1693 " --kill-who=WHO Who to send signal to\n"
1694 " -s --signal=SIGNAL Which signal to send\n"
1695 " --read-only Create read-only bind mount\n"
1696 " --mkdir Create directory before bind mounting, if missing\n\n"
1697 "Machine Commands:\n"
1698 " list List running VMs and containers\n"
1699 " status NAME... Show VM/container details\n"
1700 " show NAME... Show properties of one or more VMs/containers\n"
1701 " login NAME Get a login prompt on a container\n"
1702 " start NAME... Start container as a service\n"
1703 " enable NAME... Enable automatic container start at boot\n"
1704 " disable NAME... Disable automatic container start at boot\n"
1705 " poweroff NAME... Power off one or more containers\n"
1706 " reboot NAME... Reboot one or more containers\n"
1707 " terminate NAME... Terminate one or more VMs/containers\n"
1708 " kill NAME... Send signal to processes of a VM/container\n"
1709 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1710 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1711 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1713 " list-images Show available images\n"
1714 " image-status NAME... Show image details\n"
1715 " show-image NAME... Show properties of image\n"
1716 " clone NAME NAME Clone an image\n"
1717 " rename NAME NAME Rename an image\n"
1718 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1719 " remove NAME... Remove an image\n",
1720 program_invocation_short_name);
1725 static int parse_argv(int argc, char *argv[]) {
1728 ARG_VERSION = 0x100,
1736 static const struct option options[] = {
1737 { "help", no_argument, NULL, 'h' },
1738 { "version", no_argument, NULL, ARG_VERSION },
1739 { "property", required_argument, NULL, 'p' },
1740 { "all", no_argument, NULL, 'a' },
1741 { "full", no_argument, NULL, 'l' },
1742 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1743 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1744 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1745 { "signal", required_argument, NULL, 's' },
1746 { "host", required_argument, NULL, 'H' },
1747 { "machine", required_argument, NULL, 'M' },
1748 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1749 { "mkdir", no_argument, NULL, ARG_MKDIR },
1750 { "quiet", no_argument, NULL, 'q' },
1759 while ((c = getopt_long(argc, argv, "hp:als:H:M:q", options, NULL)) >= 0)
1764 return help(0, NULL, NULL);
1767 puts(PACKAGE_STRING);
1768 puts(SYSTEMD_FEATURES);
1772 r = strv_extend(&arg_property, optarg);
1776 /* If the user asked for a particular
1777 * property, show it to him, even if it is
1791 arg_no_pager = true;
1799 arg_kill_who = optarg;
1803 arg_signal = signal_from_string_try_harder(optarg);
1804 if (arg_signal < 0) {
1805 log_error("Failed to parse signal string %s.", optarg);
1811 arg_transport = BUS_TRANSPORT_REMOTE;
1816 arg_transport = BUS_TRANSPORT_MACHINE;
1821 arg_read_only = true;
1836 assert_not_reached("Unhandled option");
1842 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1844 static const Verb verbs[] = {
1845 { "help", VERB_ANY, VERB_ANY, 0, help },
1846 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1847 { "list-images", VERB_ANY, 1, 0, list_images },
1848 { "status", 2, VERB_ANY, 0, show_machine },
1849 { "image-status",2, VERB_ANY, 0, show_image },
1850 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1851 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1852 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1853 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1854 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1855 { "kill", 2, VERB_ANY, 0, kill_machine },
1856 { "login", 2, 2, 0, login_machine },
1857 { "bind", 3, 4, 0, bind_mount },
1858 { "copy-to", 3, 4, 0, copy_files },
1859 { "copy-from", 3, 4, 0, copy_files },
1860 { "remove", 2, VERB_ANY, 0, remove_image },
1861 { "rename", 3, 3, 0, rename_image },
1862 { "clone", 3, 3, 0, clone_image },
1863 { "read-only", 2, 3, 0, read_only_image },
1864 { "start", 2, VERB_ANY, 0, start_machine },
1865 { "enable", 2, VERB_ANY, 0, enable_machine },
1866 { "disable", 2, VERB_ANY, 0, enable_machine },
1870 return dispatch_verb(argc, argv, verbs, bus);
1873 int main(int argc, char*argv[]) {
1874 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1877 setlocale(LC_ALL, "");
1878 log_parse_environment();
1881 r = parse_argv(argc, argv);
1885 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1887 log_error_errno(r, "Failed to create bus connection: %m");
1891 r = machinectl_main(argc, argv, bus);
1896 strv_free(arg_property);
1898 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;