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"
54 static char **arg_property = NULL;
55 static bool arg_all = false;
56 static bool arg_full = false;
57 static bool arg_no_pager = false;
58 static bool arg_legend = true;
59 static const char *arg_kill_who = NULL;
60 static int arg_signal = SIGTERM;
61 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
62 static char *arg_host = NULL;
63 static bool arg_read_only = false;
64 static bool arg_mkdir = false;
66 static void pager_open_if_enabled(void) {
68 /* Cache result before we open the pager */
75 static int list_machines(sd_bus *bus, char **args, unsigned n) {
76 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
77 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
78 const char *name, *class, *service, *object;
82 pager_open_if_enabled();
84 r = sd_bus_call_method(
86 "org.freedesktop.machine1",
87 "/org/freedesktop/machine1",
88 "org.freedesktop.machine1.Manager",
94 log_error("Could not get machines: %s", bus_error_message(&error, -r));
99 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
101 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
103 return bus_log_parse_error(r);
105 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
106 printf("%-32s %-9s %-16s\n", name, class, service);
111 return bus_log_parse_error(r);
113 r = sd_bus_message_exit_container(reply);
115 return bus_log_parse_error(r);
118 printf("\n%u machines listed.\n", k);
123 typedef struct ImageInfo {
129 static int compare_image_info(const void *a, const void *b) {
130 const ImageInfo *x = a, *y = b;
132 return strcmp(x->name, y->name);
135 static int list_images(sd_bus *bus, char **args, unsigned n) {
137 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
138 size_t max_name = strlen("NAME"), max_type = strlen("TYPE");
139 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
140 _cleanup_free_ ImageInfo *images = NULL;
141 size_t n_images = 0, n_allocated = 0, j;
142 const char *name, *type, *object;
146 pager_open_if_enabled();
148 r = sd_bus_call_method(
150 "org.freedesktop.machine1",
151 "/org/freedesktop/machine1",
152 "org.freedesktop.machine1.Manager",
158 log_error("Could not get images: %s", bus_error_message(&error, -r));
162 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbo)");
164 return bus_log_parse_error(r);
166 while ((r = sd_bus_message_read(reply, "(ssbo)", &name, &type, &read_only, &object)) > 0) {
168 if (name[0] == '.' && !arg_all)
171 if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
174 images[n_images].name = name;
175 images[n_images].type = type;
176 images[n_images].read_only = read_only;
178 if (strlen(name) > max_name)
179 max_name = strlen(name);
181 if (strlen(type) > max_type)
182 max_type = strlen(type);
187 return bus_log_parse_error(r);
189 r = sd_bus_message_exit_container(reply);
191 return bus_log_parse_error(r);
193 qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
196 printf("%-*s %-*s %-3s\n", (int) max_name, "NAME", (int) max_type, "TYPE", "RO");
198 for (j = 0; j < n_images; j++) {
199 printf("%-*s %-*s %-3s\n",
200 (int) max_name, images[j].name,
201 (int) max_type, images[j].type,
202 yes_no(images[j].read_only));
206 return bus_log_parse_error(r);
210 printf("\n%zu images listed.\n", n_images);
215 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
216 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
217 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
218 _cleanup_free_ char *path = NULL;
226 if (arg_transport == BUS_TRANSPORT_REMOTE)
229 path = unit_dbus_path_from_name(unit);
233 r = sd_bus_get_property(
235 "org.freedesktop.systemd1",
237 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
243 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
247 r = sd_bus_message_read(reply, "s", &cgroup);
249 return bus_log_parse_error(r);
254 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
258 arg_all * OUTPUT_SHOW_ALL |
259 arg_full * OUTPUT_FULL_WIDTH;
267 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
271 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
272 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
280 r = sd_bus_call_method(bus,
281 "org.freedesktop.machine1",
282 "/org/freedesktop/machine1",
283 "org.freedesktop.machine1.Manager",
284 "GetMachineAddresses",
291 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
293 return bus_log_parse_error(r);
295 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
299 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
301 r = sd_bus_message_read(reply, "i", &family);
303 return bus_log_parse_error(r);
305 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
307 return bus_log_parse_error(r);
309 fputs(prefix, stdout);
310 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
311 if (family == AF_INET6 && ifi > 0)
315 r = sd_bus_message_exit_container(reply);
317 return bus_log_parse_error(r);
319 if (prefix != prefix2)
323 return bus_log_parse_error(r);
325 r = sd_bus_message_exit_container(reply);
327 return bus_log_parse_error(r);
332 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
333 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
334 const char *k, *v, *pretty = NULL;
341 r = sd_bus_call_method(bus,
342 "org.freedesktop.machine1",
343 "/org/freedesktop/machine1",
344 "org.freedesktop.machine1.Manager",
345 "GetMachineOSRelease",
352 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
354 return bus_log_parse_error(r);
356 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
357 if (streq(k, "PRETTY_NAME"))
362 return bus_log_parse_error(r);
364 r = sd_bus_message_exit_container(reply);
366 return bus_log_parse_error(r);
369 printf("%s%s\n", prefix, pretty);
374 typedef struct MachineStatusInfo {
380 char *root_directory;
387 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
388 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
389 char since2[FORMAT_TIMESTAMP_MAX], *s2;
394 fputs(strna(i->name), stdout);
396 if (!sd_id128_equal(i->id, SD_ID128_NULL))
397 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
401 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
402 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
405 printf("\t Since: %s; %s\n", s2, s1);
407 printf("\t Since: %s\n", s2);
410 _cleanup_free_ char *t = NULL;
412 printf("\t Leader: %u", (unsigned) i->leader);
414 get_process_comm(i->leader, &t);
422 printf("\t Service: %s", i->service);
425 printf("; class %s", i->class);
429 printf("\t Class: %s\n", i->class);
431 if (i->root_directory)
432 printf("\t Root: %s\n", i->root_directory);
434 if (i->n_netif > 0) {
437 fputs("\t Iface:", stdout);
439 for (c = 0; c < i->n_netif; c++) {
440 char name[IF_NAMESIZE+1] = "";
442 if (if_indextoname(i->netif[c], name)) {
451 printf(" %i", i->netif[c]);
457 print_addresses(bus, i->name, ifi,
461 print_os_release(bus, i->name, "\t OS: ");
464 printf("\t Unit: %s\n", i->unit);
465 show_unit_cgroup(bus, i->unit, i->leader);
469 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
470 MachineStatusInfo *i = userdata;
475 assert_cc(sizeof(int32_t) == sizeof(int));
476 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
482 i->n_netif = l / sizeof(int32_t);
483 i->netif = memdup(v, l);
490 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
492 static const struct bus_properties_map map[] = {
493 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
494 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
495 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
496 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
497 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
498 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
499 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
500 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
501 { "NetworkInterfaces", "ai", map_netif, 0 },
505 MachineStatusInfo info = {};
511 r = bus_map_all_properties(bus,
512 "org.freedesktop.machine1",
517 return log_error_errno(r, "Could not get properties: %m");
523 print_machine_status_info(bus, &info);
529 free(info.root_directory);
535 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
543 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
545 log_error_errno(r, "Could not get properties: %m");
550 static int show(sd_bus *bus, char **args, unsigned n) {
551 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
552 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
555 bool properties, new_line = false;
560 properties = !strstr(args[0], "status");
562 pager_open_if_enabled();
564 if (properties && n <= 1) {
566 /* If no argument is specified, inspect the manager
568 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
573 for (i = 1; i < n; i++) {
574 const char *path = NULL;
576 r = sd_bus_call_method(
578 "org.freedesktop.machine1",
579 "/org/freedesktop/machine1",
580 "org.freedesktop.machine1.Manager",
586 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
590 r = sd_bus_message_read(reply, "o", &path);
592 return bus_log_parse_error(r);
595 r = show_properties(bus, path, &new_line);
597 r = show_info(args[0], bus, path, &new_line);
603 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
604 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
610 arg_kill_who = "all";
612 for (i = 1; i < n; i++) {
615 r = sd_bus_call_method(
617 "org.freedesktop.machine1",
618 "/org/freedesktop/machine1",
619 "org.freedesktop.machine1.Manager",
623 "ssi", args[i], arg_kill_who, arg_signal);
625 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
633 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
634 arg_kill_who = "leader";
635 arg_signal = SIGINT; /* sysvinit + systemd */
637 return kill_machine(bus, args, n);
640 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
641 arg_kill_who = "leader";
642 arg_signal = SIGRTMIN+4; /* only systemd */
644 return kill_machine(bus, args, n);
647 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
648 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
653 for (i = 1; i < n; i++) {
656 r = sd_bus_call_method(
658 "org.freedesktop.machine1",
659 "/org/freedesktop/machine1",
660 "org.freedesktop.machine1.Manager",
666 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
674 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
675 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
676 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
685 r = sd_bus_call_method(
687 "org.freedesktop.machine1",
688 "/org/freedesktop/machine1",
689 "org.freedesktop.machine1.Manager",
695 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
699 r = sd_bus_message_read(reply, "o", &object);
701 return bus_log_parse_error(r);
703 r = sd_bus_get_property(
705 "org.freedesktop.machine1",
707 "org.freedesktop.machine1.Machine",
713 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
715 r = sd_bus_message_read(reply2, "u", &leader);
717 return bus_log_parse_error(r);
723 static int copy_files(sd_bus *bus, char **args, unsigned n) {
724 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
725 _cleanup_close_ int hostfd = -1;
732 log_error("Too many arguments.");
736 copy_from = streq(args[0], "copy-from");
737 dest = args[3] ?: args[2];
738 host_path = strdupa(copy_from ? dest : args[2]);
739 container_path = strdupa(copy_from ? args[2] : dest);
741 if (!path_is_absolute(container_path)) {
742 log_error("Container path not absolute.");
746 t = strdup(host_path);
747 host_basename = basename(t);
748 host_dirname = dirname(host_path);
750 t = strdup(container_path);
751 container_basename = basename(t);
752 container_dirname = dirname(container_path);
754 r = machine_get_leader(bus, args[1], &leader);
758 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
760 return log_error_errno(errno, "Failed to open source directory: %m");
764 return log_error_errno(errno, "Failed to fork(): %m");
771 q = procfs_file_alloca(leader, "ns/mnt");
772 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
774 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
778 if (setns(mntfd, CLONE_NEWNS) < 0) {
779 log_error_errno(errno, "Failed to join namespace of leader: %m");
783 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
784 if (containerfd < 0) {
785 log_error_errno(errno, "Failed top open destination directory: %m");
790 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
792 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
794 log_error_errno(errno, "Failed to copy tree: %m");
801 r = wait_for_terminate(child, &si);
803 return log_error_errno(r, "Failed to wait for client: %m");
804 if (si.si_code != CLD_EXITED) {
805 log_error("Client died abnormally.");
808 if (si.si_status != EXIT_SUCCESS)
814 static int bind_mount(sd_bus *bus, char **args, unsigned n) {
815 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
819 bool mount_slave_created = false, mount_slave_mounted = false,
820 mount_tmp_created = false, mount_tmp_mounted = false,
821 mount_outside_created = false, mount_outside_mounted = false;
824 /* One day, when bind mounting /proc/self/fd/n works across
825 * namespace boundaries we should rework this logic to make
829 log_error("Too many arguments.");
833 dest = args[3] ?: args[2];
834 if (!path_is_absolute(dest)) {
835 log_error("Destination path not absolute.");
839 p = strappenda("/run/systemd/nspawn/propagate/", args[1], "/");
840 if (access(p, F_OK) < 0) {
841 log_error("Container does not allow propagation of mount points.");
845 r = machine_get_leader(bus, args[1], &leader);
849 /* Our goal is to install a new bind mount into the container,
850 possibly read-only. This is irritatingly complex
851 unfortunately, currently.
853 First, we start by creating a private playground in /tmp,
854 that we can mount MS_SLAVE. (Which is necessary, since
855 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
858 if (!mkdtemp(mount_slave))
859 return log_error_errno(errno, "Failed to create playground: %m");
861 mount_slave_created = true;
863 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
864 r = log_error_errno(errno, "Failed to make bind mount: %m");
868 mount_slave_mounted = true;
870 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
871 r = log_error_errno(errno, "Failed to remount slave: %m");
875 /* Second, we mount the source directory to a directory inside
876 of our MS_SLAVE playground. */
877 mount_tmp = strappenda(mount_slave, "/mount");
878 if (mkdir(mount_tmp, 0700) < 0) {
879 r = log_error_errno(errno, "Failed to create temporary mount: %m");
883 mount_tmp_created = true;
885 if (mount(args[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
886 r = log_error_errno(errno, "Failed to overmount: %m");
890 mount_tmp_mounted = true;
892 /* Third, we remount the new bind mount read-only if requested. */
894 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
895 r = log_error_errno(errno, "Failed to mark read-only: %m");
899 /* Fourth, we move the new bind mount into the propagation
900 * directory. This way it will appear there read-only
903 mount_outside = strappenda("/run/systemd/nspawn/propagate/", args[1], "/XXXXXX");
904 if (!mkdtemp(mount_outside)) {
905 r = log_error_errno(errno, "Cannot create propagation directory: %m");
909 mount_outside_created = true;
911 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
912 r = log_error_errno(errno, "Failed to move: %m");
916 mount_outside_mounted = true;
917 mount_tmp_mounted = false;
919 (void) rmdir(mount_tmp);
920 mount_tmp_created = false;
922 (void) umount(mount_slave);
923 mount_slave_mounted = false;
925 (void) rmdir(mount_slave);
926 mount_slave_created = false;
930 r = log_error_errno(errno, "Failed to fork(): %m");
935 const char *mount_inside;
939 q = procfs_file_alloca(leader, "ns/mnt");
940 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
942 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
946 if (setns(mntfd, CLONE_NEWNS) < 0) {
947 log_error_errno(errno, "Failed to join namespace of leader: %m");
954 /* Fifth, move the mount to the right place inside */
955 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
956 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
957 log_error_errno(errno, "Failed to mount: %m");
964 r = wait_for_terminate(child, &si);
966 log_error_errno(r, "Failed to wait for client: %m");
969 if (si.si_code != CLD_EXITED) {
970 log_error("Client died abnormally.");
974 if (si.si_status != EXIT_SUCCESS) {
982 if (mount_outside_mounted)
983 umount(mount_outside);
984 if (mount_outside_created)
985 rmdir(mount_outside);
987 if (mount_tmp_mounted)
989 if (mount_tmp_created)
992 if (mount_slave_mounted)
994 if (mount_slave_created)
1000 static int openpt_in_namespace(pid_t pid, int flags) {
1001 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1002 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
1004 struct cmsghdr cmsghdr;
1005 uint8_t buf[CMSG_SPACE(sizeof(int))];
1007 struct msghdr mh = {
1008 .msg_control = &control,
1009 .msg_controllen = sizeof(control),
1011 struct cmsghdr *cmsg;
1016 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
1020 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1028 pair[0] = safe_close(pair[0]);
1030 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
1032 _exit(EXIT_FAILURE);
1034 master = posix_openpt(flags);
1036 _exit(EXIT_FAILURE);
1038 cmsg = CMSG_FIRSTHDR(&mh);
1039 cmsg->cmsg_level = SOL_SOCKET;
1040 cmsg->cmsg_type = SCM_RIGHTS;
1041 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
1042 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
1044 mh.msg_controllen = cmsg->cmsg_len;
1046 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
1047 _exit(EXIT_FAILURE);
1049 _exit(EXIT_SUCCESS);
1052 pair[1] = safe_close(pair[1]);
1054 r = wait_for_terminate(child, &si);
1057 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
1060 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
1063 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
1064 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
1068 fds = (int*) CMSG_DATA(cmsg);
1069 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
1072 close_many(fds, n_fds);
1085 static int login_machine(sd_bus *bus, char **args, unsigned n) {
1086 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1087 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1088 _cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
1089 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
1090 _cleanup_event_unref_ sd_event *event = NULL;
1091 _cleanup_close_ int master = -1;
1092 _cleanup_free_ char *getty = NULL;
1093 const char *pty, *p;
1101 if (arg_transport != BUS_TRANSPORT_LOCAL) {
1102 log_error("Login only supported on local machines.");
1106 r = sd_event_default(&event);
1108 return log_error_errno(r, "Failed to get event loop: %m");
1110 r = sd_bus_attach_event(bus, event, 0);
1112 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1114 r = machine_get_leader(bus, args[1], &leader);
1118 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
1120 return log_error_errno(master, "Failed to acquire pseudo tty: %m");
1122 pty = ptsname(master);
1124 return log_error_errno(errno, "Failed to get pty name: %m");
1126 p = startswith(pty, "/dev/pts/");
1128 log_error("Invalid pty name %s.", pty);
1132 r = sd_bus_open_system_container(&container_bus, args[1]);
1134 return log_error_errno(r, "Failed to get container bus: %m");
1136 getty = strjoin("container-getty@", p, ".service", NULL);
1140 if (unlockpt(master) < 0)
1141 return log_error_errno(errno, "Failed to unlock tty: %m");
1143 r = sd_bus_call_method(container_bus,
1144 "org.freedesktop.systemd1",
1145 "/org/freedesktop/systemd1",
1146 "org.freedesktop.systemd1.Manager",
1149 "ss", getty, "replace");
1151 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
1155 container_bus = sd_bus_unref(container_bus);
1157 assert_se(sigemptyset(&mask) == 0);
1158 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1159 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1161 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
1163 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1164 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1166 r = pty_forward_new(event, master, &forward);
1168 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1170 r = sd_event_loop(event);
1172 return log_error_errno(r, "Failed to run event loop: %m");
1174 forward = pty_forward_free(forward);
1176 fputc('\n', stdout);
1178 log_info("Connection to container %s terminated.", args[1]);
1180 sd_event_get_exit_code(event, &ret);
1184 static void help(void) {
1185 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1186 "Send control commands to or query the virtual machine and container\n"
1187 "registration manager.\n\n"
1188 " -h --help Show this help\n"
1189 " --version Show package version\n"
1190 " --no-pager Do not pipe output into a pager\n"
1191 " --no-legend Do not show the headers and footers\n"
1192 " -H --host=[USER@]HOST Operate on remote host\n"
1193 " -M --machine=CONTAINER Operate on local container\n"
1194 " -p --property=NAME Show only properties by this name\n"
1195 " -a --all Show all properties, including empty ones\n"
1196 " -l --full Do not ellipsize output\n"
1197 " --kill-who=WHO Who to send signal to\n"
1198 " -s --signal=SIGNAL Which signal to send\n"
1199 " --read-only Create read-only bind mount\n"
1200 " --mkdir Create directory before bind mounting, if missing\n\n"
1201 "Machine Commands:\n"
1202 " list List running VMs and containers\n"
1203 " status NAME... Show VM/container status\n"
1204 " show NAME... Show properties of one or more VMs/containers\n"
1205 " login NAME Get a login prompt on a container\n"
1206 " poweroff NAME... Power off one or more containers\n"
1207 " reboot NAME... Reboot one or more containers\n"
1208 " kill NAME... Send signal to processes of a VM/container\n"
1209 " terminate NAME... Terminate one or more VMs/containers\n"
1210 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
1211 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1212 " copy-from NAME PATH [PATH] Copy files from a container to the host\n\n"
1214 " list-images Show available images\n",
1215 program_invocation_short_name);
1218 static int parse_argv(int argc, char *argv[]) {
1221 ARG_VERSION = 0x100,
1229 static const struct option options[] = {
1230 { "help", no_argument, NULL, 'h' },
1231 { "version", no_argument, NULL, ARG_VERSION },
1232 { "property", required_argument, NULL, 'p' },
1233 { "all", no_argument, NULL, 'a' },
1234 { "full", no_argument, NULL, 'l' },
1235 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1236 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1237 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1238 { "signal", required_argument, NULL, 's' },
1239 { "host", required_argument, NULL, 'H' },
1240 { "machine", required_argument, NULL, 'M' },
1241 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1242 { "mkdir", no_argument, NULL, ARG_MKDIR },
1251 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1260 puts(PACKAGE_STRING);
1261 puts(SYSTEMD_FEATURES);
1265 r = strv_extend(&arg_property, optarg);
1269 /* If the user asked for a particular
1270 * property, show it to him, even if it is
1284 arg_no_pager = true;
1292 arg_kill_who = optarg;
1296 arg_signal = signal_from_string_try_harder(optarg);
1297 if (arg_signal < 0) {
1298 log_error("Failed to parse signal string %s.", optarg);
1304 arg_transport = BUS_TRANSPORT_REMOTE;
1309 arg_transport = BUS_TRANSPORT_CONTAINER;
1314 arg_read_only = true;
1325 assert_not_reached("Unhandled option");
1331 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
1333 static const struct {
1341 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1343 { "list", LESS, 1, list_machines },
1344 { "list-images", LESS, 1, list_images },
1345 { "status", MORE, 2, show },
1346 { "show", MORE, 1, show },
1347 { "terminate", MORE, 2, terminate_machine },
1348 { "reboot", MORE, 2, reboot_machine },
1349 { "poweroff", MORE, 2, poweroff_machine },
1350 { "kill", MORE, 2, kill_machine },
1351 { "login", MORE, 2, login_machine },
1352 { "bind", MORE, 3, bind_mount },
1353 { "copy-to", MORE, 3, copy_files },
1354 { "copy-from", MORE, 3, copy_files },
1363 left = argc - optind;
1366 /* Special rule: no arguments means "list" */
1369 if (streq(argv[optind], "help")) {
1374 for (i = 0; i < ELEMENTSOF(verbs); i++)
1375 if (streq(argv[optind], verbs[i].verb))
1378 if (i >= ELEMENTSOF(verbs)) {
1379 log_error("Unknown operation %s", argv[optind]);
1384 switch (verbs[i].argc_cmp) {
1387 if (left != verbs[i].argc) {
1388 log_error("Invalid number of arguments.");
1395 if (left < verbs[i].argc) {
1396 log_error("Too few arguments.");
1403 if (left > verbs[i].argc) {
1404 log_error("Too many arguments.");
1411 assert_not_reached("Unknown comparison operator.");
1414 return verbs[i].dispatch(bus, argv + optind, left);
1417 int main(int argc, char*argv[]) {
1418 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1421 setlocale(LC_ALL, "");
1422 log_parse_environment();
1425 r = parse_argv(argc, argv);
1429 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1431 log_error_errno(r, "Failed to create bus connection: %m");
1435 r = machinectl_main(bus, argc, argv);
1440 strv_free(arg_property);
1442 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;