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 static int list_machines(int argc, char *argv[], void *userdata) {
79 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
80 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
81 const char *name, *class, *service, *object;
82 sd_bus *bus = userdata;
88 pager_open_if_enabled();
90 r = sd_bus_call_method(
92 "org.freedesktop.machine1",
93 "/org/freedesktop/machine1",
94 "org.freedesktop.machine1.Manager",
100 log_error("Could not get machines: %s", bus_error_message(&error, -r));
105 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
107 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
109 return bus_log_parse_error(r);
111 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
112 printf("%-32s %-9s %-16s\n", name, class, service);
117 return bus_log_parse_error(r);
119 r = sd_bus_message_exit_container(reply);
121 return bus_log_parse_error(r);
124 printf("\n%u machines listed.\n", k);
129 typedef struct ImageInfo {
138 static int compare_image_info(const void *a, const void *b) {
139 const ImageInfo *x = a, *y = b;
141 return strcmp(x->name, y->name);
144 static int list_images(int argc, char *argv[], void *userdata) {
146 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
147 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("SIZE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
148 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
149 _cleanup_free_ ImageInfo *images = NULL;
150 size_t n_images = 0, n_allocated = 0, j;
151 const char *name, *type, *object;
152 sd_bus *bus = userdata;
153 uint64_t crtime, mtime, size;
158 pager_open_if_enabled();
160 r = sd_bus_call_method(
162 "org.freedesktop.machine1",
163 "/org/freedesktop/machine1",
164 "org.freedesktop.machine1.Manager",
170 log_error("Could not get images: %s", bus_error_message(&error, -r));
174 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
176 return bus_log_parse_error(r);
178 while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
179 char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
182 if (name[0] == '.' && !arg_all)
185 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
188 images[n_images].name = name;
189 images[n_images].type = type;
190 images[n_images].read_only = read_only;
191 images[n_images].crtime = crtime;
192 images[n_images].mtime = mtime;
193 images[n_images].size = size;
204 l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
210 l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
215 if (size != (uint64_t) -1) {
216 l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
224 return bus_log_parse_error(r);
226 r = sd_bus_message_exit_container(reply);
228 return bus_log_parse_error(r);
230 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
233 printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
234 (int) max_name, "NAME",
235 (int) max_type, "TYPE",
237 (int) max_size, "SIZE",
238 (int) max_crtime, "CREATED",
239 (int) max_mtime, "MODIFIED");
241 for (j = 0; j < n_images; j++) {
242 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
244 printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
245 (int) max_name, images[j].name,
246 (int) max_type, images[j].type,
247 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
248 (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
249 (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
250 (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
254 return bus_log_parse_error(r);
258 printf("\n%zu images listed.\n", n_images);
263 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
264 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
265 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
266 _cleanup_free_ char *path = NULL;
274 if (arg_transport == BUS_TRANSPORT_REMOTE)
277 path = unit_dbus_path_from_name(unit);
281 r = sd_bus_get_property(
283 "org.freedesktop.systemd1",
285 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
291 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
295 r = sd_bus_message_read(reply, "s", &cgroup);
297 return bus_log_parse_error(r);
302 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
306 arg_all * OUTPUT_SHOW_ALL |
307 arg_full * OUTPUT_FULL_WIDTH;
315 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
319 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
320 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
328 r = sd_bus_call_method(bus,
329 "org.freedesktop.machine1",
330 "/org/freedesktop/machine1",
331 "org.freedesktop.machine1.Manager",
332 "GetMachineAddresses",
339 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
341 return bus_log_parse_error(r);
343 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
347 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
349 r = sd_bus_message_read(reply, "i", &family);
351 return bus_log_parse_error(r);
353 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
355 return bus_log_parse_error(r);
357 fputs(prefix, stdout);
358 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
359 if (family == AF_INET6 && ifi > 0)
363 r = sd_bus_message_exit_container(reply);
365 return bus_log_parse_error(r);
367 if (prefix != prefix2)
371 return bus_log_parse_error(r);
373 r = sd_bus_message_exit_container(reply);
375 return bus_log_parse_error(r);
380 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
381 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
382 const char *k, *v, *pretty = NULL;
389 r = sd_bus_call_method(bus,
390 "org.freedesktop.machine1",
391 "/org/freedesktop/machine1",
392 "org.freedesktop.machine1.Manager",
393 "GetMachineOSRelease",
400 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
402 return bus_log_parse_error(r);
404 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
405 if (streq(k, "PRETTY_NAME"))
410 return bus_log_parse_error(r);
412 r = sd_bus_message_exit_container(reply);
414 return bus_log_parse_error(r);
417 printf("%s%s\n", prefix, pretty);
422 typedef struct MachineStatusInfo {
428 char *root_directory;
435 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
436 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
437 char since2[FORMAT_TIMESTAMP_MAX], *s2;
443 fputs(strna(i->name), stdout);
445 if (!sd_id128_equal(i->id, SD_ID128_NULL))
446 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
450 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
451 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
454 printf("\t Since: %s; %s\n", s2, s1);
456 printf("\t Since: %s\n", s2);
459 _cleanup_free_ char *t = NULL;
461 printf("\t Leader: %u", (unsigned) i->leader);
463 get_process_comm(i->leader, &t);
471 printf("\t Service: %s", i->service);
474 printf("; class %s", i->class);
478 printf("\t Class: %s\n", i->class);
480 if (i->root_directory)
481 printf("\t Root: %s\n", i->root_directory);
483 if (i->n_netif > 0) {
486 fputs("\t Iface:", stdout);
488 for (c = 0; c < i->n_netif; c++) {
489 char name[IF_NAMESIZE+1] = "";
491 if (if_indextoname(i->netif[c], name)) {
500 printf(" %i", i->netif[c]);
506 print_addresses(bus, i->name, ifi,
510 print_os_release(bus, i->name, "\t OS: ");
513 printf("\t Unit: %s\n", i->unit);
514 show_unit_cgroup(bus, i->unit, i->leader);
518 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
519 MachineStatusInfo *i = userdata;
524 assert_cc(sizeof(int32_t) == sizeof(int));
525 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
531 i->n_netif = l / sizeof(int32_t);
532 i->netif = memdup(v, l);
539 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
541 static const struct bus_properties_map map[] = {
542 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
543 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
544 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
545 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
546 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
547 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
548 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
549 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
550 { "NetworkInterfaces", "ai", map_netif, 0 },
554 MachineStatusInfo info = {};
562 r = bus_map_all_properties(bus,
563 "org.freedesktop.machine1",
568 return log_error_errno(r, "Could not get properties: %m");
574 print_machine_status_info(bus, &info);
580 free(info.root_directory);
586 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
598 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
600 log_error_errno(r, "Could not get properties: %m");
605 static int show_machine(int argc, char *argv[], void *userdata) {
607 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
608 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
609 bool properties, new_line = false;
610 sd_bus *bus = userdata;
615 properties = !strstr(argv[0], "status");
617 pager_open_if_enabled();
619 if (properties && argc <= 1) {
621 /* If no argument is specified, inspect the manager
623 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
628 for (i = 1; i < argc; i++) {
629 const char *path = NULL;
631 r = sd_bus_call_method(
633 "org.freedesktop.machine1",
634 "/org/freedesktop/machine1",
635 "org.freedesktop.machine1.Manager",
641 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
645 r = sd_bus_message_read(reply, "o", &path);
647 return bus_log_parse_error(r);
650 r = show_machine_properties(bus, path, &new_line);
652 r = show_machine_info(argv[0], bus, path, &new_line);
658 typedef struct ImageStatusInfo {
667 uint64_t size_exclusive;
668 uint64_t limit_exclusive;
671 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
672 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
673 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
674 char bs[FORMAT_BYTES_MAX], *s3;
675 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
681 fputs(i->name, stdout);
686 printf("\t Type: %s\n", i->type);
689 printf("\t Path: %s\n", i->path);
691 printf("\t RO: %s%s%s\n",
692 i->read_only ? ansi_highlight_red() : "",
693 i->read_only ? "read-only" : "writable",
694 i->read_only ? ansi_highlight_off() : "");
696 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
697 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
699 printf("\t Created: %s; %s\n", s2, s1);
701 printf("\t Created: %s\n", s2);
703 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
704 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
706 printf("\tModified: %s; %s\n", s2, s1);
708 printf("\tModified: %s\n", s2);
710 s3 = format_bytes(bs, sizeof(bs), i->size);
711 s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
713 printf("\t Size: %s (exclusive: %s)\n", s3, s4);
715 printf("\t Size: %s\n", s3);
717 s3 = format_bytes(bs, sizeof(bs), i->limit);
718 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
720 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
722 printf("\t Limit: %s\n", s3);
725 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
727 static const struct bus_properties_map map[] = {
728 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
729 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
730 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
731 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
732 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
733 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
734 { "Size", "t", NULL, offsetof(ImageStatusInfo, size) },
735 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
736 { "SizeExclusive", "t", NULL, offsetof(ImageStatusInfo, size_exclusive) },
737 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
741 ImageStatusInfo info = {};
749 r = bus_map_all_properties(bus,
750 "org.freedesktop.machine1",
755 return log_error_errno(r, "Could not get properties: %m");
761 print_image_status_info(bus, &info);
770 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
782 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
784 log_error_errno(r, "Could not get properties: %m");
789 static int show_image(int argc, char *argv[], void *userdata) {
791 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
792 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
793 bool properties, new_line = false;
794 sd_bus *bus = userdata;
799 properties = !strstr(argv[0], "status");
801 pager_open_if_enabled();
803 if (properties && argc <= 1) {
805 /* If no argument is specified, inspect the manager
807 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
812 for (i = 1; i < argc; i++) {
813 const char *path = NULL;
815 r = sd_bus_call_method(
817 "org.freedesktop.machine1",
818 "/org/freedesktop/machine1",
819 "org.freedesktop.machine1.Manager",
825 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
829 r = sd_bus_message_read(reply, "o", &path);
831 return bus_log_parse_error(r);
834 r = show_image_properties(bus, path, &new_line);
836 r = show_image_info(argv[0], bus, path, &new_line);
842 static int kill_machine(int argc, char *argv[], void *userdata) {
843 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
844 sd_bus *bus = userdata;
850 arg_kill_who = "all";
852 for (i = 1; i < argc; i++) {
855 r = sd_bus_call_method(
857 "org.freedesktop.machine1",
858 "/org/freedesktop/machine1",
859 "org.freedesktop.machine1.Manager",
863 "ssi", argv[i], arg_kill_who, arg_signal);
865 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
873 static int reboot_machine(int argc, char *argv[], void *userdata) {
874 arg_kill_who = "leader";
875 arg_signal = SIGINT; /* sysvinit + systemd */
877 return kill_machine(argc, argv, userdata);
880 static int poweroff_machine(int argc, char *argv[], void *userdata) {
881 arg_kill_who = "leader";
882 arg_signal = SIGRTMIN+4; /* only systemd */
884 return kill_machine(argc, argv, userdata);
887 static int terminate_machine(int argc, char *argv[], void *userdata) {
888 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
889 sd_bus *bus = userdata;
894 for (i = 1; i < argc; i++) {
897 r = sd_bus_call_method(
899 "org.freedesktop.machine1",
900 "/org/freedesktop/machine1",
901 "org.freedesktop.machine1.Manager",
907 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
915 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
916 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
917 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
926 r = sd_bus_call_method(
928 "org.freedesktop.machine1",
929 "/org/freedesktop/machine1",
930 "org.freedesktop.machine1.Manager",
936 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
940 r = sd_bus_message_read(reply, "o", &object);
942 return bus_log_parse_error(r);
944 r = sd_bus_get_property(
946 "org.freedesktop.machine1",
948 "org.freedesktop.machine1.Machine",
954 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
956 r = sd_bus_message_read(reply2, "u", &leader);
958 return bus_log_parse_error(r);
964 static int copy_files(int argc, char *argv[], void *userdata) {
965 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
966 _cleanup_close_ int hostfd = -1;
967 sd_bus *bus = userdata;
975 copy_from = streq(argv[0], "copy-from");
976 dest = argv[3] ?: argv[2];
977 host_path = strdupa(copy_from ? dest : argv[2]);
978 container_path = strdupa(copy_from ? argv[2] : dest);
980 if (!path_is_absolute(container_path)) {
981 log_error("Container path not absolute.");
985 t = strdup(host_path);
986 host_basename = basename(t);
987 host_dirname = dirname(host_path);
989 t = strdup(container_path);
990 container_basename = basename(t);
991 container_dirname = dirname(container_path);
993 r = machine_get_leader(bus, argv[1], &leader);
997 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
999 return log_error_errno(errno, "Failed to open source directory: %m");
1003 return log_error_errno(errno, "Failed to fork(): %m");
1010 q = procfs_file_alloca(leader, "ns/mnt");
1011 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1013 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1014 _exit(EXIT_FAILURE);
1017 if (setns(mntfd, CLONE_NEWNS) < 0) {
1018 log_error_errno(errno, "Failed to join namespace of leader: %m");
1019 _exit(EXIT_FAILURE);
1022 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1023 if (containerfd < 0) {
1024 log_error_errno(errno, "Failed top open destination directory: %m");
1025 _exit(EXIT_FAILURE);
1029 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1031 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1033 log_error_errno(errno, "Failed to copy tree: %m");
1034 _exit(EXIT_FAILURE);
1037 _exit(EXIT_SUCCESS);
1040 r = wait_for_terminate(child, &si);
1042 return log_error_errno(r, "Failed to wait for client: %m");
1043 if (si.si_code != CLD_EXITED) {
1044 log_error("Client died abnormally.");
1047 if (si.si_status != EXIT_SUCCESS)
1053 static int bind_mount(int argc, char *argv[], void *userdata) {
1054 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1055 sd_bus *bus = userdata;
1056 pid_t child, leader;
1059 bool mount_slave_created = false, mount_slave_mounted = false,
1060 mount_tmp_created = false, mount_tmp_mounted = false,
1061 mount_outside_created = false, mount_outside_mounted = false;
1066 /* One day, when bind mounting /proc/self/fd/n works across
1067 * namespace boundaries we should rework this logic to make
1070 dest = argv[3] ?: argv[2];
1071 if (!path_is_absolute(dest)) {
1072 log_error("Destination path not absolute.");
1076 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1077 if (access(p, F_OK) < 0) {
1078 log_error("Container does not allow propagation of mount points.");
1082 r = machine_get_leader(bus, argv[1], &leader);
1086 /* Our goal is to install a new bind mount into the container,
1087 possibly read-only. This is irritatingly complex
1088 unfortunately, currently.
1090 First, we start by creating a private playground in /tmp,
1091 that we can mount MS_SLAVE. (Which is necessary, since
1092 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1095 if (!mkdtemp(mount_slave))
1096 return log_error_errno(errno, "Failed to create playground: %m");
1098 mount_slave_created = true;
1100 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1101 r = log_error_errno(errno, "Failed to make bind mount: %m");
1105 mount_slave_mounted = true;
1107 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1108 r = log_error_errno(errno, "Failed to remount slave: %m");
1112 /* Second, we mount the source directory to a directory inside
1113 of our MS_SLAVE playground. */
1114 mount_tmp = strappenda(mount_slave, "/mount");
1115 if (mkdir(mount_tmp, 0700) < 0) {
1116 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1120 mount_tmp_created = true;
1122 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1123 r = log_error_errno(errno, "Failed to overmount: %m");
1127 mount_tmp_mounted = true;
1129 /* Third, we remount the new bind mount read-only if requested. */
1131 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1132 r = log_error_errno(errno, "Failed to mark read-only: %m");
1136 /* Fourth, we move the new bind mount into the propagation
1137 * directory. This way it will appear there read-only
1140 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1141 if (!mkdtemp(mount_outside)) {
1142 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1146 mount_outside_created = true;
1148 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1149 r = log_error_errno(errno, "Failed to move: %m");
1153 mount_outside_mounted = true;
1154 mount_tmp_mounted = false;
1156 (void) rmdir(mount_tmp);
1157 mount_tmp_created = false;
1159 (void) umount(mount_slave);
1160 mount_slave_mounted = false;
1162 (void) rmdir(mount_slave);
1163 mount_slave_created = false;
1167 r = log_error_errno(errno, "Failed to fork(): %m");
1172 const char *mount_inside;
1176 q = procfs_file_alloca(leader, "ns/mnt");
1177 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1179 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1180 _exit(EXIT_FAILURE);
1183 if (setns(mntfd, CLONE_NEWNS) < 0) {
1184 log_error_errno(errno, "Failed to join namespace of leader: %m");
1185 _exit(EXIT_FAILURE);
1189 mkdir_p(dest, 0755);
1191 /* Fifth, move the mount to the right place inside */
1192 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1193 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1194 log_error_errno(errno, "Failed to mount: %m");
1195 _exit(EXIT_FAILURE);
1198 _exit(EXIT_SUCCESS);
1201 r = wait_for_terminate(child, &si);
1203 log_error_errno(r, "Failed to wait for client: %m");
1206 if (si.si_code != CLD_EXITED) {
1207 log_error("Client died abnormally.");
1211 if (si.si_status != EXIT_SUCCESS) {
1219 if (mount_outside_mounted)
1220 umount(mount_outside);
1221 if (mount_outside_created)
1222 rmdir(mount_outside);
1224 if (mount_tmp_mounted)
1226 if (mount_tmp_created)
1229 if (mount_slave_mounted)
1230 umount(mount_slave);
1231 if (mount_slave_created)
1232 umount(mount_slave);
1237 static int login_machine(int argc, char *argv[], void *userdata) {
1238 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1239 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1240 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1241 _cleanup_event_unref_ sd_event *event = NULL;
1242 int master = -1, r, ret = 0;
1243 sd_bus *bus = userdata;
1250 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1251 arg_transport != BUS_TRANSPORT_MACHINE) {
1252 log_error("Login only supported on local machines.");
1256 r = sd_event_default(&event);
1258 return log_error_errno(r, "Failed to get event loop: %m");
1260 r = sd_bus_attach_event(bus, event, 0);
1262 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1264 r = sd_bus_message_new_method_call(bus,
1266 "org.freedesktop.machine1",
1267 "/org/freedesktop/machine1",
1268 "org.freedesktop.machine1.Manager",
1269 "OpenMachineLogin");
1271 return bus_log_create_error(r);
1273 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1275 return bus_log_create_error(r);
1277 r = sd_bus_message_append(m, "s", argv[1]);
1279 return bus_log_create_error(r);
1281 r = sd_bus_call(bus, m, 0, &error, &reply);
1283 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1287 r = sd_bus_message_read(reply, "hs", &master, &pty);
1289 return bus_log_parse_error(r);
1291 assert_se(sigemptyset(&mask) == 0);
1292 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1293 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1295 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
1297 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1298 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1300 r = pty_forward_new(event, master, true, &forward);
1302 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1304 r = sd_event_loop(event);
1306 return log_error_errno(r, "Failed to run event loop: %m");
1308 pty_forward_last_char(forward, &last_char);
1310 forward = pty_forward_free(forward);
1312 if (last_char != '\n')
1313 fputc('\n', stdout);
1315 log_info("Connection to container %s terminated.", argv[1]);
1317 sd_event_get_exit_code(event, &ret);
1321 static int remove_image(int argc, char *argv[], void *userdata) {
1322 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1323 sd_bus *bus = userdata;
1328 for (i = 1; i < argc; i++) {
1329 r = sd_bus_call_method(
1331 "org.freedesktop.machine1",
1332 "/org/freedesktop/machine1",
1333 "org.freedesktop.machine1.Manager",
1339 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1347 static int rename_image(int argc, char *argv[], void *userdata) {
1348 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1349 sd_bus *bus = userdata;
1352 r = sd_bus_call_method(
1354 "org.freedesktop.machine1",
1355 "/org/freedesktop/machine1",
1356 "org.freedesktop.machine1.Manager",
1360 "ss", argv[1], argv[2]);
1362 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1369 static int clone_image(int argc, char *argv[], void *userdata) {
1370 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1371 sd_bus *bus = userdata;
1374 r = sd_bus_call_method(
1376 "org.freedesktop.machine1",
1377 "/org/freedesktop/machine1",
1378 "org.freedesktop.machine1.Manager",
1382 "ssb", argv[1], argv[2], arg_read_only);
1384 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1391 static int read_only_image(int argc, char *argv[], void *userdata) {
1392 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1393 sd_bus *bus = userdata;
1397 b = parse_boolean(argv[2]);
1399 log_error("Failed to parse boolean argument: %s", argv[2]);
1404 r = sd_bus_call_method(
1406 "org.freedesktop.machine1",
1407 "/org/freedesktop/machine1",
1408 "org.freedesktop.machine1.Manager",
1409 "MarkImageReadOnly",
1414 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1421 static int start_machine(int argc, char *argv[], void *userdata) {
1422 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1423 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1424 sd_bus *bus = userdata;
1429 r = bus_wait_for_jobs_new(bus, &w);
1433 for (i = 1; i < argc; i++) {
1434 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1435 _cleanup_free_ char *e = NULL, *unit = NULL;
1438 if (!machine_name_is_valid(argv[i])) {
1439 log_error("Invalid machine name %s.", argv[i]);
1443 e = unit_name_escape(argv[i]);
1447 unit = unit_name_build("systemd-nspawn", e, ".service");
1451 r = sd_bus_message_new_method_call(
1454 "org.freedesktop.systemd1",
1455 "/org/freedesktop/systemd1",
1456 "org.freedesktop.systemd1.Manager",
1459 return bus_log_create_error(r);
1461 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1463 return bus_log_create_error(r);
1465 r = sd_bus_message_append(m, "ss", unit, "fail");
1467 return bus_log_create_error(r);
1469 r = sd_bus_call(bus, m, 0, &error, &reply);
1471 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1475 r = sd_bus_message_read(reply, "o", &object);
1477 return bus_log_parse_error(r);
1479 r = bus_wait_for_jobs_add(w, object);
1484 r = bus_wait_for_jobs(w, arg_quiet);
1491 static int enable_machine(int argc, char *argv[], void *userdata) {
1492 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1493 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1494 int carries_install_info = 0;
1495 const char *method = NULL;
1496 sd_bus *bus = userdata;
1501 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1503 r = sd_bus_message_new_method_call(
1506 "org.freedesktop.systemd1",
1507 "/org/freedesktop/systemd1",
1508 "org.freedesktop.systemd1.Manager",
1511 return bus_log_create_error(r);
1513 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1515 return bus_log_create_error(r);
1517 r = sd_bus_message_open_container(m, 'a', "s");
1519 return bus_log_create_error(r);
1521 for (i = 1; i < argc; i++) {
1522 _cleanup_free_ char *e = NULL, *unit = NULL;
1524 if (!machine_name_is_valid(argv[i])) {
1525 log_error("Invalid machine name %s.", argv[i]);
1529 e = unit_name_escape(argv[i]);
1533 unit = unit_name_build("systemd-nspawn", e, ".service");
1537 r = sd_bus_message_append(m, "s", unit);
1539 return bus_log_create_error(r);
1542 r = sd_bus_message_close_container(m);
1544 return bus_log_create_error(r);
1546 if (streq(argv[0], "enable"))
1547 r = sd_bus_message_append(m, "bb", false, false);
1549 r = sd_bus_message_append(m, "b", false);
1551 return bus_log_create_error(r);
1553 r = sd_bus_call(bus, m, 0, &error, &reply);
1555 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1559 if (streq(argv[0], "enable")) {
1560 r = sd_bus_message_read(reply, "b", carries_install_info);
1562 return bus_log_parse_error(r);
1565 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1569 m = sd_bus_message_unref(m);
1571 r = sd_bus_message_new_method_call(
1574 "org.freedesktop.systemd1",
1575 "/org/freedesktop/systemd1",
1576 "org.freedesktop.systemd1.Manager",
1579 return bus_log_create_error(r);
1581 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1583 return bus_log_create_error(r);
1585 r = sd_bus_call(bus, m, 0, &error, NULL);
1587 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1594 static int help(int argc, char *argv[], void *userdata) {
1596 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1597 "Send control commands to or query the virtual machine and container\n"
1598 "registration manager.\n\n"
1599 " -h --help Show this help\n"
1600 " --version Show package version\n"
1601 " --no-pager Do not pipe output into a pager\n"
1602 " --no-legend Do not show the headers and footers\n"
1603 " -H --host=[USER@]HOST Operate on remote host\n"
1604 " -M --machine=CONTAINER Operate on local container\n"
1605 " -p --property=NAME Show only properties by this name\n"
1606 " -q --quiet Suppress output\n"
1607 " -a --all Show all properties, including empty ones\n"
1608 " -l --full Do not ellipsize output\n"
1609 " --kill-who=WHO Who to send signal to\n"
1610 " -s --signal=SIGNAL Which signal to send\n"
1611 " --read-only Create read-only bind mount\n"
1612 " --mkdir Create directory before bind mounting, if missing\n\n"
1613 "Machine Commands:\n"
1614 " list List running VMs and containers\n"
1615 " status NAME... Show VM/container details\n"
1616 " show NAME... Show properties of one or more VMs/containers\n"
1617 " login NAME Get a login prompt on a container\n"
1618 " start NAME... Start container as a service\n"
1619 " enable NAME... Enable automatic container start at boot\n"
1620 " disable NAME... Disable automatic container start at boot\n"
1621 " poweroff NAME... Power off one or more containers\n"
1622 " reboot NAME... Reboot one or more containers\n"
1623 " terminate NAME... Terminate one or more VMs/containers\n"
1624 " kill NAME... Send signal to processes of a VM/container\n"
1625 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1626 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1627 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1629 " list-images Show available images\n"
1630 " image-status NAME... Show image details\n"
1631 " show-image NAME... Show properties of image\n"
1632 " clone NAME NAME Clone an image\n"
1633 " rename NAME NAME Rename an image\n"
1634 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1635 " remove NAME... Remove an image\n",
1636 program_invocation_short_name);
1641 static int parse_argv(int argc, char *argv[]) {
1644 ARG_VERSION = 0x100,
1652 static const struct option options[] = {
1653 { "help", no_argument, NULL, 'h' },
1654 { "version", no_argument, NULL, ARG_VERSION },
1655 { "property", required_argument, NULL, 'p' },
1656 { "all", no_argument, NULL, 'a' },
1657 { "full", no_argument, NULL, 'l' },
1658 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1659 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1660 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1661 { "signal", required_argument, NULL, 's' },
1662 { "host", required_argument, NULL, 'H' },
1663 { "machine", required_argument, NULL, 'M' },
1664 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1665 { "mkdir", no_argument, NULL, ARG_MKDIR },
1666 { "quiet", no_argument, NULL, 'q' },
1675 while ((c = getopt_long(argc, argv, "hp:als:H:M:q", options, NULL)) >= 0)
1680 return help(0, NULL, NULL);
1683 puts(PACKAGE_STRING);
1684 puts(SYSTEMD_FEATURES);
1688 r = strv_extend(&arg_property, optarg);
1692 /* If the user asked for a particular
1693 * property, show it to him, even if it is
1707 arg_no_pager = true;
1715 arg_kill_who = optarg;
1719 arg_signal = signal_from_string_try_harder(optarg);
1720 if (arg_signal < 0) {
1721 log_error("Failed to parse signal string %s.", optarg);
1727 arg_transport = BUS_TRANSPORT_REMOTE;
1732 arg_transport = BUS_TRANSPORT_MACHINE;
1737 arg_read_only = true;
1752 assert_not_reached("Unhandled option");
1758 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1760 static const Verb verbs[] = {
1761 { "help", VERB_ANY, VERB_ANY, 0, help },
1762 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1763 { "list-images", VERB_ANY, 1, 0, list_images },
1764 { "status", 2, VERB_ANY, 0, show_machine },
1765 { "image-status",2, VERB_ANY, 0, show_image },
1766 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1767 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1768 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1769 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1770 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1771 { "kill", 2, VERB_ANY, 0, kill_machine },
1772 { "login", 2, 2, 0, login_machine },
1773 { "bind", 3, 4, 0, bind_mount },
1774 { "copy-to", 3, 4, 0, copy_files },
1775 { "copy-from", 3, 4, 0, copy_files },
1776 { "remove", 2, VERB_ANY, 0, remove_image },
1777 { "rename", 3, 3, 0, rename_image },
1778 { "clone", 3, 3, 0, clone_image },
1779 { "read-only", 2, 3, 0, read_only_image },
1780 { "start", 2, VERB_ANY, 0, start_machine },
1781 { "enable", 2, VERB_ANY, 0, enable_machine },
1782 { "disable", 2, VERB_ANY, 0, enable_machine },
1786 return dispatch_verb(argc, argv, verbs, bus);
1789 int main(int argc, char*argv[]) {
1790 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1793 setlocale(LC_ALL, "");
1794 log_parse_environment();
1797 r = parse_argv(argc, argv);
1801 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1803 log_error_errno(r, "Failed to create bus connection: %m");
1807 r = machinectl_main(argc, argv, bus);
1812 strv_free(arg_property);
1814 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;