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 printf("\n%zu images listed.\n", n_images);
259 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
260 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
261 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
262 _cleanup_free_ char *path = NULL;
270 if (arg_transport == BUS_TRANSPORT_REMOTE)
273 path = unit_dbus_path_from_name(unit);
277 r = sd_bus_get_property(
279 "org.freedesktop.systemd1",
281 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
287 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
291 r = sd_bus_message_read(reply, "s", &cgroup);
293 return bus_log_parse_error(r);
298 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
302 arg_all * OUTPUT_SHOW_ALL |
303 arg_full * OUTPUT_FULL_WIDTH;
311 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
315 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
316 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
324 r = sd_bus_call_method(bus,
325 "org.freedesktop.machine1",
326 "/org/freedesktop/machine1",
327 "org.freedesktop.machine1.Manager",
328 "GetMachineAddresses",
335 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
337 return bus_log_parse_error(r);
339 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
343 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
345 r = sd_bus_message_read(reply, "i", &family);
347 return bus_log_parse_error(r);
349 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
351 return bus_log_parse_error(r);
353 fputs(prefix, stdout);
354 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
355 if (family == AF_INET6 && ifi > 0)
359 r = sd_bus_message_exit_container(reply);
361 return bus_log_parse_error(r);
363 if (prefix != prefix2)
367 return bus_log_parse_error(r);
369 r = sd_bus_message_exit_container(reply);
371 return bus_log_parse_error(r);
376 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
377 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
378 const char *k, *v, *pretty = NULL;
385 r = sd_bus_call_method(bus,
386 "org.freedesktop.machine1",
387 "/org/freedesktop/machine1",
388 "org.freedesktop.machine1.Manager",
389 "GetMachineOSRelease",
396 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
398 return bus_log_parse_error(r);
400 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
401 if (streq(k, "PRETTY_NAME"))
406 return bus_log_parse_error(r);
408 r = sd_bus_message_exit_container(reply);
410 return bus_log_parse_error(r);
413 printf("%s%s\n", prefix, pretty);
418 typedef struct MachineStatusInfo {
424 char *root_directory;
431 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
432 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
433 char since2[FORMAT_TIMESTAMP_MAX], *s2;
439 fputs(strna(i->name), stdout);
441 if (!sd_id128_equal(i->id, SD_ID128_NULL))
442 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
446 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
447 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
450 printf("\t Since: %s; %s\n", s2, s1);
452 printf("\t Since: %s\n", s2);
455 _cleanup_free_ char *t = NULL;
457 printf("\t Leader: %u", (unsigned) i->leader);
459 get_process_comm(i->leader, &t);
467 printf("\t Service: %s", i->service);
470 printf("; class %s", i->class);
474 printf("\t Class: %s\n", i->class);
476 if (i->root_directory)
477 printf("\t Root: %s\n", i->root_directory);
479 if (i->n_netif > 0) {
482 fputs("\t Iface:", stdout);
484 for (c = 0; c < i->n_netif; c++) {
485 char name[IF_NAMESIZE+1] = "";
487 if (if_indextoname(i->netif[c], name)) {
496 printf(" %i", i->netif[c]);
502 print_addresses(bus, i->name, ifi,
506 print_os_release(bus, i->name, "\t OS: ");
509 printf("\t Unit: %s\n", i->unit);
510 show_unit_cgroup(bus, i->unit, i->leader);
514 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
515 MachineStatusInfo *i = userdata;
520 assert_cc(sizeof(int32_t) == sizeof(int));
521 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
527 i->n_netif = l / sizeof(int32_t);
528 i->netif = memdup(v, l);
535 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
537 static const struct bus_properties_map map[] = {
538 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
539 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
540 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
541 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
542 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
543 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
544 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
545 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
546 { "NetworkInterfaces", "ai", map_netif, 0 },
550 MachineStatusInfo info = {};
558 r = bus_map_all_properties(bus,
559 "org.freedesktop.machine1",
564 return log_error_errno(r, "Could not get properties: %m");
570 print_machine_status_info(bus, &info);
576 free(info.root_directory);
582 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
594 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
596 log_error_errno(r, "Could not get properties: %m");
601 static int show_machine(int argc, char *argv[], void *userdata) {
603 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
604 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
605 bool properties, new_line = false;
606 sd_bus *bus = userdata;
611 properties = !strstr(argv[0], "status");
613 pager_open_if_enabled();
615 if (properties && argc <= 1) {
617 /* If no argument is specified, inspect the manager
619 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
624 for (i = 1; i < argc; i++) {
625 const char *path = NULL;
627 r = sd_bus_call_method(
629 "org.freedesktop.machine1",
630 "/org/freedesktop/machine1",
631 "org.freedesktop.machine1.Manager",
637 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
641 r = sd_bus_message_read(reply, "o", &path);
643 return bus_log_parse_error(r);
646 r = show_machine_properties(bus, path, &new_line);
648 r = show_machine_info(argv[0], bus, path, &new_line);
654 typedef struct ImageStatusInfo {
663 uint64_t size_exclusive;
664 uint64_t limit_exclusive;
667 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
668 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
669 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
670 char bs[FORMAT_BYTES_MAX], *s3;
671 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
677 fputs(i->name, stdout);
682 printf("\t Type: %s\n", i->type);
685 printf("\t Path: %s\n", i->path);
687 printf("\t RO: %s%s%s\n",
688 i->read_only ? ansi_highlight_red() : "",
689 i->read_only ? "read-only" : "writable",
690 i->read_only ? ansi_highlight_off() : "");
692 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
693 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
695 printf("\t Created: %s; %s\n", s2, s1);
697 printf("\t Created: %s\n", s2);
699 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
700 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
702 printf("\tModified: %s; %s\n", s2, s1);
704 printf("\tModified: %s\n", s2);
706 s3 = format_bytes(bs, sizeof(bs), i->size);
707 s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
709 printf("\t Size: %s (exclusive: %s)\n", s3, s4);
711 printf("\t Size: %s\n", s3);
713 s3 = format_bytes(bs, sizeof(bs), i->limit);
714 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
716 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
718 printf("\t Limit: %s\n", s3);
721 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
723 static const struct bus_properties_map map[] = {
724 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
725 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
726 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
727 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
728 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
729 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
730 { "Size", "t", NULL, offsetof(ImageStatusInfo, size) },
731 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
732 { "SizeExclusive", "t", NULL, offsetof(ImageStatusInfo, size_exclusive) },
733 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
737 ImageStatusInfo info = {};
745 r = bus_map_all_properties(bus,
746 "org.freedesktop.machine1",
751 return log_error_errno(r, "Could not get properties: %m");
757 print_image_status_info(bus, &info);
766 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
778 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
780 log_error_errno(r, "Could not get properties: %m");
785 static int show_image(int argc, char *argv[], void *userdata) {
787 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
788 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
789 bool properties, new_line = false;
790 sd_bus *bus = userdata;
795 properties = !strstr(argv[0], "status");
797 pager_open_if_enabled();
799 if (properties && argc <= 1) {
801 /* If no argument is specified, inspect the manager
803 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
808 for (i = 1; i < argc; i++) {
809 const char *path = NULL;
811 r = sd_bus_call_method(
813 "org.freedesktop.machine1",
814 "/org/freedesktop/machine1",
815 "org.freedesktop.machine1.Manager",
821 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
825 r = sd_bus_message_read(reply, "o", &path);
827 return bus_log_parse_error(r);
830 r = show_image_properties(bus, path, &new_line);
832 r = show_image_info(argv[0], bus, path, &new_line);
838 static int kill_machine(int argc, char *argv[], void *userdata) {
839 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
840 sd_bus *bus = userdata;
846 arg_kill_who = "all";
848 for (i = 1; i < argc; i++) {
851 r = sd_bus_call_method(
853 "org.freedesktop.machine1",
854 "/org/freedesktop/machine1",
855 "org.freedesktop.machine1.Manager",
859 "ssi", argv[i], arg_kill_who, arg_signal);
861 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
869 static int reboot_machine(int argc, char *argv[], void *userdata) {
870 arg_kill_who = "leader";
871 arg_signal = SIGINT; /* sysvinit + systemd */
873 return kill_machine(argc, argv, userdata);
876 static int poweroff_machine(int argc, char *argv[], void *userdata) {
877 arg_kill_who = "leader";
878 arg_signal = SIGRTMIN+4; /* only systemd */
880 return kill_machine(argc, argv, userdata);
883 static int terminate_machine(int argc, char *argv[], void *userdata) {
884 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
885 sd_bus *bus = userdata;
890 for (i = 1; i < argc; i++) {
893 r = sd_bus_call_method(
895 "org.freedesktop.machine1",
896 "/org/freedesktop/machine1",
897 "org.freedesktop.machine1.Manager",
903 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
911 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
912 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
913 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
922 r = sd_bus_call_method(
924 "org.freedesktop.machine1",
925 "/org/freedesktop/machine1",
926 "org.freedesktop.machine1.Manager",
932 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
936 r = sd_bus_message_read(reply, "o", &object);
938 return bus_log_parse_error(r);
940 r = sd_bus_get_property(
942 "org.freedesktop.machine1",
944 "org.freedesktop.machine1.Machine",
950 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
952 r = sd_bus_message_read(reply2, "u", &leader);
954 return bus_log_parse_error(r);
960 static int copy_files(int argc, char *argv[], void *userdata) {
961 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
962 _cleanup_close_ int hostfd = -1;
963 sd_bus *bus = userdata;
971 copy_from = streq(argv[0], "copy-from");
972 dest = argv[3] ?: argv[2];
973 host_path = strdupa(copy_from ? dest : argv[2]);
974 container_path = strdupa(copy_from ? argv[2] : dest);
976 if (!path_is_absolute(container_path)) {
977 log_error("Container path not absolute.");
981 t = strdup(host_path);
982 host_basename = basename(t);
983 host_dirname = dirname(host_path);
985 t = strdup(container_path);
986 container_basename = basename(t);
987 container_dirname = dirname(container_path);
989 r = machine_get_leader(bus, argv[1], &leader);
993 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
995 return log_error_errno(errno, "Failed to open source directory: %m");
999 return log_error_errno(errno, "Failed to fork(): %m");
1006 q = procfs_file_alloca(leader, "ns/mnt");
1007 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1009 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1010 _exit(EXIT_FAILURE);
1013 if (setns(mntfd, CLONE_NEWNS) < 0) {
1014 log_error_errno(errno, "Failed to join namespace of leader: %m");
1015 _exit(EXIT_FAILURE);
1018 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1019 if (containerfd < 0) {
1020 log_error_errno(errno, "Failed top open destination directory: %m");
1021 _exit(EXIT_FAILURE);
1025 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1027 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1029 log_error_errno(errno, "Failed to copy tree: %m");
1030 _exit(EXIT_FAILURE);
1033 _exit(EXIT_SUCCESS);
1036 r = wait_for_terminate(child, &si);
1038 return log_error_errno(r, "Failed to wait for client: %m");
1039 if (si.si_code != CLD_EXITED) {
1040 log_error("Client died abnormally.");
1043 if (si.si_status != EXIT_SUCCESS)
1049 static int bind_mount(int argc, char *argv[], void *userdata) {
1050 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1051 sd_bus *bus = userdata;
1052 pid_t child, leader;
1055 bool mount_slave_created = false, mount_slave_mounted = false,
1056 mount_tmp_created = false, mount_tmp_mounted = false,
1057 mount_outside_created = false, mount_outside_mounted = false;
1062 /* One day, when bind mounting /proc/self/fd/n works across
1063 * namespace boundaries we should rework this logic to make
1066 dest = argv[3] ?: argv[2];
1067 if (!path_is_absolute(dest)) {
1068 log_error("Destination path not absolute.");
1072 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1073 if (access(p, F_OK) < 0) {
1074 log_error("Container does not allow propagation of mount points.");
1078 r = machine_get_leader(bus, argv[1], &leader);
1082 /* Our goal is to install a new bind mount into the container,
1083 possibly read-only. This is irritatingly complex
1084 unfortunately, currently.
1086 First, we start by creating a private playground in /tmp,
1087 that we can mount MS_SLAVE. (Which is necessary, since
1088 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1091 if (!mkdtemp(mount_slave))
1092 return log_error_errno(errno, "Failed to create playground: %m");
1094 mount_slave_created = true;
1096 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1097 r = log_error_errno(errno, "Failed to make bind mount: %m");
1101 mount_slave_mounted = true;
1103 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1104 r = log_error_errno(errno, "Failed to remount slave: %m");
1108 /* Second, we mount the source directory to a directory inside
1109 of our MS_SLAVE playground. */
1110 mount_tmp = strappenda(mount_slave, "/mount");
1111 if (mkdir(mount_tmp, 0700) < 0) {
1112 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1116 mount_tmp_created = true;
1118 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1119 r = log_error_errno(errno, "Failed to overmount: %m");
1123 mount_tmp_mounted = true;
1125 /* Third, we remount the new bind mount read-only if requested. */
1127 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1128 r = log_error_errno(errno, "Failed to mark read-only: %m");
1132 /* Fourth, we move the new bind mount into the propagation
1133 * directory. This way it will appear there read-only
1136 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1137 if (!mkdtemp(mount_outside)) {
1138 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1142 mount_outside_created = true;
1144 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1145 r = log_error_errno(errno, "Failed to move: %m");
1149 mount_outside_mounted = true;
1150 mount_tmp_mounted = false;
1152 (void) rmdir(mount_tmp);
1153 mount_tmp_created = false;
1155 (void) umount(mount_slave);
1156 mount_slave_mounted = false;
1158 (void) rmdir(mount_slave);
1159 mount_slave_created = false;
1163 r = log_error_errno(errno, "Failed to fork(): %m");
1168 const char *mount_inside;
1172 q = procfs_file_alloca(leader, "ns/mnt");
1173 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1175 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1176 _exit(EXIT_FAILURE);
1179 if (setns(mntfd, CLONE_NEWNS) < 0) {
1180 log_error_errno(errno, "Failed to join namespace of leader: %m");
1181 _exit(EXIT_FAILURE);
1185 mkdir_p(dest, 0755);
1187 /* Fifth, move the mount to the right place inside */
1188 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1189 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1190 log_error_errno(errno, "Failed to mount: %m");
1191 _exit(EXIT_FAILURE);
1194 _exit(EXIT_SUCCESS);
1197 r = wait_for_terminate(child, &si);
1199 log_error_errno(r, "Failed to wait for client: %m");
1202 if (si.si_code != CLD_EXITED) {
1203 log_error("Client died abnormally.");
1207 if (si.si_status != EXIT_SUCCESS) {
1215 if (mount_outside_mounted)
1216 umount(mount_outside);
1217 if (mount_outside_created)
1218 rmdir(mount_outside);
1220 if (mount_tmp_mounted)
1222 if (mount_tmp_created)
1225 if (mount_slave_mounted)
1226 umount(mount_slave);
1227 if (mount_slave_created)
1228 umount(mount_slave);
1233 static int on_machine_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
1234 PTYForward ** forward = (PTYForward**) userdata;
1242 /* If the forwarder is already initialized, tell it to
1243 * exit on the next hangup */
1245 r = pty_forward_set_repeat(*forward, false);
1249 log_error_errno(r, "Failed to set repeat flag: %m");
1252 /* On error, or when the forwarder is not initialized yet, quit immediately */
1253 sd_event_exit(sd_bus_get_event(bus), EXIT_FAILURE);
1257 static int login_machine(int argc, char *argv[], void *userdata) {
1258 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1259 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1260 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
1261 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1262 _cleanup_event_unref_ sd_event *event = NULL;
1263 int master = -1, r, ret = 0;
1264 sd_bus *bus = userdata;
1265 const char *pty, *match;
1272 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1273 arg_transport != BUS_TRANSPORT_MACHINE) {
1274 log_error("Login only supported on local machines.");
1278 r = sd_event_default(&event);
1280 return log_error_errno(r, "Failed to get event loop: %m");
1282 r = sd_bus_attach_event(bus, event, 0);
1284 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1286 match = strappenda("type='signal',"
1287 "sender='org.freedesktop.machine1',"
1288 "path='/org/freedesktop/machine1',",
1289 "interface='org.freedesktop.machine1.Manager',"
1290 "member='MachineRemoved',"
1295 r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward);
1297 return log_error_errno(r, "Failed to add machine removal match: %m");
1299 r = sd_bus_message_new_method_call(bus,
1301 "org.freedesktop.machine1",
1302 "/org/freedesktop/machine1",
1303 "org.freedesktop.machine1.Manager",
1304 "OpenMachineLogin");
1306 return bus_log_create_error(r);
1308 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1310 return bus_log_create_error(r);
1312 r = sd_bus_message_append(m, "s", argv[1]);
1314 return bus_log_create_error(r);
1316 r = sd_bus_call(bus, m, 0, &error, &reply);
1318 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1322 r = sd_bus_message_read(reply, "hs", &master, &pty);
1324 return bus_log_parse_error(r);
1326 assert_se(sigemptyset(&mask) == 0);
1327 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1328 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1330 log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]);
1332 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1333 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1335 r = pty_forward_new(event, master, true, &forward);
1337 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1339 r = sd_event_loop(event);
1341 return log_error_errno(r, "Failed to run event loop: %m");
1343 pty_forward_get_last_char(forward, &last_char);
1344 machine_died = pty_forward_get_repeat(forward) == 0;
1346 forward = pty_forward_free(forward);
1348 if (last_char != '\n')
1349 fputc('\n', stdout);
1352 log_info("Machine %s terminated.", argv[1]);
1354 log_info("Connection to machine %s terminated.", argv[1]);
1356 sd_event_get_exit_code(event, &ret);
1360 static int remove_image(int argc, char *argv[], void *userdata) {
1361 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1362 sd_bus *bus = userdata;
1367 for (i = 1; i < argc; i++) {
1368 r = sd_bus_call_method(
1370 "org.freedesktop.machine1",
1371 "/org/freedesktop/machine1",
1372 "org.freedesktop.machine1.Manager",
1378 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1386 static int rename_image(int argc, char *argv[], void *userdata) {
1387 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1388 sd_bus *bus = userdata;
1391 r = sd_bus_call_method(
1393 "org.freedesktop.machine1",
1394 "/org/freedesktop/machine1",
1395 "org.freedesktop.machine1.Manager",
1399 "ss", argv[1], argv[2]);
1401 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1408 static int clone_image(int argc, char *argv[], void *userdata) {
1409 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1410 sd_bus *bus = userdata;
1413 r = sd_bus_call_method(
1415 "org.freedesktop.machine1",
1416 "/org/freedesktop/machine1",
1417 "org.freedesktop.machine1.Manager",
1421 "ssb", argv[1], argv[2], arg_read_only);
1423 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1430 static int read_only_image(int argc, char *argv[], void *userdata) {
1431 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1432 sd_bus *bus = userdata;
1436 b = parse_boolean(argv[2]);
1438 log_error("Failed to parse boolean argument: %s", argv[2]);
1443 r = sd_bus_call_method(
1445 "org.freedesktop.machine1",
1446 "/org/freedesktop/machine1",
1447 "org.freedesktop.machine1.Manager",
1448 "MarkImageReadOnly",
1453 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1460 static int start_machine(int argc, char *argv[], void *userdata) {
1461 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1462 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1463 sd_bus *bus = userdata;
1468 r = bus_wait_for_jobs_new(bus, &w);
1472 for (i = 1; i < argc; i++) {
1473 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1474 _cleanup_free_ char *e = NULL, *unit = NULL;
1477 if (!machine_name_is_valid(argv[i])) {
1478 log_error("Invalid machine name %s.", argv[i]);
1482 e = unit_name_escape(argv[i]);
1486 unit = unit_name_build("systemd-nspawn", e, ".service");
1490 r = sd_bus_message_new_method_call(
1493 "org.freedesktop.systemd1",
1494 "/org/freedesktop/systemd1",
1495 "org.freedesktop.systemd1.Manager",
1498 return bus_log_create_error(r);
1500 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1502 return bus_log_create_error(r);
1504 r = sd_bus_message_append(m, "ss", unit, "fail");
1506 return bus_log_create_error(r);
1508 r = sd_bus_call(bus, m, 0, &error, &reply);
1510 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1514 r = sd_bus_message_read(reply, "o", &object);
1516 return bus_log_parse_error(r);
1518 r = bus_wait_for_jobs_add(w, object);
1523 r = bus_wait_for_jobs(w, arg_quiet);
1530 static int enable_machine(int argc, char *argv[], void *userdata) {
1531 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1532 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1533 int carries_install_info = 0;
1534 const char *method = NULL;
1535 sd_bus *bus = userdata;
1540 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1542 r = sd_bus_message_new_method_call(
1545 "org.freedesktop.systemd1",
1546 "/org/freedesktop/systemd1",
1547 "org.freedesktop.systemd1.Manager",
1550 return bus_log_create_error(r);
1552 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1554 return bus_log_create_error(r);
1556 r = sd_bus_message_open_container(m, 'a', "s");
1558 return bus_log_create_error(r);
1560 for (i = 1; i < argc; i++) {
1561 _cleanup_free_ char *e = NULL, *unit = NULL;
1563 if (!machine_name_is_valid(argv[i])) {
1564 log_error("Invalid machine name %s.", argv[i]);
1568 e = unit_name_escape(argv[i]);
1572 unit = unit_name_build("systemd-nspawn", e, ".service");
1576 r = sd_bus_message_append(m, "s", unit);
1578 return bus_log_create_error(r);
1581 r = sd_bus_message_close_container(m);
1583 return bus_log_create_error(r);
1585 if (streq(argv[0], "enable"))
1586 r = sd_bus_message_append(m, "bb", false, false);
1588 r = sd_bus_message_append(m, "b", false);
1590 return bus_log_create_error(r);
1592 r = sd_bus_call(bus, m, 0, &error, &reply);
1594 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1598 if (streq(argv[0], "enable")) {
1599 r = sd_bus_message_read(reply, "b", carries_install_info);
1601 return bus_log_parse_error(r);
1604 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1608 m = sd_bus_message_unref(m);
1610 r = sd_bus_message_new_method_call(
1613 "org.freedesktop.systemd1",
1614 "/org/freedesktop/systemd1",
1615 "org.freedesktop.systemd1.Manager",
1618 return bus_log_create_error(r);
1620 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1622 return bus_log_create_error(r);
1624 r = sd_bus_call(bus, m, 0, &error, NULL);
1626 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1633 static int help(int argc, char *argv[], void *userdata) {
1635 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1636 "Send control commands to or query the virtual machine and container\n"
1637 "registration manager.\n\n"
1638 " -h --help Show this help\n"
1639 " --version Show package version\n"
1640 " --no-pager Do not pipe output into a pager\n"
1641 " --no-legend Do not show the headers and footers\n"
1642 " -H --host=[USER@]HOST Operate on remote host\n"
1643 " -M --machine=CONTAINER Operate on local container\n"
1644 " -p --property=NAME Show only properties by this name\n"
1645 " -q --quiet Suppress output\n"
1646 " -a --all Show all properties, including empty ones\n"
1647 " -l --full Do not ellipsize output\n"
1648 " --kill-who=WHO Who to send signal to\n"
1649 " -s --signal=SIGNAL Which signal to send\n"
1650 " --read-only Create read-only bind mount\n"
1651 " --mkdir Create directory before bind mounting, if missing\n\n"
1652 "Machine Commands:\n"
1653 " list List running VMs and containers\n"
1654 " status NAME... Show VM/container details\n"
1655 " show NAME... Show properties of one or more VMs/containers\n"
1656 " login NAME Get a login prompt on a container\n"
1657 " start NAME... Start container as a service\n"
1658 " enable NAME... Enable automatic container start at boot\n"
1659 " disable NAME... Disable automatic container start at boot\n"
1660 " poweroff NAME... Power off one or more containers\n"
1661 " reboot NAME... Reboot one or more containers\n"
1662 " terminate NAME... Terminate one or more VMs/containers\n"
1663 " kill NAME... Send signal to processes of a VM/container\n"
1664 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1665 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1666 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1668 " list-images Show available images\n"
1669 " image-status NAME... Show image details\n"
1670 " show-image NAME... Show properties of image\n"
1671 " clone NAME NAME Clone an image\n"
1672 " rename NAME NAME Rename an image\n"
1673 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1674 " remove NAME... Remove an image\n",
1675 program_invocation_short_name);
1680 static int parse_argv(int argc, char *argv[]) {
1683 ARG_VERSION = 0x100,
1691 static const struct option options[] = {
1692 { "help", no_argument, NULL, 'h' },
1693 { "version", no_argument, NULL, ARG_VERSION },
1694 { "property", required_argument, NULL, 'p' },
1695 { "all", no_argument, NULL, 'a' },
1696 { "full", no_argument, NULL, 'l' },
1697 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1698 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1699 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1700 { "signal", required_argument, NULL, 's' },
1701 { "host", required_argument, NULL, 'H' },
1702 { "machine", required_argument, NULL, 'M' },
1703 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1704 { "mkdir", no_argument, NULL, ARG_MKDIR },
1705 { "quiet", no_argument, NULL, 'q' },
1714 while ((c = getopt_long(argc, argv, "hp:als:H:M:q", options, NULL)) >= 0)
1719 return help(0, NULL, NULL);
1722 puts(PACKAGE_STRING);
1723 puts(SYSTEMD_FEATURES);
1727 r = strv_extend(&arg_property, optarg);
1731 /* If the user asked for a particular
1732 * property, show it to him, even if it is
1746 arg_no_pager = true;
1754 arg_kill_who = optarg;
1758 arg_signal = signal_from_string_try_harder(optarg);
1759 if (arg_signal < 0) {
1760 log_error("Failed to parse signal string %s.", optarg);
1766 arg_transport = BUS_TRANSPORT_REMOTE;
1771 arg_transport = BUS_TRANSPORT_MACHINE;
1776 arg_read_only = true;
1791 assert_not_reached("Unhandled option");
1797 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1799 static const Verb verbs[] = {
1800 { "help", VERB_ANY, VERB_ANY, 0, help },
1801 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1802 { "list-images", VERB_ANY, 1, 0, list_images },
1803 { "status", 2, VERB_ANY, 0, show_machine },
1804 { "image-status",2, VERB_ANY, 0, show_image },
1805 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1806 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1807 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1808 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1809 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1810 { "kill", 2, VERB_ANY, 0, kill_machine },
1811 { "login", 2, 2, 0, login_machine },
1812 { "bind", 3, 4, 0, bind_mount },
1813 { "copy-to", 3, 4, 0, copy_files },
1814 { "copy-from", 3, 4, 0, copy_files },
1815 { "remove", 2, VERB_ANY, 0, remove_image },
1816 { "rename", 3, 3, 0, rename_image },
1817 { "clone", 3, 3, 0, clone_image },
1818 { "read-only", 2, 3, 0, read_only_image },
1819 { "start", 2, VERB_ANY, 0, start_machine },
1820 { "enable", 2, VERB_ANY, 0, enable_machine },
1821 { "disable", 2, VERB_ANY, 0, enable_machine },
1825 return dispatch_verb(argc, argv, verbs, bus);
1828 int main(int argc, char*argv[]) {
1829 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1832 setlocale(LC_ALL, "");
1833 log_parse_environment();
1836 r = parse_argv(argc, argv);
1840 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1842 log_error_errno(r, "Failed to create bus connection: %m");
1846 r = machinectl_main(argc, argv, bus);
1851 strv_free(arg_property);
1853 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;