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 typedef struct MachineStatusInfo {
233 char *root_directory;
238 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
239 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
240 char since2[FORMAT_TIMESTAMP_MAX], *s2;
243 fputs(strna(i->name), stdout);
245 if (!sd_id128_equal(i->id, SD_ID128_NULL))
246 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
250 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
251 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
254 printf("\t Since: %s; %s\n", s2, s1);
256 printf("\t Since: %s\n", s2);
259 _cleanup_free_ char *t = NULL;
261 printf("\t Leader: %u", (unsigned) i->leader);
263 get_process_comm(i->leader, &t);
271 printf("\t Service: %s", i->service);
274 printf("; class %s", i->class);
278 printf("\t Class: %s\n", i->class);
280 if (i->root_directory)
281 printf("\t Root: %s\n", i->root_directory);
283 print_addresses(bus, i->name,
288 printf("\t Unit: %s\n", i->unit);
289 show_unit_cgroup(bus, i->unit, i->leader);
293 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
295 static const struct bus_properties_map map[] = {
296 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
297 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
298 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
299 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
300 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
301 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
302 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
303 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
307 MachineStatusInfo info = {};
313 r = bus_map_all_properties(bus,
314 "org.freedesktop.machine1",
319 log_error("Could not get properties: %s", strerror(-r));
327 print_machine_status_info(bus, &info);
333 free(info.root_directory);
338 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
346 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
348 log_error("Could not get properties: %s", strerror(-r));
353 static int show(sd_bus *bus, char **args, unsigned n) {
354 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
355 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
358 bool properties, new_line = false;
363 properties = !strstr(args[0], "status");
365 pager_open_if_enabled();
367 if (properties && n <= 1) {
369 /* If no argument is specified, inspect the manager
371 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
376 for (i = 1; i < n; i++) {
377 const char *path = NULL;
379 r = sd_bus_call_method(
381 "org.freedesktop.machine1",
382 "/org/freedesktop/machine1",
383 "org.freedesktop.machine1.Manager",
389 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
393 r = sd_bus_message_read(reply, "o", &path);
395 return bus_log_parse_error(r);
398 r = show_properties(bus, path, &new_line);
400 r = show_info(args[0], bus, path, &new_line);
406 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
407 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
413 arg_kill_who = "all";
415 for (i = 1; i < n; i++) {
418 r = sd_bus_call_method(
420 "org.freedesktop.machine1",
421 "/org/freedesktop/machine1",
422 "org.freedesktop.machine1.Manager",
426 "ssi", args[i], arg_kill_who, arg_signal);
428 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
436 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
437 arg_kill_who = "leader";
438 arg_signal = SIGINT; /* sysvinit + systemd */
440 return kill_machine(bus, args, n);
443 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
444 arg_kill_who = "leader";
445 arg_signal = SIGRTMIN+4; /* only systemd */
447 return kill_machine(bus, args, n);
450 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
451 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
456 for (i = 1; i < n; i++) {
459 r = sd_bus_call_method(
461 "org.freedesktop.machine1",
462 "/org/freedesktop/machine1",
463 "org.freedesktop.machine1.Manager",
469 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
477 static int openpt_in_namespace(pid_t pid, int flags) {
478 _cleanup_close_pair_ int pair[2] = { -1, -1 };
479 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
481 struct cmsghdr cmsghdr;
482 uint8_t buf[CMSG_SPACE(sizeof(int))];
485 .msg_control = &control,
486 .msg_controllen = sizeof(control),
488 struct cmsghdr *cmsg;
493 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
497 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
505 pair[0] = safe_close(pair[0]);
507 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
511 master = posix_openpt(flags);
515 cmsg = CMSG_FIRSTHDR(&mh);
516 cmsg->cmsg_level = SOL_SOCKET;
517 cmsg->cmsg_type = SCM_RIGHTS;
518 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
519 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
521 mh.msg_controllen = cmsg->cmsg_len;
523 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
529 pair[1] = safe_close(pair[1]);
531 r = wait_for_terminate(child, &si);
534 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
537 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
540 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
541 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
545 fds = (int*) CMSG_DATA(cmsg);
546 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
549 close_many(fds, n_fds);
562 static int login_machine(sd_bus *bus, char **args, unsigned n) {
563 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
564 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
565 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
566 _cleanup_close_ int master = -1;
567 _cleanup_free_ char *getty = NULL;
568 const char *path, *pty, *p;
576 if (arg_transport != BUS_TRANSPORT_LOCAL) {
577 log_error("Login only supported on local machines.");
581 r = sd_bus_call_method(
583 "org.freedesktop.machine1",
584 "/org/freedesktop/machine1",
585 "org.freedesktop.machine1.Manager",
591 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
595 r = sd_bus_message_read(reply, "o", &path);
597 return bus_log_parse_error(r);
599 r = sd_bus_get_property(
601 "org.freedesktop.machine1",
603 "org.freedesktop.machine1.Machine",
609 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
613 r = sd_bus_message_read(reply2, "u", &leader);
615 return bus_log_parse_error(r);
617 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
619 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
623 pty = ptsname(master);
625 log_error("Failed to get pty name: %m");
629 p = startswith(pty, "/dev/pts/");
631 log_error("Invalid pty name %s.", pty);
635 r = sd_bus_open_system_container(&container_bus, args[1]);
637 log_error("Failed to get container bus: %s", strerror(-r));
641 getty = strjoin("container-getty@", p, ".service", NULL);
645 if (unlockpt(master) < 0) {
646 log_error("Failed to unlock tty: %m");
650 r = sd_bus_call_method(container_bus,
651 "org.freedesktop.systemd1",
652 "/org/freedesktop/systemd1",
653 "org.freedesktop.systemd1.Manager",
656 "ss", getty, "replace");
658 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
662 container_bus = sd_bus_unref(container_bus);
664 assert_se(sigemptyset(&mask) == 0);
665 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
666 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
668 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
670 r = process_pty(master, &mask, 0, 0);
672 log_error("Failed to process pseudo tty: %s", strerror(-r));
678 log_info("Connection to container %s terminated.", args[1]);
683 static int help(void) {
685 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
686 "Send control commands to or query the virtual machine and container registration manager.\n\n"
687 " -h --help Show this help\n"
688 " --version Show package version\n"
689 " --no-pager Do not pipe output into a pager\n"
690 " --no-legend Do not show the headers and footers\n"
691 " -H --host=[USER@]HOST Operate on remote host\n"
692 " -M --machine=CONTAINER Operate on local container\n"
693 " -p --property=NAME Show only properties by this name\n"
694 " -a --all Show all properties, including empty ones\n"
695 " -l --full Do not ellipsize output\n"
696 " --kill-who=WHO Who to send signal to\n"
697 " -s --signal=SIGNAL Which signal to send\n\n"
699 " list List running VMs and containers\n"
700 " status NAME... Show VM/container status\n"
701 " show NAME... Show properties of one or more VMs/containers\n"
702 " login NAME Get a login prompt on a container\n"
703 " poweroff NAME... Power off one or more containers\n"
704 " reboot NAME... Reboot one or more containers\n"
705 " kill NAME... Send signal to processes of a VM/container\n"
706 " terminate NAME... Terminate one or more VMs/containers\n",
707 program_invocation_short_name);
712 static int parse_argv(int argc, char *argv[]) {
721 static const struct option options[] = {
722 { "help", no_argument, NULL, 'h' },
723 { "version", no_argument, NULL, ARG_VERSION },
724 { "property", required_argument, NULL, 'p' },
725 { "all", no_argument, NULL, 'a' },
726 { "full", no_argument, NULL, 'l' },
727 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
728 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
729 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
730 { "signal", required_argument, NULL, 's' },
731 { "host", required_argument, NULL, 'H' },
732 { "machine", required_argument, NULL, 'M' },
741 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
749 puts(PACKAGE_STRING);
750 puts(SYSTEMD_FEATURES);
754 r = strv_extend(&arg_property, optarg);
758 /* If the user asked for a particular
759 * property, show it to him, even if it is
781 arg_kill_who = optarg;
785 arg_signal = signal_from_string_try_harder(optarg);
786 if (arg_signal < 0) {
787 log_error("Failed to parse signal string %s.", optarg);
793 arg_transport = BUS_TRANSPORT_REMOTE;
798 arg_transport = BUS_TRANSPORT_CONTAINER;
806 assert_not_reached("Unhandled option");
813 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
815 static const struct {
823 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
825 { "list", LESS, 1, list_machines },
826 { "status", MORE, 2, show },
827 { "show", MORE, 1, show },
828 { "terminate", MORE, 2, terminate_machine },
829 { "reboot", MORE, 2, reboot_machine },
830 { "poweroff", MORE, 2, poweroff_machine },
831 { "kill", MORE, 2, kill_machine },
832 { "login", MORE, 2, login_machine },
841 left = argc - optind;
844 /* Special rule: no arguments means "list" */
847 if (streq(argv[optind], "help")) {
852 for (i = 0; i < ELEMENTSOF(verbs); i++)
853 if (streq(argv[optind], verbs[i].verb))
856 if (i >= ELEMENTSOF(verbs)) {
857 log_error("Unknown operation %s", argv[optind]);
862 switch (verbs[i].argc_cmp) {
865 if (left != verbs[i].argc) {
866 log_error("Invalid number of arguments.");
873 if (left < verbs[i].argc) {
874 log_error("Too few arguments.");
881 if (left > verbs[i].argc) {
882 log_error("Too many arguments.");
889 assert_not_reached("Unknown comparison operator.");
892 return verbs[i].dispatch(bus, argv + optind, left);
895 int main(int argc, char*argv[]) {
896 _cleanup_bus_unref_ sd_bus *bus = NULL;
899 setlocale(LC_ALL, "");
900 log_parse_environment();
903 r = parse_argv(argc, argv);
907 r = bus_open_transport(arg_transport, arg_host, false, &bus);
909 log_error("Failed to create bus connection: %s", strerror(-r));
913 r = machinectl_main(bus, argc, argv);
918 strv_free(arg_property);
920 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;