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;
67 static void pager_open_if_enabled(void) {
69 /* Cache result before we open the pager */
76 static int list_machines(int argc, char *argv[], void *userdata) {
78 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
79 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
80 const char *name, *class, *service, *object;
81 sd_bus *bus = userdata;
87 pager_open_if_enabled();
89 r = sd_bus_call_method(
91 "org.freedesktop.machine1",
92 "/org/freedesktop/machine1",
93 "org.freedesktop.machine1.Manager",
99 log_error("Could not get machines: %s", bus_error_message(&error, -r));
104 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
106 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
108 return bus_log_parse_error(r);
110 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
111 printf("%-32s %-9s %-16s\n", name, class, service);
116 return bus_log_parse_error(r);
118 r = sd_bus_message_exit_container(reply);
120 return bus_log_parse_error(r);
123 printf("\n%u machines listed.\n", k);
128 typedef struct ImageInfo {
137 static int compare_image_info(const void *a, const void *b) {
138 const ImageInfo *x = a, *y = b;
140 return strcmp(x->name, y->name);
143 static int list_images(int argc, char *argv[], void *userdata) {
145 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
146 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("SIZE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
147 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
148 _cleanup_free_ ImageInfo *images = NULL;
149 size_t n_images = 0, n_allocated = 0, j;
150 const char *name, *type, *object;
151 sd_bus *bus = userdata;
152 uint64_t crtime, mtime, size;
157 pager_open_if_enabled();
159 r = sd_bus_call_method(
161 "org.freedesktop.machine1",
162 "/org/freedesktop/machine1",
163 "org.freedesktop.machine1.Manager",
169 log_error("Could not get images: %s", bus_error_message(&error, -r));
173 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
175 return bus_log_parse_error(r);
177 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
178 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
181 if (name[0] == '.' && !arg_all)
184 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
187 images[n_images].name = name;
188 images[n_images].type = type;
189 images[n_images].read_only = read_only;
190 images[n_images].crtime = crtime;
191 images[n_images].mtime = mtime;
192 images[n_images].size = size;
203 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
209 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
214 if (size != (uint64_t) -1) {
215 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
223 return bus_log_parse_error(r);
225 r = sd_bus_message_exit_container(reply);
227 return bus_log_parse_error(r);
229 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
232 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
233 (int) max_name, "NAME",
234 (int) max_type, "TYPE",
236 (int) max_size, "SIZE",
237 (int) max_crtime, "CREATED",
238 (int) max_mtime, "MODIFIED");
240 for (j = 0; j < n_images; j++) {
241 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
243 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
244 (int) max_name, images[j].name,
245 (int) max_type, images[j].type,
246 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
247 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
248 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
249 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
253 return bus_log_parse_error(r);
257 printf("\n%zu images listed.\n", n_images);
262 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
263 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
264 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
265 _cleanup_free_ char *path = NULL;
273 if (arg_transport == BUS_TRANSPORT_REMOTE)
276 path = unit_dbus_path_from_name(unit);
280 r = sd_bus_get_property(
282 "org.freedesktop.systemd1",
284 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
290 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
294 r = sd_bus_message_read(reply, "s", &cgroup);
296 return bus_log_parse_error(r);
301 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
305 arg_all * OUTPUT_SHOW_ALL |
306 arg_full * OUTPUT_FULL_WIDTH;
314 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
318 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
319 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
327 r = sd_bus_call_method(bus,
328 "org.freedesktop.machine1",
329 "/org/freedesktop/machine1",
330 "org.freedesktop.machine1.Manager",
331 "GetMachineAddresses",
338 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
340 return bus_log_parse_error(r);
342 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
346 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
348 r = sd_bus_message_read(reply, "i", &family);
350 return bus_log_parse_error(r);
352 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
354 return bus_log_parse_error(r);
356 fputs(prefix, stdout);
357 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
358 if (family == AF_INET6 && ifi > 0)
362 r = sd_bus_message_exit_container(reply);
364 return bus_log_parse_error(r);
366 if (prefix != prefix2)
370 return bus_log_parse_error(r);
372 r = sd_bus_message_exit_container(reply);
374 return bus_log_parse_error(r);
379 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
380 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
381 const char *k, *v, *pretty = NULL;
388 r = sd_bus_call_method(bus,
389 "org.freedesktop.machine1",
390 "/org/freedesktop/machine1",
391 "org.freedesktop.machine1.Manager",
392 "GetMachineOSRelease",
399 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
401 return bus_log_parse_error(r);
403 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
404 if (streq(k, "PRETTY_NAME"))
409 return bus_log_parse_error(r);
411 r = sd_bus_message_exit_container(reply);
413 return bus_log_parse_error(r);
416 printf("%s%s\n", prefix, pretty);
421 typedef struct MachineStatusInfo {
427 char *root_directory;
434 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
435 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
436 char since2[FORMAT_TIMESTAMP_MAX], *s2;
442 fputs(strna(i->name), stdout);
444 if (!sd_id128_equal(i->id, SD_ID128_NULL))
445 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
449 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
450 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
453 printf("\t Since: %s; %s\n", s2, s1);
455 printf("\t Since: %s\n", s2);
458 _cleanup_free_ char *t = NULL;
460 printf("\t Leader: %u", (unsigned) i->leader);
462 get_process_comm(i->leader, &t);
470 printf("\t Service: %s", i->service);
473 printf("; class %s", i->class);
477 printf("\t Class: %s\n", i->class);
479 if (i->root_directory)
480 printf("\t Root: %s\n", i->root_directory);
482 if (i->n_netif > 0) {
485 fputs("\t Iface:", stdout);
487 for (c = 0; c < i->n_netif; c++) {
488 char name[IF_NAMESIZE+1] = "";
490 if (if_indextoname(i->netif[c], name)) {
499 printf(" %i", i->netif[c]);
505 print_addresses(bus, i->name, ifi,
509 print_os_release(bus, i->name, "\t OS: ");
512 printf("\t Unit: %s\n", i->unit);
513 show_unit_cgroup(bus, i->unit, i->leader);
517 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
518 MachineStatusInfo *i = userdata;
523 assert_cc(sizeof(int32_t) == sizeof(int));
524 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
530 i->n_netif = l / sizeof(int32_t);
531 i->netif = memdup(v, l);
538 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
540 static const struct bus_properties_map map[] = {
541 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
542 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
543 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
544 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
545 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
546 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
547 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
548 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
549 { "NetworkInterfaces", "ai", map_netif, 0 },
553 MachineStatusInfo info = {};
561 r = bus_map_all_properties(bus,
562 "org.freedesktop.machine1",
567 return log_error_errno(r, "Could not get properties: %m");
573 print_machine_status_info(bus, &info);
579 free(info.root_directory);
585 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
597 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
599 log_error_errno(r, "Could not get properties: %m");
604 static int show_machine(int argc, char *argv[], void *userdata) {
606 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
607 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
608 bool properties, new_line = false;
609 sd_bus *bus = userdata;
614 properties = !strstr(argv[0], "status");
616 pager_open_if_enabled();
618 if (properties && argc <= 1) {
620 /* If no argument is specified, inspect the manager
622 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
627 for (i = 1; i < argc; i++) {
628 const char *path = NULL;
630 r = sd_bus_call_method(
632 "org.freedesktop.machine1",
633 "/org/freedesktop/machine1",
634 "org.freedesktop.machine1.Manager",
640 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
644 r = sd_bus_message_read(reply, "o", &path);
646 return bus_log_parse_error(r);
649 r = show_machine_properties(bus, path, &new_line);
651 r = show_machine_info(argv[0], bus, path, &new_line);
657 typedef struct ImageStatusInfo {
666 uint64_t size_exclusive;
667 uint64_t limit_exclusive;
670 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
671 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
672 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
673 char bs[FORMAT_BYTES_MAX], *s3;
674 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
680 fputs(i->name, stdout);
685 printf("\t Type: %s\n", i->type);
688 printf("\t Path: %s\n", i->path);
690 printf("\t RO: %s%s%s\n",
691 i->read_only ? ansi_highlight_red() : "",
692 i->read_only ? "read-only" : "writable",
693 i->read_only ? ansi_highlight_off() : "");
695 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
696 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
698 printf("\t Created: %s; %s\n", s2, s1);
700 printf("\t Created: %s\n", s2);
702 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
703 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
705 printf("\tModified: %s; %s\n", s2, s1);
707 printf("\tModified: %s\n", s2);
709 s3 = format_bytes(bs, sizeof(bs), i->size);
710 s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
712 printf("\t Size: %s (exclusive: %s)\n", s3, s4);
714 printf("\t Size: %s\n", s3);
716 s3 = format_bytes(bs, sizeof(bs), i->limit);
717 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
719 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
721 printf("\t Limit: %s\n", s3);
724 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
726 static const struct bus_properties_map map[] = {
727 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
728 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
729 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
730 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
731 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
732 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
733 { "Size", "t", NULL, offsetof(ImageStatusInfo, size) },
734 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
735 { "SizeExclusive", "t", NULL, offsetof(ImageStatusInfo, size_exclusive) },
736 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
740 ImageStatusInfo info = {};
748 r = bus_map_all_properties(bus,
749 "org.freedesktop.machine1",
754 return log_error_errno(r, "Could not get properties: %m");
760 print_image_status_info(bus, &info);
769 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
781 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
783 log_error_errno(r, "Could not get properties: %m");
788 static int show_image(int argc, char *argv[], void *userdata) {
790 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
791 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
792 bool properties, new_line = false;
793 sd_bus *bus = userdata;
798 properties = !strstr(argv[0], "status");
800 pager_open_if_enabled();
802 if (properties && argc <= 1) {
804 /* If no argument is specified, inspect the manager
806 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
811 for (i = 1; i < argc; i++) {
812 const char *path = NULL;
814 r = sd_bus_call_method(
816 "org.freedesktop.machine1",
817 "/org/freedesktop/machine1",
818 "org.freedesktop.machine1.Manager",
824 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
828 r = sd_bus_message_read(reply, "o", &path);
830 return bus_log_parse_error(r);
833 r = show_image_properties(bus, path, &new_line);
835 r = show_image_info(argv[0], bus, path, &new_line);
841 static int kill_machine(int argc, char *argv[], void *userdata) {
842 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
843 sd_bus *bus = userdata;
849 arg_kill_who = "all";
851 for (i = 1; i < argc; i++) {
854 r = sd_bus_call_method(
856 "org.freedesktop.machine1",
857 "/org/freedesktop/machine1",
858 "org.freedesktop.machine1.Manager",
862 "ssi", argv[i], arg_kill_who, arg_signal);
864 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
872 static int reboot_machine(int argc, char *argv[], void *userdata) {
873 arg_kill_who = "leader";
874 arg_signal = SIGINT; /* sysvinit + systemd */
876 return kill_machine(argc, argv, userdata);
879 static int poweroff_machine(int argc, char *argv[], void *userdata) {
880 arg_kill_who = "leader";
881 arg_signal = SIGRTMIN+4; /* only systemd */
883 return kill_machine(argc, argv, userdata);
886 static int terminate_machine(int argc, char *argv[], void *userdata) {
887 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
888 sd_bus *bus = userdata;
893 for (i = 1; i < argc; i++) {
896 r = sd_bus_call_method(
898 "org.freedesktop.machine1",
899 "/org/freedesktop/machine1",
900 "org.freedesktop.machine1.Manager",
906 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
914 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
915 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
916 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
925 r = sd_bus_call_method(
927 "org.freedesktop.machine1",
928 "/org/freedesktop/machine1",
929 "org.freedesktop.machine1.Manager",
935 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
939 r = sd_bus_message_read(reply, "o", &object);
941 return bus_log_parse_error(r);
943 r = sd_bus_get_property(
945 "org.freedesktop.machine1",
947 "org.freedesktop.machine1.Machine",
953 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
955 r = sd_bus_message_read(reply2, "u", &leader);
957 return bus_log_parse_error(r);
963 static int copy_files(int argc, char *argv[], void *userdata) {
964 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
965 _cleanup_close_ int hostfd = -1;
966 sd_bus *bus = userdata;
974 copy_from = streq(argv[0], "copy-from");
975 dest = argv[3] ?: argv[2];
976 host_path = strdupa(copy_from ? dest : argv[2]);
977 container_path = strdupa(copy_from ? argv[2] : dest);
979 if (!path_is_absolute(container_path)) {
980 log_error("Container path not absolute.");
984 t = strdup(host_path);
985 host_basename = basename(t);
986 host_dirname = dirname(host_path);
988 t = strdup(container_path);
989 container_basename = basename(t);
990 container_dirname = dirname(container_path);
992 r = machine_get_leader(bus, argv[1], &leader);
996 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
998 return log_error_errno(errno, "Failed to open source directory: %m");
1002 return log_error_errno(errno, "Failed to fork(): %m");
1009 q = procfs_file_alloca(leader, "ns/mnt");
1010 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1012 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1013 _exit(EXIT_FAILURE);
1016 if (setns(mntfd, CLONE_NEWNS) < 0) {
1017 log_error_errno(errno, "Failed to join namespace of leader: %m");
1018 _exit(EXIT_FAILURE);
1021 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1022 if (containerfd < 0) {
1023 log_error_errno(errno, "Failed top open destination directory: %m");
1024 _exit(EXIT_FAILURE);
1028 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1030 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1032 log_error_errno(errno, "Failed to copy tree: %m");
1033 _exit(EXIT_FAILURE);
1036 _exit(EXIT_SUCCESS);
1039 r = wait_for_terminate(child, &si);
1041 return log_error_errno(r, "Failed to wait for client: %m");
1042 if (si.si_code != CLD_EXITED) {
1043 log_error("Client died abnormally.");
1046 if (si.si_status != EXIT_SUCCESS)
1052 static int bind_mount(int argc, char *argv[], void *userdata) {
1053 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1054 sd_bus *bus = userdata;
1055 pid_t child, leader;
1058 bool mount_slave_created = false, mount_slave_mounted = false,
1059 mount_tmp_created = false, mount_tmp_mounted = false,
1060 mount_outside_created = false, mount_outside_mounted = false;
1065 /* One day, when bind mounting /proc/self/fd/n works across
1066 * namespace boundaries we should rework this logic to make
1069 dest = argv[3] ?: argv[2];
1070 if (!path_is_absolute(dest)) {
1071 log_error("Destination path not absolute.");
1075 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1076 if (access(p, F_OK) < 0) {
1077 log_error("Container does not allow propagation of mount points.");
1081 r = machine_get_leader(bus, argv[1], &leader);
1085 /* Our goal is to install a new bind mount into the container,
1086 possibly read-only. This is irritatingly complex
1087 unfortunately, currently.
1089 First, we start by creating a private playground in /tmp,
1090 that we can mount MS_SLAVE. (Which is necessary, since
1091 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1094 if (!mkdtemp(mount_slave))
1095 return log_error_errno(errno, "Failed to create playground: %m");
1097 mount_slave_created = true;
1099 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1100 r = log_error_errno(errno, "Failed to make bind mount: %m");
1104 mount_slave_mounted = true;
1106 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1107 r = log_error_errno(errno, "Failed to remount slave: %m");
1111 /* Second, we mount the source directory to a directory inside
1112 of our MS_SLAVE playground. */
1113 mount_tmp = strappenda(mount_slave, "/mount");
1114 if (mkdir(mount_tmp, 0700) < 0) {
1115 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1119 mount_tmp_created = true;
1121 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1122 r = log_error_errno(errno, "Failed to overmount: %m");
1126 mount_tmp_mounted = true;
1128 /* Third, we remount the new bind mount read-only if requested. */
1130 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1131 r = log_error_errno(errno, "Failed to mark read-only: %m");
1135 /* Fourth, we move the new bind mount into the propagation
1136 * directory. This way it will appear there read-only
1139 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1140 if (!mkdtemp(mount_outside)) {
1141 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1145 mount_outside_created = true;
1147 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1148 r = log_error_errno(errno, "Failed to move: %m");
1152 mount_outside_mounted = true;
1153 mount_tmp_mounted = false;
1155 (void) rmdir(mount_tmp);
1156 mount_tmp_created = false;
1158 (void) umount(mount_slave);
1159 mount_slave_mounted = false;
1161 (void) rmdir(mount_slave);
1162 mount_slave_created = false;
1166 r = log_error_errno(errno, "Failed to fork(): %m");
1171 const char *mount_inside;
1175 q = procfs_file_alloca(leader, "ns/mnt");
1176 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1178 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1179 _exit(EXIT_FAILURE);
1182 if (setns(mntfd, CLONE_NEWNS) < 0) {
1183 log_error_errno(errno, "Failed to join namespace of leader: %m");
1184 _exit(EXIT_FAILURE);
1188 mkdir_p(dest, 0755);
1190 /* Fifth, move the mount to the right place inside */
1191 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1192 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1193 log_error_errno(errno, "Failed to mount: %m");
1194 _exit(EXIT_FAILURE);
1197 _exit(EXIT_SUCCESS);
1200 r = wait_for_terminate(child, &si);
1202 log_error_errno(r, "Failed to wait for client: %m");
1205 if (si.si_code != CLD_EXITED) {
1206 log_error("Client died abnormally.");
1210 if (si.si_status != EXIT_SUCCESS) {
1218 if (mount_outside_mounted)
1219 umount(mount_outside);
1220 if (mount_outside_created)
1221 rmdir(mount_outside);
1223 if (mount_tmp_mounted)
1225 if (mount_tmp_created)
1228 if (mount_slave_mounted)
1229 umount(mount_slave);
1230 if (mount_slave_created)
1231 umount(mount_slave);
1236 static int login_machine(int argc, char *argv[], void *userdata) {
1237 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1238 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1239 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1240 _cleanup_event_unref_ sd_event *event = NULL;
1241 int master = -1, r, ret = 0;
1242 sd_bus *bus = userdata;
1249 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1250 arg_transport != BUS_TRANSPORT_MACHINE) {
1251 log_error("Login only supported on local machines.");
1255 r = sd_event_default(&event);
1257 return log_error_errno(r, "Failed to get event loop: %m");
1259 r = sd_bus_attach_event(bus, event, 0);
1261 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1263 r = sd_bus_message_new_method_call(bus,
1265 "org.freedesktop.machine1",
1266 "/org/freedesktop/machine1",
1267 "org.freedesktop.machine1.Manager",
1268 "OpenMachineLogin");
1270 return bus_log_create_error(r);
1272 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1274 return bus_log_create_error(r);
1276 r = sd_bus_message_append(m, "s", argv[1]);
1278 return bus_log_create_error(r);
1280 r = sd_bus_call(bus, m, 0, &error, &reply);
1282 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1286 r = sd_bus_message_read(reply, "hs", &master, &pty);
1288 return bus_log_parse_error(r);
1290 assert_se(sigemptyset(&mask) == 0);
1291 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1292 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1294 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
1296 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1297 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1299 r = pty_forward_new(event, master, true, &forward);
1301 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1303 r = sd_event_loop(event);
1305 return log_error_errno(r, "Failed to run event loop: %m");
1307 pty_forward_last_char(forward, &last_char);
1309 forward = pty_forward_free(forward);
1311 if (last_char != '\n')
1312 fputc('\n', stdout);
1314 log_info("Connection to container %s terminated.", argv[1]);
1316 sd_event_get_exit_code(event, &ret);
1320 static int remove_image(int argc, char *argv[], void *userdata) {
1321 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1322 sd_bus *bus = userdata;
1327 for (i = 1; i < argc; i++) {
1328 r = sd_bus_call_method(
1330 "org.freedesktop.machine1",
1331 "/org/freedesktop/machine1",
1332 "org.freedesktop.machine1.Manager",
1338 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1346 static int rename_image(int argc, char *argv[], void *userdata) {
1347 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1348 sd_bus *bus = userdata;
1351 r = sd_bus_call_method(
1353 "org.freedesktop.machine1",
1354 "/org/freedesktop/machine1",
1355 "org.freedesktop.machine1.Manager",
1359 "ss", argv[1], argv[2]);
1361 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1368 static int clone_image(int argc, char *argv[], void *userdata) {
1369 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1370 sd_bus *bus = userdata;
1373 r = sd_bus_call_method(
1375 "org.freedesktop.machine1",
1376 "/org/freedesktop/machine1",
1377 "org.freedesktop.machine1.Manager",
1381 "ssb", argv[1], argv[2], arg_read_only);
1383 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1390 static int read_only_image(int argc, char *argv[], void *userdata) {
1391 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1392 sd_bus *bus = userdata;
1396 b = parse_boolean(argv[2]);
1398 log_error("Failed to parse boolean argument: %s", argv[2]);
1403 r = sd_bus_call_method(
1405 "org.freedesktop.machine1",
1406 "/org/freedesktop/machine1",
1407 "org.freedesktop.machine1.Manager",
1408 "MarkImageReadOnly",
1413 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1420 static int help(int argc, char *argv[], void *userdata) {
1422 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1423 "Send control commands to or query the virtual machine and container\n"
1424 "registration manager.\n\n"
1425 " -h --help Show this help\n"
1426 " --version Show package version\n"
1427 " --no-pager Do not pipe output into a pager\n"
1428 " --no-legend Do not show the headers and footers\n"
1429 " -H --host=[USER@]HOST Operate on remote host\n"
1430 " -M --machine=CONTAINER Operate on local container\n"
1431 " -p --property=NAME Show only properties by this name\n"
1432 " -a --all Show all properties, including empty ones\n"
1433 " -l --full Do not ellipsize output\n"
1434 " --kill-who=WHO Who to send signal to\n"
1435 " -s --signal=SIGNAL Which signal to send\n"
1436 " --read-only Create read-only bind mount\n"
1437 " --mkdir Create directory before bind mounting, if missing\n\n"
1438 "Machine Commands:\n"
1439 " list List running VMs and containers\n"
1440 " status NAME... Show VM/container details\n"
1441 " show NAME... Show properties of one or more VMs/containers\n"
1442 " login NAME Get a login prompt on a container\n"
1443 " poweroff NAME... Power off one or more containers\n"
1444 " reboot NAME... Reboot one or more containers\n"
1445 " terminate NAME... Terminate one or more VMs/containers\n"
1446 " kill NAME... Send signal to processes of a VM/container\n"
1447 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1448 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1449 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1451 " list-images Show available images\n"
1452 " image-status NAME... Show image details\n"
1453 " show-image NAME... Show properties of image\n"
1454 " clone NAME NAME Clone an image\n"
1455 " rename NAME NAME Rename an image\n"
1456 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1457 " remove NAME... Remove an image\n",
1458 program_invocation_short_name);
1463 static int parse_argv(int argc, char *argv[]) {
1466 ARG_VERSION = 0x100,
1474 static const struct option options[] = {
1475 { "help", no_argument, NULL, 'h' },
1476 { "version", no_argument, NULL, ARG_VERSION },
1477 { "property", required_argument, NULL, 'p' },
1478 { "all", no_argument, NULL, 'a' },
1479 { "full", no_argument, NULL, 'l' },
1480 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1481 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1482 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1483 { "signal", required_argument, NULL, 's' },
1484 { "host", required_argument, NULL, 'H' },
1485 { "machine", required_argument, NULL, 'M' },
1486 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1487 { "mkdir", no_argument, NULL, ARG_MKDIR },
1496 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1501 return help(0, NULL, NULL);
1504 puts(PACKAGE_STRING);
1505 puts(SYSTEMD_FEATURES);
1509 r = strv_extend(&arg_property, optarg);
1513 /* If the user asked for a particular
1514 * property, show it to him, even if it is
1528 arg_no_pager = true;
1536 arg_kill_who = optarg;
1540 arg_signal = signal_from_string_try_harder(optarg);
1541 if (arg_signal < 0) {
1542 log_error("Failed to parse signal string %s.", optarg);
1548 arg_transport = BUS_TRANSPORT_REMOTE;
1553 arg_transport = BUS_TRANSPORT_MACHINE;
1558 arg_read_only = true;
1569 assert_not_reached("Unhandled option");
1575 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1577 static const Verb verbs[] = {
1578 { "help", VERB_ANY, VERB_ANY, 0, help },
1579 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1580 { "list-images", VERB_ANY, 1, 0, list_images },
1581 { "status", 2, VERB_ANY, 0, show_machine },
1582 { "image-status",2, VERB_ANY, 0, show_image },
1583 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1584 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1585 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1586 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1587 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1588 { "kill", 2, VERB_ANY, 0, kill_machine },
1589 { "login", 2, 2, 0, login_machine },
1590 { "bind", 3, 4, 0, bind_mount },
1591 { "copy-to", 3, 4, 0, copy_files },
1592 { "copy-from", 3, 4, 0, copy_files },
1593 { "remove", 2, VERB_ANY, 0, remove_image },
1594 { "rename", 3, 3, 0, rename_image },
1595 { "clone", 3, 3, 0, clone_image },
1596 { "read-only", 2, 3, 0, read_only_image },
1600 return dispatch_verb(argc, argv, verbs, bus);
1603 int main(int argc, char*argv[]) {
1604 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1607 setlocale(LC_ALL, "");
1608 log_parse_environment();
1611 r = parse_argv(argc, argv);
1615 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1617 log_error_errno(r, "Failed to create bus connection: %m");
1621 r = machinectl_main(argc, argv, bus);
1626 strv_free(arg_property);
1628 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;