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 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
124 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
125 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
126 _cleanup_free_ char *path = NULL;
134 if (arg_transport == BUS_TRANSPORT_REMOTE)
137 path = unit_dbus_path_from_name(unit);
141 r = sd_bus_get_property(
143 "org.freedesktop.systemd1",
145 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
151 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
155 r = sd_bus_message_read(reply, "s", &cgroup);
157 return bus_log_parse_error(r);
162 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
166 arg_all * OUTPUT_SHOW_ALL |
167 arg_full * OUTPUT_FULL_WIDTH;
175 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
179 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
180 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
188 r = sd_bus_call_method(bus,
189 "org.freedesktop.machine1",
190 "/org/freedesktop/machine1",
191 "org.freedesktop.machine1.Manager",
192 "GetMachineAddresses",
199 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
201 return bus_log_parse_error(r);
203 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
207 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
209 r = sd_bus_message_read(reply, "i", &family);
211 return bus_log_parse_error(r);
213 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
215 return bus_log_parse_error(r);
217 fputs(prefix, stdout);
218 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
219 if (family == AF_INET6 && ifi > 0)
223 r = sd_bus_message_exit_container(reply);
225 return bus_log_parse_error(r);
227 if (prefix != prefix2)
231 return bus_log_parse_error(r);
233 r = sd_bus_message_exit_container(reply);
235 return bus_log_parse_error(r);
240 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
241 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
242 const char *k, *v, *pretty = NULL;
249 r = sd_bus_call_method(bus,
250 "org.freedesktop.machine1",
251 "/org/freedesktop/machine1",
252 "org.freedesktop.machine1.Manager",
253 "GetMachineOSRelease",
260 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
262 return bus_log_parse_error(r);
264 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
265 if (streq(k, "PRETTY_NAME"))
270 return bus_log_parse_error(r);
272 r = sd_bus_message_exit_container(reply);
274 return bus_log_parse_error(r);
277 printf("%s%s\n", prefix, pretty);
282 typedef struct MachineStatusInfo {
288 char *root_directory;
295 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
296 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
297 char since2[FORMAT_TIMESTAMP_MAX], *s2;
302 fputs(strna(i->name), stdout);
304 if (!sd_id128_equal(i->id, SD_ID128_NULL))
305 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
309 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
310 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
313 printf("\t Since: %s; %s\n", s2, s1);
315 printf("\t Since: %s\n", s2);
318 _cleanup_free_ char *t = NULL;
320 printf("\t Leader: %u", (unsigned) i->leader);
322 get_process_comm(i->leader, &t);
330 printf("\t Service: %s", i->service);
333 printf("; class %s", i->class);
337 printf("\t Class: %s\n", i->class);
339 if (i->root_directory)
340 printf("\t Root: %s\n", i->root_directory);
342 if (i->n_netif > 0) {
345 fputs("\t Iface:", stdout);
347 for (c = 0; c < i->n_netif; c++) {
348 char name[IF_NAMESIZE+1] = "";
350 if (if_indextoname(i->netif[c], name)) {
359 printf(" %i", i->netif[c]);
365 print_addresses(bus, i->name, ifi,
369 print_os_release(bus, i->name, "\t OS: ");
372 printf("\t Unit: %s\n", i->unit);
373 show_unit_cgroup(bus, i->unit, i->leader);
377 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
378 MachineStatusInfo *i = userdata;
383 assert_cc(sizeof(int32_t) == sizeof(int));
384 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
390 i->n_netif = l / sizeof(int32_t);
391 i->netif = memdup(v, l);
398 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
400 static const struct bus_properties_map map[] = {
401 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
402 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
403 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
404 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
405 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
406 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
407 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
408 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
409 { "NetworkInterfaces", "ai", map_netif, 0 },
413 MachineStatusInfo info = {};
419 r = bus_map_all_properties(bus,
420 "org.freedesktop.machine1",
425 return log_error_errno(r, "Could not get properties: %m");
431 print_machine_status_info(bus, &info);
437 free(info.root_directory);
443 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
451 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
453 log_error_errno(r, "Could not get properties: %m");
458 static int show(sd_bus *bus, char **args, unsigned n) {
459 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
460 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
463 bool properties, new_line = false;
468 properties = !strstr(args[0], "status");
470 pager_open_if_enabled();
472 if (properties && n <= 1) {
474 /* If no argument is specified, inspect the manager
476 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
481 for (i = 1; i < n; i++) {
482 const char *path = NULL;
484 r = sd_bus_call_method(
486 "org.freedesktop.machine1",
487 "/org/freedesktop/machine1",
488 "org.freedesktop.machine1.Manager",
494 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
498 r = sd_bus_message_read(reply, "o", &path);
500 return bus_log_parse_error(r);
503 r = show_properties(bus, path, &new_line);
505 r = show_info(args[0], bus, path, &new_line);
511 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
512 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
518 arg_kill_who = "all";
520 for (i = 1; i < n; i++) {
523 r = sd_bus_call_method(
525 "org.freedesktop.machine1",
526 "/org/freedesktop/machine1",
527 "org.freedesktop.machine1.Manager",
531 "ssi", args[i], arg_kill_who, arg_signal);
533 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
541 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
542 arg_kill_who = "leader";
543 arg_signal = SIGINT; /* sysvinit + systemd */
545 return kill_machine(bus, args, n);
548 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
549 arg_kill_who = "leader";
550 arg_signal = SIGRTMIN+4; /* only systemd */
552 return kill_machine(bus, args, n);
555 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
556 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
561 for (i = 1; i < n; i++) {
564 r = sd_bus_call_method(
566 "org.freedesktop.machine1",
567 "/org/freedesktop/machine1",
568 "org.freedesktop.machine1.Manager",
574 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
582 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
583 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
584 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
593 r = sd_bus_call_method(
595 "org.freedesktop.machine1",
596 "/org/freedesktop/machine1",
597 "org.freedesktop.machine1.Manager",
603 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
607 r = sd_bus_message_read(reply, "o", &object);
609 return bus_log_parse_error(r);
611 r = sd_bus_get_property(
613 "org.freedesktop.machine1",
615 "org.freedesktop.machine1.Machine",
621 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
623 r = sd_bus_message_read(reply2, "u", &leader);
625 return bus_log_parse_error(r);
631 static int copy_files(sd_bus *bus, char **args, unsigned n) {
632 char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
633 _cleanup_close_ int hostfd = -1;
640 log_error("Too many arguments.");
644 copy_from = streq(args[0], "copy-from");
645 dest = args[3] ?: args[2];
646 host_path = strdupa(copy_from ? dest : args[2]);
647 container_path = strdupa(copy_from ? args[2] : dest);
649 if (!path_is_absolute(container_path)) {
650 log_error("Container path not absolute.");
654 t = strdup(host_path);
655 host_basename = basename(t);
656 host_dirname = dirname(host_path);
658 t = strdup(container_path);
659 container_basename = basename(t);
660 container_dirname = dirname(container_path);
662 r = machine_get_leader(bus, args[1], &leader);
666 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
668 return log_error_errno(errno, "Failed to open source directory: %m");
672 return log_error_errno(errno, "Failed to fork(): %m");
679 q = procfs_file_alloca(leader, "ns/mnt");
680 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
682 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
686 if (setns(mntfd, CLONE_NEWNS) < 0) {
687 log_error_errno(errno, "Failed to join namespace of leader: %m");
691 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
692 if (containerfd < 0) {
693 log_error_errno(errno, "Failed top open destination directory: %m");
698 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
700 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
702 log_error_errno(errno, "Failed to copy tree: %m");
709 r = wait_for_terminate(child, &si);
711 return log_error_errno(r, "Failed to wait for client: %m");
712 if (si.si_code != CLD_EXITED) {
713 log_error("Client died abnormally.");
716 if (si.si_status != EXIT_SUCCESS)
722 static int bind_mount(sd_bus *bus, char **args, unsigned n) {
723 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
727 bool mount_slave_created = false, mount_slave_mounted = false,
728 mount_tmp_created = false, mount_tmp_mounted = false,
729 mount_outside_created = false, mount_outside_mounted = false;
732 /* One day, when bind mounting /proc/self/fd/n works across
733 * namespace boundaries we should rework this logic to make
737 log_error("Too many arguments.");
741 dest = args[3] ?: args[2];
742 if (!path_is_absolute(dest)) {
743 log_error("Destination path not absolute.");
747 p = strappenda("/run/systemd/nspawn/propagate/", args[1], "/");
748 if (access(p, F_OK) < 0) {
749 log_error("Container does not allow propagation of mount points.");
753 r = machine_get_leader(bus, args[1], &leader);
757 /* Our goal is to install a new bind mount into the container,
758 possibly read-only. This is irritatingly complex
759 unfortunately, currently.
761 First, we start by creating a private playground in /tmp,
762 that we can mount MS_SLAVE. (Which is necessary, since
763 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
766 if (!mkdtemp(mount_slave))
767 return log_error_errno(errno, "Failed to create playground: %m");
769 mount_slave_created = true;
771 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
772 r = log_error_errno(errno, "Failed to make bind mount: %m");
776 mount_slave_mounted = true;
778 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
779 r = log_error_errno(errno, "Failed to remount slave: %m");
783 /* Second, we mount the source directory to a directory inside
784 of our MS_SLAVE playground. */
785 mount_tmp = strappenda(mount_slave, "/mount");
786 if (mkdir(mount_tmp, 0700) < 0) {
787 r = log_error_errno(errno, "Failed to create temporary mount: %m");
791 mount_tmp_created = true;
793 if (mount(args[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
794 r = log_error_errno(errno, "Failed to overmount: %m");
798 mount_tmp_mounted = true;
800 /* Third, we remount the new bind mount read-only if requested. */
802 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
803 r = log_error_errno(errno, "Failed to mark read-only: %m");
807 /* Fourth, we move the new bind mount into the propagation
808 * directory. This way it will appear there read-only
811 mount_outside = strappenda("/run/systemd/nspawn/propagate/", args[1], "/XXXXXX");
812 if (!mkdtemp(mount_outside)) {
813 r = log_error_errno(errno, "Cannot create propagation directory: %m");
817 mount_outside_created = true;
819 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
820 r = log_error_errno(errno, "Failed to move: %m");
824 mount_outside_mounted = true;
825 mount_tmp_mounted = false;
827 (void) rmdir(mount_tmp);
828 mount_tmp_created = false;
830 (void) umount(mount_slave);
831 mount_slave_mounted = false;
833 (void) rmdir(mount_slave);
834 mount_slave_created = false;
838 r = log_error_errno(errno, "Failed to fork(): %m");
843 const char *mount_inside;
847 q = procfs_file_alloca(leader, "ns/mnt");
848 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
850 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
854 if (setns(mntfd, CLONE_NEWNS) < 0) {
855 log_error_errno(errno, "Failed to join namespace of leader: %m");
862 /* Fifth, move the mount to the right place inside */
863 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
864 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
865 log_error_errno(errno, "Failed to mount: %m");
872 r = wait_for_terminate(child, &si);
874 log_error_errno(r, "Failed to wait for client: %m");
877 if (si.si_code != CLD_EXITED) {
878 log_error("Client died abnormally.");
882 if (si.si_status != EXIT_SUCCESS) {
890 if (mount_outside_mounted)
891 umount(mount_outside);
892 if (mount_outside_created)
893 rmdir(mount_outside);
895 if (mount_tmp_mounted)
897 if (mount_tmp_created)
900 if (mount_slave_mounted)
902 if (mount_slave_created)
908 static int openpt_in_namespace(pid_t pid, int flags) {
909 _cleanup_close_pair_ int pair[2] = { -1, -1 };
910 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
912 struct cmsghdr cmsghdr;
913 uint8_t buf[CMSG_SPACE(sizeof(int))];
916 .msg_control = &control,
917 .msg_controllen = sizeof(control),
919 struct cmsghdr *cmsg;
924 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
928 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
936 pair[0] = safe_close(pair[0]);
938 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
942 master = posix_openpt(flags);
946 cmsg = CMSG_FIRSTHDR(&mh);
947 cmsg->cmsg_level = SOL_SOCKET;
948 cmsg->cmsg_type = SCM_RIGHTS;
949 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
950 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
952 mh.msg_controllen = cmsg->cmsg_len;
954 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
960 pair[1] = safe_close(pair[1]);
962 r = wait_for_terminate(child, &si);
965 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
968 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
971 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
972 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
976 fds = (int*) CMSG_DATA(cmsg);
977 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
980 close_many(fds, n_fds);
993 static int login_machine(sd_bus *bus, char **args, unsigned n) {
994 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
995 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
996 _cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
997 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
998 _cleanup_event_unref_ sd_event *event = NULL;
999 _cleanup_close_ int master = -1;
1000 _cleanup_free_ char *getty = NULL;
1001 const char *pty, *p;
1009 if (arg_transport != BUS_TRANSPORT_LOCAL) {
1010 log_error("Login only supported on local machines.");
1014 r = sd_event_default(&event);
1016 return log_error_errno(r, "Failed to get event loop: %m");
1018 r = sd_bus_attach_event(bus, event, 0);
1020 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1022 r = machine_get_leader(bus, args[1], &leader);
1026 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
1028 return log_error_errno(master, "Failed to acquire pseudo tty: %m");
1030 pty = ptsname(master);
1032 return log_error_errno(errno, "Failed to get pty name: %m");
1034 p = startswith(pty, "/dev/pts/");
1036 log_error("Invalid pty name %s.", pty);
1040 r = sd_bus_open_system_container(&container_bus, args[1]);
1042 return log_error_errno(r, "Failed to get container bus: %m");
1044 getty = strjoin("container-getty@", p, ".service", NULL);
1048 if (unlockpt(master) < 0)
1049 return log_error_errno(errno, "Failed to unlock tty: %m");
1051 r = sd_bus_call_method(container_bus,
1052 "org.freedesktop.systemd1",
1053 "/org/freedesktop/systemd1",
1054 "org.freedesktop.systemd1.Manager",
1057 "ss", getty, "replace");
1059 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
1063 container_bus = sd_bus_unref(container_bus);
1065 assert_se(sigemptyset(&mask) == 0);
1066 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
1067 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
1069 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
1071 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
1072 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
1074 r = pty_forward_new(event, master, &forward);
1076 return log_error_errno(r, "Failed to create PTY forwarder: %m");
1078 r = sd_event_loop(event);
1080 return log_error_errno(r, "Failed to run event loop: %m");
1082 forward = pty_forward_free(forward);
1084 fputc('\n', stdout);
1086 log_info("Connection to container %s terminated.", args[1]);
1088 sd_event_get_exit_code(event, &ret);
1092 static void help(void) {
1093 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1094 "Send control commands to or query the virtual machine and container\n"
1095 "registration manager.\n\n"
1096 " -h --help Show this help\n"
1097 " --version Show package version\n"
1098 " --no-pager Do not pipe output into a pager\n"
1099 " --no-legend Do not show the headers and footers\n"
1100 " -H --host=[USER@]HOST Operate on remote host\n"
1101 " -M --machine=CONTAINER Operate on local container\n"
1102 " -p --property=NAME Show only properties by this name\n"
1103 " -a --all Show all properties, including empty ones\n"
1104 " -l --full Do not ellipsize output\n"
1105 " --kill-who=WHO Who to send signal to\n"
1106 " -s --signal=SIGNAL Which signal to send\n"
1107 " --read-only Create read-only bind mount\n"
1108 " --mkdir Create directory before bind mounting, if missing\n\n"
1110 " list List running VMs and containers\n"
1111 " status NAME... Show VM/container status\n"
1112 " show NAME... Show properties of one or more VMs/containers\n"
1113 " login NAME Get a login prompt on a container\n"
1114 " poweroff NAME... Power off one or more containers\n"
1115 " reboot NAME... Reboot one or more containers\n"
1116 " kill NAME... Send signal to processes of a VM/container\n"
1117 " terminate NAME... Terminate one or more VMs/containers\n"
1118 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
1119 " copy-to NAME PATH [PATH] Copy files from the host to a container\n"
1120 " copy-from NAME PATH [PATH] Copy files from a container to the host\n",
1121 program_invocation_short_name);
1124 static int parse_argv(int argc, char *argv[]) {
1127 ARG_VERSION = 0x100,
1135 static const struct option options[] = {
1136 { "help", no_argument, NULL, 'h' },
1137 { "version", no_argument, NULL, ARG_VERSION },
1138 { "property", required_argument, NULL, 'p' },
1139 { "all", no_argument, NULL, 'a' },
1140 { "full", no_argument, NULL, 'l' },
1141 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1142 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1143 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1144 { "signal", required_argument, NULL, 's' },
1145 { "host", required_argument, NULL, 'H' },
1146 { "machine", required_argument, NULL, 'M' },
1147 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1148 { "mkdir", no_argument, NULL, ARG_MKDIR },
1157 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1166 puts(PACKAGE_STRING);
1167 puts(SYSTEMD_FEATURES);
1171 r = strv_extend(&arg_property, optarg);
1175 /* If the user asked for a particular
1176 * property, show it to him, even if it is
1190 arg_no_pager = true;
1198 arg_kill_who = optarg;
1202 arg_signal = signal_from_string_try_harder(optarg);
1203 if (arg_signal < 0) {
1204 log_error("Failed to parse signal string %s.", optarg);
1210 arg_transport = BUS_TRANSPORT_REMOTE;
1215 arg_transport = BUS_TRANSPORT_CONTAINER;
1220 arg_read_only = true;
1231 assert_not_reached("Unhandled option");
1237 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
1239 static const struct {
1247 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1249 { "list", LESS, 1, list_machines },
1250 { "status", MORE, 2, show },
1251 { "show", MORE, 1, show },
1252 { "terminate", MORE, 2, terminate_machine },
1253 { "reboot", MORE, 2, reboot_machine },
1254 { "poweroff", MORE, 2, poweroff_machine },
1255 { "kill", MORE, 2, kill_machine },
1256 { "login", MORE, 2, login_machine },
1257 { "bind", MORE, 3, bind_mount },
1258 { "copy-to", MORE, 3, copy_files },
1259 { "copy-from", MORE, 3, copy_files },
1268 left = argc - optind;
1271 /* Special rule: no arguments means "list" */
1274 if (streq(argv[optind], "help")) {
1279 for (i = 0; i < ELEMENTSOF(verbs); i++)
1280 if (streq(argv[optind], verbs[i].verb))
1283 if (i >= ELEMENTSOF(verbs)) {
1284 log_error("Unknown operation %s", argv[optind]);
1289 switch (verbs[i].argc_cmp) {
1292 if (left != verbs[i].argc) {
1293 log_error("Invalid number of arguments.");
1300 if (left < verbs[i].argc) {
1301 log_error("Too few arguments.");
1308 if (left > verbs[i].argc) {
1309 log_error("Too many arguments.");
1316 assert_not_reached("Unknown comparison operator.");
1319 return verbs[i].dispatch(bus, argv + optind, left);
1322 int main(int argc, char*argv[]) {
1323 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1326 setlocale(LC_ALL, "");
1327 log_parse_environment();
1330 r = parse_argv(argc, argv);
1334 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1336 log_error_errno(r, "Failed to create bus connection: %m");
1340 r = machinectl_main(bus, argc, argv);
1345 strv_free(arg_property);
1347 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;