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>
39 #include "bus-error.h"
42 #include "unit-name.h"
43 #include "cgroup-show.h"
44 #include "cgroup-util.h"
47 static char **arg_property = NULL;
48 static bool arg_all = false;
49 static bool arg_full = false;
50 static bool arg_no_pager = false;
51 static bool arg_legend = true;
52 static const char *arg_kill_who = NULL;
53 static int arg_signal = SIGTERM;
54 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
55 static char *arg_host = NULL;
57 static void pager_open_if_enabled(void) {
59 /* Cache result before we open the pager */
66 static int list_machines(sd_bus *bus, char **args, unsigned n) {
67 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
68 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
69 const char *name, *class, *service, *object;
73 pager_open_if_enabled();
75 r = sd_bus_call_method(
77 "org.freedesktop.machine1",
78 "/org/freedesktop/machine1",
79 "org.freedesktop.machine1.Manager",
85 log_error("Could not get machines: %s", bus_error_message(&error, -r));
90 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
92 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
94 return bus_log_parse_error(r);
96 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
97 printf("%-32s %-9s %-16s\n", name, class, service);
102 return bus_log_parse_error(r);
104 r = sd_bus_message_exit_container(reply);
106 return bus_log_parse_error(r);
109 printf("\n%u machines listed.\n", k);
114 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
115 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
116 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
117 _cleanup_free_ char *path = NULL;
125 if (arg_transport == BUS_TRANSPORT_REMOTE)
128 path = unit_dbus_path_from_name(unit);
132 r = sd_bus_get_property(
134 "org.freedesktop.systemd1",
136 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
142 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
146 r = sd_bus_message_read(reply, "s", &cgroup);
148 return bus_log_parse_error(r);
153 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
157 arg_all * OUTPUT_SHOW_ALL |
158 arg_full * OUTPUT_FULL_WIDTH;
166 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
170 static int print_addresses(sd_bus *bus, const char *name, const char *prefix, const char *prefix2) {
171 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
179 r = sd_bus_call_method(bus,
180 "org.freedesktop.machine1",
181 "/org/freedesktop/machine1",
182 "org.freedesktop.machine1.Manager",
183 "GetMachineAddresses",
190 r = sd_bus_message_enter_container(reply, 'a', "(yay)");
192 return bus_log_parse_error(r);
194 while ((r = sd_bus_message_enter_container(reply, 'r', "yay")) > 0) {
195 unsigned char family;
198 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
200 r = sd_bus_message_read(reply, "y", &family);
202 return bus_log_parse_error(r);
204 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
206 return bus_log_parse_error(r);
208 printf("%s%s\n", prefix, inet_ntop(family, a, buffer, sizeof(buffer)));
210 r = sd_bus_message_exit_container(reply);
212 return bus_log_parse_error(r);
214 if (prefix != prefix2)
218 return bus_log_parse_error(r);
220 r = sd_bus_message_exit_container(reply);
222 return bus_log_parse_error(r);
227 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
228 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
229 const char *k, *v, *pretty = NULL;
236 r = sd_bus_call_method(bus,
237 "org.freedesktop.machine1",
238 "/org/freedesktop/machine1",
239 "org.freedesktop.machine1.Manager",
240 "GetMachineOSRelease",
247 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
249 return bus_log_parse_error(r);
251 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
252 if (streq(k, "PRETTY_NAME"))
257 return bus_log_parse_error(r);
259 r = sd_bus_message_exit_container(reply);
261 return bus_log_parse_error(r);
264 printf("%s%s\n", prefix, pretty);
269 typedef struct MachineStatusInfo {
275 char *root_directory;
280 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
281 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
282 char since2[FORMAT_TIMESTAMP_MAX], *s2;
285 fputs(strna(i->name), stdout);
287 if (!sd_id128_equal(i->id, SD_ID128_NULL))
288 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
292 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
293 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
296 printf("\t Since: %s; %s\n", s2, s1);
298 printf("\t Since: %s\n", s2);
301 _cleanup_free_ char *t = NULL;
303 printf("\t Leader: %u", (unsigned) i->leader);
305 get_process_comm(i->leader, &t);
313 printf("\t Service: %s", i->service);
316 printf("; class %s", i->class);
320 printf("\t Class: %s\n", i->class);
322 if (i->root_directory)
323 printf("\t Root: %s\n", i->root_directory);
325 print_addresses(bus, i->name,
329 print_os_release(bus, i->name, "\t OS: ");
332 printf("\t Unit: %s\n", i->unit);
333 show_unit_cgroup(bus, i->unit, i->leader);
337 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
339 static const struct bus_properties_map map[] = {
340 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
341 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
342 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
343 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
344 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
345 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
346 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
347 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
351 MachineStatusInfo info = {};
357 r = bus_map_all_properties(bus,
358 "org.freedesktop.machine1",
363 log_error("Could not get properties: %s", strerror(-r));
371 print_machine_status_info(bus, &info);
377 free(info.root_directory);
382 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
390 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
392 log_error("Could not get properties: %s", strerror(-r));
397 static int show(sd_bus *bus, char **args, unsigned n) {
398 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
399 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
402 bool properties, new_line = false;
407 properties = !strstr(args[0], "status");
409 pager_open_if_enabled();
411 if (properties && n <= 1) {
413 /* If no argument is specified, inspect the manager
415 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
420 for (i = 1; i < n; i++) {
421 const char *path = NULL;
423 r = sd_bus_call_method(
425 "org.freedesktop.machine1",
426 "/org/freedesktop/machine1",
427 "org.freedesktop.machine1.Manager",
433 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
437 r = sd_bus_message_read(reply, "o", &path);
439 return bus_log_parse_error(r);
442 r = show_properties(bus, path, &new_line);
444 r = show_info(args[0], bus, path, &new_line);
450 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
451 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
457 arg_kill_who = "all";
459 for (i = 1; i < n; i++) {
462 r = sd_bus_call_method(
464 "org.freedesktop.machine1",
465 "/org/freedesktop/machine1",
466 "org.freedesktop.machine1.Manager",
470 "ssi", args[i], arg_kill_who, arg_signal);
472 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
480 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
481 arg_kill_who = "leader";
482 arg_signal = SIGINT; /* sysvinit + systemd */
484 return kill_machine(bus, args, n);
487 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
488 arg_kill_who = "leader";
489 arg_signal = SIGRTMIN+4; /* only systemd */
491 return kill_machine(bus, args, n);
494 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
495 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
500 for (i = 1; i < n; i++) {
503 r = sd_bus_call_method(
505 "org.freedesktop.machine1",
506 "/org/freedesktop/machine1",
507 "org.freedesktop.machine1.Manager",
513 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
521 static int openpt_in_namespace(pid_t pid, int flags) {
522 _cleanup_close_pair_ int pair[2] = { -1, -1 };
523 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
525 struct cmsghdr cmsghdr;
526 uint8_t buf[CMSG_SPACE(sizeof(int))];
529 .msg_control = &control,
530 .msg_controllen = sizeof(control),
532 struct cmsghdr *cmsg;
537 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
541 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
549 pair[0] = safe_close(pair[0]);
551 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
555 master = posix_openpt(flags);
559 cmsg = CMSG_FIRSTHDR(&mh);
560 cmsg->cmsg_level = SOL_SOCKET;
561 cmsg->cmsg_type = SCM_RIGHTS;
562 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
563 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
565 mh.msg_controllen = cmsg->cmsg_len;
567 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
573 pair[1] = safe_close(pair[1]);
575 r = wait_for_terminate(child, &si);
578 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
581 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
584 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
585 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
589 fds = (int*) CMSG_DATA(cmsg);
590 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
593 close_many(fds, n_fds);
606 static int login_machine(sd_bus *bus, char **args, unsigned n) {
607 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
608 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
609 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
610 _cleanup_close_ int master = -1;
611 _cleanup_free_ char *getty = NULL;
612 const char *path, *pty, *p;
620 if (arg_transport != BUS_TRANSPORT_LOCAL) {
621 log_error("Login only supported on local machines.");
625 r = sd_bus_call_method(
627 "org.freedesktop.machine1",
628 "/org/freedesktop/machine1",
629 "org.freedesktop.machine1.Manager",
635 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
639 r = sd_bus_message_read(reply, "o", &path);
641 return bus_log_parse_error(r);
643 r = sd_bus_get_property(
645 "org.freedesktop.machine1",
647 "org.freedesktop.machine1.Machine",
653 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
657 r = sd_bus_message_read(reply2, "u", &leader);
659 return bus_log_parse_error(r);
661 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
663 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
667 pty = ptsname(master);
669 log_error("Failed to get pty name: %m");
673 p = startswith(pty, "/dev/pts/");
675 log_error("Invalid pty name %s.", pty);
679 r = sd_bus_open_system_container(&container_bus, args[1]);
681 log_error("Failed to get container bus: %s", strerror(-r));
685 getty = strjoin("container-getty@", p, ".service", NULL);
689 if (unlockpt(master) < 0) {
690 log_error("Failed to unlock tty: %m");
694 r = sd_bus_call_method(container_bus,
695 "org.freedesktop.systemd1",
696 "/org/freedesktop/systemd1",
697 "org.freedesktop.systemd1.Manager",
700 "ss", getty, "replace");
702 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
706 container_bus = sd_bus_unref(container_bus);
708 assert_se(sigemptyset(&mask) == 0);
709 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
710 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
712 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
714 r = process_pty(master, &mask, 0, 0);
716 log_error("Failed to process pseudo tty: %s", strerror(-r));
722 log_info("Connection to container %s terminated.", args[1]);
727 static int help(void) {
729 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
730 "Send control commands to or query the virtual machine and container registration manager.\n\n"
731 " -h --help Show this help\n"
732 " --version Show package version\n"
733 " --no-pager Do not pipe output into a pager\n"
734 " --no-legend Do not show the headers and footers\n"
735 " -H --host=[USER@]HOST Operate on remote host\n"
736 " -M --machine=CONTAINER Operate on local container\n"
737 " -p --property=NAME Show only properties by this name\n"
738 " -a --all Show all properties, including empty ones\n"
739 " -l --full Do not ellipsize output\n"
740 " --kill-who=WHO Who to send signal to\n"
741 " -s --signal=SIGNAL Which signal to send\n\n"
743 " list List running VMs and containers\n"
744 " status NAME... Show VM/container status\n"
745 " show NAME... Show properties of one or more VMs/containers\n"
746 " login NAME Get a login prompt on a container\n"
747 " poweroff NAME... Power off one or more containers\n"
748 " reboot NAME... Reboot one or more containers\n"
749 " kill NAME... Send signal to processes of a VM/container\n"
750 " terminate NAME... Terminate one or more VMs/containers\n",
751 program_invocation_short_name);
756 static int parse_argv(int argc, char *argv[]) {
765 static const struct option options[] = {
766 { "help", no_argument, NULL, 'h' },
767 { "version", no_argument, NULL, ARG_VERSION },
768 { "property", required_argument, NULL, 'p' },
769 { "all", no_argument, NULL, 'a' },
770 { "full", no_argument, NULL, 'l' },
771 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
772 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
773 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
774 { "signal", required_argument, NULL, 's' },
775 { "host", required_argument, NULL, 'H' },
776 { "machine", required_argument, NULL, 'M' },
785 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
793 puts(PACKAGE_STRING);
794 puts(SYSTEMD_FEATURES);
798 r = strv_extend(&arg_property, optarg);
802 /* If the user asked for a particular
803 * property, show it to him, even if it is
825 arg_kill_who = optarg;
829 arg_signal = signal_from_string_try_harder(optarg);
830 if (arg_signal < 0) {
831 log_error("Failed to parse signal string %s.", optarg);
837 arg_transport = BUS_TRANSPORT_REMOTE;
842 arg_transport = BUS_TRANSPORT_CONTAINER;
850 assert_not_reached("Unhandled option");
857 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
859 static const struct {
867 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
869 { "list", LESS, 1, list_machines },
870 { "status", MORE, 2, show },
871 { "show", MORE, 1, show },
872 { "terminate", MORE, 2, terminate_machine },
873 { "reboot", MORE, 2, reboot_machine },
874 { "poweroff", MORE, 2, poweroff_machine },
875 { "kill", MORE, 2, kill_machine },
876 { "login", MORE, 2, login_machine },
885 left = argc - optind;
888 /* Special rule: no arguments means "list" */
891 if (streq(argv[optind], "help")) {
896 for (i = 0; i < ELEMENTSOF(verbs); i++)
897 if (streq(argv[optind], verbs[i].verb))
900 if (i >= ELEMENTSOF(verbs)) {
901 log_error("Unknown operation %s", argv[optind]);
906 switch (verbs[i].argc_cmp) {
909 if (left != verbs[i].argc) {
910 log_error("Invalid number of arguments.");
917 if (left < verbs[i].argc) {
918 log_error("Too few arguments.");
925 if (left > verbs[i].argc) {
926 log_error("Too many arguments.");
933 assert_not_reached("Unknown comparison operator.");
936 return verbs[i].dispatch(bus, argv + optind, left);
939 int main(int argc, char*argv[]) {
940 _cleanup_bus_unref_ sd_bus *bus = NULL;
943 setlocale(LC_ALL, "");
944 log_parse_environment();
947 r = parse_argv(argc, argv);
951 r = bus_open_transport(arg_transport, arg_host, false, &bus);
953 log_error("Failed to create bus connection: %s", strerror(-r));
957 r = machinectl_main(bus, argc, argv);
962 strv_free(arg_property);
964 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;