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 terminate_machine(sd_bus *bus, char **args, unsigned n) {
374 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
379 for (i = 1; i < n; i++) {
382 r = sd_bus_call_method(
384 "org.freedesktop.machine1",
385 "/org/freedesktop/machine1",
386 "org.freedesktop.machine1.Manager",
392 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
400 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
401 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
407 if (arg_transport != BUS_TRANSPORT_LOCAL) {
408 log_error("Reboot only supported on local machines.");
412 for (i = 1; i < n; i++) {
413 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
417 r = sd_bus_call_method(
419 "org.freedesktop.machine1",
420 "/org/freedesktop/machine1",
421 "org.freedesktop.machine1.Manager",
428 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
432 r = sd_bus_message_read(reply, "o", &path);
434 return bus_log_parse_error(r);
436 r = sd_bus_get_property(
438 "org.freedesktop.machine1",
440 "org.freedesktop.machine1.Machine",
446 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
450 r = sd_bus_message_read(reply2, "u", &leader);
452 return bus_log_parse_error(r);
454 if (kill(leader, SIGINT) < 0) {
455 log_error("Failed to kill init process " PID_FMT ": %m", (pid_t) leader);
463 static int openpt_in_namespace(pid_t pid, int flags) {
464 _cleanup_close_pipe_ int pair[2] = { -1, -1 };
465 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
467 struct cmsghdr cmsghdr;
468 uint8_t buf[CMSG_SPACE(sizeof(int))];
471 .msg_control = &control,
472 .msg_controllen = sizeof(control),
474 struct cmsghdr *cmsg;
479 r = namespace_open(pid, &pidnsfd, &mntnsfd, &rootfd);
483 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
491 close_nointr_nofail(pair[0]);
494 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
498 master = posix_openpt(flags);
502 cmsg = CMSG_FIRSTHDR(&mh);
503 cmsg->cmsg_level = SOL_SOCKET;
504 cmsg->cmsg_type = SCM_RIGHTS;
505 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
506 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
508 mh.msg_controllen = cmsg->cmsg_len;
510 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
516 close_nointr_nofail(pair[1]);
519 r = wait_for_terminate(child, &si);
520 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
522 return r < 0 ? r : -EIO;
525 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
528 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
529 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
533 fds = (int*) CMSG_DATA(cmsg);
534 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
537 close_many(fds, n_fds);
550 static int login_machine(sd_bus *bus, char **args, unsigned n) {
551 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
552 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
553 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
554 _cleanup_close_ int master = -1;
555 _cleanup_free_ char *getty = NULL;
556 const char *path, *pty, *p;
564 if (arg_transport != BUS_TRANSPORT_LOCAL) {
565 log_error("Login only supported on local machines.");
569 r = sd_bus_call_method(
571 "org.freedesktop.machine1",
572 "/org/freedesktop/machine1",
573 "org.freedesktop.machine1.Manager",
579 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
583 r = sd_bus_message_read(reply, "o", &path);
585 return bus_log_parse_error(r);
587 r = sd_bus_get_property(
589 "org.freedesktop.machine1",
591 "org.freedesktop.machine1.Machine",
597 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
601 r = sd_bus_message_read(reply2, "u", &leader);
603 return bus_log_parse_error(r);
605 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
607 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
611 pty = ptsname(master);
613 log_error("Failed to get pty name: %m");
617 p = startswith(pty, "/dev/pts/");
619 log_error("Invalid pty name %s.", pty);
623 r = sd_bus_open_system_container(&container_bus, args[1]);
625 log_error("Failed to get container bus: %s", strerror(-r));
629 getty = strjoin("container-getty@", p, ".service", NULL);
633 if (unlockpt(master) < 0) {
634 log_error("Failed to unlock tty: %m");
638 r = sd_bus_call_method(container_bus,
639 "org.freedesktop.systemd1",
640 "/org/freedesktop/systemd1",
641 "org.freedesktop.systemd1.Manager",
644 "ss", getty, "replace");
646 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
650 container_bus = sd_bus_unref(container_bus);
652 assert_se(sigemptyset(&mask) == 0);
653 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
654 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
656 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
658 r = process_pty(master, &mask, 0, 0);
660 log_error("Failed to process pseudo tty: %s", strerror(-r));
666 log_info("Connection to container %s terminated.", args[1]);
671 static int help(void) {
673 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
674 "Send control commands to or query the virtual machine and container registration manager.\n\n"
675 " -h --help Show this help\n"
676 " --version Show package version\n"
677 " --no-pager Do not pipe output into a pager\n"
678 " --no-legend Do not show the headers and footers\n"
679 " -H --host=[USER@]HOST Operate on remote host\n"
680 " -M --machine=CONTAINER Operate on local container\n"
681 " -p --property=NAME Show only properties by this name\n"
682 " -a --all Show all properties, including empty ones\n"
683 " -l --full Do not ellipsize output\n"
684 " --kill-who=WHO Who to send signal to\n"
685 " -s --signal=SIGNAL Which signal to send\n\n"
687 " list List running VMs and containers\n"
688 " status NAME... Show VM/container status\n"
689 " show NAME... Show properties of one or more VMs/containers\n"
690 " terminate NAME... Terminate one or more VMs/containers\n"
691 " kill NAME... Send signal to processes of a VM/container\n"
692 " reboot NAME... Reboot one or more containers\n"
693 " login NAME Get a login prompt on a container\n",
694 program_invocation_short_name);
699 static int parse_argv(int argc, char *argv[]) {
708 static const struct option options[] = {
709 { "help", no_argument, NULL, 'h' },
710 { "version", no_argument, NULL, ARG_VERSION },
711 { "property", required_argument, NULL, 'p' },
712 { "all", no_argument, NULL, 'a' },
713 { "full", no_argument, NULL, 'l' },
714 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
715 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
716 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
717 { "signal", required_argument, NULL, 's' },
718 { "host", required_argument, NULL, 'H' },
719 { "machine", required_argument, NULL, 'M' },
728 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
736 puts(PACKAGE_STRING);
737 puts(SYSTEMD_FEATURES);
741 r = strv_extend(&arg_property, optarg);
745 /* If the user asked for a particular
746 * property, show it to him, even if it is
768 arg_kill_who = optarg;
772 arg_signal = signal_from_string_try_harder(optarg);
773 if (arg_signal < 0) {
774 log_error("Failed to parse signal string %s.", optarg);
780 arg_transport = BUS_TRANSPORT_REMOTE;
785 arg_transport = BUS_TRANSPORT_CONTAINER;
793 assert_not_reached("Unhandled option");
800 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
802 static const struct {
810 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
812 { "list", LESS, 1, list_machines },
813 { "status", MORE, 2, show },
814 { "show", MORE, 1, show },
815 { "terminate", MORE, 2, terminate_machine },
816 { "reboot", MORE, 2, reboot_machine },
817 { "kill", MORE, 2, kill_machine },
818 { "login", MORE, 2, login_machine },
827 left = argc - optind;
830 /* Special rule: no arguments means "list" */
833 if (streq(argv[optind], "help")) {
838 for (i = 0; i < ELEMENTSOF(verbs); i++)
839 if (streq(argv[optind], verbs[i].verb))
842 if (i >= ELEMENTSOF(verbs)) {
843 log_error("Unknown operation %s", argv[optind]);
848 switch (verbs[i].argc_cmp) {
851 if (left != verbs[i].argc) {
852 log_error("Invalid number of arguments.");
859 if (left < verbs[i].argc) {
860 log_error("Too few arguments.");
867 if (left > verbs[i].argc) {
868 log_error("Too many arguments.");
875 assert_not_reached("Unknown comparison operator.");
878 return verbs[i].dispatch(bus, argv + optind, left);
881 int main(int argc, char*argv[]) {
882 _cleanup_bus_unref_ sd_bus *bus = NULL;
885 setlocale(LC_ALL, "");
886 log_parse_environment();
889 r = parse_argv(argc, argv);
893 r = bus_open_transport(arg_transport, arg_host, false, &bus);
895 log_error("Failed to create bus connection: %s", strerror(-r));
899 r = machinectl_main(bus, argc, argv);
904 strv_free(arg_property);
906 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;