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>
37 #include "bus-error.h"
40 #include "unit-name.h"
41 #include "cgroup-show.h"
42 #include "cgroup-util.h"
45 static char **arg_property = NULL;
46 static bool arg_all = false;
47 static bool arg_full = false;
48 static bool arg_no_pager = false;
49 static bool arg_legend = true;
50 static const char *arg_kill_who = NULL;
51 static int arg_signal = SIGTERM;
52 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
53 static char *arg_host = NULL;
55 static void pager_open_if_enabled(void) {
57 /* Cache result before we open the pager */
64 static int list_machines(sd_bus *bus, char **args, unsigned n) {
65 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
66 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
67 const char *name, *class, *service, *object;
71 pager_open_if_enabled();
73 r = sd_bus_call_method(
75 "org.freedesktop.machine1",
76 "/org/freedesktop/machine1",
77 "org.freedesktop.machine1.Manager",
83 log_error("Could not get machines: %s", bus_error_message(&error, -r));
88 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
90 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
92 return bus_log_parse_error(r);
94 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
95 printf("%-32s %-9s %-16s\n", name, class, service);
100 return bus_log_parse_error(r);
102 r = sd_bus_message_exit_container(reply);
104 return bus_log_parse_error(r);
107 printf("\n%u machines listed.\n", k);
112 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
113 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
114 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
115 _cleanup_free_ char *path = NULL;
123 if (arg_transport == BUS_TRANSPORT_REMOTE)
126 path = unit_dbus_path_from_name(unit);
130 r = sd_bus_get_property(
132 "org.freedesktop.systemd1",
134 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
140 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
144 r = sd_bus_message_read(reply, "s", &cgroup);
146 return bus_log_parse_error(r);
151 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
155 arg_all * OUTPUT_SHOW_ALL |
156 arg_full * OUTPUT_FULL_WIDTH;
164 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
168 typedef struct MachineStatusInfo {
174 char *root_directory;
179 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
180 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
181 char since2[FORMAT_TIMESTAMP_MAX], *s2;
184 fputs(strna(i->name), stdout);
186 if (!sd_id128_equal(i->id, SD_ID128_NULL))
187 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
191 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
192 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
195 printf("\t Since: %s; %s\n", s2, s1);
197 printf("\t Since: %s\n", s2);
200 _cleanup_free_ char *t = NULL;
202 printf("\t Leader: %u", (unsigned) i->leader);
204 get_process_comm(i->leader, &t);
212 printf("\t Service: %s", i->service);
215 printf("; class %s", i->class);
219 printf("\t Class: %s\n", i->class);
221 if (i->root_directory)
222 printf("\t Root: %s\n", i->root_directory);
225 printf("\t Unit: %s\n", i->unit);
226 show_unit_cgroup(bus, i->unit, i->leader);
230 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
232 static const struct bus_properties_map map[] = {
233 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
234 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
235 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
236 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
237 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
238 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
239 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
240 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
244 MachineStatusInfo info = {};
250 r = bus_map_all_properties(bus,
251 "org.freedesktop.machine1",
256 log_error("Could not get properties: %s", strerror(-r));
264 print_machine_status_info(bus, &info);
270 free(info.root_directory);
275 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
283 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
285 log_error("Could not get properties: %s", strerror(-r));
290 static int show(sd_bus *bus, char **args, unsigned n) {
291 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
292 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
295 bool properties, new_line = false;
300 properties = !strstr(args[0], "status");
302 pager_open_if_enabled();
304 if (properties && n <= 1) {
306 /* If no argument is specified, inspect the manager
308 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
313 for (i = 1; i < n; i++) {
314 const char *path = NULL;
316 r = sd_bus_call_method(
318 "org.freedesktop.machine1",
319 "/org/freedesktop/machine1",
320 "org.freedesktop.machine1.Manager",
326 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
330 r = sd_bus_message_read(reply, "o", &path);
332 return bus_log_parse_error(r);
335 r = show_properties(bus, path, &new_line);
337 r = show_info(args[0], bus, path, &new_line);
343 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
344 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
350 arg_kill_who = "all";
352 for (i = 1; i < n; i++) {
355 r = sd_bus_call_method(
357 "org.freedesktop.machine1",
358 "/org/freedesktop/machine1",
359 "org.freedesktop.machine1.Manager",
363 "ssi", args[i], arg_kill_who, arg_signal);
365 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
373 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
374 arg_kill_who = "leader";
375 arg_signal = SIGINT; /* sysvinit + systemd */
377 return kill_machine(bus, args, n);
380 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
381 arg_kill_who = "leader";
382 arg_signal = SIGRTMIN+4; /* only systemd */
384 return kill_machine(bus, args, n);
387 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
388 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
393 for (i = 1; i < n; i++) {
396 r = sd_bus_call_method(
398 "org.freedesktop.machine1",
399 "/org/freedesktop/machine1",
400 "org.freedesktop.machine1.Manager",
406 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
414 static int openpt_in_namespace(pid_t pid, int flags) {
415 _cleanup_close_pair_ int pair[2] = { -1, -1 };
416 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
418 struct cmsghdr cmsghdr;
419 uint8_t buf[CMSG_SPACE(sizeof(int))];
422 .msg_control = &control,
423 .msg_controllen = sizeof(control),
425 struct cmsghdr *cmsg;
430 r = namespace_open(pid, &pidnsfd, &mntnsfd, &rootfd);
434 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
442 pair[0] = safe_close(pair[0]);
444 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
448 master = posix_openpt(flags);
452 cmsg = CMSG_FIRSTHDR(&mh);
453 cmsg->cmsg_level = SOL_SOCKET;
454 cmsg->cmsg_type = SCM_RIGHTS;
455 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
456 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
458 mh.msg_controllen = cmsg->cmsg_len;
460 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
466 pair[1] = safe_close(pair[1]);
468 r = wait_for_terminate(child, &si);
469 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
471 return r < 0 ? r : -EIO;
474 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
477 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
478 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
482 fds = (int*) CMSG_DATA(cmsg);
483 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
486 close_many(fds, n_fds);
499 static int login_machine(sd_bus *bus, char **args, unsigned n) {
500 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
501 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
502 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
503 _cleanup_close_ int master = -1;
504 _cleanup_free_ char *getty = NULL;
505 const char *path, *pty, *p;
513 if (arg_transport != BUS_TRANSPORT_LOCAL) {
514 log_error("Login only supported on local machines.");
518 r = sd_bus_call_method(
520 "org.freedesktop.machine1",
521 "/org/freedesktop/machine1",
522 "org.freedesktop.machine1.Manager",
528 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
532 r = sd_bus_message_read(reply, "o", &path);
534 return bus_log_parse_error(r);
536 r = sd_bus_get_property(
538 "org.freedesktop.machine1",
540 "org.freedesktop.machine1.Machine",
546 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
550 r = sd_bus_message_read(reply2, "u", &leader);
552 return bus_log_parse_error(r);
554 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
556 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
560 pty = ptsname(master);
562 log_error("Failed to get pty name: %m");
566 p = startswith(pty, "/dev/pts/");
568 log_error("Invalid pty name %s.", pty);
572 r = sd_bus_open_system_container(&container_bus, args[1]);
574 log_error("Failed to get container bus: %s", strerror(-r));
578 getty = strjoin("container-getty@", p, ".service", NULL);
582 if (unlockpt(master) < 0) {
583 log_error("Failed to unlock tty: %m");
587 r = sd_bus_call_method(container_bus,
588 "org.freedesktop.systemd1",
589 "/org/freedesktop/systemd1",
590 "org.freedesktop.systemd1.Manager",
593 "ss", getty, "replace");
595 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
599 container_bus = sd_bus_unref(container_bus);
601 assert_se(sigemptyset(&mask) == 0);
602 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
603 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
605 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
607 r = process_pty(master, &mask, 0, 0);
609 log_error("Failed to process pseudo tty: %s", strerror(-r));
615 log_info("Connection to container %s terminated.", args[1]);
620 static int help(void) {
622 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
623 "Send control commands to or query the virtual machine and container registration manager.\n\n"
624 " -h --help Show this help\n"
625 " --version Show package version\n"
626 " --no-pager Do not pipe output into a pager\n"
627 " --no-legend Do not show the headers and footers\n"
628 " -H --host=[USER@]HOST Operate on remote host\n"
629 " -M --machine=CONTAINER Operate on local container\n"
630 " -p --property=NAME Show only properties by this name\n"
631 " -a --all Show all properties, including empty ones\n"
632 " -l --full Do not ellipsize output\n"
633 " --kill-who=WHO Who to send signal to\n"
634 " -s --signal=SIGNAL Which signal to send\n\n"
636 " list List running VMs and containers\n"
637 " status NAME... Show VM/container status\n"
638 " show NAME... Show properties of one or more VMs/containers\n"
639 " login NAME Get a login prompt on a container\n"
640 " poweroff NAME... Power off one or more containers\n"
641 " reboot NAME... Reboot one or more containers\n"
642 " kill NAME... Send signal to processes of a VM/container\n"
643 " terminate NAME... Terminate one or more VMs/containers\n",
644 program_invocation_short_name);
649 static int parse_argv(int argc, char *argv[]) {
658 static const struct option options[] = {
659 { "help", no_argument, NULL, 'h' },
660 { "version", no_argument, NULL, ARG_VERSION },
661 { "property", required_argument, NULL, 'p' },
662 { "all", no_argument, NULL, 'a' },
663 { "full", no_argument, NULL, 'l' },
664 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
665 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
666 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
667 { "signal", required_argument, NULL, 's' },
668 { "host", required_argument, NULL, 'H' },
669 { "machine", required_argument, NULL, 'M' },
678 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
686 puts(PACKAGE_STRING);
687 puts(SYSTEMD_FEATURES);
691 r = strv_extend(&arg_property, optarg);
695 /* If the user asked for a particular
696 * property, show it to him, even if it is
718 arg_kill_who = optarg;
722 arg_signal = signal_from_string_try_harder(optarg);
723 if (arg_signal < 0) {
724 log_error("Failed to parse signal string %s.", optarg);
730 arg_transport = BUS_TRANSPORT_REMOTE;
735 arg_transport = BUS_TRANSPORT_CONTAINER;
743 assert_not_reached("Unhandled option");
750 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
752 static const struct {
760 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
762 { "list", LESS, 1, list_machines },
763 { "status", MORE, 2, show },
764 { "show", MORE, 1, show },
765 { "terminate", MORE, 2, terminate_machine },
766 { "reboot", MORE, 2, reboot_machine },
767 { "poweroff", MORE, 2, poweroff_machine },
768 { "kill", MORE, 2, kill_machine },
769 { "login", MORE, 2, login_machine },
778 left = argc - optind;
781 /* Special rule: no arguments means "list" */
784 if (streq(argv[optind], "help")) {
789 for (i = 0; i < ELEMENTSOF(verbs); i++)
790 if (streq(argv[optind], verbs[i].verb))
793 if (i >= ELEMENTSOF(verbs)) {
794 log_error("Unknown operation %s", argv[optind]);
799 switch (verbs[i].argc_cmp) {
802 if (left != verbs[i].argc) {
803 log_error("Invalid number of arguments.");
810 if (left < verbs[i].argc) {
811 log_error("Too few arguments.");
818 if (left > verbs[i].argc) {
819 log_error("Too many arguments.");
826 assert_not_reached("Unknown comparison operator.");
829 return verbs[i].dispatch(bus, argv + optind, left);
832 int main(int argc, char*argv[]) {
833 _cleanup_bus_unref_ sd_bus *bus = NULL;
836 setlocale(LC_ALL, "");
837 log_parse_environment();
840 r = parse_argv(argc, argv);
844 r = bus_open_transport(arg_transport, arg_host, false, &bus);
846 log_error("Failed to create bus connection: %s", strerror(-r));
850 r = machinectl_main(bus, argc, argv);
855 strv_free(arg_property);
857 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;