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 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 show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
237 MachineStatusInfo info = {};
238 static const struct bus_properties_map map[] = {
239 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
240 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
241 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
242 { "Scope", "s", NULL, offsetof(MachineStatusInfo, scope) },
243 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
244 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
245 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
246 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
254 r = bus_map_all_properties(bus,
255 "org.freedesktop.machine1",
260 log_error("Could not get properties: %s", strerror(-r));
268 print_machine_status_info(bus, &info);
274 free(info.root_directory);
279 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
287 r = bus_print_all_properties(bus, path, arg_property, arg_all);
289 log_error("Could not get properties: %s", strerror(-r));
294 static int show(sd_bus *bus, char **args, unsigned n) {
295 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
296 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
299 bool properties, new_line = false;
304 properties = !strstr(args[0], "status");
306 pager_open_if_enabled();
308 if (properties && n <= 1) {
310 /* If no argument is specified, inspect the manager
312 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
314 log_error("Failed to query properties: %s", strerror(-r));
319 for (i = 1; i < n; i++) {
320 const char *path = NULL;
322 r = sd_bus_call_method(
324 "org.freedesktop.machine1",
325 "/org/freedesktop/machine1",
326 "org.freedesktop.machine1.Manager",
332 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
336 r = sd_bus_message_read(reply, "o", &path);
338 log_error("Failed to parse reply: %s", strerror(-r));
343 r = show_properties(bus, path, &new_line);
345 r = show_info(args[0], bus, path, &new_line);
351 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
352 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
358 arg_kill_who = "all";
360 for (i = 1; i < n; i++) {
363 r = sd_bus_call_method(
365 "org.freedesktop.machine1",
366 "/org/freedesktop/machine1",
367 "org.freedesktop.machine1.Manager",
371 "ssi", args[i], arg_kill_who, arg_signal);
373 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
381 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
382 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
387 for (i = 1; i < n; i++) {
390 r = sd_bus_call_method(
392 "org.freedesktop.machine1",
393 "/org/freedesktop/machine1",
394 "org.freedesktop.machine1.Manager",
400 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
408 static int openpt_in_namespace(pid_t pid, int flags) {
409 _cleanup_close_ int nsfd = -1, rootfd = -1;
410 _cleanup_free_ char *ns = NULL, *root = NULL;
411 _cleanup_close_pipe_ int sock[2] = { -1, -1 };
414 struct cmsghdr cmsghdr;
415 uint8_t buf[CMSG_SPACE(sizeof(int))];
417 struct cmsghdr *cmsg;
422 r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) pid);
426 nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
430 r = asprintf(&root, "/proc/%lu/root", (unsigned long) pid);
434 rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
438 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0)
443 mh.msg_control = &control;
444 mh.msg_controllen = sizeof(control);
451 close_nointr_nofail(sock[0]);
454 r = setns(nsfd, CLONE_NEWNS);
458 if (fchdir(rootfd) < 0)
464 master = posix_openpt(flags);
468 cmsg = CMSG_FIRSTHDR(&mh);
469 cmsg->cmsg_level = SOL_SOCKET;
470 cmsg->cmsg_type = SCM_RIGHTS;
471 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
472 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
474 mh.msg_controllen = cmsg->cmsg_len;
476 r = sendmsg(sock[1], &mh, MSG_NOSIGNAL);
477 close_nointr_nofail(master);
484 close_nointr_nofail(sock[1]);
487 if (recvmsg(sock[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
490 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
491 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
495 fds = (int*) CMSG_DATA(cmsg);
496 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
499 close_many(fds, n_fds);
506 r = wait_for_terminate(child, &si);
507 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS || master < 0) {
510 close_nointr_nofail(master);
512 return r < 0 ? r : -EIO;
518 static int login_machine(sd_bus *bus, char **args, unsigned n) {
519 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
520 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
521 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
522 _cleanup_close_ int master = -1;
523 _cleanup_free_ char *getty = NULL;
524 const char *path, *pty, *p;
532 if (arg_transport != BUS_TRANSPORT_LOCAL) {
533 log_error("Login only support on local machines.");
537 r = sd_bus_call_method(
539 "org.freedesktop.machine1",
540 "/org/freedesktop/machine1",
541 "org.freedesktop.machine1.Manager",
547 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
551 r = sd_bus_message_read(reply, "o", &path);
553 log_error("Failed to parse reply: %s", strerror(-r));
557 r = sd_bus_get_property(
559 "org.freedesktop.machine1",
561 "org.freedesktop.machine1.Machine",
567 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
571 r = sd_bus_message_read(reply2, "u", &leader);
573 log_error("Failed to parse reply: %s", strerror(-r));
577 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
579 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
583 pty = ptsname(master);
585 log_error("Failed to get pty name: %m");
589 p = startswith(pty, "/dev/pts/");
591 log_error("Invalid pty name %s.", pty);
595 r = sd_bus_open_system_container(args[1], &container_bus);
597 log_error("Failed to get container bus: %s", strerror(-r));
601 getty = strjoin("container-getty@", p, ".service", NULL);
605 if (unlockpt(master) < 0) {
606 log_error("Failed to unlock tty: %m");
610 r = sd_bus_call_method(container_bus,
611 "org.freedesktop.systemd1",
612 "/org/freedesktop/systemd1",
613 "org.freedesktop.systemd1.Manager",
616 "ss", getty, "replace");
618 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
622 assert_se(sigemptyset(&mask) == 0);
623 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
624 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
626 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
628 r = process_pty(master, &mask, 0, 0);
630 log_error("Failed to process pseudo tty: %s", strerror(-r));
636 log_info("Connection to container %s terminated.", args[1]);
641 static int help(void) {
643 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
644 "Send control commands to or query the virtual machine and container registration manager.\n\n"
645 " -h --help Show this help\n"
646 " --version Show package version\n"
647 " --no-pager Do not pipe output into a pager\n"
648 " --no-ask-password Don't prompt for password\n"
649 " -H --host=[USER@]HOST Operate on remote host\n"
650 " -M --machine=CONTAINER Operate on local container\n"
651 " -p --property=NAME Show only properties by this name\n"
652 " -a --all Show all properties, including empty ones\n"
653 " -l --full Do not ellipsize output\n"
654 " --kill-who=WHO Who to send signal to\n"
655 " -s --signal=SIGNAL Which signal to send\n\n"
657 " list List running VMs and containers\n"
658 " status [NAME...] Show VM/container status\n"
659 " show [NAME...] Show properties of one or more VMs/containers\n"
660 " terminate [NAME...] Terminate one or more VMs/containers\n"
661 " kill [NAME...] Send signal to processes of a VM/container\n"
662 " login [NAME] Get a login prompt on a container\n",
663 program_invocation_short_name);
668 static int parse_argv(int argc, char *argv[]) {
677 static const struct option options[] = {
678 { "help", no_argument, NULL, 'h' },
679 { "version", no_argument, NULL, ARG_VERSION },
680 { "property", required_argument, NULL, 'p' },
681 { "all", no_argument, NULL, 'a' },
682 { "full", no_argument, NULL, 'l' },
683 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
684 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
685 { "signal", required_argument, NULL, 's' },
686 { "host", required_argument, NULL, 'H' },
687 { "machine", required_argument, NULL, 'M' },
688 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
697 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
706 puts(PACKAGE_STRING);
707 puts(SYSTEMD_FEATURES);
711 r = strv_extend(&arg_property, optarg);
715 /* If the user asked for a particular
716 * property, show it to him, even if it is
733 case ARG_NO_ASK_PASSWORD:
734 arg_ask_password = false;
738 arg_kill_who = optarg;
742 arg_signal = signal_from_string_try_harder(optarg);
743 if (arg_signal < 0) {
744 log_error("Failed to parse signal string %s.", optarg);
750 arg_transport = BUS_TRANSPORT_REMOTE;
755 arg_transport = BUS_TRANSPORT_CONTAINER;
763 log_error("Unknown option code %c", c);
771 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
773 static const struct {
781 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
783 { "list", LESS, 1, list_machines },
784 { "status", MORE, 2, show },
785 { "show", MORE, 1, show },
786 { "terminate", MORE, 2, terminate_machine },
787 { "kill", MORE, 2, kill_machine },
788 { "login", MORE, 2, login_machine },
797 left = argc - optind;
800 /* Special rule: no arguments means "list-sessions" */
803 if (streq(argv[optind], "help")) {
808 for (i = 0; i < ELEMENTSOF(verbs); i++)
809 if (streq(argv[optind], verbs[i].verb))
812 if (i >= ELEMENTSOF(verbs)) {
813 log_error("Unknown operation %s", argv[optind]);
818 switch (verbs[i].argc_cmp) {
821 if (left != verbs[i].argc) {
822 log_error("Invalid number of arguments.");
829 if (left < verbs[i].argc) {
830 log_error("Too few arguments.");
837 if (left > verbs[i].argc) {
838 log_error("Too many arguments.");
845 assert_not_reached("Unknown comparison operator.");
848 return verbs[i].dispatch(bus, argv + optind, left);
851 int main(int argc, char*argv[]) {
852 _cleanup_bus_unref_ sd_bus *bus = NULL;
855 setlocale(LC_ALL, "");
856 log_parse_environment();
859 r = parse_argv(argc, argv);
863 r = bus_open_transport(arg_transport, arg_host, false, &bus);
865 log_error("Failed to create bus connection: %s", strerror(-r));
869 r = machinectl_main(bus, argc, argv);
874 strv_free(arg_property);
876 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;