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 };
413 struct cmsghdr cmsghdr;
414 uint8_t buf[CMSG_SPACE(sizeof(int))];
417 .msg_control = &control,
418 .msg_controllen = sizeof(control),
420 struct cmsghdr *cmsg;
425 r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) pid);
429 nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
433 r = asprintf(&root, "/proc/%lu/root", (unsigned long) pid);
437 rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
441 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sock) < 0)
449 close_nointr_nofail(sock[0]);
452 r = setns(nsfd, CLONE_NEWNS);
456 if (fchdir(rootfd) < 0)
462 master = posix_openpt(flags);
466 cmsg = CMSG_FIRSTHDR(&mh);
467 cmsg->cmsg_level = SOL_SOCKET;
468 cmsg->cmsg_type = SCM_RIGHTS;
469 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
470 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
472 mh.msg_controllen = cmsg->cmsg_len;
474 r = sendmsg(sock[1], &mh, MSG_NOSIGNAL);
475 close_nointr_nofail(master);
482 close_nointr_nofail(sock[1]);
485 if (recvmsg(sock[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
488 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
489 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
493 fds = (int*) CMSG_DATA(cmsg);
494 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
497 close_many(fds, n_fds);
504 r = wait_for_terminate(child, &si);
505 if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS || master < 0) {
508 close_nointr_nofail(master);
510 return r < 0 ? r : -EIO;
516 static int login_machine(sd_bus *bus, char **args, unsigned n) {
517 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
518 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
519 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
520 _cleanup_close_ int master = -1;
521 _cleanup_free_ char *getty = NULL;
522 const char *path, *pty, *p;
530 if (arg_transport != BUS_TRANSPORT_LOCAL) {
531 log_error("Login only support on local machines.");
535 r = sd_bus_call_method(
537 "org.freedesktop.machine1",
538 "/org/freedesktop/machine1",
539 "org.freedesktop.machine1.Manager",
545 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
549 r = sd_bus_message_read(reply, "o", &path);
551 log_error("Failed to parse reply: %s", strerror(-r));
555 r = sd_bus_get_property(
557 "org.freedesktop.machine1",
559 "org.freedesktop.machine1.Machine",
565 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
569 r = sd_bus_message_read(reply2, "u", &leader);
571 log_error("Failed to parse reply: %s", strerror(-r));
575 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
577 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
581 pty = ptsname(master);
583 log_error("Failed to get pty name: %m");
587 p = startswith(pty, "/dev/pts/");
589 log_error("Invalid pty name %s.", pty);
593 r = sd_bus_open_system_container(args[1], &container_bus);
595 log_error("Failed to get container bus: %s", strerror(-r));
599 getty = strjoin("container-getty@", p, ".service", NULL);
603 if (unlockpt(master) < 0) {
604 log_error("Failed to unlock tty: %m");
608 r = sd_bus_call_method(container_bus,
609 "org.freedesktop.systemd1",
610 "/org/freedesktop/systemd1",
611 "org.freedesktop.systemd1.Manager",
614 "ss", getty, "replace");
616 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
620 assert_se(sigemptyset(&mask) == 0);
621 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
622 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
624 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
626 r = process_pty(master, &mask, 0, 0);
628 log_error("Failed to process pseudo tty: %s", strerror(-r));
634 log_info("Connection to container %s terminated.", args[1]);
639 static int help(void) {
641 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
642 "Send control commands to or query the virtual machine and container registration manager.\n\n"
643 " -h --help Show this help\n"
644 " --version Show package version\n"
645 " --no-pager Do not pipe output into a pager\n"
646 " --no-ask-password Don't prompt for password\n"
647 " -H --host=[USER@]HOST Operate on remote host\n"
648 " -M --machine=CONTAINER Operate on local container\n"
649 " -p --property=NAME Show only properties by this name\n"
650 " -a --all Show all properties, including empty ones\n"
651 " -l --full Do not ellipsize output\n"
652 " --kill-who=WHO Who to send signal to\n"
653 " -s --signal=SIGNAL Which signal to send\n\n"
655 " list List running VMs and containers\n"
656 " status [NAME...] Show VM/container status\n"
657 " show [NAME...] Show properties of one or more VMs/containers\n"
658 " terminate [NAME...] Terminate one or more VMs/containers\n"
659 " kill [NAME...] Send signal to processes of a VM/container\n"
660 " login [NAME] Get a login prompt on a container\n",
661 program_invocation_short_name);
666 static int parse_argv(int argc, char *argv[]) {
675 static const struct option options[] = {
676 { "help", no_argument, NULL, 'h' },
677 { "version", no_argument, NULL, ARG_VERSION },
678 { "property", required_argument, NULL, 'p' },
679 { "all", no_argument, NULL, 'a' },
680 { "full", no_argument, NULL, 'l' },
681 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
682 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
683 { "signal", required_argument, NULL, 's' },
684 { "host", required_argument, NULL, 'H' },
685 { "machine", required_argument, NULL, 'M' },
686 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
695 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
703 puts(PACKAGE_STRING);
704 puts(SYSTEMD_FEATURES);
708 r = strv_extend(&arg_property, optarg);
712 /* If the user asked for a particular
713 * property, show it to him, even if it is
730 case ARG_NO_ASK_PASSWORD:
731 arg_ask_password = false;
735 arg_kill_who = optarg;
739 arg_signal = signal_from_string_try_harder(optarg);
740 if (arg_signal < 0) {
741 log_error("Failed to parse signal string %s.", optarg);
747 arg_transport = BUS_TRANSPORT_REMOTE;
752 arg_transport = BUS_TRANSPORT_CONTAINER;
760 assert_not_reached("Unhandled option");
767 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
769 static const struct {
777 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
779 { "list", LESS, 1, list_machines },
780 { "status", MORE, 2, show },
781 { "show", MORE, 1, show },
782 { "terminate", MORE, 2, terminate_machine },
783 { "kill", MORE, 2, kill_machine },
784 { "login", MORE, 2, login_machine },
793 left = argc - optind;
796 /* Special rule: no arguments means "list-sessions" */
799 if (streq(argv[optind], "help")) {
804 for (i = 0; i < ELEMENTSOF(verbs); i++)
805 if (streq(argv[optind], verbs[i].verb))
808 if (i >= ELEMENTSOF(verbs)) {
809 log_error("Unknown operation %s", argv[optind]);
814 switch (verbs[i].argc_cmp) {
817 if (left != verbs[i].argc) {
818 log_error("Invalid number of arguments.");
825 if (left < verbs[i].argc) {
826 log_error("Too few arguments.");
833 if (left > verbs[i].argc) {
834 log_error("Too many arguments.");
841 assert_not_reached("Unknown comparison operator.");
844 return verbs[i].dispatch(bus, argv + optind, left);
847 int main(int argc, char*argv[]) {
848 _cleanup_bus_unref_ sd_bus *bus = NULL;
851 setlocale(LC_ALL, "");
852 log_parse_environment();
855 r = parse_argv(argc, argv);
859 r = bus_open_transport(arg_transport, arg_host, false, &bus);
861 log_error("Failed to create bus connection: %s", strerror(-r));
865 r = machinectl_main(bus, argc, argv);
870 strv_free(arg_property);
872 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;