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 {
136 static int compare_image_info(const void *a, const void *b) {
137 const ImageInfo *x = a, *y = b;
139 return strcmp(x->name, y->name);
142 static int list_images(int argc, char *argv[], void *userdata) {
144 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
145 size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
146 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
147 _cleanup_free_ ImageInfo *images = NULL;
148 size_t n_images = 0, n_allocated = 0, j;
149 const char *name, *type, *object;
150 sd_bus *bus = userdata;
151 uint64_t crtime, mtime;
156 pager_open_if_enabled();
158 r = sd_bus_call_method(
160 "org.freedesktop.machine1",
161 "/org/freedesktop/machine1",
162 "org.freedesktop.machine1.Manager",
168 log_error("Could not get images: %s", bus_error_message(&error, -r));
172 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbtto)");
174 return bus_log_parse_error(r);
176 while ((r = sd_bus_message_read(reply, "(ssbtto)", &name, &type, &read_only, &crtime, &mtime, &object)) > 0) {
177 char buf[FORMAT_TIMESTAMP_MAX];
180 if (name[0] == '.' && !arg_all)
183 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
186 images[n_images].name = name;
187 images[n_images].type = type;
188 images[n_images].read_only = read_only;
189 images[n_images].crtime = crtime;
190 images[n_images].mtime = mtime;
201 l = strlen(format_timestamp(buf, sizeof(buf), crtime));
207 l = strlen(format_timestamp(buf, sizeof(buf), mtime));
215 return bus_log_parse_error(r);
217 r = sd_bus_message_exit_container(reply);
219 return bus_log_parse_error(r);
221 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
224 printf("%-*s %-*s %-3s %-*s %-*s\n",
225 (int) max_name, "NAME",
226 (int) max_type, "TYPE",
228 (int) max_crtime, "CREATED",
229 (int) max_mtime, "MODIFIED");
231 for (j = 0; j < n_images; j++) {
232 char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX];
234 printf("%-*s %-*s %s%-3s%s %-*s %-*s\n",
235 (int) max_name, images[j].name,
236 (int) max_type, images[j].type,
237 images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
238 (int) max_crtime, images[j].crtime != 0 ? format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime) : "-",
239 (int) max_mtime, images[j].mtime != 0 ? format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime) : "-");
243 return bus_log_parse_error(r);
247 printf("\n%zu images listed.\n", n_images);
252 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
253 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
254 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
255 _cleanup_free_ char *path = NULL;
263 if (arg_transport == BUS_TRANSPORT_REMOTE)
266 path = unit_dbus_path_from_name(unit);
270 r = sd_bus_get_property(
272 "org.freedesktop.systemd1",
274 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
280 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
284 r = sd_bus_message_read(reply, "s", &cgroup);
286 return bus_log_parse_error(r);
291 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
295 arg_all * OUTPUT_SHOW_ALL |
296 arg_full * OUTPUT_FULL_WIDTH;
304 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
308 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
309 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
317 r = sd_bus_call_method(bus,
318 "org.freedesktop.machine1",
319 "/org/freedesktop/machine1",
320 "org.freedesktop.machine1.Manager",
321 "GetMachineAddresses",
328 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
330 return bus_log_parse_error(r);
332 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
336 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
338 r = sd_bus_message_read(reply, "i", &family);
340 return bus_log_parse_error(r);
342 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
344 return bus_log_parse_error(r);
346 fputs(prefix, stdout);
347 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
348 if (family == AF_INET6 && ifi > 0)
352 r = sd_bus_message_exit_container(reply);
354 return bus_log_parse_error(r);
356 if (prefix != prefix2)
360 return bus_log_parse_error(r);
362 r = sd_bus_message_exit_container(reply);
364 return bus_log_parse_error(r);
369 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
370 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
371 const char *k, *v, *pretty = NULL;
378 r = sd_bus_call_method(bus,
379 "org.freedesktop.machine1",
380 "/org/freedesktop/machine1",
381 "org.freedesktop.machine1.Manager",
382 "GetMachineOSRelease",
389 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
391 return bus_log_parse_error(r);
393 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
394 if (streq(k, "PRETTY_NAME"))
399 return bus_log_parse_error(r);
401 r = sd_bus_message_exit_container(reply);
403 return bus_log_parse_error(r);
406 printf("%s%s\n", prefix, pretty);
411 typedef struct MachineStatusInfo {
417 char *root_directory;
424 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
425 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
426 char since2[FORMAT_TIMESTAMP_MAX], *s2;
432 fputs(strna(i->name), stdout);
434 if (!sd_id128_equal(i->id, SD_ID128_NULL))
435 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
439 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
440 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
443 printf("\t Since: %s; %s\n", s2, s1);
445 printf("\t Since: %s\n", s2);
448 _cleanup_free_ char *t = NULL;
450 printf("\t Leader: %u", (unsigned) i->leader);
452 get_process_comm(i->leader, &t);
460 printf("\t Service: %s", i->service);
463 printf("; class %s", i->class);
467 printf("\t Class: %s\n", i->class);
469 if (i->root_directory)
470 printf("\t Root: %s\n", i->root_directory);
472 if (i->n_netif > 0) {
475 fputs("\t Iface:", stdout);
477 for (c = 0; c < i->n_netif; c++) {
478 char name[IF_NAMESIZE+1] = "";
480 if (if_indextoname(i->netif[c], name)) {
489 printf(" %i", i->netif[c]);
495 print_addresses(bus, i->name, ifi,
499 print_os_release(bus, i->name, "\t OS: ");
502 printf("\t Unit: %s\n", i->unit);
503 show_unit_cgroup(bus, i->unit, i->leader);
507 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
508 MachineStatusInfo *i = userdata;
513 assert_cc(sizeof(int32_t) == sizeof(int));
514 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
520 i->n_netif = l / sizeof(int32_t);
521 i->netif = memdup(v, l);
528 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
530 static const struct bus_properties_map map[] = {
531 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
532 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
533 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
534 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
535 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
536 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
537 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
538 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
539 { "NetworkInterfaces", "ai", map_netif, 0 },
543 MachineStatusInfo info = {};
551 r = bus_map_all_properties(bus,
552 "org.freedesktop.machine1",
557 return log_error_errno(r, "Could not get properties: %m");
563 print_machine_status_info(bus, &info);
569 free(info.root_directory);
575 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
587 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
589 log_error_errno(r, "Could not get properties: %m");
594 static int show_machine(int argc, char *argv[], void *userdata) {
596 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
597 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
598 bool properties, new_line = false;
599 sd_bus *bus = userdata;
604 properties = !strstr(argv[0], "status");
606 pager_open_if_enabled();
608 if (properties && argc <= 1) {
610 /* If no argument is specified, inspect the manager
612 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
617 for (i = 1; i < argc; i++) {
618 const char *path = NULL;
620 r = sd_bus_call_method(
622 "org.freedesktop.machine1",
623 "/org/freedesktop/machine1",
624 "org.freedesktop.machine1.Manager",
630 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
634 r = sd_bus_message_read(reply, "o", &path);
636 return bus_log_parse_error(r);
639 r = show_machine_properties(bus, path, &new_line);
641 r = show_machine_info(argv[0], bus, path, &new_line);
647 typedef struct ImageStatusInfo {
656 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
657 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
658 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
664 fputs(i->name, stdout);
669 printf("\t Type: %s\n", i->type);
672 printf("\t Path: %s\n", i->path);
674 printf("\t RO: %s%s%s\n",
675 i->read_only ? ansi_highlight_red() : "",
676 i->read_only ? "read-only" : "writable",
677 i->read_only ? ansi_highlight_off() : "");
679 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
680 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
682 printf("\t Created: %s; %s\n", s2, s1);
684 printf("\t Created: %s\n", s2);
686 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
687 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
689 printf("\tModified: %s; %s\n", s2, s1);
691 printf("\tModified: %s\n", s2);
694 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
696 static const struct bus_properties_map map[] = {
697 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
698 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
699 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
700 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
701 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
702 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
706 ImageStatusInfo info = {};
714 r = bus_map_all_properties(bus,
715 "org.freedesktop.machine1",
720 return log_error_errno(r, "Could not get properties: %m");
726 print_image_status_info(bus, &info);
735 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
747 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
749 log_error_errno(r, "Could not get properties: %m");
754 static int show_image(int argc, char *argv[], void *userdata) {
756 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
757 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
758 bool properties, new_line = false;
759 sd_bus *bus = userdata;
764 properties = !strstr(argv[0], "status");
766 pager_open_if_enabled();
768 if (properties && argc <= 1) {
770 /* If no argument is specified, inspect the manager
772 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
777 for (i = 1; i < argc; i++) {
778 const char *path = NULL;
780 r = sd_bus_call_method(
782 "org.freedesktop.machine1",
783 "/org/freedesktop/machine1",
784 "org.freedesktop.machine1.Manager",
790 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
794 r = sd_bus_message_read(reply, "o", &path);
796 return bus_log_parse_error(r);
799 r = show_image_properties(bus, path, &new_line);
801 r = show_image_info(argv[0], bus, path, &new_line);
807 static int kill_machine(int argc, char *argv[], void *userdata) {
808 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
809 sd_bus *bus = userdata;
815 arg_kill_who = "all";
817 for (i = 1; i < argc; i++) {
820 r = sd_bus_call_method(
822 "org.freedesktop.machine1",
823 "/org/freedesktop/machine1",
824 "org.freedesktop.machine1.Manager",
828 "ssi", argv[i], arg_kill_who, arg_signal);
830 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
838 static int reboot_machine(int argc, char *argv[], void *userdata) {
839 arg_kill_who = "leader";
840 arg_signal = SIGINT; /* sysvinit + systemd */
842 return kill_machine(argc, argv, userdata);
845 static int poweroff_machine(int argc, char *argv[], void *userdata) {
846 arg_kill_who = "leader";
847 arg_signal = SIGRTMIN+4; /* only systemd */
849 return kill_machine(argc, argv, userdata);
852 static int terminate_machine(int argc, char *argv[], void *userdata) {
853 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
854 sd_bus *bus = userdata;
859 for (i = 1; i < argc; i++) {
862 r = sd_bus_call_method(
864 "org.freedesktop.machine1",
865 "/org/freedesktop/machine1",
866 "org.freedesktop.machine1.Manager",
872 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
880 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
881 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
882 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
891 r = sd_bus_call_method(
893 "org.freedesktop.machine1",
894 "/org/freedesktop/machine1",
895 "org.freedesktop.machine1.Manager",
901 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
905 r = sd_bus_message_read(reply, "o", &object);
907 return bus_log_parse_error(r);
909 r = sd_bus_get_property(
911 "org.freedesktop.machine1",
913 "org.freedesktop.machine1.Machine",
919 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
921 r = sd_bus_message_read(reply2, "u", &leader);
923 return bus_log_parse_error(r);
929 static int copy_files(int argc, char *argv[], void *userdata) {
930 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
931 _cleanup_close_ int hostfd = -1;
932 sd_bus *bus = userdata;
940 copy_from = streq(argv[0], "copy-from");
941 dest = argv[3] ?: argv[2];
942 host_path = strdupa(copy_from ? dest : argv[2]);
943 container_path = strdupa(copy_from ? argv[2] : dest);
945 if (!path_is_absolute(container_path)) {
946 log_error("Container path not absolute.");
950 t = strdup(host_path);
951 host_basename = basename(t);
952 host_dirname = dirname(host_path);
954 t = strdup(container_path);
955 container_basename = basename(t);
956 container_dirname = dirname(container_path);
958 r = machine_get_leader(bus, argv[1], &leader);
962 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
964 return log_error_errno(errno, "Failed to open source directory: %m");
968 return log_error_errno(errno, "Failed to fork(): %m");
975 q = procfs_file_alloca(leader, "ns/mnt");
976 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
978 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
982 if (setns(mntfd, CLONE_NEWNS) < 0) {
983 log_error_errno(errno, "Failed to join namespace of leader: %m");
987 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
988 if (containerfd < 0) {
989 log_error_errno(errno, "Failed top open destination directory: %m");
994 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
996 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
998 log_error_errno(errno, "Failed to copy tree: %m");
1002 _exit(EXIT_SUCCESS);
1005 r = wait_for_terminate(child, &si);
1007 return log_error_errno(r, "Failed to wait for client: %m");
1008 if (si.si_code != CLD_EXITED) {
1009 log_error("Client died abnormally.");
1012 if (si.si_status != EXIT_SUCCESS)
1018 static int bind_mount(int argc, char *argv[], void *userdata) {
1019 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1020 sd_bus *bus = userdata;
1021 pid_t child, leader;
1024 bool mount_slave_created = false, mount_slave_mounted = false,
1025 mount_tmp_created = false, mount_tmp_mounted = false,
1026 mount_outside_created = false, mount_outside_mounted = false;
1031 /* One day, when bind mounting /proc/self/fd/n works across
1032 * namespace boundaries we should rework this logic to make
1035 dest = argv[3] ?: argv[2];
1036 if (!path_is_absolute(dest)) {
1037 log_error("Destination path not absolute.");
1041 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1042 if (access(p, F_OK) < 0) {
1043 log_error("Container does not allow propagation of mount points.");
1047 r = machine_get_leader(bus, argv[1], &leader);
1051 /* Our goal is to install a new bind mount into the container,
1052 possibly read-only. This is irritatingly complex
1053 unfortunately, currently.
1055 First, we start by creating a private playground in /tmp,
1056 that we can mount MS_SLAVE. (Which is necessary, since
1057 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1060 if (!mkdtemp(mount_slave))
1061 return log_error_errno(errno, "Failed to create playground: %m");
1063 mount_slave_created = true;
1065 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1066 r = log_error_errno(errno, "Failed to make bind mount: %m");
1070 mount_slave_mounted = true;
1072 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1073 r = log_error_errno(errno, "Failed to remount slave: %m");
1077 /* Second, we mount the source directory to a directory inside
1078 of our MS_SLAVE playground. */
1079 mount_tmp = strappenda(mount_slave, "/mount");
1080 if (mkdir(mount_tmp, 0700) < 0) {
1081 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1085 mount_tmp_created = true;
1087 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1088 r = log_error_errno(errno, "Failed to overmount: %m");
1092 mount_tmp_mounted = true;
1094 /* Third, we remount the new bind mount read-only if requested. */
1096 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1097 r = log_error_errno(errno, "Failed to mark read-only: %m");
1101 /* Fourth, we move the new bind mount into the propagation
1102 * directory. This way it will appear there read-only
1105 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1106 if (!mkdtemp(mount_outside)) {
1107 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1111 mount_outside_created = true;
1113 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1114 r = log_error_errno(errno, "Failed to move: %m");
1118 mount_outside_mounted = true;
1119 mount_tmp_mounted = false;
1121 (void) rmdir(mount_tmp);
1122 mount_tmp_created = false;
1124 (void) umount(mount_slave);
1125 mount_slave_mounted = false;
1127 (void) rmdir(mount_slave);
1128 mount_slave_created = false;
1132 r = log_error_errno(errno, "Failed to fork(): %m");
1137 const char *mount_inside;
1141 q = procfs_file_alloca(leader, "ns/mnt");
1142 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1144 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1145 _exit(EXIT_FAILURE);
1148 if (setns(mntfd, CLONE_NEWNS) < 0) {
1149 log_error_errno(errno, "Failed to join namespace of leader: %m");
1150 _exit(EXIT_FAILURE);
1154 mkdir_p(dest, 0755);
1156 /* Fifth, move the mount to the right place inside */
1157 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1158 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1159 log_error_errno(errno, "Failed to mount: %m");
1160 _exit(EXIT_FAILURE);
1163 _exit(EXIT_SUCCESS);
1166 r = wait_for_terminate(child, &si);
1168 log_error_errno(r, "Failed to wait for client: %m");
1171 if (si.si_code != CLD_EXITED) {
1172 log_error("Client died abnormally.");
1176 if (si.si_status != EXIT_SUCCESS) {
1184 if (mount_outside_mounted)
1185 umount(mount_outside);
1186 if (mount_outside_created)
1187 rmdir(mount_outside);
1189 if (mount_tmp_mounted)
1191 if (mount_tmp_created)
1194 if (mount_slave_mounted)
1195 umount(mount_slave);
1196 if (mount_slave_created)
1197 umount(mount_slave);
1202 static int login_machine(int argc, char *argv[], void *userdata) {
1203 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1204 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1205 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1206 _cleanup_event_unref_ sd_event *event = NULL;
1207 int master = -1, r, ret = 0;
1208 sd_bus *bus = userdata;
1215 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1216 arg_transport != BUS_TRANSPORT_MACHINE) {
1217 log_error("Login only supported on local machines.");
1221 r = sd_event_default(&event);
1223 return log_error_errno(r, "Failed to get event loop: %m");
1225 r = sd_bus_attach_event(bus, event, 0);
1227 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1229 r = sd_bus_message_new_method_call(bus,
1231 "org.freedesktop.machine1",
1232 "/org/freedesktop/machine1",
1233 "org.freedesktop.machine1.Manager",
1234 "OpenMachineLogin");
1236 return bus_log_create_error(r);
1238 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1240 return bus_log_create_error(r);
1242 r = sd_bus_message_append(m, "s", argv[1]);
1244 return bus_log_create_error(r);
1246 r = sd_bus_call(bus, m, 0, &error, &reply);
1248 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1252 r = sd_bus_message_read(reply, "hs", &master, &pty);
1254 return bus_log_parse_error(r);
1256 assert_se(sigemptyset(&mask) == 0);
1257 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1258 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1260 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
1262 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1263 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1265 r = pty_forward_new(event, master, true, &forward);
1267 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1269 r = sd_event_loop(event);
1271 return log_error_errno(r, "Failed to run event loop: %m");
1273 pty_forward_last_char(forward, &last_char);
1275 forward = pty_forward_free(forward);
1277 if (last_char != '\n')
1278 fputc('\n', stdout);
1280 log_info("Connection to container %s terminated.", argv[1]);
1282 sd_event_get_exit_code(event, &ret);
1286 static int help(int argc, char *argv[], void *userdata) {
1288 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1289 "Send control commands to or query the virtual machine and container\n"
1290 "registration manager.\n\n"
1291 " -h --help Show this help\n"
1292 " --version Show package version\n"
1293 " --no-pager Do not pipe output into a pager\n"
1294 " --no-legend Do not show the headers and footers\n"
1295 " -H --host=[USER@]HOST Operate on remote host\n"
1296 " -M --machine=CONTAINER Operate on local container\n"
1297 " -p --property=NAME Show only properties by this name\n"
1298 " -a --all Show all properties, including empty ones\n"
1299 " -l --full Do not ellipsize output\n"
1300 " --kill-who=WHO Who to send signal to\n"
1301 " -s --signal=SIGNAL Which signal to send\n"
1302 " --read-only Create read-only bind mount\n"
1303 " --mkdir Create directory before bind mounting, if missing\n\n"
1304 "Machine Commands:\n"
1305 " list List running VMs and containers\n"
1306 " status NAME... Show VM/container details\n"
1307 " show NAME... Show properties of one or more VMs/containers\n"
1308 " login NAME Get a login prompt on a container\n"
1309 " poweroff NAME... Power off one or more containers\n"
1310 " reboot NAME... Reboot one or more containers\n"
1311 " kill NAME... Send signal to processes of a VM/container\n"
1312 " terminate NAME... Terminate one or more VMs/containers\n"
1313 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
1314 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1315 " copy-from NAME PATH [PATH] Copy files from a container to the host\n\n"
1317 " list-images Show available images\n"
1318 " image-status NAME... Show image details\n"
1319 " show-image NAME... Show properties of image\n",
1320 program_invocation_short_name);
1325 static int parse_argv(int argc, char *argv[]) {
1328 ARG_VERSION = 0x100,
1336 static const struct option options[] = {
1337 { "help", no_argument, NULL, 'h' },
1338 { "version", no_argument, NULL, ARG_VERSION },
1339 { "property", required_argument, NULL, 'p' },
1340 { "all", no_argument, NULL, 'a' },
1341 { "full", no_argument, NULL, 'l' },
1342 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1343 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1344 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1345 { "signal", required_argument, NULL, 's' },
1346 { "host", required_argument, NULL, 'H' },
1347 { "machine", required_argument, NULL, 'M' },
1348 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1349 { "mkdir", no_argument, NULL, ARG_MKDIR },
1358 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1363 return help(0, NULL, NULL);
1366 puts(PACKAGE_STRING);
1367 puts(SYSTEMD_FEATURES);
1371 r = strv_extend(&arg_property, optarg);
1375 /* If the user asked for a particular
1376 * property, show it to him, even if it is
1390 arg_no_pager = true;
1398 arg_kill_who = optarg;
1402 arg_signal = signal_from_string_try_harder(optarg);
1403 if (arg_signal < 0) {
1404 log_error("Failed to parse signal string %s.", optarg);
1410 arg_transport = BUS_TRANSPORT_REMOTE;
1415 arg_transport = BUS_TRANSPORT_MACHINE;
1420 arg_read_only = true;
1431 assert_not_reached("Unhandled option");
1437 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1439 static const Verb verbs[] = {
1440 { "help", VERB_ANY, VERB_ANY, 0, help },
1441 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1442 { "list-images", VERB_ANY, 1, 0, list_images },
1443 { "status", 2, VERB_ANY, 0, show_machine },
1444 { "image-status",2, VERB_ANY, 0, show_image },
1445 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1446 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1447 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1448 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1449 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1450 { "kill", 2, VERB_ANY, 0, kill_machine },
1451 { "login", 2, 2, 0, login_machine },
1452 { "bind", 3, 4, 0, bind_mount },
1453 { "copy-to", 3, 4, 0, copy_files },
1454 { "copy-from", 3, 4, 0, copy_files },
1458 return dispatch_verb(argc, argv, verbs, bus);
1461 int main(int argc, char*argv[]) {
1462 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1465 setlocale(LC_ALL, "");
1466 log_parse_environment();
1469 r = parse_argv(argc, argv);
1473 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1475 log_error_errno(r, "Failed to create bus connection: %m");
1479 r = machinectl_main(argc, argv, bus);
1484 strv_free(arg_property);
1486 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;