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 <dbus/dbus.h>
34 #include "dbus-common.h"
37 #include "unit-name.h"
38 #include "cgroup-show.h"
39 #include "spawn-polkit-agent.h"
41 static char **arg_property = NULL;
42 static bool arg_all = false;
43 static bool arg_full = false;
44 static bool arg_no_pager = false;
45 static const char *arg_kill_who = NULL;
46 static int arg_signal = SIGTERM;
47 static enum transport {
51 } arg_transport = TRANSPORT_NORMAL;
52 static bool arg_ask_password = true;
53 static char *arg_host = NULL;
54 static char *arg_user = NULL;
56 static void pager_open_if_enabled(void) {
58 /* Cache result before we open the pager */
65 static int list_machines(DBusConnection *bus, char **args, unsigned n) {
66 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
67 DBusMessageIter iter, sub, sub2;
71 pager_open_if_enabled();
73 r = bus_method_call_with_reply (
75 "org.freedesktop.machine1",
76 "/org/freedesktop/machine1",
77 "org.freedesktop.machine1.Manager",
85 if (!dbus_message_iter_init(reply, &iter) ||
86 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
87 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
88 log_error("Failed to parse reply.");
92 dbus_message_iter_recurse(&iter, &sub);
95 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
97 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
98 const char *name, *class, *service, *object;
100 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
101 log_error("Failed to parse reply.");
105 dbus_message_iter_recurse(&sub, &sub2);
107 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
108 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &class, true) < 0 ||
109 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &service, true) < 0 ||
110 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
111 log_error("Failed to parse reply.");
115 printf("%-32s %-9s %-16s\n", name, class, service);
119 dbus_message_iter_next(&sub);
123 printf("\n%u machines listed.\n", k);
128 static int show_scope_cgroup(DBusConnection *bus, const char *unit) {
129 const char *interface = "org.freedesktop.systemd1.Scope";
130 const char *property = "ControlGroup";
131 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
132 _cleanup_free_ char *path = NULL;
133 DBusMessageIter iter, sub;
142 if (arg_transport == TRANSPORT_SSH)
145 path = unit_dbus_path_from_name(unit);
149 r = bus_method_call_with_reply(
151 "org.freedesktop.systemd1",
153 "org.freedesktop.DBus.Properties",
157 DBUS_TYPE_STRING, &interface,
158 DBUS_TYPE_STRING, &property,
161 log_error("Failed to query ControlGroup: %s", bus_error(&error, r));
162 dbus_error_free(&error);
166 if (!dbus_message_iter_init(reply, &iter) ||
167 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
168 log_error("Failed to parse reply.");
172 dbus_message_iter_recurse(&iter, &sub);
173 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
174 log_error("Failed to parse reply.");
178 dbus_message_iter_get_basic(&sub, &cgroup);
181 arg_all * OUTPUT_SHOW_ALL |
182 arg_full * OUTPUT_FULL_WIDTH;
190 show_cgroup_by_path(cgroup, "\t\t ", c, false, output_flags);
194 typedef struct MachineStatusInfo {
200 const char *root_directory;
205 static void print_machine_status_info(DBusConnection *bus, MachineStatusInfo *i) {
206 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
207 char since2[FORMAT_TIMESTAMP_MAX], *s2;
210 fputs(strna(i->name), stdout);
212 if (!sd_id128_equal(i->id, SD_ID128_NULL))
213 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
217 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
218 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
221 printf("\t Since: %s; %s\n", s2, s1);
223 printf("\t Since: %s\n", s2);
226 _cleanup_free_ char *t = NULL;
228 printf("\t Leader: %u", (unsigned) i->leader);
230 get_process_comm(i->leader, &t);
238 printf("\t Service: %s", i->service);
241 printf("; class %s", i->class);
245 printf("\t Class: %s\n", i->class);
247 if (i->root_directory)
248 printf("\t Root: %s\n", i->root_directory);
251 printf("\t Unit: %s\n", i->scope);
252 show_scope_cgroup(bus, i->scope);
256 static int status_property_machine(const char *name, DBusMessageIter *iter, MachineStatusInfo *i) {
261 switch (dbus_message_iter_get_arg_type(iter)) {
263 case DBUS_TYPE_STRING: {
266 dbus_message_iter_get_basic(iter, &s);
269 if (streq(name, "Name"))
271 else if (streq(name, "Class"))
273 else if (streq(name, "Service"))
275 else if (streq(name, "Scope"))
277 else if (streq(name, "RootDirectory"))
278 i->root_directory = s;
283 case DBUS_TYPE_UINT32: {
286 dbus_message_iter_get_basic(iter, &u);
288 if (streq(name, "Leader"))
289 i->leader = (pid_t) u;
294 case DBUS_TYPE_UINT64: {
297 dbus_message_iter_get_basic(iter, &u);
299 if (streq(name, "Timestamp"))
300 i->timestamp = (usec_t) u;
305 case DBUS_TYPE_ARRAY: {
308 dbus_message_iter_recurse(iter, &sub);
310 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE && streq(name, "Id")) {
314 dbus_message_iter_get_fixed_array(&sub, &v, &n);
316 i->id = SD_ID128_NULL;
318 memcpy(&i->id, v, n);
328 static int print_property(const char *name, DBusMessageIter *iter) {
332 if (arg_property && !strv_find(arg_property, name))
335 if (generic_print_property(name, iter, arg_all) > 0)
339 printf("%s=[unprintable]\n", name);
344 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
345 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
346 const char *interface = "";
348 DBusMessageIter iter, sub, sub2, sub3;
349 MachineStatusInfo machine_info = {};
354 r = bus_method_call_with_reply(
356 "org.freedesktop.machine1",
358 "org.freedesktop.DBus.Properties",
362 DBUS_TYPE_STRING, &interface,
367 if (!dbus_message_iter_init(reply, &iter) ||
368 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
369 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
370 log_error("Failed to parse reply.");
375 dbus_message_iter_recurse(&iter, &sub);
382 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
385 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
386 log_error("Failed to parse reply.");
391 dbus_message_iter_recurse(&sub, &sub2);
393 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
394 log_error("Failed to parse reply.");
399 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
400 log_error("Failed to parse reply.");
405 dbus_message_iter_recurse(&sub2, &sub3);
408 r = print_property(name, &sub3);
410 r = status_property_machine(name, &sub3, &machine_info);
413 log_error("Failed to parse reply.");
417 dbus_message_iter_next(&sub);
420 if (!show_properties)
421 print_machine_status_info(bus, &machine_info);
430 static int show(DBusConnection *bus, char **args, unsigned n) {
431 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
435 bool show_properties, new_line = false;
440 dbus_error_init(&error);
442 show_properties = !strstr(args[0], "status");
444 pager_open_if_enabled();
446 if (show_properties && n <= 1) {
447 /* If not argument is specified inspect the manager
450 ret = show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line);
454 for (i = 1; i < n; i++) {
455 const char *path = NULL;
457 ret = bus_method_call_with_reply(
459 "org.freedesktop.machine1",
460 "/org/freedesktop/machine1",
461 "org.freedesktop.machine1.Manager",
465 DBUS_TYPE_STRING, &args[i],
470 if (!dbus_message_get_args(reply, &error,
471 DBUS_TYPE_OBJECT_PATH, &path,
472 DBUS_TYPE_INVALID)) {
473 log_error("Failed to parse reply: %s", bus_error_message(&error));
478 r = show_one(args[0], bus, path, show_properties, &new_line);
484 dbus_error_free(&error);
489 static int kill_machine(DBusConnection *bus, char **args, unsigned n) {
495 arg_kill_who = "all";
497 for (i = 1; i < n; i++) {
500 r = bus_method_call_with_reply (
502 "org.freedesktop.machine1",
503 "/org/freedesktop/machine1",
504 "org.freedesktop.machine1.Manager",
508 DBUS_TYPE_STRING, &args[i],
509 DBUS_TYPE_STRING, &arg_kill_who,
510 DBUS_TYPE_INT32, &arg_signal,
519 static int terminate_machine(DBusConnection *bus, char **args, unsigned n) {
524 for (i = 1; i < n; i++) {
527 r = bus_method_call_with_reply (
529 "org.freedesktop.machine1",
530 "/org/freedesktop/machine1",
531 "org.freedesktop.machine1.Manager",
535 DBUS_TYPE_STRING, &args[i],
544 static int help(void) {
546 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
547 "Send control commands to or query the virtual machine and container registration manager.\n\n"
548 " -h --help Show this help\n"
549 " --version Show package version\n"
550 " -p --property=NAME Show only properties by this name\n"
551 " -a --all Show all properties, including empty ones\n"
552 " --kill-who=WHO Who to send signal to\n"
553 " -l --full Do not ellipsize output\n"
554 " -s --signal=SIGNAL Which signal to send\n"
555 " --no-ask-password Don't prompt for password\n"
556 " -H --host=[USER@]HOST Show information for remote host\n"
557 " -P --privileged Acquire privileges before execution\n"
558 " --no-pager Do not pipe output into a pager\n\n"
560 " list List running VMs and containers\n"
561 " status [NAME...] Show VM/container status\n"
562 " show [NAME...] Show properties of one or more VMs/containers\n"
563 " terminate [NAME...] Terminate one or more VMs/containers\n"
564 " kill [NAME...] Send signal to processes of a VM/container\n",
565 program_invocation_short_name);
570 static int parse_argv(int argc, char *argv[]) {
579 static const struct option options[] = {
580 { "help", no_argument, NULL, 'h' },
581 { "version", no_argument, NULL, ARG_VERSION },
582 { "property", required_argument, NULL, 'p' },
583 { "all", no_argument, NULL, 'a' },
584 { "full", no_argument, NULL, 'l' },
585 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
586 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
587 { "signal", required_argument, NULL, 's' },
588 { "host", required_argument, NULL, 'H' },
589 { "privileged", no_argument, NULL, 'P' },
590 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
599 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
608 puts(PACKAGE_STRING);
609 puts(SYSTEMD_FEATURES);
615 l = strv_append(arg_property, optarg);
619 strv_free(arg_property);
622 /* If the user asked for a particular
623 * property, show it to him, even if it is
641 case ARG_NO_ASK_PASSWORD:
642 arg_ask_password = false;
646 arg_kill_who = optarg;
650 arg_signal = signal_from_string_try_harder(optarg);
651 if (arg_signal < 0) {
652 log_error("Failed to parse signal string %s.", optarg);
658 arg_transport = TRANSPORT_POLKIT;
662 arg_transport = TRANSPORT_SSH;
663 parse_user_at_host(optarg, &arg_user, &arg_host);
670 log_error("Unknown option code %c", c);
678 static int machinectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
680 static const struct {
688 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
690 { "list", LESS, 1, list_machines },
691 { "status", MORE, 2, show },
692 { "show", MORE, 1, show },
693 { "terminate", MORE, 2, terminate_machine },
694 { "kill", MORE, 2, kill_machine },
704 left = argc - optind;
707 /* Special rule: no arguments means "list-sessions" */
710 if (streq(argv[optind], "help")) {
715 for (i = 0; i < ELEMENTSOF(verbs); i++)
716 if (streq(argv[optind], verbs[i].verb))
719 if (i >= ELEMENTSOF(verbs)) {
720 log_error("Unknown operation %s", argv[optind]);
725 switch (verbs[i].argc_cmp) {
728 if (left != verbs[i].argc) {
729 log_error("Invalid number of arguments.");
736 if (left < verbs[i].argc) {
737 log_error("Too few arguments.");
744 if (left > verbs[i].argc) {
745 log_error("Too many arguments.");
752 assert_not_reached("Unknown comparison operator.");
756 log_error("Failed to get D-Bus connection: %s", error->message);
760 return verbs[i].dispatch(bus, argv + optind, left);
763 int main(int argc, char*argv[]) {
764 int r, retval = EXIT_FAILURE;
765 DBusConnection *bus = NULL;
768 dbus_error_init(&error);
770 setlocale(LC_ALL, "");
771 log_parse_environment();
774 r = parse_argv(argc, argv);
778 retval = EXIT_SUCCESS;
782 if (arg_transport == TRANSPORT_NORMAL)
783 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
784 else if (arg_transport == TRANSPORT_POLKIT)
785 bus_connect_system_polkit(&bus, &error);
786 else if (arg_transport == TRANSPORT_SSH)
787 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
789 assert_not_reached("Uh, invalid transport...");
791 r = machinectl_main(bus, argc, argv, &error);
792 retval = r < 0 ? EXIT_FAILURE : r;
796 dbus_connection_flush(bus);
797 dbus_connection_close(bus);
798 dbus_connection_unref(bus);
801 dbus_error_free(&error);
804 strv_free(arg_property);