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 %-3s %*s %*s\n",
235 (int) max_name, images[j].name,
236 (int) max_type, images[j].type,
237 yes_no(images[j].read_only),
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_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_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(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_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_properties(bus, path, &new_line);
641 r = show_info(argv[0], bus, path, &new_line);
647 static int kill_machine(int argc, char *argv[], void *userdata) {
648 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
649 sd_bus *bus = userdata;
655 arg_kill_who = "all";
657 for (i = 1; i < argc; i++) {
660 r = sd_bus_call_method(
662 "org.freedesktop.machine1",
663 "/org/freedesktop/machine1",
664 "org.freedesktop.machine1.Manager",
668 "ssi", argv[i], arg_kill_who, arg_signal);
670 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
678 static int reboot_machine(int argc, char *argv[], void *userdata) {
679 arg_kill_who = "leader";
680 arg_signal = SIGINT; /* sysvinit + systemd */
682 return kill_machine(argc, argv, userdata);
685 static int poweroff_machine(int argc, char *argv[], void *userdata) {
686 arg_kill_who = "leader";
687 arg_signal = SIGRTMIN+4; /* only systemd */
689 return kill_machine(argc, argv, userdata);
692 static int terminate_machine(int argc, char *argv[], void *userdata) {
693 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
694 sd_bus *bus = userdata;
699 for (i = 1; i < argc; i++) {
702 r = sd_bus_call_method(
704 "org.freedesktop.machine1",
705 "/org/freedesktop/machine1",
706 "org.freedesktop.machine1.Manager",
712 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
720 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
721 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
722 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
731 r = sd_bus_call_method(
733 "org.freedesktop.machine1",
734 "/org/freedesktop/machine1",
735 "org.freedesktop.machine1.Manager",
741 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
745 r = sd_bus_message_read(reply, "o", &object);
747 return bus_log_parse_error(r);
749 r = sd_bus_get_property(
751 "org.freedesktop.machine1",
753 "org.freedesktop.machine1.Machine",
759 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
761 r = sd_bus_message_read(reply2, "u", &leader);
763 return bus_log_parse_error(r);
769 static int copy_files(int argc, char *argv[], void *userdata) {
770 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
771 _cleanup_close_ int hostfd = -1;
772 sd_bus *bus = userdata;
780 copy_from = streq(argv[0], "copy-from");
781 dest = argv[3] ?: argv[2];
782 host_path = strdupa(copy_from ? dest : argv[2]);
783 container_path = strdupa(copy_from ? argv[2] : dest);
785 if (!path_is_absolute(container_path)) {
786 log_error("Container path not absolute.");
790 t = strdup(host_path);
791 host_basename = basename(t);
792 host_dirname = dirname(host_path);
794 t = strdup(container_path);
795 container_basename = basename(t);
796 container_dirname = dirname(container_path);
798 r = machine_get_leader(bus, argv[1], &leader);
802 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
804 return log_error_errno(errno, "Failed to open source directory: %m");
808 return log_error_errno(errno, "Failed to fork(): %m");
815 q = procfs_file_alloca(leader, "ns/mnt");
816 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
818 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
822 if (setns(mntfd, CLONE_NEWNS) < 0) {
823 log_error_errno(errno, "Failed to join namespace of leader: %m");
827 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
828 if (containerfd < 0) {
829 log_error_errno(errno, "Failed top open destination directory: %m");
834 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
836 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
838 log_error_errno(errno, "Failed to copy tree: %m");
845 r = wait_for_terminate(child, &si);
847 return log_error_errno(r, "Failed to wait for client: %m");
848 if (si.si_code != CLD_EXITED) {
849 log_error("Client died abnormally.");
852 if (si.si_status != EXIT_SUCCESS)
858 static int bind_mount(int argc, char *argv[], void *userdata) {
859 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
860 sd_bus *bus = userdata;
864 bool mount_slave_created = false, mount_slave_mounted = false,
865 mount_tmp_created = false, mount_tmp_mounted = false,
866 mount_outside_created = false, mount_outside_mounted = false;
871 /* One day, when bind mounting /proc/self/fd/n works across
872 * namespace boundaries we should rework this logic to make
875 dest = argv[3] ?: argv[2];
876 if (!path_is_absolute(dest)) {
877 log_error("Destination path not absolute.");
881 p = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/");
882 if (access(p, F_OK) < 0) {
883 log_error("Container does not allow propagation of mount points.");
887 r = machine_get_leader(bus, argv[1], &leader);
891 /* Our goal is to install a new bind mount into the container,
892 possibly read-only. This is irritatingly complex
893 unfortunately, currently.
895 First, we start by creating a private playground in /tmp,
896 that we can mount MS_SLAVE. (Which is necessary, since
897 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
900 if (!mkdtemp(mount_slave))
901 return log_error_errno(errno, "Failed to create playground: %m");
903 mount_slave_created = true;
905 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
906 r = log_error_errno(errno, "Failed to make bind mount: %m");
910 mount_slave_mounted = true;
912 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
913 r = log_error_errno(errno, "Failed to remount slave: %m");
917 /* Second, we mount the source directory to a directory inside
918 of our MS_SLAVE playground. */
919 mount_tmp = strappenda(mount_slave, "/mount");
920 if (mkdir(mount_tmp, 0700) < 0) {
921 r = log_error_errno(errno, "Failed to create temporary mount: %m");
925 mount_tmp_created = true;
927 if (mount(argv[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
928 r = log_error_errno(errno, "Failed to overmount: %m");
932 mount_tmp_mounted = true;
934 /* Third, we remount the new bind mount read-only if requested. */
936 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
937 r = log_error_errno(errno, "Failed to mark read-only: %m");
941 /* Fourth, we move the new bind mount into the propagation
942 * directory. This way it will appear there read-only
945 mount_outside = strappenda("/run/systemd/nspawn/propagate/", argv[1], "/XXXXXX");
946 if (!mkdtemp(mount_outside)) {
947 r = log_error_errno(errno, "Cannot create propagation directory: %m");
951 mount_outside_created = true;
953 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
954 r = log_error_errno(errno, "Failed to move: %m");
958 mount_outside_mounted = true;
959 mount_tmp_mounted = false;
961 (void) rmdir(mount_tmp);
962 mount_tmp_created = false;
964 (void) umount(mount_slave);
965 mount_slave_mounted = false;
967 (void) rmdir(mount_slave);
968 mount_slave_created = false;
972 r = log_error_errno(errno, "Failed to fork(): %m");
977 const char *mount_inside;
981 q = procfs_file_alloca(leader, "ns/mnt");
982 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
984 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
988 if (setns(mntfd, CLONE_NEWNS) < 0) {
989 log_error_errno(errno, "Failed to join namespace of leader: %m");
996 /* Fifth, move the mount to the right place inside */
997 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
998 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
999 log_error_errno(errno, "Failed to mount: %m");
1000 _exit(EXIT_FAILURE);
1003 _exit(EXIT_SUCCESS);
1006 r = wait_for_terminate(child, &si);
1008 log_error_errno(r, "Failed to wait for client: %m");
1011 if (si.si_code != CLD_EXITED) {
1012 log_error("Client died abnormally.");
1016 if (si.si_status != EXIT_SUCCESS) {
1024 if (mount_outside_mounted)
1025 umount(mount_outside);
1026 if (mount_outside_created)
1027 rmdir(mount_outside);
1029 if (mount_tmp_mounted)
1031 if (mount_tmp_created)
1034 if (mount_slave_mounted)
1035 umount(mount_slave);
1036 if (mount_slave_created)
1037 umount(mount_slave);
1042 static int login_machine(int argc, char *argv[], void *userdata) {
1043 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1044 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1045 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1046 _cleanup_event_unref_ sd_event *event = NULL;
1047 int master = -1, r, ret = 0;
1048 sd_bus *bus = userdata;
1055 if (arg_transport != BUS_TRANSPORT_LOCAL &&
1056 arg_transport != BUS_TRANSPORT_MACHINE) {
1057 log_error("Login only supported on local machines.");
1061 r = sd_event_default(&event);
1063 return log_error_errno(r, "Failed to get event loop: %m");
1065 r = sd_bus_attach_event(bus, event, 0);
1067 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1069 r = sd_bus_message_new_method_call(bus,
1071 "org.freedesktop.machine1",
1072 "/org/freedesktop/machine1",
1073 "org.freedesktop.machine1.Manager",
1074 "OpenMachineLogin");
1076 return bus_log_create_error(r);
1078 r = sd_bus_message_set_allow_interactive_authorization(m, true);
1080 return bus_log_create_error(r);
1082 r = sd_bus_message_append(m, "s", argv[1]);
1084 return bus_log_create_error(r);
1086 r = sd_bus_call(bus, m, 0, &error, &reply);
1088 log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r));
1092 r = sd_bus_message_read(reply, "hs", &master, &pty);
1094 return bus_log_parse_error(r);
1096 assert_se(sigemptyset(&mask) == 0);
1097 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1098 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1100 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", argv[1]);
1102 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1103 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1105 r = pty_forward_new(event, master, true, &forward);
1107 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1109 r = sd_event_loop(event);
1111 return log_error_errno(r, "Failed to run event loop: %m");
1113 pty_forward_last_char(forward, &last_char);
1115 forward = pty_forward_free(forward);
1117 if (last_char != '\n')
1118 fputc('\n', stdout);
1120 log_info("Connection to container %s terminated.", argv[1]);
1122 sd_event_get_exit_code(event, &ret);
1126 static int help(int argc, char *argv[], void *userdata) {
1128 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1129 "Send control commands to or query the virtual machine and container\n"
1130 "registration manager.\n\n"
1131 " -h --help Show this help\n"
1132 " --version Show package version\n"
1133 " --no-pager Do not pipe output into a pager\n"
1134 " --no-legend Do not show the headers and footers\n"
1135 " -H --host=[USER@]HOST Operate on remote host\n"
1136 " -M --machine=CONTAINER Operate on local container\n"
1137 " -p --property=NAME Show only properties by this name\n"
1138 " -a --all Show all properties, including empty ones\n"
1139 " -l --full Do not ellipsize output\n"
1140 " --kill-who=WHO Who to send signal to\n"
1141 " -s --signal=SIGNAL Which signal to send\n"
1142 " --read-only Create read-only bind mount\n"
1143 " --mkdir Create directory before bind mounting, if missing\n\n"
1144 "Machine Commands:\n"
1145 " list List running VMs and containers\n"
1146 " status NAME... Show VM/container status\n"
1147 " show NAME... Show properties of one or more VMs/containers\n"
1148 " login NAME Get a login prompt on a container\n"
1149 " poweroff NAME... Power off one or more containers\n"
1150 " reboot NAME... Reboot one or more containers\n"
1151 " kill NAME... Send signal to processes of a VM/container\n"
1152 " terminate NAME... Terminate one or more VMs/containers\n"
1153 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
1154 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1155 " copy-from NAME PATH [PATH] Copy files from a container to the host\n\n"
1157 " list-images Show available images\n",
1158 program_invocation_short_name);
1163 static int parse_argv(int argc, char *argv[]) {
1166 ARG_VERSION = 0x100,
1174 static const struct option options[] = {
1175 { "help", no_argument, NULL, 'h' },
1176 { "version", no_argument, NULL, ARG_VERSION },
1177 { "property", required_argument, NULL, 'p' },
1178 { "all", no_argument, NULL, 'a' },
1179 { "full", no_argument, NULL, 'l' },
1180 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1181 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1182 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1183 { "signal", required_argument, NULL, 's' },
1184 { "host", required_argument, NULL, 'H' },
1185 { "machine", required_argument, NULL, 'M' },
1186 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1187 { "mkdir", no_argument, NULL, ARG_MKDIR },
1196 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1201 return help(0, NULL, NULL);
1204 puts(PACKAGE_STRING);
1205 puts(SYSTEMD_FEATURES);
1209 r = strv_extend(&arg_property, optarg);
1213 /* If the user asked for a particular
1214 * property, show it to him, even if it is
1228 arg_no_pager = true;
1236 arg_kill_who = optarg;
1240 arg_signal = signal_from_string_try_harder(optarg);
1241 if (arg_signal < 0) {
1242 log_error("Failed to parse signal string %s.", optarg);
1248 arg_transport = BUS_TRANSPORT_REMOTE;
1253 arg_transport = BUS_TRANSPORT_MACHINE;
1258 arg_read_only = true;
1269 assert_not_reached("Unhandled option");
1275 static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
1277 static const Verb verbs[] = {
1278 { "help", VERB_ANY, VERB_ANY, 0, help },
1279 { "list", VERB_ANY, 1, VERB_DEFAULT, list_machines },
1280 { "list-images", VERB_ANY, 1, 0, list_images },
1281 { "status", 2, VERB_ANY, 0, show },
1282 { "show", VERB_ANY, VERB_ANY, 0, show },
1283 { "terminate", 2, VERB_ANY, 0, terminate_machine },
1284 { "reboot", 2, VERB_ANY, 0, reboot_machine },
1285 { "poweroff", 2, VERB_ANY, 0, poweroff_machine },
1286 { "kill", 2, VERB_ANY, 0, kill_machine },
1287 { "login", 2, 2, 0, login_machine },
1288 { "bind", 3, 4, 0, bind_mount },
1289 { "copy-to", 3, 4, 0, copy_files },
1290 { "copy-from", 3, 4, 0, copy_files },
1294 return dispatch_verb(argc, argv, verbs, bus);
1297 int main(int argc, char*argv[]) {
1298 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1301 setlocale(LC_ALL, "");
1302 log_parse_environment();
1305 r = parse_argv(argc, argv);
1309 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1311 log_error_errno(r, "Failed to create bus connection: %m");
1315 r = machinectl_main(argc, argv, bus);
1320 strv_free(arg_property);
1322 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;