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>
41 #include "bus-error.h"
44 #include "unit-name.h"
45 #include "cgroup-show.h"
46 #include "cgroup-util.h"
48 #include "event-util.h"
49 #include "path-util.h"
52 static char **arg_property = NULL;
53 static bool arg_all = false;
54 static bool arg_full = false;
55 static bool arg_no_pager = false;
56 static bool arg_legend = true;
57 static const char *arg_kill_who = NULL;
58 static int arg_signal = SIGTERM;
59 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
60 static char *arg_host = NULL;
61 static bool arg_read_only = false;
62 static bool arg_mkdir = false;
64 static void pager_open_if_enabled(void) {
66 /* Cache result before we open the pager */
73 static int list_machines(sd_bus *bus, char **args, unsigned n) {
74 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
75 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
76 const char *name, *class, *service, *object;
80 pager_open_if_enabled();
82 r = sd_bus_call_method(
84 "org.freedesktop.machine1",
85 "/org/freedesktop/machine1",
86 "org.freedesktop.machine1.Manager",
92 log_error("Could not get machines: %s", bus_error_message(&error, -r));
97 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
99 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
101 return bus_log_parse_error(r);
103 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
104 printf("%-32s %-9s %-16s\n", name, class, service);
109 return bus_log_parse_error(r);
111 r = sd_bus_message_exit_container(reply);
113 return bus_log_parse_error(r);
116 printf("\n%u machines listed.\n", k);
121 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
122 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
123 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
124 _cleanup_free_ char *path = NULL;
132 if (arg_transport == BUS_TRANSPORT_REMOTE)
135 path = unit_dbus_path_from_name(unit);
139 r = sd_bus_get_property(
141 "org.freedesktop.systemd1",
143 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
149 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
153 r = sd_bus_message_read(reply, "s", &cgroup);
155 return bus_log_parse_error(r);
160 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
164 arg_all * OUTPUT_SHOW_ALL |
165 arg_full * OUTPUT_FULL_WIDTH;
173 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
177 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
178 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
186 r = sd_bus_call_method(bus,
187 "org.freedesktop.machine1",
188 "/org/freedesktop/machine1",
189 "org.freedesktop.machine1.Manager",
190 "GetMachineAddresses",
197 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
199 return bus_log_parse_error(r);
201 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
205 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
207 r = sd_bus_message_read(reply, "i", &family);
209 return bus_log_parse_error(r);
211 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
213 return bus_log_parse_error(r);
215 fputs(prefix, stdout);
216 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
217 if (family == AF_INET6 && ifi > 0)
221 r = sd_bus_message_exit_container(reply);
223 return bus_log_parse_error(r);
225 if (prefix != prefix2)
229 return bus_log_parse_error(r);
231 r = sd_bus_message_exit_container(reply);
233 return bus_log_parse_error(r);
238 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
239 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
240 const char *k, *v, *pretty = NULL;
247 r = sd_bus_call_method(bus,
248 "org.freedesktop.machine1",
249 "/org/freedesktop/machine1",
250 "org.freedesktop.machine1.Manager",
251 "GetMachineOSRelease",
258 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
260 return bus_log_parse_error(r);
262 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
263 if (streq(k, "PRETTY_NAME"))
268 return bus_log_parse_error(r);
270 r = sd_bus_message_exit_container(reply);
272 return bus_log_parse_error(r);
275 printf("%s%s\n", prefix, pretty);
280 typedef struct MachineStatusInfo {
286 char *root_directory;
293 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
294 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
295 char since2[FORMAT_TIMESTAMP_MAX], *s2;
300 fputs(strna(i->name), stdout);
302 if (!sd_id128_equal(i->id, SD_ID128_NULL))
303 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
307 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
308 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
311 printf("\t Since: %s; %s\n", s2, s1);
313 printf("\t Since: %s\n", s2);
316 _cleanup_free_ char *t = NULL;
318 printf("\t Leader: %u", (unsigned) i->leader);
320 get_process_comm(i->leader, &t);
328 printf("\t Service: %s", i->service);
331 printf("; class %s", i->class);
335 printf("\t Class: %s\n", i->class);
337 if (i->root_directory)
338 printf("\t Root: %s\n", i->root_directory);
340 if (i->n_netif > 0) {
343 fputs("\t Iface:", stdout);
345 for (c = 0; c < i->n_netif; c++) {
346 char name[IF_NAMESIZE+1] = "";
348 if (if_indextoname(i->netif[c], name)) {
357 printf(" %i", i->netif[c]);
363 print_addresses(bus, i->name, ifi,
367 print_os_release(bus, i->name, "\t OS: ");
370 printf("\t Unit: %s\n", i->unit);
371 show_unit_cgroup(bus, i->unit, i->leader);
375 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
376 MachineStatusInfo *i = userdata;
381 assert_cc(sizeof(int32_t) == sizeof(int));
382 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
388 i->n_netif = l / sizeof(int32_t);
389 i->netif = memdup(v, l);
396 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
398 static const struct bus_properties_map map[] = {
399 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
400 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
401 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
402 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
403 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
404 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
405 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
406 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
407 { "NetworkInterfaces", "ai", map_netif, 0 },
411 MachineStatusInfo info = {};
417 r = bus_map_all_properties(bus,
418 "org.freedesktop.machine1",
423 return log_error_errno(r, "Could not get properties: %m");
429 print_machine_status_info(bus, &info);
435 free(info.root_directory);
441 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
449 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
451 log_error_errno(r, "Could not get properties: %m");
456 static int show(sd_bus *bus, char **args, unsigned n) {
457 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
458 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
461 bool properties, new_line = false;
466 properties = !strstr(args[0], "status");
468 pager_open_if_enabled();
470 if (properties && n <= 1) {
472 /* If no argument is specified, inspect the manager
474 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
479 for (i = 1; i < n; i++) {
480 const char *path = NULL;
482 r = sd_bus_call_method(
484 "org.freedesktop.machine1",
485 "/org/freedesktop/machine1",
486 "org.freedesktop.machine1.Manager",
492 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
496 r = sd_bus_message_read(reply, "o", &path);
498 return bus_log_parse_error(r);
501 r = show_properties(bus, path, &new_line);
503 r = show_info(args[0], bus, path, &new_line);
509 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
510 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
516 arg_kill_who = "all";
518 for (i = 1; i < n; i++) {
521 r = sd_bus_call_method(
523 "org.freedesktop.machine1",
524 "/org/freedesktop/machine1",
525 "org.freedesktop.machine1.Manager",
529 "ssi", args[i], arg_kill_who, arg_signal);
531 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
539 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
540 arg_kill_who = "leader";
541 arg_signal = SIGINT; /* sysvinit + systemd */
543 return kill_machine(bus, args, n);
546 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
547 arg_kill_who = "leader";
548 arg_signal = SIGRTMIN+4; /* only systemd */
550 return kill_machine(bus, args, n);
553 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
554 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
559 for (i = 1; i < n; i++) {
562 r = sd_bus_call_method(
564 "org.freedesktop.machine1",
565 "/org/freedesktop/machine1",
566 "org.freedesktop.machine1.Manager",
572 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
580 static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
581 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
582 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
591 r = sd_bus_call_method(
593 "org.freedesktop.machine1",
594 "/org/freedesktop/machine1",
595 "org.freedesktop.machine1.Manager",
601 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
605 r = sd_bus_message_read(reply, "o", &object);
607 return bus_log_parse_error(r);
609 r = sd_bus_get_property(
611 "org.freedesktop.machine1",
613 "org.freedesktop.machine1.Machine",
619 return log_error_errno(r, "Failed to retrieve PID of leader: %m");
621 r = sd_bus_message_read(reply2, "u", &leader);
623 return bus_log_parse_error(r);
629 static int bind_mount(sd_bus *bus, char **args, unsigned n) {
630 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
634 bool mount_slave_created = false, mount_slave_mounted = false,
635 mount_tmp_created = false, mount_tmp_mounted = false,
636 mount_outside_created = false, mount_outside_mounted = false;
639 /* One day, when bind mounting /proc/self/fd/n works across
640 * namespace boundaries we should rework this logic to make
644 log_error("Too many arguments.");
648 dest = args[3] ?: args[2];
649 if (!path_is_absolute(dest)) {
650 log_error("Destination path not absolute.");
654 p = strappenda("/run/systemd/nspawn/propagate/", args[1], "/");
655 if (access(p, F_OK) < 0) {
656 log_error("Container does not allow propagation of mount points.");
660 r = machine_get_leader(bus, args[1], &leader);
664 /* Our goal is to install a new bind mount into the container,
665 possibly read-only. This is irritatingly complex
666 unfortunately, currently.
668 First, we start by creating a private playground in /tmp,
669 that we can mount MS_SLAVE. (Which is necessary, since
670 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
673 if (!mkdtemp(mount_slave))
674 return log_error_errno(errno, "Failed to create playground: %m");
676 mount_slave_created = true;
678 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
679 r = log_error_errno(errno, "Failed to make bind mount: %m");
683 mount_slave_mounted = true;
685 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
686 r = log_error_errno(errno, "Failed to remount slave: %m");
690 /* Second, we mount the source directory to a directory inside
691 of our MS_SLAVE playground. */
692 mount_tmp = strappenda(mount_slave, "/mount");
693 if (mkdir(mount_tmp, 0700) < 0) {
694 r = log_error_errno(errno, "Failed to create temporary mount: %m");
698 mount_tmp_created = true;
700 if (mount(args[2], mount_tmp, NULL, MS_BIND, NULL) < 0) {
701 r = log_error_errno(errno, "Failed to overmount: %m");
705 mount_tmp_mounted = true;
707 /* Third, we remount the new bind mount read-only if requested. */
709 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
710 r = log_error_errno(errno, "Failed to mark read-only: %m");
714 /* Fourth, we move the new bind mount into the propagation
715 * directory. This way it will appear there read-only
718 mount_outside = strappenda("/run/systemd/nspawn/propagate/", args[1], "/XXXXXX");
719 if (!mkdtemp(mount_outside)) {
720 r = log_error_errno(errno, "Cannot create propagation directory: %m");
724 mount_outside_created = true;
726 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
727 r = log_error_errno(errno, "Failed to move: %m");
731 mount_outside_mounted = true;
732 mount_tmp_mounted = false;
734 (void) rmdir(mount_tmp);
735 mount_tmp_created = false;
737 (void) umount(mount_slave);
738 mount_slave_mounted = false;
740 (void) rmdir(mount_slave);
741 mount_slave_created = false;
745 r = log_error_errno(errno, "Failed to fork(): %m");
750 const char *mount_inside;
754 q = procfs_file_alloca(leader, "ns/mnt");
755 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
757 log_error_errno(errno, "Failed to open mount namespace of leader: %m");
761 if (setns(mntfd, CLONE_NEWNS) < 0) {
762 log_error_errno(errno, "Failed to join namespace of leader: %m");
769 /* Fifth, move the mount to the right place inside */
770 mount_inside = strappenda("/run/systemd/nspawn/incoming/", basename(mount_outside));
771 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
772 log_error_errno(errno, "Failed to mount: %m");
779 r = wait_for_terminate(child, &si);
781 log_error_errno(r, "Failed to wait for client: %m");
784 if (si.si_code != CLD_EXITED) {
785 log_error("Client died abnormally.");
789 if (si.si_status != EXIT_SUCCESS) {
797 if (mount_outside_mounted)
798 umount(mount_outside);
799 if (mount_outside_created)
800 rmdir(mount_outside);
802 if (mount_tmp_mounted)
804 if (mount_tmp_created)
807 if (mount_slave_mounted)
809 if (mount_slave_created)
815 static int openpt_in_namespace(pid_t pid, int flags) {
816 _cleanup_close_pair_ int pair[2] = { -1, -1 };
817 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
819 struct cmsghdr cmsghdr;
820 uint8_t buf[CMSG_SPACE(sizeof(int))];
823 .msg_control = &control,
824 .msg_controllen = sizeof(control),
826 struct cmsghdr *cmsg;
831 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
835 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
843 pair[0] = safe_close(pair[0]);
845 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
849 master = posix_openpt(flags);
853 cmsg = CMSG_FIRSTHDR(&mh);
854 cmsg->cmsg_level = SOL_SOCKET;
855 cmsg->cmsg_type = SCM_RIGHTS;
856 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
857 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
859 mh.msg_controllen = cmsg->cmsg_len;
861 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
867 pair[1] = safe_close(pair[1]);
869 r = wait_for_terminate(child, &si);
872 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
875 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
878 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
879 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
883 fds = (int*) CMSG_DATA(cmsg);
884 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
887 close_many(fds, n_fds);
900 static int login_machine(sd_bus *bus, char **args, unsigned n) {
901 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
902 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
903 _cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
904 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
905 _cleanup_event_unref_ sd_event *event = NULL;
906 _cleanup_close_ int master = -1;
907 _cleanup_free_ char *getty = NULL;
916 if (arg_transport != BUS_TRANSPORT_LOCAL) {
917 log_error("Login only supported on local machines.");
921 r = sd_event_default(&event);
923 return log_error_errno(r, "Failed to get event loop: %m");
925 r = sd_bus_attach_event(bus, event, 0);
927 return log_error_errno(r, "Failed to attach bus to event loop: %m");
929 r = machine_get_leader(bus, args[1], &leader);
933 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
935 return log_error_errno(master, "Failed to acquire pseudo tty: %m");
937 pty = ptsname(master);
939 return log_error_errno(errno, "Failed to get pty name: %m");
941 p = startswith(pty, "/dev/pts/");
943 log_error("Invalid pty name %s.", pty);
947 r = sd_bus_open_system_container(&container_bus, args[1]);
949 return log_error_errno(r, "Failed to get container bus: %m");
951 getty = strjoin("container-getty@", p, ".service", NULL);
955 if (unlockpt(master) < 0)
956 return log_error_errno(errno, "Failed to unlock tty: %m");
958 r = sd_bus_call_method(container_bus,
959 "org.freedesktop.systemd1",
960 "/org/freedesktop/systemd1",
961 "org.freedesktop.systemd1.Manager",
964 "ss", getty, "replace");
966 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
970 container_bus = sd_bus_unref(container_bus);
972 assert_se(sigemptyset(&mask) == 0);
973 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
974 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
976 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
978 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
979 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
981 r = pty_forward_new(event, master, &forward);
983 return log_error_errno(r, "Failed to create PTY forwarder: %m");
985 r = sd_event_loop(event);
987 return log_error_errno(r, "Failed to run event loop: %m");
989 forward = pty_forward_free(forward);
993 log_info("Connection to container %s terminated.", args[1]);
995 sd_event_get_exit_code(event, &ret);
999 static void help(void) {
1000 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1001 "Send control commands to or query the virtual machine and container registration manager.\n\n"
1002 " -h --help Show this help\n"
1003 " --version Show package version\n"
1004 " --no-pager Do not pipe output into a pager\n"
1005 " --no-legend Do not show the headers and footers\n"
1006 " -H --host=[USER@]HOST Operate on remote host\n"
1007 " -M --machine=CONTAINER Operate on local container\n"
1008 " -p --property=NAME Show only properties by this name\n"
1009 " -a --all Show all properties, including empty ones\n"
1010 " -l --full Do not ellipsize output\n"
1011 " --kill-who=WHO Who to send signal to\n"
1012 " -s --signal=SIGNAL Which signal to send\n"
1013 " --read-only Create read-only bind mount\n"
1014 " --mkdir Create directory before bind mounting, if missing\n\n"
1016 " list List running VMs and containers\n"
1017 " status NAME... Show VM/container status\n"
1018 " show NAME... Show properties of one or more VMs/containers\n"
1019 " login NAME Get a login prompt on a container\n"
1020 " poweroff NAME... Power off one or more containers\n"
1021 " reboot NAME... Reboot one or more containers\n"
1022 " kill NAME... Send signal to processes of a VM/container\n"
1023 " terminate NAME... Terminate one or more VMs/containers\n"
1024 " bind NAME PATH [PATH] Bind mount a path from the host into a container\n",
1025 program_invocation_short_name);
1028 static int parse_argv(int argc, char *argv[]) {
1031 ARG_VERSION = 0x100,
1039 static const struct option options[] = {
1040 { "help", no_argument, NULL, 'h' },
1041 { "version", no_argument, NULL, ARG_VERSION },
1042 { "property", required_argument, NULL, 'p' },
1043 { "all", no_argument, NULL, 'a' },
1044 { "full", no_argument, NULL, 'l' },
1045 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1046 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1047 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1048 { "signal", required_argument, NULL, 's' },
1049 { "host", required_argument, NULL, 'H' },
1050 { "machine", required_argument, NULL, 'M' },
1051 { "read-only", no_argument, NULL, ARG_READ_ONLY },
1052 { "mkdir", no_argument, NULL, ARG_MKDIR },
1061 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1070 puts(PACKAGE_STRING);
1071 puts(SYSTEMD_FEATURES);
1075 r = strv_extend(&arg_property, optarg);
1079 /* If the user asked for a particular
1080 * property, show it to him, even if it is
1094 arg_no_pager = true;
1102 arg_kill_who = optarg;
1106 arg_signal = signal_from_string_try_harder(optarg);
1107 if (arg_signal < 0) {
1108 log_error("Failed to parse signal string %s.", optarg);
1114 arg_transport = BUS_TRANSPORT_REMOTE;
1119 arg_transport = BUS_TRANSPORT_CONTAINER;
1124 arg_read_only = true;
1135 assert_not_reached("Unhandled option");
1141 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
1143 static const struct {
1151 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1153 { "list", LESS, 1, list_machines },
1154 { "status", MORE, 2, show },
1155 { "show", MORE, 1, show },
1156 { "terminate", MORE, 2, terminate_machine },
1157 { "reboot", MORE, 2, reboot_machine },
1158 { "poweroff", MORE, 2, poweroff_machine },
1159 { "kill", MORE, 2, kill_machine },
1160 { "login", MORE, 2, login_machine },
1161 { "bind", MORE, 3, bind_mount },
1170 left = argc - optind;
1173 /* Special rule: no arguments means "list" */
1176 if (streq(argv[optind], "help")) {
1181 for (i = 0; i < ELEMENTSOF(verbs); i++)
1182 if (streq(argv[optind], verbs[i].verb))
1185 if (i >= ELEMENTSOF(verbs)) {
1186 log_error("Unknown operation %s", argv[optind]);
1191 switch (verbs[i].argc_cmp) {
1194 if (left != verbs[i].argc) {
1195 log_error("Invalid number of arguments.");
1202 if (left < verbs[i].argc) {
1203 log_error("Too few arguments.");
1210 if (left > verbs[i].argc) {
1211 log_error("Too many arguments.");
1218 assert_not_reached("Unknown comparison operator.");
1221 return verbs[i].dispatch(bus, argv + optind, left);
1224 int main(int argc, char*argv[]) {
1225 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1228 setlocale(LC_ALL, "");
1229 log_parse_environment();
1232 r = parse_argv(argc, argv);
1236 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1238 log_error_errno(r, "Failed to create bus connection: %m");
1242 r = machinectl_main(bus, argc, argv);
1247 strv_free(arg_property);
1249 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;