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>
40 #include "bus-error.h"
43 #include "unit-name.h"
44 #include "cgroup-show.h"
45 #include "cgroup-util.h"
48 static char **arg_property = NULL;
49 static bool arg_all = false;
50 static bool arg_full = false;
51 static bool arg_no_pager = false;
52 static bool arg_legend = true;
53 static const char *arg_kill_who = NULL;
54 static int arg_signal = SIGTERM;
55 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
56 static char *arg_host = NULL;
58 static void pager_open_if_enabled(void) {
60 /* Cache result before we open the pager */
67 static int list_machines(sd_bus *bus, char **args, unsigned n) {
68 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
69 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
70 const char *name, *class, *service, *object;
74 pager_open_if_enabled();
76 r = sd_bus_call_method(
78 "org.freedesktop.machine1",
79 "/org/freedesktop/machine1",
80 "org.freedesktop.machine1.Manager",
86 log_error("Could not get machines: %s", bus_error_message(&error, -r));
91 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
93 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
95 return bus_log_parse_error(r);
97 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
98 printf("%-32s %-9s %-16s\n", name, class, service);
103 return bus_log_parse_error(r);
105 r = sd_bus_message_exit_container(reply);
107 return bus_log_parse_error(r);
110 printf("\n%u machines listed.\n", k);
115 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
116 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
117 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
118 _cleanup_free_ char *path = NULL;
126 if (arg_transport == BUS_TRANSPORT_REMOTE)
129 path = unit_dbus_path_from_name(unit);
133 r = sd_bus_get_property(
135 "org.freedesktop.systemd1",
137 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
143 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
147 r = sd_bus_message_read(reply, "s", &cgroup);
149 return bus_log_parse_error(r);
154 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
158 arg_all * OUTPUT_SHOW_ALL |
159 arg_full * OUTPUT_FULL_WIDTH;
167 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
171 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
172 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
180 r = sd_bus_call_method(bus,
181 "org.freedesktop.machine1",
182 "/org/freedesktop/machine1",
183 "org.freedesktop.machine1.Manager",
184 "GetMachineAddresses",
191 r = sd_bus_message_enter_container(reply, 'a', "(yay)");
193 return bus_log_parse_error(r);
195 while ((r = sd_bus_message_enter_container(reply, 'r', "yay")) > 0) {
196 unsigned char family;
199 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
201 r = sd_bus_message_read(reply, "y", &family);
203 return bus_log_parse_error(r);
205 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
207 return bus_log_parse_error(r);
209 fputs(prefix, stdout);
210 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
211 if (family == AF_INET6 && ifi > 0)
215 r = sd_bus_message_exit_container(reply);
217 return bus_log_parse_error(r);
219 if (prefix != prefix2)
223 return bus_log_parse_error(r);
225 r = sd_bus_message_exit_container(reply);
227 return bus_log_parse_error(r);
232 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
233 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
234 const char *k, *v, *pretty = NULL;
241 r = sd_bus_call_method(bus,
242 "org.freedesktop.machine1",
243 "/org/freedesktop/machine1",
244 "org.freedesktop.machine1.Manager",
245 "GetMachineOSRelease",
252 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
254 return bus_log_parse_error(r);
256 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
257 if (streq(k, "PRETTY_NAME"))
262 return bus_log_parse_error(r);
264 r = sd_bus_message_exit_container(reply);
266 return bus_log_parse_error(r);
269 printf("%s%s\n", prefix, pretty);
274 typedef struct MachineStatusInfo {
280 char *root_directory;
287 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
288 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
289 char since2[FORMAT_TIMESTAMP_MAX], *s2;
294 fputs(strna(i->name), stdout);
296 if (!sd_id128_equal(i->id, SD_ID128_NULL))
297 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
301 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
302 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
305 printf("\t Since: %s; %s\n", s2, s1);
307 printf("\t Since: %s\n", s2);
310 _cleanup_free_ char *t = NULL;
312 printf("\t Leader: %u", (unsigned) i->leader);
314 get_process_comm(i->leader, &t);
322 printf("\t Service: %s", i->service);
325 printf("; class %s", i->class);
329 printf("\t Class: %s\n", i->class);
331 if (i->root_directory)
332 printf("\t Root: %s\n", i->root_directory);
334 if (i->n_netif > 0) {
337 fputs("\t Iface:", stdout);
339 for (c = 0; c < i->n_netif; c++) {
340 char name[IF_NAMESIZE+1] = "";
342 if (if_indextoname(i->netif[c], name)) {
351 printf(" %i", i->netif[c]);
357 print_addresses(bus, i->name, ifi,
361 print_os_release(bus, i->name, "\t OS: ");
364 printf("\t Unit: %s\n", i->unit);
365 show_unit_cgroup(bus, i->unit, i->leader);
369 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
370 MachineStatusInfo *i = userdata;
375 assert_cc(sizeof(int32_t) == sizeof(int));
376 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
380 i->n_netif = l / sizeof(int32_t);
381 i->netif = memdup(v, l);
388 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
390 static const struct bus_properties_map map[] = {
391 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
392 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
393 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
394 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
395 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
396 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
397 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
398 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
399 { "NetworkInterfaces", "ai", map_netif, 0 },
403 MachineStatusInfo info = {};
409 r = bus_map_all_properties(bus,
410 "org.freedesktop.machine1",
415 log_error("Could not get properties: %s", strerror(-r));
423 print_machine_status_info(bus, &info);
429 free(info.root_directory);
435 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
443 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
445 log_error("Could not get properties: %s", strerror(-r));
450 static int show(sd_bus *bus, char **args, unsigned n) {
451 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
452 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
455 bool properties, new_line = false;
460 properties = !strstr(args[0], "status");
462 pager_open_if_enabled();
464 if (properties && n <= 1) {
466 /* If no argument is specified, inspect the manager
468 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
473 for (i = 1; i < n; i++) {
474 const char *path = NULL;
476 r = sd_bus_call_method(
478 "org.freedesktop.machine1",
479 "/org/freedesktop/machine1",
480 "org.freedesktop.machine1.Manager",
486 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
490 r = sd_bus_message_read(reply, "o", &path);
492 return bus_log_parse_error(r);
495 r = show_properties(bus, path, &new_line);
497 r = show_info(args[0], bus, path, &new_line);
503 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
504 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
510 arg_kill_who = "all";
512 for (i = 1; i < n; i++) {
515 r = sd_bus_call_method(
517 "org.freedesktop.machine1",
518 "/org/freedesktop/machine1",
519 "org.freedesktop.machine1.Manager",
523 "ssi", args[i], arg_kill_who, arg_signal);
525 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
533 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
534 arg_kill_who = "leader";
535 arg_signal = SIGINT; /* sysvinit + systemd */
537 return kill_machine(bus, args, n);
540 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
541 arg_kill_who = "leader";
542 arg_signal = SIGRTMIN+4; /* only systemd */
544 return kill_machine(bus, args, n);
547 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
548 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
553 for (i = 1; i < n; i++) {
556 r = sd_bus_call_method(
558 "org.freedesktop.machine1",
559 "/org/freedesktop/machine1",
560 "org.freedesktop.machine1.Manager",
566 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
574 static int openpt_in_namespace(pid_t pid, int flags) {
575 _cleanup_close_pair_ int pair[2] = { -1, -1 };
576 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
578 struct cmsghdr cmsghdr;
579 uint8_t buf[CMSG_SPACE(sizeof(int))];
582 .msg_control = &control,
583 .msg_controllen = sizeof(control),
585 struct cmsghdr *cmsg;
590 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
594 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
602 pair[0] = safe_close(pair[0]);
604 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
608 master = posix_openpt(flags);
612 cmsg = CMSG_FIRSTHDR(&mh);
613 cmsg->cmsg_level = SOL_SOCKET;
614 cmsg->cmsg_type = SCM_RIGHTS;
615 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
616 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
618 mh.msg_controllen = cmsg->cmsg_len;
620 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
626 pair[1] = safe_close(pair[1]);
628 r = wait_for_terminate(child, &si);
631 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
634 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
637 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
638 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
642 fds = (int*) CMSG_DATA(cmsg);
643 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
646 close_many(fds, n_fds);
659 static int login_machine(sd_bus *bus, char **args, unsigned n) {
660 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
661 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
662 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
663 _cleanup_close_ int master = -1;
664 _cleanup_free_ char *getty = NULL;
665 const char *path, *pty, *p;
673 if (arg_transport != BUS_TRANSPORT_LOCAL) {
674 log_error("Login only supported on local machines.");
678 r = sd_bus_call_method(
680 "org.freedesktop.machine1",
681 "/org/freedesktop/machine1",
682 "org.freedesktop.machine1.Manager",
688 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
692 r = sd_bus_message_read(reply, "o", &path);
694 return bus_log_parse_error(r);
696 r = sd_bus_get_property(
698 "org.freedesktop.machine1",
700 "org.freedesktop.machine1.Machine",
706 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
710 r = sd_bus_message_read(reply2, "u", &leader);
712 return bus_log_parse_error(r);
714 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
716 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
720 pty = ptsname(master);
722 log_error("Failed to get pty name: %m");
726 p = startswith(pty, "/dev/pts/");
728 log_error("Invalid pty name %s.", pty);
732 r = sd_bus_open_system_container(&container_bus, args[1]);
734 log_error("Failed to get container bus: %s", strerror(-r));
738 getty = strjoin("container-getty@", p, ".service", NULL);
742 if (unlockpt(master) < 0) {
743 log_error("Failed to unlock tty: %m");
747 r = sd_bus_call_method(container_bus,
748 "org.freedesktop.systemd1",
749 "/org/freedesktop/systemd1",
750 "org.freedesktop.systemd1.Manager",
753 "ss", getty, "replace");
755 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
759 container_bus = sd_bus_unref(container_bus);
761 assert_se(sigemptyset(&mask) == 0);
762 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
763 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
765 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
767 r = process_pty(master, &mask, 0, 0);
769 log_error("Failed to process pseudo tty: %s", strerror(-r));
775 log_info("Connection to container %s terminated.", args[1]);
780 static int help(void) {
782 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
783 "Send control commands to or query the virtual machine and container registration manager.\n\n"
784 " -h --help Show this help\n"
785 " --version Show package version\n"
786 " --no-pager Do not pipe output into a pager\n"
787 " --no-legend Do not show the headers and footers\n"
788 " -H --host=[USER@]HOST Operate on remote host\n"
789 " -M --machine=CONTAINER Operate on local container\n"
790 " -p --property=NAME Show only properties by this name\n"
791 " -a --all Show all properties, including empty ones\n"
792 " -l --full Do not ellipsize output\n"
793 " --kill-who=WHO Who to send signal to\n"
794 " -s --signal=SIGNAL Which signal to send\n\n"
796 " list List running VMs and containers\n"
797 " status NAME... Show VM/container status\n"
798 " show NAME... Show properties of one or more VMs/containers\n"
799 " login NAME Get a login prompt on a container\n"
800 " poweroff NAME... Power off one or more containers\n"
801 " reboot NAME... Reboot one or more containers\n"
802 " kill NAME... Send signal to processes of a VM/container\n"
803 " terminate NAME... Terminate one or more VMs/containers\n",
804 program_invocation_short_name);
809 static int parse_argv(int argc, char *argv[]) {
818 static const struct option options[] = {
819 { "help", no_argument, NULL, 'h' },
820 { "version", no_argument, NULL, ARG_VERSION },
821 { "property", required_argument, NULL, 'p' },
822 { "all", no_argument, NULL, 'a' },
823 { "full", no_argument, NULL, 'l' },
824 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
825 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
826 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
827 { "signal", required_argument, NULL, 's' },
828 { "host", required_argument, NULL, 'H' },
829 { "machine", required_argument, NULL, 'M' },
838 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
846 puts(PACKAGE_STRING);
847 puts(SYSTEMD_FEATURES);
851 r = strv_extend(&arg_property, optarg);
855 /* If the user asked for a particular
856 * property, show it to him, even if it is
878 arg_kill_who = optarg;
882 arg_signal = signal_from_string_try_harder(optarg);
883 if (arg_signal < 0) {
884 log_error("Failed to parse signal string %s.", optarg);
890 arg_transport = BUS_TRANSPORT_REMOTE;
895 arg_transport = BUS_TRANSPORT_CONTAINER;
903 assert_not_reached("Unhandled option");
910 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
912 static const struct {
920 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
922 { "list", LESS, 1, list_machines },
923 { "status", MORE, 2, show },
924 { "show", MORE, 1, show },
925 { "terminate", MORE, 2, terminate_machine },
926 { "reboot", MORE, 2, reboot_machine },
927 { "poweroff", MORE, 2, poweroff_machine },
928 { "kill", MORE, 2, kill_machine },
929 { "login", MORE, 2, login_machine },
938 left = argc - optind;
941 /* Special rule: no arguments means "list" */
944 if (streq(argv[optind], "help")) {
949 for (i = 0; i < ELEMENTSOF(verbs); i++)
950 if (streq(argv[optind], verbs[i].verb))
953 if (i >= ELEMENTSOF(verbs)) {
954 log_error("Unknown operation %s", argv[optind]);
959 switch (verbs[i].argc_cmp) {
962 if (left != verbs[i].argc) {
963 log_error("Invalid number of arguments.");
970 if (left < verbs[i].argc) {
971 log_error("Too few arguments.");
978 if (left > verbs[i].argc) {
979 log_error("Too many arguments.");
986 assert_not_reached("Unknown comparison operator.");
989 return verbs[i].dispatch(bus, argv + optind, left);
992 int main(int argc, char*argv[]) {
993 _cleanup_bus_unref_ sd_bus *bus = NULL;
996 setlocale(LC_ALL, "");
997 log_parse_environment();
1000 r = parse_argv(argc, argv);
1004 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1006 log_error("Failed to create bus connection: %s", strerror(-r));
1010 r = machinectl_main(bus, argc, argv);
1015 strv_free(arg_property);
1017 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;