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_scope_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 "org.freedesktop.systemd1.Scope",
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->scope);
224 show_scope_cgroup(bus, i->scope, i->leader);
228 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
229 MachineStatusInfo info = {};
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 { "Scope", "s", NULL, offsetof(MachineStatusInfo, scope) },
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) },
246 r = bus_map_all_properties(bus,
247 "org.freedesktop.machine1",
252 log_error("Could not get properties: %s", strerror(-r));
260 print_machine_status_info(bus, &info);
266 free(info.root_directory);
271 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
279 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
281 log_error("Could not get properties: %s", strerror(-r));
286 static int show(sd_bus *bus, char **args, unsigned n) {
287 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
288 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
291 bool properties, new_line = false;
296 properties = !strstr(args[0], "status");
298 pager_open_if_enabled();
300 if (properties && n <= 1) {
302 /* If no argument is specified, inspect the manager
304 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
306 log_error("Failed to query properties: %s", strerror(-r));
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 openpt_in_namespace(pid_t pid, int flags) {
399 _cleanup_close_ int nsfd = -1, rootfd = -1;
400 _cleanup_free_ char *ns = NULL, *root = NULL;
401 _cleanup_close_pipe_ int sock[2] = { -1, -1 };
403 struct cmsghdr cmsghdr;
404 uint8_t buf[CMSG_SPACE(sizeof(int))];
407 .msg_control = &control,
408 .msg_controllen = sizeof(control),
410 struct cmsghdr *cmsg;
415 r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) pid);
419 nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
423 r = asprintf(&root, "/proc/%lu/root", (unsigned long) pid);
427 rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
431 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0)
439 close_nointr_nofail(sock[0]);
442 r = setns(nsfd, CLONE_NEWNS);
446 if (fchdir(rootfd) < 0)
452 master = posix_openpt(flags);
456 cmsg = CMSG_FIRSTHDR(&mh);
457 cmsg->cmsg_level = SOL_SOCKET;
458 cmsg->cmsg_type = SCM_RIGHTS;
459 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
460 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
462 mh.msg_controllen = cmsg->cmsg_len;
464 r = sendmsg(sock[1], &mh, MSG_NOSIGNAL);
465 close_nointr_nofail(master);
472 close_nointr_nofail(sock[1]);
475 if (recvmsg(sock[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
478 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
479 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
483 fds = (int*) CMSG_DATA(cmsg);
484 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
487 close_many(fds, n_fds);
494 r = wait_for_terminate(child, &si);
495 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS || master < 0) {
498 close_nointr_nofail(master);
500 return r < 0 ? r : -EIO;
506 static int login_machine(sd_bus *bus, char **args, unsigned n) {
507 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
508 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
509 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
510 _cleanup_close_ int master = -1;
511 _cleanup_free_ char *getty = NULL;
512 const char *path, *pty, *p;
520 if (arg_transport != BUS_TRANSPORT_LOCAL) {
521 log_error("Login only support on local machines.");
525 r = sd_bus_call_method(
527 "org.freedesktop.machine1",
528 "/org/freedesktop/machine1",
529 "org.freedesktop.machine1.Manager",
535 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
539 r = sd_bus_message_read(reply, "o", &path);
541 return bus_log_parse_error(r);
543 r = sd_bus_get_property(
545 "org.freedesktop.machine1",
547 "org.freedesktop.machine1.Machine",
553 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
557 r = sd_bus_message_read(reply2, "u", &leader);
559 return bus_log_parse_error(r);
561 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
563 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
567 pty = ptsname(master);
569 log_error("Failed to get pty name: %m");
573 p = startswith(pty, "/dev/pts/");
575 log_error("Invalid pty name %s.", pty);
579 r = sd_bus_open_system_container(args[1], &container_bus);
581 log_error("Failed to get container bus: %s", strerror(-r));
585 getty = strjoin("container-getty@", p, ".service", NULL);
589 if (unlockpt(master) < 0) {
590 log_error("Failed to unlock tty: %m");
594 r = sd_bus_call_method(container_bus,
595 "org.freedesktop.systemd1",
596 "/org/freedesktop/systemd1",
597 "org.freedesktop.systemd1.Manager",
600 "ss", getty, "replace");
602 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
606 assert_se(sigemptyset(&mask) == 0);
607 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
608 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
610 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
612 r = process_pty(master, &mask, 0, 0);
614 log_error("Failed to process pseudo tty: %s", strerror(-r));
620 log_info("Connection to container %s terminated.", args[1]);
625 static int help(void) {
627 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
628 "Send control commands to or query the virtual machine and container registration manager.\n\n"
629 " -h --help Show this help\n"
630 " --version Show package version\n"
631 " --no-pager Do not pipe output into a pager\n"
632 " --no-ask-password Don't prompt for password\n"
633 " -H --host=[USER@]HOST Operate on remote host\n"
634 " -M --machine=CONTAINER Operate on local container\n"
635 " -p --property=NAME Show only properties by this name\n"
636 " -a --all Show all properties, including empty ones\n"
637 " -l --full Do not ellipsize output\n"
638 " --kill-who=WHO Who to send signal to\n"
639 " -s --signal=SIGNAL Which signal to send\n\n"
641 " list List running VMs and containers\n"
642 " status [NAME...] Show VM/container status\n"
643 " show [NAME...] Show properties of one or more VMs/containers\n"
644 " terminate [NAME...] Terminate one or more VMs/containers\n"
645 " kill [NAME...] Send signal to processes of a VM/container\n"
646 " login [NAME] Get a login prompt on a container\n",
647 program_invocation_short_name);
652 static int parse_argv(int argc, char *argv[]) {
661 static const struct option options[] = {
662 { "help", no_argument, NULL, 'h' },
663 { "version", no_argument, NULL, ARG_VERSION },
664 { "property", required_argument, NULL, 'p' },
665 { "all", no_argument, NULL, 'a' },
666 { "full", no_argument, NULL, 'l' },
667 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
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' },
672 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
681 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
689 puts(PACKAGE_STRING);
690 puts(SYSTEMD_FEATURES);
694 r = strv_extend(&arg_property, optarg);
698 /* If the user asked for a particular
699 * property, show it to him, even if it is
716 case ARG_NO_ASK_PASSWORD:
717 arg_ask_password = false;
721 arg_kill_who = optarg;
725 arg_signal = signal_from_string_try_harder(optarg);
726 if (arg_signal < 0) {
727 log_error("Failed to parse signal string %s.", optarg);
733 arg_transport = BUS_TRANSPORT_REMOTE;
738 arg_transport = BUS_TRANSPORT_CONTAINER;
746 assert_not_reached("Unhandled option");
753 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
755 static const struct {
763 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
765 { "list", LESS, 1, list_machines },
766 { "status", MORE, 2, show },
767 { "show", MORE, 1, show },
768 { "terminate", MORE, 2, terminate_machine },
769 { "kill", MORE, 2, kill_machine },
770 { "login", MORE, 2, login_machine },
779 left = argc - optind;
782 /* Special rule: no arguments means "list" */
785 if (streq(argv[optind], "help")) {
790 for (i = 0; i < ELEMENTSOF(verbs); i++)
791 if (streq(argv[optind], verbs[i].verb))
794 if (i >= ELEMENTSOF(verbs)) {
795 log_error("Unknown operation %s", argv[optind]);
800 switch (verbs[i].argc_cmp) {
803 if (left != verbs[i].argc) {
804 log_error("Invalid number of arguments.");
811 if (left < verbs[i].argc) {
812 log_error("Too few arguments.");
819 if (left > verbs[i].argc) {
820 log_error("Too many arguments.");
827 assert_not_reached("Unknown comparison operator.");
830 return verbs[i].dispatch(bus, argv + optind, left);
833 int main(int argc, char*argv[]) {
834 _cleanup_bus_unref_ sd_bus *bus = NULL;
837 setlocale(LC_ALL, "");
838 log_parse_environment();
841 r = parse_argv(argc, argv);
845 r = bus_open_transport(arg_transport, arg_host, false, &bus);
847 log_error("Failed to create bus connection: %s", strerror(-r));
851 r = machinectl_main(bus, argc, argv);
856 strv_free(arg_property);
858 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;