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 const char *arg_kill_who = NULL;
50 static int arg_signal = SIGTERM;
51 static bool arg_ask_password = true;
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));
87 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
89 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
91 return bus_log_parse_error(r);
93 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
94 printf("%-32s %-9s %-16s\n", name, class, service);
99 return bus_log_parse_error(r);
101 r = sd_bus_message_exit_container(reply);
103 return bus_log_parse_error(r);
105 printf("\n%u machines listed.\n", k);
110 static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
111 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
112 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
113 _cleanup_free_ char *path = NULL;
121 if (arg_transport == BUS_TRANSPORT_REMOTE)
124 path = unit_dbus_path_from_name(unit);
128 r = sd_bus_get_property(
130 "org.freedesktop.systemd1",
132 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
138 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
142 r = sd_bus_message_read(reply, "s", &cgroup);
144 return bus_log_parse_error(r);
149 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
153 arg_all * OUTPUT_SHOW_ALL |
154 arg_full * OUTPUT_FULL_WIDTH;
162 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
166 typedef struct MachineStatusInfo {
172 char *root_directory;
177 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
178 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
179 char since2[FORMAT_TIMESTAMP_MAX], *s2;
182 fputs(strna(i->name), stdout);
184 if (!sd_id128_equal(i->id, SD_ID128_NULL))
185 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
189 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
190 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
193 printf("\t Since: %s; %s\n", s2, s1);
195 printf("\t Since: %s\n", s2);
198 _cleanup_free_ char *t = NULL;
200 printf("\t Leader: %u", (unsigned) i->leader);
202 get_process_comm(i->leader, &t);
210 printf("\t Service: %s", i->service);
213 printf("; class %s", i->class);
217 printf("\t Class: %s\n", i->class);
219 if (i->root_directory)
220 printf("\t Root: %s\n", i->root_directory);
223 printf("\t Unit: %s\n", i->unit);
224 show_unit_cgroup(bus, i->unit, i->leader);
228 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
230 static const struct bus_properties_map map[] = {
231 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
232 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
233 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
234 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
235 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
236 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
237 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
238 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
242 MachineStatusInfo info = {};
248 r = bus_map_all_properties(bus,
249 "org.freedesktop.machine1",
254 log_error("Could not get properties: %s", strerror(-r));
262 print_machine_status_info(bus, &info);
268 free(info.root_directory);
273 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
281 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
283 log_error("Could not get properties: %s", strerror(-r));
288 static int show(sd_bus *bus, char **args, unsigned n) {
289 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
290 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
293 bool properties, new_line = false;
298 properties = !strstr(args[0], "status");
300 pager_open_if_enabled();
302 if (properties && n <= 1) {
304 /* If no argument is specified, inspect the manager
306 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
311 for (i = 1; i < n; i++) {
312 const char *path = NULL;
314 r = sd_bus_call_method(
316 "org.freedesktop.machine1",
317 "/org/freedesktop/machine1",
318 "org.freedesktop.machine1.Manager",
324 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
328 r = sd_bus_message_read(reply, "o", &path);
330 return bus_log_parse_error(r);
333 r = show_properties(bus, path, &new_line);
335 r = show_info(args[0], bus, path, &new_line);
341 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
342 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
348 arg_kill_who = "all";
350 for (i = 1; i < n; i++) {
353 r = sd_bus_call_method(
355 "org.freedesktop.machine1",
356 "/org/freedesktop/machine1",
357 "org.freedesktop.machine1.Manager",
361 "ssi", args[i], arg_kill_who, arg_signal);
363 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
371 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
372 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
377 for (i = 1; i < n; i++) {
380 r = sd_bus_call_method(
382 "org.freedesktop.machine1",
383 "/org/freedesktop/machine1",
384 "org.freedesktop.machine1.Manager",
390 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
398 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
399 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
405 if (arg_transport != BUS_TRANSPORT_LOCAL) {
406 log_error("Reboot only supported on local machines.");
410 for (i = 1; i < n; i++) {
411 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL;
415 r = sd_bus_call_method(
417 "org.freedesktop.machine1",
418 "/org/freedesktop/machine1",
419 "org.freedesktop.machine1.Manager",
426 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
430 r = sd_bus_message_read(reply, "o", &path);
432 return bus_log_parse_error(r);
434 r = sd_bus_get_property(
436 "org.freedesktop.machine1",
438 "org.freedesktop.machine1.Machine",
444 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
448 r = sd_bus_message_read(reply2, "u", &leader);
450 return bus_log_parse_error(r);
452 if (kill(leader, SIGINT) < 0) {
453 log_error("Failed to kill init process " PID_FMT ": %m", (pid_t) leader);
461 static int openpt_in_namespace(pid_t pid, int flags) {
462 _cleanup_close_pipe_ int pair[2] = { -1, -1 };
463 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
465 struct cmsghdr cmsghdr;
466 uint8_t buf[CMSG_SPACE(sizeof(int))];
469 .msg_control = &control,
470 .msg_controllen = sizeof(control),
472 struct cmsghdr *cmsg;
477 r = namespace_open(pid, &pidnsfd, &mntnsfd, &rootfd);
481 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
489 close_nointr_nofail(pair[0]);
492 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
496 master = posix_openpt(flags);
500 cmsg = CMSG_FIRSTHDR(&mh);
501 cmsg->cmsg_level = SOL_SOCKET;
502 cmsg->cmsg_type = SCM_RIGHTS;
503 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
504 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
506 mh.msg_controllen = cmsg->cmsg_len;
508 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
514 close_nointr_nofail(pair[1]);
517 r = wait_for_terminate(child, &si);
518 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
520 return r < 0 ? r : -EIO;
523 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
526 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
527 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
531 fds = (int*) CMSG_DATA(cmsg);
532 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
535 close_many(fds, n_fds);
548 static int login_machine(sd_bus *bus, char **args, unsigned n) {
549 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
550 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
551 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
552 _cleanup_close_ int master = -1;
553 _cleanup_free_ char *getty = NULL;
554 const char *path, *pty, *p;
562 if (arg_transport != BUS_TRANSPORT_LOCAL) {
563 log_error("Login only supported on local machines.");
567 r = sd_bus_call_method(
569 "org.freedesktop.machine1",
570 "/org/freedesktop/machine1",
571 "org.freedesktop.machine1.Manager",
577 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
581 r = sd_bus_message_read(reply, "o", &path);
583 return bus_log_parse_error(r);
585 r = sd_bus_get_property(
587 "org.freedesktop.machine1",
589 "org.freedesktop.machine1.Machine",
595 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
599 r = sd_bus_message_read(reply2, "u", &leader);
601 return bus_log_parse_error(r);
603 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
605 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
609 pty = ptsname(master);
611 log_error("Failed to get pty name: %m");
615 p = startswith(pty, "/dev/pts/");
617 log_error("Invalid pty name %s.", pty);
621 r = sd_bus_open_system_container(args[1], &container_bus);
623 log_error("Failed to get container bus: %s", strerror(-r));
627 getty = strjoin("container-getty@", p, ".service", NULL);
631 if (unlockpt(master) < 0) {
632 log_error("Failed to unlock tty: %m");
636 r = sd_bus_call_method(container_bus,
637 "org.freedesktop.systemd1",
638 "/org/freedesktop/systemd1",
639 "org.freedesktop.systemd1.Manager",
642 "ss", getty, "replace");
644 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
648 container_bus = sd_bus_unref(container_bus);
650 assert_se(sigemptyset(&mask) == 0);
651 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
652 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
654 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
656 r = process_pty(master, &mask, 0, 0);
658 log_error("Failed to process pseudo tty: %s", strerror(-r));
664 log_info("Connection to container %s terminated.", args[1]);
669 static int help(void) {
671 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
672 "Send control commands to or query the virtual machine and container registration manager.\n\n"
673 " -h --help Show this help\n"
674 " --version Show package version\n"
675 " --no-pager Do not pipe output into a pager\n"
676 " --no-ask-password Don't prompt for password\n"
677 " -H --host=[USER@]HOST Operate on remote host\n"
678 " -M --machine=CONTAINER Operate on local container\n"
679 " -p --property=NAME Show only properties by this name\n"
680 " -a --all Show all properties, including empty ones\n"
681 " -l --full Do not ellipsize output\n"
682 " --kill-who=WHO Who to send signal to\n"
683 " -s --signal=SIGNAL Which signal to send\n\n"
685 " list List running VMs and containers\n"
686 " status NAME... Show VM/container status\n"
687 " show NAME... Show properties of one or more VMs/containers\n"
688 " terminate NAME... Terminate one or more VMs/containers\n"
689 " kill NAME... Send signal to processes of a VM/container\n"
690 " reboot NAME... Reboot one or more containers\n"
691 " login NAME Get a login prompt on a container\n",
692 program_invocation_short_name);
697 static int parse_argv(int argc, char *argv[]) {
706 static const struct option options[] = {
707 { "help", no_argument, NULL, 'h' },
708 { "version", no_argument, NULL, ARG_VERSION },
709 { "property", required_argument, NULL, 'p' },
710 { "all", no_argument, NULL, 'a' },
711 { "full", no_argument, NULL, 'l' },
712 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
713 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
714 { "signal", required_argument, NULL, 's' },
715 { "host", required_argument, NULL, 'H' },
716 { "machine", required_argument, NULL, 'M' },
717 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
726 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
734 puts(PACKAGE_STRING);
735 puts(SYSTEMD_FEATURES);
739 r = strv_extend(&arg_property, optarg);
743 /* If the user asked for a particular
744 * property, show it to him, even if it is
761 case ARG_NO_ASK_PASSWORD:
762 arg_ask_password = false;
766 arg_kill_who = optarg;
770 arg_signal = signal_from_string_try_harder(optarg);
771 if (arg_signal < 0) {
772 log_error("Failed to parse signal string %s.", optarg);
778 arg_transport = BUS_TRANSPORT_REMOTE;
783 arg_transport = BUS_TRANSPORT_CONTAINER;
791 assert_not_reached("Unhandled option");
798 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
800 static const struct {
808 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
810 { "list", LESS, 1, list_machines },
811 { "status", MORE, 2, show },
812 { "show", MORE, 1, show },
813 { "terminate", MORE, 2, terminate_machine },
814 { "reboot", MORE, 2, reboot_machine },
815 { "kill", MORE, 2, kill_machine },
816 { "login", MORE, 2, login_machine },
825 left = argc - optind;
828 /* Special rule: no arguments means "list" */
831 if (streq(argv[optind], "help")) {
836 for (i = 0; i < ELEMENTSOF(verbs); i++)
837 if (streq(argv[optind], verbs[i].verb))
840 if (i >= ELEMENTSOF(verbs)) {
841 log_error("Unknown operation %s", argv[optind]);
846 switch (verbs[i].argc_cmp) {
849 if (left != verbs[i].argc) {
850 log_error("Invalid number of arguments.");
857 if (left < verbs[i].argc) {
858 log_error("Too few arguments.");
865 if (left > verbs[i].argc) {
866 log_error("Too many arguments.");
873 assert_not_reached("Unknown comparison operator.");
876 return verbs[i].dispatch(bus, argv + optind, left);
879 int main(int argc, char*argv[]) {
880 _cleanup_bus_unref_ sd_bus *bus = NULL;
883 setlocale(LC_ALL, "");
884 log_parse_environment();
887 r = parse_argv(argc, argv);
891 r = bus_open_transport(arg_transport, arg_host, false, &bus);
893 log_error("Failed to create bus connection: %s", strerror(-r));
897 r = machinectl_main(bus, argc, argv);
902 strv_free(arg_property);
904 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;