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 bool arg_ask_password = true;
53 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
54 static char *arg_host = NULL;
56 static void pager_open_if_enabled(void) {
58 /* Cache result before we open the pager */
65 static int list_machines(sd_bus *bus, char **args, unsigned n) {
66 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
67 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
68 const char *name, *class, *service, *object;
72 pager_open_if_enabled();
74 r = sd_bus_call_method(
76 "org.freedesktop.machine1",
77 "/org/freedesktop/machine1",
78 "org.freedesktop.machine1.Manager",
84 log_error("Could not get machines: %s", bus_error_message(&error, -r));
89 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
91 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
93 return bus_log_parse_error(r);
95 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
96 printf("%-32s %-9s %-16s\n", name, class, service);
101 return bus_log_parse_error(r);
103 r = sd_bus_message_exit_container(reply);
105 return bus_log_parse_error(r);
108 printf("\n%u machines listed.\n", k);
113 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
114 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
115 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
116 _cleanup_free_ char *path = NULL;
124 if (arg_transport == BUS_TRANSPORT_REMOTE)
127 path = unit_dbus_path_from_name(unit);
131 r = sd_bus_get_property(
133 "org.freedesktop.systemd1",
135 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
141 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
145 r = sd_bus_message_read(reply, "s", &cgroup);
147 return bus_log_parse_error(r);
152 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
156 arg_all * OUTPUT_SHOW_ALL |
157 arg_full * OUTPUT_FULL_WIDTH;
165 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
169 typedef struct MachineStatusInfo {
175 char *root_directory;
180 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
181 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
182 char since2[FORMAT_TIMESTAMP_MAX], *s2;
185 fputs(strna(i->name), stdout);
187 if (!sd_id128_equal(i->id, SD_ID128_NULL))
188 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
192 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
193 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
196 printf("\t Since: %s; %s\n", s2, s1);
198 printf("\t Since: %s\n", s2);
201 _cleanup_free_ char *t = NULL;
203 printf("\t Leader: %u", (unsigned) i->leader);
205 get_process_comm(i->leader, &t);
213 printf("\t Service: %s", i->service);
216 printf("; class %s", i->class);
220 printf("\t Class: %s\n", i->class);
222 if (i->root_directory)
223 printf("\t Root: %s\n", i->root_directory);
226 printf("\t Unit: %s\n", i->unit);
227 show_unit_cgroup(bus, i->unit, i->leader);
231 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
233 static const struct bus_properties_map map[] = {
234 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
235 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
236 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
237 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
238 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
239 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
240 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
241 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
245 MachineStatusInfo info = {};
251 r = bus_map_all_properties(bus,
252 "org.freedesktop.machine1",
257 log_error("Could not get properties: %s", strerror(-r));
265 print_machine_status_info(bus, &info);
271 free(info.root_directory);
276 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
284 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
286 log_error("Could not get properties: %s", strerror(-r));
291 static int show(sd_bus *bus, char **args, unsigned n) {
292 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
293 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
296 bool properties, new_line = false;
301 properties = !strstr(args[0], "status");
303 pager_open_if_enabled();
305 if (properties && n <= 1) {
307 /* If no argument is specified, inspect the manager
309 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
314 for (i = 1; i < n; i++) {
315 const char *path = NULL;
317 r = sd_bus_call_method(
319 "org.freedesktop.machine1",
320 "/org/freedesktop/machine1",
321 "org.freedesktop.machine1.Manager",
327 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
331 r = sd_bus_message_read(reply, "o", &path);
333 return bus_log_parse_error(r);
336 r = show_properties(bus, path, &new_line);
338 r = show_info(args[0], bus, path, &new_line);
344 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
345 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
351 arg_kill_who = "all";
353 for (i = 1; i < n; i++) {
356 r = sd_bus_call_method(
358 "org.freedesktop.machine1",
359 "/org/freedesktop/machine1",
360 "org.freedesktop.machine1.Manager",
364 "ssi", args[i], arg_kill_who, arg_signal);
366 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
374 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
375 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
380 for (i = 1; i < n; i++) {
383 r = sd_bus_call_method(
385 "org.freedesktop.machine1",
386 "/org/freedesktop/machine1",
387 "org.freedesktop.machine1.Manager",
393 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
401 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
402 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
408 if (arg_transport != BUS_TRANSPORT_LOCAL) {
409 log_error("Reboot only supported on local machines.");
413 for (i = 1; i < n; i++) {
414 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
418 r = sd_bus_call_method(
420 "org.freedesktop.machine1",
421 "/org/freedesktop/machine1",
422 "org.freedesktop.machine1.Manager",
429 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
433 r = sd_bus_message_read(reply, "o", &path);
435 return bus_log_parse_error(r);
437 r = sd_bus_get_property(
439 "org.freedesktop.machine1",
441 "org.freedesktop.machine1.Machine",
447 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
451 r = sd_bus_message_read(reply2, "u", &leader);
453 return bus_log_parse_error(r);
455 if (kill(leader, SIGINT) < 0) {
456 log_error("Failed to kill init process " PID_FMT ": %m", (pid_t) leader);
464 static int openpt_in_namespace(pid_t pid, int flags) {
465 _cleanup_close_pipe_ int pair[2] = { -1, -1 };
466 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
468 struct cmsghdr cmsghdr;
469 uint8_t buf[CMSG_SPACE(sizeof(int))];
472 .msg_control = &control,
473 .msg_controllen = sizeof(control),
475 struct cmsghdr *cmsg;
480 r = namespace_open(pid, &pidnsfd, &mntnsfd, &rootfd);
484 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
492 close_nointr_nofail(pair[0]);
495 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
499 master = posix_openpt(flags);
503 cmsg = CMSG_FIRSTHDR(&mh);
504 cmsg->cmsg_level = SOL_SOCKET;
505 cmsg->cmsg_type = SCM_RIGHTS;
506 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
507 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
509 mh.msg_controllen = cmsg->cmsg_len;
511 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
517 close_nointr_nofail(pair[1]);
520 r = wait_for_terminate(child, &si);
521 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
523 return r < 0 ? r : -EIO;
526 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
529 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
530 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
534 fds = (int*) CMSG_DATA(cmsg);
535 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
538 close_many(fds, n_fds);
551 static int login_machine(sd_bus *bus, char **args, unsigned n) {
552 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
553 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
554 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
555 _cleanup_close_ int master = -1;
556 _cleanup_free_ char *getty = NULL;
557 const char *path, *pty, *p;
565 if (arg_transport != BUS_TRANSPORT_LOCAL) {
566 log_error("Login only supported on local machines.");
570 r = sd_bus_call_method(
572 "org.freedesktop.machine1",
573 "/org/freedesktop/machine1",
574 "org.freedesktop.machine1.Manager",
580 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
584 r = sd_bus_message_read(reply, "o", &path);
586 return bus_log_parse_error(r);
588 r = sd_bus_get_property(
590 "org.freedesktop.machine1",
592 "org.freedesktop.machine1.Machine",
598 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
602 r = sd_bus_message_read(reply2, "u", &leader);
604 return bus_log_parse_error(r);
606 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
608 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
612 pty = ptsname(master);
614 log_error("Failed to get pty name: %m");
618 p = startswith(pty, "/dev/pts/");
620 log_error("Invalid pty name %s.", pty);
624 r = sd_bus_open_system_container(args[1], &container_bus);
626 log_error("Failed to get container bus: %s", strerror(-r));
630 getty = strjoin("container-getty@", p, ".service", NULL);
634 if (unlockpt(master) < 0) {
635 log_error("Failed to unlock tty: %m");
639 r = sd_bus_call_method(container_bus,
640 "org.freedesktop.systemd1",
641 "/org/freedesktop/systemd1",
642 "org.freedesktop.systemd1.Manager",
645 "ss", getty, "replace");
647 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
651 container_bus = sd_bus_unref(container_bus);
653 assert_se(sigemptyset(&mask) == 0);
654 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
655 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
657 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
659 r = process_pty(master, &mask, 0, 0);
661 log_error("Failed to process pseudo tty: %s", strerror(-r));
667 log_info("Connection to container %s terminated.", args[1]);
672 static int help(void) {
674 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
675 "Send control commands to or query the virtual machine and container registration manager.\n\n"
676 " -h --help Show this help\n"
677 " --version Show package version\n"
678 " --no-pager Do not pipe output into a pager\n"
679 " --no-legend Do not show the headers and footers\n"
680 " --no-ask-password Don't prompt for password\n"
681 " -H --host=[USER@]HOST Operate on remote host\n"
682 " -M --machine=CONTAINER Operate on local container\n"
683 " -p --property=NAME Show only properties by this name\n"
684 " -a --all Show all properties, including empty ones\n"
685 " -l --full Do not ellipsize output\n"
686 " --kill-who=WHO Who to send signal to\n"
687 " -s --signal=SIGNAL Which signal to send\n\n"
689 " list List running VMs and containers\n"
690 " status NAME... Show VM/container status\n"
691 " show NAME... Show properties of one or more VMs/containers\n"
692 " terminate NAME... Terminate one or more VMs/containers\n"
693 " kill NAME... Send signal to processes of a VM/container\n"
694 " reboot NAME... Reboot one or more containers\n"
695 " login NAME Get a login prompt on a container\n",
696 program_invocation_short_name);
701 static int parse_argv(int argc, char *argv[]) {
711 static const struct option options[] = {
712 { "help", no_argument, NULL, 'h' },
713 { "version", no_argument, NULL, ARG_VERSION },
714 { "property", required_argument, NULL, 'p' },
715 { "all", no_argument, NULL, 'a' },
716 { "full", no_argument, NULL, 'l' },
717 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
718 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
719 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
720 { "signal", required_argument, NULL, 's' },
721 { "host", required_argument, NULL, 'H' },
722 { "machine", required_argument, NULL, 'M' },
723 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
732 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
740 puts(PACKAGE_STRING);
741 puts(SYSTEMD_FEATURES);
745 r = strv_extend(&arg_property, optarg);
749 /* If the user asked for a particular
750 * property, show it to him, even if it is
771 case ARG_NO_ASK_PASSWORD:
772 arg_ask_password = false;
776 arg_kill_who = optarg;
780 arg_signal = signal_from_string_try_harder(optarg);
781 if (arg_signal < 0) {
782 log_error("Failed to parse signal string %s.", optarg);
788 arg_transport = BUS_TRANSPORT_REMOTE;
793 arg_transport = BUS_TRANSPORT_CONTAINER;
801 assert_not_reached("Unhandled option");
808 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
810 static const struct {
818 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
820 { "list", LESS, 1, list_machines },
821 { "status", MORE, 2, show },
822 { "show", MORE, 1, show },
823 { "terminate", MORE, 2, terminate_machine },
824 { "reboot", MORE, 2, reboot_machine },
825 { "kill", MORE, 2, kill_machine },
826 { "login", MORE, 2, login_machine },
835 left = argc - optind;
838 /* Special rule: no arguments means "list" */
841 if (streq(argv[optind], "help")) {
846 for (i = 0; i < ELEMENTSOF(verbs); i++)
847 if (streq(argv[optind], verbs[i].verb))
850 if (i >= ELEMENTSOF(verbs)) {
851 log_error("Unknown operation %s", argv[optind]);
856 switch (verbs[i].argc_cmp) {
859 if (left != verbs[i].argc) {
860 log_error("Invalid number of arguments.");
867 if (left < verbs[i].argc) {
868 log_error("Too few arguments.");
875 if (left > verbs[i].argc) {
876 log_error("Too many arguments.");
883 assert_not_reached("Unknown comparison operator.");
886 return verbs[i].dispatch(bus, argv + optind, left);
889 int main(int argc, char*argv[]) {
890 _cleanup_bus_unref_ sd_bus *bus = NULL;
893 setlocale(LC_ALL, "");
894 log_parse_environment();
897 r = parse_argv(argc, argv);
901 r = bus_open_transport(arg_transport, arg_host, false, &bus);
903 log_error("Failed to create bus connection: %s", strerror(-r));
907 r = machinectl_main(bus, argc, argv);
912 strv_free(arg_property);
914 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;