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/>.
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));
88 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
90 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
94 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
95 printf("%-32s %-9s %-16s\n", name, class, service);
102 r = sd_bus_message_exit_container(reply);
107 printf("\n%u machines listed.\n", k);
112 log_error("Failed to parse reply: %s", strerror(-r));
116 static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
117 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
118 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
119 _cleanup_free_ char *path = NULL;
127 if (arg_transport == BUS_TRANSPORT_REMOTE)
130 path = unit_dbus_path_from_name(unit);
134 r = sd_bus_get_property(
136 "org.freedesktop.systemd1",
138 "org.freedesktop.systemd1.Scope",
144 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
148 r = sd_bus_message_read(reply, "s", &cgroup);
150 log_error("Failed to parse reply: %s", strerror(-r));
157 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
161 arg_all * OUTPUT_SHOW_ALL |
162 arg_full * OUTPUT_FULL_WIDTH;
170 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
174 typedef struct MachineStatusInfo {
180 const char *root_directory;
185 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
186 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
187 char since2[FORMAT_TIMESTAMP_MAX], *s2;
190 fputs(strna(i->name), stdout);
192 if (!sd_id128_equal(i->id, SD_ID128_NULL))
193 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
197 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
198 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
201 printf("\t Since: %s; %s\n", s2, s1);
203 printf("\t Since: %s\n", s2);
206 _cleanup_free_ char *t = NULL;
208 printf("\t Leader: %u", (unsigned) i->leader);
210 get_process_comm(i->leader, &t);
218 printf("\t Service: %s", i->service);
221 printf("; class %s", i->class);
225 printf("\t Class: %s\n", i->class);
227 if (i->root_directory)
228 printf("\t Root: %s\n", i->root_directory);
231 printf("\t Unit: %s\n", i->scope);
232 show_scope_cgroup(bus, i->scope, i->leader);
236 static int status_property_machine(const char *name, sd_bus_message *property, MachineStatusInfo *i) {
238 const char *contents;
245 r = sd_bus_message_peek_type(property, &type, &contents);
247 log_error("Could not determine type of message: %s", strerror(-r));
253 case SD_BUS_TYPE_STRING: {
256 sd_bus_message_read_basic(property, type, &s);
259 if (streq(name, "Name"))
261 else if (streq(name, "Class"))
263 else if (streq(name, "Service"))
265 else if (streq(name, "Scope"))
267 else if (streq(name, "RootDirectory"))
268 i->root_directory = s;
273 case SD_BUS_TYPE_UINT32: {
276 sd_bus_message_read_basic(property, type, &u);
278 if (streq(name, "Leader"))
279 i->leader = (pid_t) u;
284 case SD_BUS_TYPE_UINT64: {
287 sd_bus_message_read_basic(property, type, &u);
289 if (streq(name, "Timestamp"))
290 i->timestamp = (usec_t) u;
295 case SD_BUS_TYPE_ARRAY: {
296 if (streq(contents, "y") && streq(name, "Id")) {
300 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, &v, &n);
302 i->id = SD_ID128_NULL;
304 memcpy(&i->id, v, n);
314 static int print_property(const char *name, sd_bus_message *reply) {
318 if (arg_property && !strv_find(arg_property, name))
321 if (bus_generic_print_property(name, reply, arg_all) > 0)
325 printf("%s=[unprintable]\n", name);
330 static int show_one(const char *verb, sd_bus *bus, const char *path, bool show_properties, bool *new_line) {
331 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
332 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
334 MachineStatusInfo machine_info = {};
339 r = sd_bus_call_method(
341 "org.freedesktop.machine1",
343 "org.freedesktop.DBus.Properties",
349 log_error("Could not get properties: %s", bus_error_message(&error, -r));
359 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
363 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
365 const char *contents;
367 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
371 r = sd_bus_message_peek_type(reply, NULL, &contents);
375 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
380 r = print_property(name, reply);
382 r = status_property_machine(name, reply, &machine_info);
386 r = sd_bus_message_exit_container(reply);
390 r = sd_bus_message_exit_container(reply);
397 r = sd_bus_message_exit_container(reply);
401 if (!show_properties)
402 print_machine_status_info(bus, &machine_info);
407 log_error("Failed to parse reply: %s", strerror(-r));
411 static int show(sd_bus *bus, char **args, unsigned n) {
412 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
413 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
416 bool show_properties, new_line = false;
421 show_properties = !strstr(args[0], "status");
423 pager_open_if_enabled();
425 if (show_properties && n <= 1) {
427 /* If no argument is specified inspect the manager
430 return show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line);
433 for (i = 1; i < n; i++) {
434 const char *path = NULL;
436 r = sd_bus_call_method(
438 "org.freedesktop.machine1",
439 "/org/freedesktop/machine1",
440 "org.freedesktop.machine1.Manager",
446 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
450 r = sd_bus_message_read(reply, "o", &path);
452 log_error("Failed to parse reply: %s", strerror(-r));
456 r = show_one(args[0], bus, path, show_properties, &new_line);
464 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
465 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
471 arg_kill_who = "all";
473 for (i = 1; i < n; i++) {
476 r = sd_bus_call_method(
478 "org.freedesktop.machine1",
479 "/org/freedesktop/machine1",
480 "org.freedesktop.machine1.Manager",
484 "ssi", args[i], arg_kill_who, arg_signal);
486 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
494 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
495 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
500 for (i = 1; i < n; i++) {
503 r = sd_bus_call_method(
505 "org.freedesktop.machine1",
506 "/org/freedesktop/machine1",
507 "org.freedesktop.machine1.Manager",
513 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
521 static int openpt_in_namespace(pid_t pid, int flags) {
522 _cleanup_close_ int nsfd = -1, rootfd = -1;
523 _cleanup_free_ char *ns = NULL, *root = NULL;
524 _cleanup_close_pipe_ int sock[2] = { -1, -1 };
527 struct cmsghdr cmsghdr;
528 uint8_t buf[CMSG_SPACE(sizeof(int))];
530 struct cmsghdr *cmsg;
535 r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) pid);
539 nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
543 r = asprintf(&root, "/proc/%lu/root", (unsigned long) pid);
547 rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
551 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0)
556 mh.msg_control = &control;
557 mh.msg_controllen = sizeof(control);
564 close_nointr_nofail(sock[0]);
567 r = setns(nsfd, CLONE_NEWNS);
571 if (fchdir(rootfd) < 0)
577 master = posix_openpt(flags);
581 cmsg = CMSG_FIRSTHDR(&mh);
582 cmsg->cmsg_level = SOL_SOCKET;
583 cmsg->cmsg_type = SCM_RIGHTS;
584 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
585 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
587 mh.msg_controllen = cmsg->cmsg_len;
589 r = sendmsg(sock[1], &mh, MSG_NOSIGNAL);
590 close_nointr_nofail(master);
597 close_nointr_nofail(sock[1]);
600 if (recvmsg(sock[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
603 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
604 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
608 fds = (int*) CMSG_DATA(cmsg);
609 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
612 close_many(fds, n_fds);
619 r = wait_for_terminate(child, &si);
620 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS || master < 0) {
623 close_nointr_nofail(master);
625 return r < 0 ? r : -EIO;
631 static int login_machine(sd_bus *bus, char **args, unsigned n) {
632 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
633 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
634 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
635 _cleanup_close_ int master = -1;
636 _cleanup_free_ char *getty = NULL;
637 const char *path, *pty, *p;
645 if (arg_transport != BUS_TRANSPORT_LOCAL) {
646 log_error("Login only support on local machines.");
650 r = sd_bus_call_method(
652 "org.freedesktop.machine1",
653 "/org/freedesktop/machine1",
654 "org.freedesktop.machine1.Manager",
660 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
664 r = sd_bus_message_read(reply, "o", &path);
666 log_error("Failed to parse reply: %s", strerror(-r));
670 r = sd_bus_get_property(
672 "org.freedesktop.machine1",
674 "org.freedesktop.machine1.Machine",
680 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
684 r = sd_bus_message_read(reply2, "u", &leader);
686 log_error("Failed to parse reply: %s", strerror(-r));
690 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
692 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
696 pty = ptsname(master);
698 log_error("Failed to get pty name: %m");
702 p = startswith(pty, "/dev/pts/");
704 log_error("Invalid pty name %s.", pty);
708 r = sd_bus_open_system_container(args[1], &container_bus);
710 log_error("Failed to get container bus: %s", strerror(-r));
714 getty = strjoin("container-getty@", p, ".service", NULL);
718 if (unlockpt(master) < 0) {
719 log_error("Failed to unlock tty: %m");
723 r = sd_bus_call_method(container_bus,
724 "org.freedesktop.systemd1",
725 "/org/freedesktop/systemd1",
726 "org.freedesktop.systemd1.Manager",
729 "ss", getty, "replace");
731 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
735 assert_se(sigemptyset(&mask) == 0);
736 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
737 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
739 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
741 r = process_pty(master, &mask, 0, 0);
743 log_error("Failed to process pseudo tty: %s", strerror(-r));
749 log_info("Connection to container %s terminated.", args[1]);
754 static int help(void) {
756 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
757 "Send control commands to or query the virtual machine and container registration manager.\n\n"
758 " -h --help Show this help\n"
759 " --version Show package version\n"
760 " --no-pager Do not pipe output into a pager\n"
761 " --no-ask-password Don't prompt for password\n"
762 " -H --host=[USER@]HOST Operate on remote host\n"
763 " -M --machine=CONTAINER Operate on local container\n"
764 " -p --property=NAME Show only properties by this name\n"
765 " -a --all Show all properties, including empty ones\n"
766 " -l --full Do not ellipsize output\n"
767 " --kill-who=WHO Who to send signal to\n"
768 " -s --signal=SIGNAL Which signal to send\n\n"
770 " list List running VMs and containers\n"
771 " status [NAME...] Show VM/container status\n"
772 " show [NAME...] Show properties of one or more VMs/containers\n"
773 " terminate [NAME...] Terminate one or more VMs/containers\n"
774 " kill [NAME...] Send signal to processes of a VM/container\n"
775 " login [NAME] Get a login prompt on a container\n",
776 program_invocation_short_name);
781 static int parse_argv(int argc, char *argv[]) {
790 static const struct option options[] = {
791 { "help", no_argument, NULL, 'h' },
792 { "version", no_argument, NULL, ARG_VERSION },
793 { "property", required_argument, NULL, 'p' },
794 { "all", no_argument, NULL, 'a' },
795 { "full", no_argument, NULL, 'l' },
796 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
797 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
798 { "signal", required_argument, NULL, 's' },
799 { "host", required_argument, NULL, 'H' },
800 { "machine", required_argument, NULL, 'M' },
801 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
810 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
819 puts(PACKAGE_STRING);
820 puts(SYSTEMD_FEATURES);
824 r = strv_extend(&arg_property, optarg);
828 /* If the user asked for a particular
829 * property, show it to him, even if it is
846 case ARG_NO_ASK_PASSWORD:
847 arg_ask_password = false;
851 arg_kill_who = optarg;
855 arg_signal = signal_from_string_try_harder(optarg);
856 if (arg_signal < 0) {
857 log_error("Failed to parse signal string %s.", optarg);
863 arg_transport = BUS_TRANSPORT_REMOTE;
868 arg_transport = BUS_TRANSPORT_CONTAINER;
876 log_error("Unknown option code %c", c);
884 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
886 static const struct {
894 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
896 { "list", LESS, 1, list_machines },
897 { "status", MORE, 2, show },
898 { "show", MORE, 1, show },
899 { "terminate", MORE, 2, terminate_machine },
900 { "kill", MORE, 2, kill_machine },
901 { "login", MORE, 2, login_machine },
910 left = argc - optind;
913 /* Special rule: no arguments means "list-sessions" */
916 if (streq(argv[optind], "help")) {
921 for (i = 0; i < ELEMENTSOF(verbs); i++)
922 if (streq(argv[optind], verbs[i].verb))
925 if (i >= ELEMENTSOF(verbs)) {
926 log_error("Unknown operation %s", argv[optind]);
931 switch (verbs[i].argc_cmp) {
934 if (left != verbs[i].argc) {
935 log_error("Invalid number of arguments.");
942 if (left < verbs[i].argc) {
943 log_error("Too few arguments.");
950 if (left > verbs[i].argc) {
951 log_error("Too many arguments.");
958 assert_not_reached("Unknown comparison operator.");
961 return verbs[i].dispatch(bus, argv + optind, left);
964 int main(int argc, char*argv[]) {
965 int r, ret = EXIT_FAILURE;
966 _cleanup_bus_unref_ sd_bus *bus = NULL;
968 setlocale(LC_ALL, "");
969 log_parse_environment();
972 r = parse_argv(argc, argv);
980 r = bus_open_transport(arg_transport, arg_host, false, &bus);
982 log_error("Failed to create bus connection: %s", strerror(-r));
987 r = machinectl_main(bus, argc, argv);
988 ret = r < 0 ? EXIT_FAILURE : r;
991 strv_free(arg_property);