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_pipe_ 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 close_nointr_nofail(pair[0]);
445 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
449 master = posix_openpt(flags);
453 cmsg = CMSG_FIRSTHDR(&mh);
454 cmsg->cmsg_level = SOL_SOCKET;
455 cmsg->cmsg_type = SCM_RIGHTS;
456 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
457 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
459 mh.msg_controllen = cmsg->cmsg_len;
461 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
467 close_nointr_nofail(pair[1]);
470 r = wait_for_terminate(child, &si);
471 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
473 return r < 0 ? r : -EIO;
476 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
479 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
480 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
484 fds = (int*) CMSG_DATA(cmsg);
485 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
488 close_many(fds, n_fds);
501 static int login_machine(sd_bus *bus, char **args, unsigned n) {
502 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
503 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
504 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
505 _cleanup_close_ int master = -1;
506 _cleanup_free_ char *getty = NULL;
507 const char *path, *pty, *p;
515 if (arg_transport != BUS_TRANSPORT_LOCAL) {
516 log_error("Login only supported on local machines.");
520 r = sd_bus_call_method(
522 "org.freedesktop.machine1",
523 "/org/freedesktop/machine1",
524 "org.freedesktop.machine1.Manager",
530 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
534 r = sd_bus_message_read(reply, "o", &path);
536 return bus_log_parse_error(r);
538 r = sd_bus_get_property(
540 "org.freedesktop.machine1",
542 "org.freedesktop.machine1.Machine",
548 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
552 r = sd_bus_message_read(reply2, "u", &leader);
554 return bus_log_parse_error(r);
556 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
558 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
562 pty = ptsname(master);
564 log_error("Failed to get pty name: %m");
568 p = startswith(pty, "/dev/pts/");
570 log_error("Invalid pty name %s.", pty);
574 r = sd_bus_open_system_container(&container_bus, args[1]);
576 log_error("Failed to get container bus: %s", strerror(-r));
580 getty = strjoin("container-getty@", p, ".service", NULL);
584 if (unlockpt(master) < 0) {
585 log_error("Failed to unlock tty: %m");
589 r = sd_bus_call_method(container_bus,
590 "org.freedesktop.systemd1",
591 "/org/freedesktop/systemd1",
592 "org.freedesktop.systemd1.Manager",
595 "ss", getty, "replace");
597 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
601 container_bus = sd_bus_unref(container_bus);
603 assert_se(sigemptyset(&mask) == 0);
604 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
605 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
607 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
609 r = process_pty(master, &mask, 0, 0);
611 log_error("Failed to process pseudo tty: %s", strerror(-r));
617 log_info("Connection to container %s terminated.", args[1]);
622 static int help(void) {
624 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
625 "Send control commands to or query the virtual machine and container registration manager.\n\n"
626 " -h --help Show this help\n"
627 " --version Show package version\n"
628 " --no-pager Do not pipe output into a pager\n"
629 " --no-legend Do not show the headers and footers\n"
630 " -H --host=[USER@]HOST Operate on remote host\n"
631 " -M --machine=CONTAINER Operate on local container\n"
632 " -p --property=NAME Show only properties by this name\n"
633 " -a --all Show all properties, including empty ones\n"
634 " -l --full Do not ellipsize output\n"
635 " --kill-who=WHO Who to send signal to\n"
636 " -s --signal=SIGNAL Which signal to send\n\n"
638 " list List running VMs and containers\n"
639 " status NAME... Show VM/container status\n"
640 " show NAME... Show properties of one or more VMs/containers\n"
641 " login NAME Get a login prompt on a container\n"
642 " poweroff NAME... Power off one or more containers\n"
643 " reboot NAME... Reboot one or more containers\n"
644 " kill NAME... Send signal to processes of a VM/container\n"
645 " terminate NAME... Terminate one or more VMs/containers\n",
646 program_invocation_short_name);
651 static int parse_argv(int argc, char *argv[]) {
660 static const struct option options[] = {
661 { "help", no_argument, NULL, 'h' },
662 { "version", no_argument, NULL, ARG_VERSION },
663 { "property", required_argument, NULL, 'p' },
664 { "all", no_argument, NULL, 'a' },
665 { "full", no_argument, NULL, 'l' },
666 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
667 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
668 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
669 { "signal", required_argument, NULL, 's' },
670 { "host", required_argument, NULL, 'H' },
671 { "machine", required_argument, NULL, 'M' },
680 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
688 puts(PACKAGE_STRING);
689 puts(SYSTEMD_FEATURES);
693 r = strv_extend(&arg_property, optarg);
697 /* If the user asked for a particular
698 * property, show it to him, even if it is
720 arg_kill_who = optarg;
724 arg_signal = signal_from_string_try_harder(optarg);
725 if (arg_signal < 0) {
726 log_error("Failed to parse signal string %s.", optarg);
732 arg_transport = BUS_TRANSPORT_REMOTE;
737 arg_transport = BUS_TRANSPORT_CONTAINER;
745 assert_not_reached("Unhandled option");
752 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
754 static const struct {
762 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
764 { "list", LESS, 1, list_machines },
765 { "status", MORE, 2, show },
766 { "show", MORE, 1, show },
767 { "terminate", MORE, 2, terminate_machine },
768 { "reboot", MORE, 2, reboot_machine },
769 { "poweroff", MORE, 2, poweroff_machine },
770 { "kill", MORE, 2, kill_machine },
771 { "login", MORE, 2, login_machine },
780 left = argc - optind;
783 /* Special rule: no arguments means "list" */
786 if (streq(argv[optind], "help")) {
791 for (i = 0; i < ELEMENTSOF(verbs); i++)
792 if (streq(argv[optind], verbs[i].verb))
795 if (i >= ELEMENTSOF(verbs)) {
796 log_error("Unknown operation %s", argv[optind]);
801 switch (verbs[i].argc_cmp) {
804 if (left != verbs[i].argc) {
805 log_error("Invalid number of arguments.");
812 if (left < verbs[i].argc) {
813 log_error("Too few arguments.");
820 if (left > verbs[i].argc) {
821 log_error("Too many arguments.");
828 assert_not_reached("Unknown comparison operator.");
831 return verbs[i].dispatch(bus, argv + optind, left);
834 int main(int argc, char*argv[]) {
835 _cleanup_bus_unref_ sd_bus *bus = NULL;
838 setlocale(LC_ALL, "");
839 log_parse_environment();
842 r = parse_argv(argc, argv);
846 r = bus_open_transport(arg_transport, arg_host, false, &bus);
848 log_error("Failed to create bus connection: %s", strerror(-r));
852 r = machinectl_main(bus, argc, argv);
857 strv_free(arg_property);
859 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;