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)));
255 printf("\n%zu images listed.\n", n_images);
260 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
261 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
262 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
263 _cleanup_free_ char *path = NULL;
271 if (arg_transport == BUS_TRANSPORT_REMOTE)
274 path = unit_dbus_path_from_name(unit);
278 r = sd_bus_get_property(
280 "org.freedesktop.systemd1",
282 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
288 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
292 r = sd_bus_message_read(reply, "s", &cgroup);
294 return bus_log_parse_error(r);
299 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
303 arg_all * OUTPUT_SHOW_ALL |
304 arg_full * OUTPUT_FULL_WIDTH;
312 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
316 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
317 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
325 r = sd_bus_call_method(bus,
326 "org.freedesktop.machine1",
327 "/org/freedesktop/machine1",
328 "org.freedesktop.machine1.Manager",
329 "GetMachineAddresses",
336 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
338 return bus_log_parse_error(r);
340 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
344 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
346 r = sd_bus_message_read(reply, "i", &family);
348 return bus_log_parse_error(r);
350 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
352 return bus_log_parse_error(r);
354 fputs(prefix, stdout);
355 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
356 if (family == AF_INET6 && ifi > 0)
360 r = sd_bus_message_exit_container(reply);
362 return bus_log_parse_error(r);
364 if (prefix != prefix2)
368 return bus_log_parse_error(r);
370 r = sd_bus_message_exit_container(reply);
372 return bus_log_parse_error(r);
377 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
378 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
379 const char *k, *v, *pretty = NULL;
386 r = sd_bus_call_method(bus,
387 "org.freedesktop.machine1",
388 "/org/freedesktop/machine1",
389 "org.freedesktop.machine1.Manager",
390 "GetMachineOSRelease",
397 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
399 return bus_log_parse_error(r);
401 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
402 if (streq(k, "PRETTY_NAME"))
407 return bus_log_parse_error(r);
409 r = sd_bus_message_exit_container(reply);
411 return bus_log_parse_error(r);
414 printf("%s%s\n", prefix, pretty);
419 typedef struct MachineStatusInfo {
425 char *root_directory;
432 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
433 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
434 char since2[FORMAT_TIMESTAMP_MAX], *s2;
440 fputs(strna(i->name), stdout);
442 if (!sd_id128_equal(i->id, SD_ID128_NULL))
443 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
447 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
448 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
451 printf("\t Since: %s; %s\n", s2, s1);
453 printf("\t Since: %s\n", s2);
456 _cleanup_free_ char *t = NULL;
458 printf("\t Leader: %u", (unsigned) i->leader);
460 get_process_comm(i->leader, &t);
468 printf("\t Service: %s", i->service);
471 printf("; class %s", i->class);
475 printf("\t Class: %s\n", i->class);
477 if (i->root_directory)
478 printf("\t Root: %s\n", i->root_directory);
480 if (i->n_netif > 0) {
483 fputs("\t Iface:", stdout);
485 for (c = 0; c < i->n_netif; c++) {
486 char name[IF_NAMESIZE+1] = "";
488 if (if_indextoname(i->netif[c], name)) {
497 printf(" %i", i->netif[c]);
503 print_addresses(bus, i->name, ifi,
507 print_os_release(bus, i->name, "\t OS: ");
510 printf("\t Unit: %s\n", i->unit);
511 show_unit_cgroup(bus, i->unit, i->leader);
515 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
516 MachineStatusInfo *i = userdata;
521 assert_cc(sizeof(int32_t) == sizeof(int));
522 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
528 i->n_netif = l / sizeof(int32_t);
529 i->netif = memdup(v, l);
536 static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
538 static const struct bus_properties_map map[] = {
539 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
540 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
541 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
542 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
543 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
544 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
545 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
546 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
547 { "NetworkInterfaces", "ai", map_netif, 0 },
551 MachineStatusInfo info = {};
559 r = bus_map_all_properties(bus,
560 "org.freedesktop.machine1",
565 return log_error_errno(r, "Could not get properties: %m");
571 print_machine_status_info(bus, &info);
577 free(info.root_directory);
583 static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line) {
595 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
597 log_error_errno(r, "Could not get properties: %m");
602 static int show_machine(int argc, char *argv[], void *userdata) {
604 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
605 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
606 bool properties, new_line = false;
607 sd_bus *bus = userdata;
612 properties = !strstr(argv[0], "status");
614 pager_open_if_enabled();
616 if (properties && argc <= 1) {
618 /* If no argument is specified, inspect the manager
620 r = show_machine_properties(bus, "/org/freedesktop/machine1", &new_line);
625 for (i = 1; i < argc; i++) {
626 const char *path = NULL;
628 r = sd_bus_call_method(
630 "org.freedesktop.machine1",
631 "/org/freedesktop/machine1",
632 "org.freedesktop.machine1.Manager",
638 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
642 r = sd_bus_message_read(reply, "o", &path);
644 return bus_log_parse_error(r);
647 r = show_machine_properties(bus, path, &new_line);
649 r = show_machine_info(argv[0], bus, path, &new_line);
655 typedef struct ImageStatusInfo {
664 uint64_t size_exclusive;
665 uint64_t limit_exclusive;
668 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
669 char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
670 char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
671 char bs[FORMAT_BYTES_MAX], *s3;
672 char bs_exclusive[FORMAT_BYTES_MAX], *s4;
678 fputs(i->name, stdout);
683 printf("\t Type: %s\n", i->type);
686 printf("\t Path: %s\n", i->path);
688 printf("\t RO: %s%s%s\n",
689 i->read_only ? ansi_highlight_red() : "",
690 i->read_only ? "read-only" : "writable",
691 i->read_only ? ansi_highlight_off() : "");
693 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
694 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
696 printf("\t Created: %s; %s\n", s2, s1);
698 printf("\t Created: %s\n", s2);
700 s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
701 s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
703 printf("\tModified: %s; %s\n", s2, s1);
705 printf("\tModified: %s\n", s2);
707 s3 = format_bytes(bs, sizeof(bs), i->size);
708 s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
710 printf("\t Size: %s (exclusive: %s)\n", s3, s4);
712 printf("\t Size: %s\n", s3);
714 s3 = format_bytes(bs, sizeof(bs), i->limit);
715 s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
717 printf("\t Limit: %s (exclusive: %s)\n", s3, s4);
719 printf("\t Limit: %s\n", s3);
722 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
724 static const struct bus_properties_map map[] = {
725 { "Name", "s", NULL, offsetof(ImageStatusInfo, name) },
726 { "Path", "s", NULL, offsetof(ImageStatusInfo, path) },
727 { "Type", "s", NULL, offsetof(ImageStatusInfo, type) },
728 { "ReadOnly", "b", NULL, offsetof(ImageStatusInfo, read_only) },
729 { "CreationTimestamp", "t", NULL, offsetof(ImageStatusInfo, crtime) },
730 { "ModificationTimestamp", "t", NULL, offsetof(ImageStatusInfo, mtime) },
731 { "Size", "t", NULL, offsetof(ImageStatusInfo, size) },
732 { "Limit", "t", NULL, offsetof(ImageStatusInfo, limit) },
733 { "SizeExclusive", "t", NULL, offsetof(ImageStatusInfo, size_exclusive) },
734 { "LimitExclusive", "t", NULL, offsetof(ImageStatusInfo, limit_exclusive) },
738 ImageStatusInfo info = {};
746 r = bus_map_all_properties(bus,
747 "org.freedesktop.machine1",
752 return log_error_errno(r, "Could not get properties: %m");
758 print_image_status_info(bus, &info);
767 static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) {
779 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
781 log_error_errno(r, "Could not get properties: %m");
786 static int show_image(int argc, char *argv[], void *userdata) {
788 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
789 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
790 bool properties, new_line = false;
791 sd_bus *bus = userdata;
796 properties = !strstr(argv[0], "status");
798 pager_open_if_enabled();
800 if (properties && argc <= 1) {
802 /* If no argument is specified, inspect the manager
804 r = show_image_properties(bus, "/org/freedesktop/machine1", &new_line);
809 for (i = 1; i < argc; i++) {
810 const char *path = NULL;
812 r = sd_bus_call_method(
814 "org.freedesktop.machine1",
815 "/org/freedesktop/machine1",
816 "org.freedesktop.machine1.Manager",
822 log_error("Could not get path to image: %s", bus_error_message(&error, -r));
826 r = sd_bus_message_read(reply, "o", &path);
828 return bus_log_parse_error(r);
831 r = show_image_properties(bus, path, &new_line);
833 r = show_image_info(argv[0], bus, path, &new_line);
839 static int kill_machine(int argc, char *argv[], void *userdata) {
840 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
841 sd_bus *bus = userdata;
847 arg_kill_who = "all";
849 for (i = 1; i < argc; i++) {
852 r = sd_bus_call_method(
854 "org.freedesktop.machine1",
855 "/org/freedesktop/machine1",
856 "org.freedesktop.machine1.Manager",
860 "ssi", argv[i], arg_kill_who, arg_signal);
862 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
870 static int reboot_machine(int argc, char *argv[], void *userdata) {
871 arg_kill_who = "leader";
872 arg_signal = SIGINT; /* sysvinit + systemd */
874 return kill_machine(argc, argv, userdata);
877 static int poweroff_machine(int argc, char *argv[], void *userdata) {
878 arg_kill_who = "leader";
879 arg_signal = SIGRTMIN+4; /* only systemd */
881 return kill_machine(argc, argv, userdata);
884 static int terminate_machine(int argc, char *argv[], void *userdata) {
885 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
886 sd_bus *bus = userdata;
891 for (i = 1; i < argc; i++) {
894 r = sd_bus_call_method(
896 "org.freedesktop.machine1",
897 "/org/freedesktop/machine1",
898 "org.freedesktop.machine1.Manager",
904 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
912 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
913 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
914 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
923 r = sd_bus_call_method(
925 "org.freedesktop.machine1",
926 "/org/freedesktop/machine1",
927 "org.freedesktop.machine1.Manager",
933 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
937 r = sd_bus_message_read(reply, "o", &object);
939 return bus_log_parse_error(r);
941 r = sd_bus_get_property(
943 "org.freedesktop.machine1",
945 "org.freedesktop.machine1.Machine",
951 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
953 r = sd_bus_message_read(reply2, "u", &leader);
955 return bus_log_parse_error(r);
961 static int copy_files(int argc, char *argv[], void *userdata) {
962 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
963 _cleanup_close_ int hostfd = -1;
964 sd_bus *bus = userdata;
972 copy_from = streq(argv[0], "copy-from");
973 dest = argv[3] ?: argv[2];
974 host_path = strdupa(copy_from ? dest : argv[2]);
975 container_path = strdupa(copy_from ? argv[2] : dest);
977 if (!path_is_absolute(container_path)) {
978 log_error("Container path not absolute.");
982 t = strdup(host_path);
983 host_basename = basename(t);
984 host_dirname = dirname(host_path);
986 t = strdup(container_path);
987 container_basename = basename(t);
988 container_dirname = dirname(container_path);
990 r = machine_get_leader(bus, argv[1], &leader);
994 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
996 return log_error_errno(errno, "Failed to open source directory: %m");
1000 return log_error_errno(errno, "Failed to fork(): %m");
1007 q = procfs_file_alloca(leader, "ns/mnt");
1008 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1010 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1011 _exit(EXIT_FAILURE);
1014 if (setns(mntfd, CLONE_NEWNS) < 0) {
1015 log_error_errno(errno, "Failed to join namespace of leader: %m");
1016 _exit(EXIT_FAILURE);
1019 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1020 if (containerfd < 0) {
1021 log_error_errno(errno, "Failed top open destination directory: %m");
1022 _exit(EXIT_FAILURE);
1026 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
1028 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
1030 log_error_errno(errno, "Failed to copy tree: %m");
1031 _exit(EXIT_FAILURE);
1034 _exit(EXIT_SUCCESS);
1037 r = wait_for_terminate(child, &si);
1039 return log_error_errno(r, "Failed to wait for client: %m");
1040 if (si.si_code != CLD_EXITED) {
1041 log_error("Client died abnormally.");
1044 if (si.si_status != EXIT_SUCCESS)
1050 static int bind_mount(int argc, char *argv[], void *userdata) {
1051 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
1052 sd_bus *bus = userdata;
1053 pid_t child, leader;
1056 bool mount_slave_created = false, mount_slave_mounted = false,
1057 mount_tmp_created = false, mount_tmp_mounted = false,
1058 mount_outside_created = false, mount_outside_mounted = false;
1063 /* One day, when bind mounting /proc/self/fd/n works across
1064 * namespace boundaries we should rework this logic to make
1067 dest = argv[3] ?: argv[2];
1068 if (!path_is_absolute(dest)) {
1069 log_error("Destination path not absolute.");
1073 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
1074 if (access(p, F_OK) < 0) {
1075 log_error("Container does not allow propagation of mount points.");
1079 r = machine_get_leader(bus, argv[1], &leader);
1083 /* Our goal is to install a new bind mount into the container,
1084 possibly read-only. This is irritatingly complex
1085 unfortunately, currently.
1087 First, we start by creating a private playground in /tmp,
1088 that we can mount MS_SLAVE. (Which is necessary, since
1089 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
1092 if (!mkdtemp(mount_slave))
1093 return log_error_errno(errno, "Failed to create playground: %m");
1095 mount_slave_created = true;
1097 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
1098 r = log_error_errno(errno, "Failed to make bind mount: %m");
1102 mount_slave_mounted = true;
1104 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
1105 r = log_error_errno(errno, "Failed to remount slave: %m");
1109 /* Second, we mount the source directory to a directory inside
1110 of our MS_SLAVE playground. */
1111 mount_tmp = strappenda(mount_slave, "/mount");
1112 if (mkdir(mount_tmp, 0700) < 0) {
1113 r = log_error_errno(errno, "Failed to create temporary mount: %m");
1117 mount_tmp_created = true;
1119 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
1120 r = log_error_errno(errno, "Failed to overmount: %m");
1124 mount_tmp_mounted = true;
1126 /* Third, we remount the new bind mount read-only if requested. */
1128 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
1129 r = log_error_errno(errno, "Failed to mark read-only: %m");
1133 /* Fourth, we move the new bind mount into the propagation
1134 * directory. This way it will appear there read-only
1137 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
1138 if (!mkdtemp(mount_outside)) {
1139 r = log_error_errno(errno, "Cannot create propagation directory: %m");
1143 mount_outside_created = true;
1145 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
1146 r = log_error_errno(errno, "Failed to move: %m");
1150 mount_outside_mounted = true;
1151 mount_tmp_mounted = false;
1153 (void) rmdir(mount_tmp);
1154 mount_tmp_created = false;
1156 (void) umount(mount_slave);
1157 mount_slave_mounted = false;
1159 (void) rmdir(mount_slave);
1160 mount_slave_created = false;
1164 r = log_error_errno(errno, "Failed to fork(): %m");
1169 const char *mount_inside;
1173 q = procfs_file_alloca(leader, "ns/mnt");
1174 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1176 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1177 _exit(EXIT_FAILURE);
1180 if (setns(mntfd, CLONE_NEWNS) < 0) {
1181 log_error_errno(errno, "Failed to join namespace of leader: %m");
1182 _exit(EXIT_FAILURE);
1186 mkdir_p(dest, 0755);
1188 /* Fifth, move the mount to the right place inside */
1189 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
1190 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
1191 log_error_errno(errno, "Failed to mount: %m");
1192 _exit(EXIT_FAILURE);
1195 _exit(EXIT_SUCCESS);
1198 r = wait_for_terminate(child, &si);
1200 log_error_errno(r, "Failed to wait for client: %m");
1203 if (si.si_code != CLD_EXITED) {
1204 log_error("Client died abnormally.");
1208 if (si.si_status != EXIT_SUCCESS) {
1216 if (mount_outside_mounted)
1217 umount(mount_outside);
1218 if (mount_outside_created)
1219 rmdir(mount_outside);
1221 if (mount_tmp_mounted)
1223 if (mount_tmp_created)
1226 if (mount_slave_mounted)
1227 umount(mount_slave);
1228 if (mount_slave_created)
1229 umount(mount_slave);
1234 static int login_machine(int argc, char *argv[], void *userdata) {
1235 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1236 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1237 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1238 _cleanup_event_unref_ sd_event *event = NULL;
1239 int master = -1, r, ret = 0;
1240 sd_bus *bus = userdata;
1247 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1248 arg_transport != BUS_TRANSPORT_MACHINE) {
1249 log_error("Login only supported on local machines.");
1253 r = sd_event_default(&event);
1255 return log_error_errno(r, "Failed to get event loop: %m");
1257 r = sd_bus_attach_event(bus, event, 0);
1259 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1261 r = sd_bus_message_new_method_call(bus,
1263 "org.freedesktop.machine1",
1264 "/org/freedesktop/machine1",
1265 "org.freedesktop.machine1.Manager",
1266 "OpenMachineLogin");
1268 return bus_log_create_error(r);
1270 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1272 return bus_log_create_error(r);
1274 r = sd_bus_message_append(m, "s", argv[1]);
1276 return bus_log_create_error(r);
1278 r = sd_bus_call(bus, m, 0, &error, &reply);
1280 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1284 r = sd_bus_message_read(reply, "hs", &master, &pty);
1286 return bus_log_parse_error(r);
1288 assert_se(sigemptyset(&mask) == 0);
1289 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1290 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1292 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
1294 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1295 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1297 r = pty_forward_new(event, master, true, &forward);
1299 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1301 r = sd_event_loop(event);
1303 return log_error_errno(r, "Failed to run event loop: %m");
1305 pty_forward_last_char(forward, &last_char);
1307 forward = pty_forward_free(forward);
1309 if (last_char != '\n')
1310 fputc('\n', stdout);
1312 log_info("Connection to container %s terminated.", argv[1]);
1314 sd_event_get_exit_code(event, &ret);
1318 static int remove_image(int argc, char *argv[], void *userdata) {
1319 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1320 sd_bus *bus = userdata;
1325 for (i = 1; i < argc; i++) {
1326 r = sd_bus_call_method(
1328 "org.freedesktop.machine1",
1329 "/org/freedesktop/machine1",
1330 "org.freedesktop.machine1.Manager",
1336 log_error("Could not remove image: %s", bus_error_message(&error, -r));
1344 static int rename_image(int argc, char *argv[], void *userdata) {
1345 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1346 sd_bus *bus = userdata;
1349 r = sd_bus_call_method(
1351 "org.freedesktop.machine1",
1352 "/org/freedesktop/machine1",
1353 "org.freedesktop.machine1.Manager",
1357 "ss", argv[1], argv[2]);
1359 log_error("Could not rename image: %s", bus_error_message(&error, -r));
1366 static int clone_image(int argc, char *argv[], void *userdata) {
1367 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1368 sd_bus *bus = userdata;
1371 r = sd_bus_call_method(
1373 "org.freedesktop.machine1",
1374 "/org/freedesktop/machine1",
1375 "org.freedesktop.machine1.Manager",
1379 "ssb", argv[1], argv[2], arg_read_only);
1381 log_error("Could not clone image: %s", bus_error_message(&error, -r));
1388 static int read_only_image(int argc, char *argv[], void *userdata) {
1389 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1390 sd_bus *bus = userdata;
1394 b = parse_boolean(argv[2]);
1396 log_error("Failed to parse boolean argument: %s", argv[2]);
1401 r = sd_bus_call_method(
1403 "org.freedesktop.machine1",
1404 "/org/freedesktop/machine1",
1405 "org.freedesktop.machine1.Manager",
1406 "MarkImageReadOnly",
1411 log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
1418 static int start_machine(int argc, char *argv[], void *userdata) {
1419 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1420 _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
1421 sd_bus *bus = userdata;
1426 r = bus_wait_for_jobs_new(bus, &w);
1430 for (i = 1; i < argc; i++) {
1431 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1432 _cleanup_free_ char *e = NULL, *unit = NULL;
1435 if (!machine_name_is_valid(argv[i])) {
1436 log_error("Invalid machine name %s.", argv[i]);
1440 e = unit_name_escape(argv[i]);
1444 unit = unit_name_build("systemd-nspawn", e, ".service");
1448 r = sd_bus_message_new_method_call(
1451 "org.freedesktop.systemd1",
1452 "/org/freedesktop/systemd1",
1453 "org.freedesktop.systemd1.Manager",
1456 return bus_log_create_error(r);
1458 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1460 return bus_log_create_error(r);
1462 r = sd_bus_message_append(m, "ss", unit, "fail");
1464 return bus_log_create_error(r);
1466 r = sd_bus_call(bus, m, 0, &error, &reply);
1468 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
1472 r = sd_bus_message_read(reply, "o", &object);
1474 return bus_log_parse_error(r);
1476 r = bus_wait_for_jobs_add(w, object);
1481 r = bus_wait_for_jobs(w, arg_quiet);
1488 static int enable_machine(int argc, char *argv[], void *userdata) {
1489 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1490 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1491 int carries_install_info = 0;
1492 const char *method = NULL;
1493 sd_bus *bus = userdata;
1498 method = streq(argv[0], "enable") ? "EnableUnitFiles" : "DisableUnitFiles";
1500 r = sd_bus_message_new_method_call(
1503 "org.freedesktop.systemd1",
1504 "/org/freedesktop/systemd1",
1505 "org.freedesktop.systemd1.Manager",
1508 return bus_log_create_error(r);
1510 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1512 return bus_log_create_error(r);
1514 r = sd_bus_message_open_container(m, 'a', "s");
1516 return bus_log_create_error(r);
1518 for (i = 1; i < argc; i++) {
1519 _cleanup_free_ char *e = NULL, *unit = NULL;
1521 if (!machine_name_is_valid(argv[i])) {
1522 log_error("Invalid machine name %s.", argv[i]);
1526 e = unit_name_escape(argv[i]);
1530 unit = unit_name_build("systemd-nspawn", e, ".service");
1534 r = sd_bus_message_append(m, "s", unit);
1536 return bus_log_create_error(r);
1539 r = sd_bus_message_close_container(m);
1541 return bus_log_create_error(r);
1543 if (streq(argv[0], "enable"))
1544 r = sd_bus_message_append(m, "bb", false, false);
1546 r = sd_bus_message_append(m, "b", false);
1548 return bus_log_create_error(r);
1550 r = sd_bus_call(bus, m, 0, &error, &reply);
1552 log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r));
1556 if (streq(argv[0], "enable")) {
1557 r = sd_bus_message_read(reply, "b", carries_install_info);
1559 return bus_log_parse_error(r);
1562 r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
1566 m = sd_bus_message_unref(m);
1568 r = sd_bus_message_new_method_call(
1571 "org.freedesktop.systemd1",
1572 "/org/freedesktop/systemd1",
1573 "org.freedesktop.systemd1.Manager",
1576 return bus_log_create_error(r);
1578 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1580 return bus_log_create_error(r);
1582 r = sd_bus_call(bus, m, 0, &error, NULL);
1584 log_error("Failed to reload daemon: %s", bus_error_message(&error, -r));
1591 static int help(int argc, char *argv[], void *userdata) {
1593 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1594 "Send control commands to or query the virtual machine and container\n"
1595 "registration manager.\n\n"
1596 " -h --help Show this help\n"
1597 " --version Show package version\n"
1598 " --no-pager Do not pipe output into a pager\n"
1599 " --no-legend Do not show the headers and footers\n"
1600 " -H --host=[USER@]HOST Operate on remote host\n"
1601 " -M --machine=CONTAINER Operate on local container\n"
1602 " -p --property=NAME Show only properties by this name\n"
1603 " -q --quiet Suppress output\n"
1604 " -a --all Show all properties, including empty ones\n"
1605 " -l --full Do not ellipsize output\n"
1606 " --kill-who=WHO Who to send signal to\n"
1607 " -s --signal=SIGNAL Which signal to send\n"
1608 " --read-only Create read-only bind mount\n"
1609 " --mkdir Create directory before bind mounting, if missing\n\n"
1610 "Machine Commands:\n"
1611 " list List running VMs and containers\n"
1612 " status NAME... Show VM/container details\n"
1613 " show NAME... Show properties of one or more VMs/containers\n"
1614 " login NAME Get a login prompt on a container\n"
1615 " start NAME... Start container as a service\n"
1616 " enable NAME... Enable automatic container start at boot\n"
1617 " disable NAME... Disable automatic container start at boot\n"
1618 " poweroff NAME... Power off one or more containers\n"
1619 " reboot NAME... Reboot one or more containers\n"
1620 " terminate NAME... Terminate one or more VMs/containers\n"
1621 " kill NAME... Send signal to processes of a VM/container\n"
1622 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1623 " copy-from NAME PATH [PATH] Copy files from a container to the host\n"
1624 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
1626 " list-images Show available images\n"
1627 " image-status NAME... Show image details\n"
1628 " show-image NAME... Show properties of image\n"
1629 " clone NAME NAME Clone an image\n"
1630 " rename NAME NAME Rename an image\n"
1631 " read-only NAME [BOOL] Mark or unmark image read-only\n"
1632 " remove NAME... Remove an image\n",
1633 program_invocation_short_name);
1638 static int parse_argv(int argc, char *argv[]) {
1641 ARG_VERSION = 0x100,
1649 static const struct option options[] = {
1650 { "help", no_argument, NULL, 'h' },
1651 { "version", no_argument, NULL, ARG_VERSION },
1652 { "property", required_argument, NULL, 'p' },
1653 { "all", no_argument, NULL, 'a' },
1654 { "full", no_argument, NULL, 'l' },
1655 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1656 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1657 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1658 { "signal", required_argument, NULL, 's' },
1659 { "host", required_argument, NULL, 'H' },
1660 { "machine", required_argument, NULL, 'M' },
1661 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1662 { "mkdir", no_argument, NULL, ARG_MKDIR },
1663 { "quiet", no_argument, NULL, 'q' },
1672 while ((c = getopt_long(argc, argv, "hp:als:H:M:q", options, NULL)) >= 0)
1677 return help(0, NULL, NULL);
1680 puts(PACKAGE_STRING);
1681 puts(SYSTEMD_FEATURES);
1685 r = strv_extend(&arg_property, optarg);
1689 /* If the user asked for a particular
1690 * property, show it to him, even if it is
1704 arg_no_pager = true;
1712 arg_kill_who = optarg;
1716 arg_signal = signal_from_string_try_harder(optarg);
1717 if (arg_signal < 0) {
1718 log_error("Failed to parse signal string %s.", optarg);
1724 arg_transport = BUS_TRANSPORT_REMOTE;
1729 arg_transport = BUS_TRANSPORT_MACHINE;
1734 arg_read_only = true;
1749 assert_not_reached("Unhandled option");
1755 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1757 static const Verb verbs[] = {
1758 { "help", VERB_ANY, VERB_ANY, 0, help },
1759 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1760 { "list-images", VERB_ANY, 1, 0, list_images },
1761 { "status", 2, VERB_ANY, 0, show_machine },
1762 { "image-status",2, VERB_ANY, 0, show_image },
1763 { "show", VERB_ANY, VERB_ANY, 0, show_machine },
1764 { "show-image", VERB_ANY, VERB_ANY, 0, show_image },
1765 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1766 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1767 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1768 { "kill", 2, VERB_ANY, 0, kill_machine },
1769 { "login", 2, 2, 0, login_machine },
1770 { "bind", 3, 4, 0, bind_mount },
1771 { "copy-to", 3, 4, 0, copy_files },
1772 { "copy-from", 3, 4, 0, copy_files },
1773 { "remove", 2, VERB_ANY, 0, remove_image },
1774 { "rename", 3, 3, 0, rename_image },
1775 { "clone", 3, 3, 0, clone_image },
1776 { "read-only", 2, 3, 0, read_only_image },
1777 { "start", 2, VERB_ANY, 0, start_machine },
1778 { "enable", 2, VERB_ANY, 0, enable_machine },
1779 { "disable", 2, VERB_ANY, 0, enable_machine },
1783 return dispatch_verb(argc, argv, verbs, bus);
1786 int main(int argc, char*argv[]) {
1787 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1790 setlocale(LC_ALL, "");
1791 log_parse_environment();
1794 r = parse_argv(argc, argv);
1798 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1800 log_error_errno(r, "Failed to create bus connection: %m");
1804 r = machinectl_main(argc, argv, bus);
1809 strv_free(arg_property);
1811 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;