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>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
40 #include "bus-error.h"
43 #include "unit-name.h"
44 #include "cgroup-show.h"
45 #include "cgroup-util.h"
47 #include "event-util.h"
49 static char **arg_property = NULL;
50 static bool arg_all = false;
51 static bool arg_full = false;
52 static bool arg_no_pager = false;
53 static bool arg_legend = true;
54 static const char *arg_kill_who = NULL;
55 static int arg_signal = SIGTERM;
56 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
57 static char *arg_host = NULL;
59 static void pager_open_if_enabled(void) {
61 /* Cache result before we open the pager */
68 static int list_machines(sd_bus *bus, char **args, unsigned n) {
69 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
70 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
71 const char *name, *class, *service, *object;
75 pager_open_if_enabled();
77 r = sd_bus_call_method(
79 "org.freedesktop.machine1",
80 "/org/freedesktop/machine1",
81 "org.freedesktop.machine1.Manager",
87 log_error("Could not get machines: %s", bus_error_message(&error, -r));
92 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
94 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
96 return bus_log_parse_error(r);
98 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
99 printf("%-32s %-9s %-16s\n", name, class, service);
104 return bus_log_parse_error(r);
106 r = sd_bus_message_exit_container(reply);
108 return bus_log_parse_error(r);
111 printf("\n%u machines listed.\n", k);
116 static int show_unit_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 endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service",
144 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
148 r = sd_bus_message_read(reply, "s", &cgroup);
150 return bus_log_parse_error(r);
155 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
159 arg_all * OUTPUT_SHOW_ALL |
160 arg_full * OUTPUT_FULL_WIDTH;
168 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
172 static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *prefix, const char *prefix2) {
173 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
181 r = sd_bus_call_method(bus,
182 "org.freedesktop.machine1",
183 "/org/freedesktop/machine1",
184 "org.freedesktop.machine1.Manager",
185 "GetMachineAddresses",
192 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
194 return bus_log_parse_error(r);
196 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
200 char buffer[MAX(INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
202 r = sd_bus_message_read(reply, "i", &family);
204 return bus_log_parse_error(r);
206 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
208 return bus_log_parse_error(r);
210 fputs(prefix, stdout);
211 fputs(inet_ntop(family, a, buffer, sizeof(buffer)), stdout);
212 if (family == AF_INET6 && ifi > 0)
216 r = sd_bus_message_exit_container(reply);
218 return bus_log_parse_error(r);
220 if (prefix != prefix2)
224 return bus_log_parse_error(r);
226 r = sd_bus_message_exit_container(reply);
228 return bus_log_parse_error(r);
233 static int print_os_release(sd_bus *bus, const char *name, const char *prefix) {
234 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
235 const char *k, *v, *pretty = NULL;
242 r = sd_bus_call_method(bus,
243 "org.freedesktop.machine1",
244 "/org/freedesktop/machine1",
245 "org.freedesktop.machine1.Manager",
246 "GetMachineOSRelease",
253 r = sd_bus_message_enter_container(reply, 'a', "{ss}");
255 return bus_log_parse_error(r);
257 while ((r = sd_bus_message_read(reply, "{ss}", &k, &v)) > 0) {
258 if (streq(k, "PRETTY_NAME"))
263 return bus_log_parse_error(r);
265 r = sd_bus_message_exit_container(reply);
267 return bus_log_parse_error(r);
270 printf("%s%s\n", prefix, pretty);
275 typedef struct MachineStatusInfo {
281 char *root_directory;
288 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
289 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
290 char since2[FORMAT_TIMESTAMP_MAX], *s2;
295 fputs(strna(i->name), stdout);
297 if (!sd_id128_equal(i->id, SD_ID128_NULL))
298 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
302 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
303 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
306 printf("\t Since: %s; %s\n", s2, s1);
308 printf("\t Since: %s\n", s2);
311 _cleanup_free_ char *t = NULL;
313 printf("\t Leader: %u", (unsigned) i->leader);
315 get_process_comm(i->leader, &t);
323 printf("\t Service: %s", i->service);
326 printf("; class %s", i->class);
330 printf("\t Class: %s\n", i->class);
332 if (i->root_directory)
333 printf("\t Root: %s\n", i->root_directory);
335 if (i->n_netif > 0) {
338 fputs("\t Iface:", stdout);
340 for (c = 0; c < i->n_netif; c++) {
341 char name[IF_NAMESIZE+1] = "";
343 if (if_indextoname(i->netif[c], name)) {
352 printf(" %i", i->netif[c]);
358 print_addresses(bus, i->name, ifi,
362 print_os_release(bus, i->name, "\t OS: ");
365 printf("\t Unit: %s\n", i->unit);
366 show_unit_cgroup(bus, i->unit, i->leader);
370 static int map_netif(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
371 MachineStatusInfo *i = userdata;
376 assert_cc(sizeof(int32_t) == sizeof(int));
377 r = sd_bus_message_read_array(m, SD_BUS_TYPE_INT32, &v, &l);
383 i->n_netif = l / sizeof(int32_t);
384 i->netif = memdup(v, l);
391 static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
393 static const struct bus_properties_map map[] = {
394 { "Name", "s", NULL, offsetof(MachineStatusInfo, name) },
395 { "Class", "s", NULL, offsetof(MachineStatusInfo, class) },
396 { "Service", "s", NULL, offsetof(MachineStatusInfo, service) },
397 { "Unit", "s", NULL, offsetof(MachineStatusInfo, unit) },
398 { "RootDirectory", "s", NULL, offsetof(MachineStatusInfo, root_directory) },
399 { "Leader", "u", NULL, offsetof(MachineStatusInfo, leader) },
400 { "Timestamp", "t", NULL, offsetof(MachineStatusInfo, timestamp) },
401 { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) },
402 { "NetworkInterfaces", "ai", map_netif, 0 },
406 MachineStatusInfo info = {};
412 r = bus_map_all_properties(bus,
413 "org.freedesktop.machine1",
418 log_error("Could not get properties: %s", strerror(-r));
426 print_machine_status_info(bus, &info);
432 free(info.root_directory);
438 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
446 r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all);
448 log_error("Could not get properties: %s", strerror(-r));
453 static int show(sd_bus *bus, char **args, unsigned n) {
454 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
455 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
458 bool properties, new_line = false;
463 properties = !strstr(args[0], "status");
465 pager_open_if_enabled();
467 if (properties && n <= 1) {
469 /* If no argument is specified, inspect the manager
471 r = show_properties(bus, "/org/freedesktop/machine1", &new_line);
476 for (i = 1; i < n; i++) {
477 const char *path = NULL;
479 r = sd_bus_call_method(
481 "org.freedesktop.machine1",
482 "/org/freedesktop/machine1",
483 "org.freedesktop.machine1.Manager",
489 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
493 r = sd_bus_message_read(reply, "o", &path);
495 return bus_log_parse_error(r);
498 r = show_properties(bus, path, &new_line);
500 r = show_info(args[0], bus, path, &new_line);
506 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
507 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
513 arg_kill_who = "all";
515 for (i = 1; i < n; i++) {
518 r = sd_bus_call_method(
520 "org.freedesktop.machine1",
521 "/org/freedesktop/machine1",
522 "org.freedesktop.machine1.Manager",
526 "ssi", args[i], arg_kill_who, arg_signal);
528 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
536 static int reboot_machine(sd_bus *bus, char **args, unsigned n) {
537 arg_kill_who = "leader";
538 arg_signal = SIGINT; /* sysvinit + systemd */
540 return kill_machine(bus, args, n);
543 static int poweroff_machine(sd_bus *bus, char **args, unsigned n) {
544 arg_kill_who = "leader";
545 arg_signal = SIGRTMIN+4; /* only systemd */
547 return kill_machine(bus, args, n);
550 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
551 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
556 for (i = 1; i < n; i++) {
559 r = sd_bus_call_method(
561 "org.freedesktop.machine1",
562 "/org/freedesktop/machine1",
563 "org.freedesktop.machine1.Manager",
569 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
577 static int openpt_in_namespace(pid_t pid, int flags) {
578 _cleanup_close_pair_ int pair[2] = { -1, -1 };
579 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
581 struct cmsghdr cmsghdr;
582 uint8_t buf[CMSG_SPACE(sizeof(int))];
585 .msg_control = &control,
586 .msg_controllen = sizeof(control),
588 struct cmsghdr *cmsg;
593 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
597 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
605 pair[0] = safe_close(pair[0]);
607 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
611 master = posix_openpt(flags);
615 cmsg = CMSG_FIRSTHDR(&mh);
616 cmsg->cmsg_level = SOL_SOCKET;
617 cmsg->cmsg_type = SCM_RIGHTS;
618 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
619 memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
621 mh.msg_controllen = cmsg->cmsg_len;
623 if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
629 pair[1] = safe_close(pair[1]);
631 r = wait_for_terminate(child, &si);
634 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
637 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
640 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
641 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
645 fds = (int*) CMSG_DATA(cmsg);
646 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
649 close_many(fds, n_fds);
662 static int login_machine(sd_bus *bus, char **args, unsigned n) {
663 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL;
664 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
665 _cleanup_bus_close_unref_ sd_bus *container_bus = NULL;
666 _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
667 _cleanup_event_unref_ sd_event *event = NULL;
668 _cleanup_close_ int master = -1;
669 _cleanup_free_ char *getty = NULL;
670 const char *path, *pty, *p;
678 if (arg_transport != BUS_TRANSPORT_LOCAL) {
679 log_error("Login only supported on local machines.");
683 r = sd_event_default(&event);
685 log_error("Failed to get event loop: %s", strerror(-r));
689 r = sd_bus_attach_event(bus, event, 0);
691 log_error("Failed to attach bus to event loop: %s", strerror(-r));
695 r = sd_bus_call_method(
697 "org.freedesktop.machine1",
698 "/org/freedesktop/machine1",
699 "org.freedesktop.machine1.Manager",
705 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
709 r = sd_bus_message_read(reply, "o", &path);
711 return bus_log_parse_error(r);
713 r = sd_bus_get_property(
715 "org.freedesktop.machine1",
717 "org.freedesktop.machine1.Machine",
723 log_error("Failed to retrieve PID of leader: %s", strerror(-r));
727 r = sd_bus_message_read(reply2, "u", &leader);
729 return bus_log_parse_error(r);
731 master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
733 log_error("Failed to acquire pseudo tty: %s", strerror(-master));
737 pty = ptsname(master);
739 log_error("Failed to get pty name: %m");
743 p = startswith(pty, "/dev/pts/");
745 log_error("Invalid pty name %s.", pty);
749 r = sd_bus_open_system_container(&container_bus, args[1]);
751 log_error("Failed to get container bus: %s", strerror(-r));
755 getty = strjoin("container-getty@", p, ".service", NULL);
759 if (unlockpt(master) < 0) {
760 log_error("Failed to unlock tty: %m");
764 r = sd_bus_call_method(container_bus,
765 "org.freedesktop.systemd1",
766 "/org/freedesktop/systemd1",
767 "org.freedesktop.systemd1.Manager",
770 "ss", getty, "replace");
772 log_error("Failed to start getty service: %s", bus_error_message(&error, r));
776 container_bus = sd_bus_unref(container_bus);
778 assert_se(sigemptyset(&mask) == 0);
779 sigset_add_many(&mask, SIGWINCH, SIGTERM, SIGINT, -1);
780 assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
782 log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]);
784 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
785 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
787 r = pty_forward_new(event, master, &forward);
789 log_error("Failed to create PTY forwarder: %s", strerror(-r));
793 r = sd_event_loop(event);
795 log_error("Failed to run event loop: %s", strerror(-r));
799 forward = pty_forward_free(forward);
803 log_info("Connection to container %s terminated.", args[1]);
805 sd_event_get_exit_code(event, &ret);
809 static void help(void) {
810 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
811 "Send control commands to or query the virtual machine and container registration manager.\n\n"
812 " -h --help Show this help\n"
813 " --version Show package version\n"
814 " --no-pager Do not pipe output into a pager\n"
815 " --no-legend Do not show the headers and footers\n"
816 " -H --host=[USER@]HOST Operate on remote host\n"
817 " -M --machine=CONTAINER Operate on local container\n"
818 " -p --property=NAME Show only properties by this name\n"
819 " -a --all Show all properties, including empty ones\n"
820 " -l --full Do not ellipsize output\n"
821 " --kill-who=WHO Who to send signal to\n"
822 " -s --signal=SIGNAL Which signal to send\n\n"
824 " list List running VMs and containers\n"
825 " status NAME... Show VM/container status\n"
826 " show NAME... Show properties of one or more VMs/containers\n"
827 " login NAME Get a login prompt on a container\n"
828 " poweroff NAME... Power off one or more containers\n"
829 " reboot NAME... Reboot one or more containers\n"
830 " kill NAME... Send signal to processes of a VM/container\n"
831 " terminate NAME... Terminate one or more VMs/containers\n",
832 program_invocation_short_name);
835 static int parse_argv(int argc, char *argv[]) {
844 static const struct option options[] = {
845 { "help", no_argument, NULL, 'h' },
846 { "version", no_argument, NULL, ARG_VERSION },
847 { "property", required_argument, NULL, 'p' },
848 { "all", no_argument, NULL, 'a' },
849 { "full", no_argument, NULL, 'l' },
850 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
851 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
852 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
853 { "signal", required_argument, NULL, 's' },
854 { "host", required_argument, NULL, 'H' },
855 { "machine", required_argument, NULL, 'M' },
864 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
873 puts(PACKAGE_STRING);
874 puts(SYSTEMD_FEATURES);
878 r = strv_extend(&arg_property, optarg);
882 /* If the user asked for a particular
883 * property, show it to him, even if it is
905 arg_kill_who = optarg;
909 arg_signal = signal_from_string_try_harder(optarg);
910 if (arg_signal < 0) {
911 log_error("Failed to parse signal string %s.", optarg);
917 arg_transport = BUS_TRANSPORT_REMOTE;
922 arg_transport = BUS_TRANSPORT_CONTAINER;
930 assert_not_reached("Unhandled option");
936 static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
938 static const struct {
946 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
948 { "list", LESS, 1, list_machines },
949 { "status", MORE, 2, show },
950 { "show", MORE, 1, show },
951 { "terminate", MORE, 2, terminate_machine },
952 { "reboot", MORE, 2, reboot_machine },
953 { "poweroff", MORE, 2, poweroff_machine },
954 { "kill", MORE, 2, kill_machine },
955 { "login", MORE, 2, login_machine },
964 left = argc - optind;
967 /* Special rule: no arguments means "list" */
970 if (streq(argv[optind], "help")) {
975 for (i = 0; i < ELEMENTSOF(verbs); i++)
976 if (streq(argv[optind], verbs[i].verb))
979 if (i >= ELEMENTSOF(verbs)) {
980 log_error("Unknown operation %s", argv[optind]);
985 switch (verbs[i].argc_cmp) {
988 if (left != verbs[i].argc) {
989 log_error("Invalid number of arguments.");
996 if (left < verbs[i].argc) {
997 log_error("Too few arguments.");
1004 if (left > verbs[i].argc) {
1005 log_error("Too many arguments.");
1012 assert_not_reached("Unknown comparison operator.");
1015 return verbs[i].dispatch(bus, argv + optind, left);
1018 int main(int argc, char*argv[]) {
1019 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1022 setlocale(LC_ALL, "");
1023 log_parse_environment();
1026 r = parse_argv(argc, argv);
1030 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1032 log_error("Failed to create bus connection: %s", strerror(-r));
1036 r = machinectl_main(bus, argc, argv);
1041 strv_free(arg_property);
1043 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;