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) {
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) },
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);
308 log_error("Failed to query properties: %s", strerror(-r));
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 openpt_in_namespace(pid_t pid, int flags) {
401 _cleanup_close_pipe_ int pair[2] = { -1, -1 };
402 _cleanup_close_ int nsfd = -1, rootfd = -1;
404 struct cmsghdr cmsghdr;
405 uint8_t buf[CMSG_SPACE(sizeof(int))];
408 .msg_control = &control,
409 .msg_controllen = sizeof(control),
411 struct cmsghdr *cmsg;
416 r = namespace_open(pid, &nsfd, &rootfd);
420 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
428 close_nointr_nofail(pair[0]);
431 r = namespace_enter(nsfd, rootfd);
435 master = posix_openpt(flags);
439 cmsg = CMSG_FIRSTHDR(&mh);
440 cmsg->cmsg_level = SOL_SOCKET;
441 cmsg->cmsg_type = SCM_RIGHTS;
442 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
443 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
445 mh.msg_controllen = cmsg->cmsg_len;
447 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
453 close_nointr_nofail(pair[1]);
456 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
459 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
460 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
464 fds = (int*) CMSG_DATA(cmsg);
465 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
468 close_many(fds, n_fds);
475 r = wait_for_terminate(child, &si);
476 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS || master < 0) {
479 close_nointr_nofail(master);
481 return r < 0 ? r : -EIO;
487 static int login_machine(sd_bus *bus, char **args, unsigned n) {
488 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
489 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
490 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
491 _cleanup_close_ int master = -1;
492 _cleanup_free_ char *getty = NULL;
493 const char *path, *pty, *p;
501 if (arg_transport != BUS_TRANSPORT_LOCAL) {
502 log_error("Login only support on local machines.");
506 r = sd_bus_call_method(
508 "org.freedesktop.machine1",
509 "/org/freedesktop/machine1",
510 "org.freedesktop.machine1.Manager",
516 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
520 r = sd_bus_message_read(reply, "o", &path);
522 return bus_log_parse_error(r);
524 r = sd_bus_get_property(
526 "org.freedesktop.machine1",
528 "org.freedesktop.machine1.Machine",
534 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
538 r = sd_bus_message_read(reply2, "u", &leader);
540 return bus_log_parse_error(r);
542 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
544 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
548 pty = ptsname(master);
550 log_error("Failed to get pty name: %m");
554 p = startswith(pty, "/dev/pts/");
556 log_error("Invalid pty name %s.", pty);
560 r = sd_bus_open_system_container(args[1], &container_bus);
562 log_error("Failed to get container bus: %s", strerror(-r));
566 getty = strjoin("container-getty@", p, ".service", NULL);
570 if (unlockpt(master) < 0) {
571 log_error("Failed to unlock tty: %m");
575 r = sd_bus_call_method(container_bus,
576 "org.freedesktop.systemd1",
577 "/org/freedesktop/systemd1",
578 "org.freedesktop.systemd1.Manager",
581 "ss", getty, "replace");
583 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
587 container_bus = sd_bus_unref(container_bus);
589 assert_se(sigemptyset(&mask) == 0);
590 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
591 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
593 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
595 r = process_pty(master, &mask, 0, 0);
597 log_error("Failed to process pseudo tty: %s", strerror(-r));
603 log_info("Connection to container %s terminated.", args[1]);
608 static int help(void) {
610 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
611 "Send control commands to or query the virtual machine and container registration manager.\n\n"
612 " -h --help Show this help\n"
613 " --version Show package version\n"
614 " --no-pager Do not pipe output into a pager\n"
615 " --no-ask-password Don't prompt for password\n"
616 " -H --host=[USER@]HOST Operate on remote host\n"
617 " -M --machine=CONTAINER Operate on local container\n"
618 " -p --property=NAME Show only properties by this name\n"
619 " -a --all Show all properties, including empty ones\n"
620 " -l --full Do not ellipsize output\n"
621 " --kill-who=WHO Who to send signal to\n"
622 " -s --signal=SIGNAL Which signal to send\n\n"
624 " list List running VMs and containers\n"
625 " status NAME... Show VM/container status\n"
626 " show NAME... Show properties of one or more VMs/containers\n"
627 " terminate NAME... Terminate one or more VMs/containers\n"
628 " kill NAME... Send signal to processes of a VM/container\n"
629 " login NAME Get a login prompt on a container\n",
630 program_invocation_short_name);
635 static int parse_argv(int argc, char *argv[]) {
644 static const struct option options[] = {
645 { "help", no_argument, NULL, 'h' },
646 { "version", no_argument, NULL, ARG_VERSION },
647 { "property", required_argument, NULL, 'p' },
648 { "all", no_argument, NULL, 'a' },
649 { "full", no_argument, NULL, 'l' },
650 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
651 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
652 { "signal", required_argument, NULL, 's' },
653 { "host", required_argument, NULL, 'H' },
654 { "machine", required_argument, NULL, 'M' },
655 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
664 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
672 puts(PACKAGE_STRING);
673 puts(SYSTEMD_FEATURES);
677 r = strv_extend(&arg_property, optarg);
681 /* If the user asked for a particular
682 * property, show it to him, even if it is
699 case ARG_NO_ASK_PASSWORD:
700 arg_ask_password = false;
704 arg_kill_who = optarg;
708 arg_signal = signal_from_string_try_harder(optarg);
709 if (arg_signal < 0) {
710 log_error("Failed to parse signal string %s.", optarg);
716 arg_transport = BUS_TRANSPORT_REMOTE;
721 arg_transport = BUS_TRANSPORT_CONTAINER;
729 assert_not_reached("Unhandled option");
736 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
738 static const struct {
746 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
748 { "list", LESS, 1, list_machines },
749 { "status", MORE, 2, show },
750 { "show", MORE, 1, show },
751 { "terminate", MORE, 2, terminate_machine },
752 { "kill", MORE, 2, kill_machine },
753 { "login", MORE, 2, login_machine },
762 left = argc - optind;
765 /* Special rule: no arguments means "list" */
768 if (streq(argv[optind], "help")) {
773 for (i = 0; i < ELEMENTSOF(verbs); i++)
774 if (streq(argv[optind], verbs[i].verb))
777 if (i >= ELEMENTSOF(verbs)) {
778 log_error("Unknown operation %s", argv[optind]);
783 switch (verbs[i].argc_cmp) {
786 if (left != verbs[i].argc) {
787 log_error("Invalid number of arguments.");
794 if (left < verbs[i].argc) {
795 log_error("Too few arguments.");
802 if (left > verbs[i].argc) {
803 log_error("Too many arguments.");
810 assert_not_reached("Unknown comparison operator.");
813 return verbs[i].dispatch(bus, argv + optind, left);
816 int main(int argc, char*argv[]) {
817 _cleanup_bus_unref_ sd_bus *bus = NULL;
820 setlocale(LC_ALL, "");
821 log_parse_environment();
824 r = parse_argv(argc, argv);
828 r = bus_open_transport(arg_transport, arg_host, false, &bus);
830 log_error("Failed to create bus connection: %s", strerror(-r));
834 r = machinectl_main(bus, argc, argv);
839 strv_free(arg_property);
841 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;