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 "cgroup-show.h"
38 #include "spawn-polkit-agent.h"
40 static char **arg_property = NULL;
41 static bool arg_all = false;
42 static bool arg_full = false;
43 static bool arg_no_pager = false;
44 static const char *arg_kill_who = NULL;
45 static int arg_signal = SIGTERM;
46 static enum transport {
50 } arg_transport = TRANSPORT_NORMAL;
51 static bool arg_ask_password = true;
52 static char *arg_host = NULL;
53 static char *arg_user = NULL;
55 static void pager_open_if_enabled(void) {
57 /* Cache result before we open the pager */
64 static int list_machines(DBusConnection *bus, char **args, unsigned n) {
65 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
66 DBusMessageIter iter, sub, sub2;
70 pager_open_if_enabled();
72 r = bus_method_call_with_reply (
74 "org.freedesktop.machine1",
75 "/org/freedesktop/machine1",
76 "org.freedesktop.machine1.Manager",
84 if (!dbus_message_iter_init(reply, &iter) ||
85 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
86 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
87 log_error("Failed to parse reply.");
91 dbus_message_iter_recurse(&iter, &sub);
94 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
96 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
97 const char *name, *class, *service, *object;
99 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
100 log_error("Failed to parse reply.");
104 dbus_message_iter_recurse(&sub, &sub2);
106 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
107 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &class, true) < 0 ||
108 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &service, true) < 0 ||
109 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
110 log_error("Failed to parse reply.");
114 printf("%-32s %-9s %-16s\n", name, class, service);
118 dbus_message_iter_next(&sub);
122 printf("\n%u machines listed.\n", k);
127 typedef struct MachineStatusInfo {
130 const char *default_control_group;
134 const char *root_directory;
139 static void print_machine_status_info(MachineStatusInfo *i) {
140 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
141 char since2[FORMAT_TIMESTAMP_MAX], *s2;
144 fputs(strna(i->name), stdout);
146 if (!sd_id128_equal(i->id, SD_ID128_NULL))
147 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
151 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
152 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
155 printf("\t Since: %s; %s\n", s2, s1);
157 printf("\t Since: %s\n", s2);
160 _cleanup_free_ char *t = NULL;
162 printf("\t Leader: %u", (unsigned) i->leader);
164 get_process_comm(i->leader, &t);
172 printf("\t Service: %s", i->service);
175 printf("; class %s", i->class);
179 printf("\t Class: %s\n", i->class);
182 printf("\t Slice: %s\n", i->slice);
183 if (i->root_directory)
184 printf("\t Root: %s\n", i->root_directory);
186 if (i->default_control_group) {
189 arg_all * OUTPUT_SHOW_ALL |
190 arg_full * OUTPUT_FULL_WIDTH;
192 printf("\t CGroup: %s\n", i->default_control_group);
194 if (arg_transport != TRANSPORT_SSH) {
201 show_cgroup_and_extra_by_spec(i->default_control_group,
202 "\t\t ", c, false, &i->leader,
203 i->leader > 0 ? 1 : 0,
209 static int status_property_machine(const char *name, DBusMessageIter *iter, MachineStatusInfo *i) {
214 switch (dbus_message_iter_get_arg_type(iter)) {
216 case DBUS_TYPE_STRING: {
219 dbus_message_iter_get_basic(iter, &s);
222 if (streq(name, "Name"))
224 else if (streq(name, "DefaultControlGroup"))
225 i->default_control_group = s;
226 else if (streq(name, "Class"))
228 else if (streq(name, "Service"))
230 else if (streq(name, "Slice"))
232 else if (streq(name, "RootDirectory"))
233 i->root_directory = s;
238 case DBUS_TYPE_UINT32: {
241 dbus_message_iter_get_basic(iter, &u);
243 if (streq(name, "Leader"))
244 i->leader = (pid_t) u;
249 case DBUS_TYPE_UINT64: {
252 dbus_message_iter_get_basic(iter, &u);
254 if (streq(name, "Timestamp"))
255 i->timestamp = (usec_t) u;
260 case DBUS_TYPE_ARRAY: {
263 dbus_message_iter_recurse(iter, &sub);
265 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE && streq(name, "Id")) {
269 dbus_message_iter_get_fixed_array(&sub, &v, &n);
271 i->id = SD_ID128_NULL;
273 memcpy(&i->id, v, n);
283 static int print_property(const char *name, DBusMessageIter *iter) {
287 if (arg_property && !strv_find(arg_property, name))
290 if (generic_print_property(name, iter, arg_all) > 0)
294 printf("%s=[unprintable]\n", name);
299 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
300 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
301 const char *interface = "";
303 DBusMessageIter iter, sub, sub2, sub3;
304 MachineStatusInfo machine_info = {};
309 r = bus_method_call_with_reply(
311 "org.freedesktop.machine1",
313 "org.freedesktop.DBus.Properties",
317 DBUS_TYPE_STRING, &interface,
322 if (!dbus_message_iter_init(reply, &iter) ||
323 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
324 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
325 log_error("Failed to parse reply.");
330 dbus_message_iter_recurse(&iter, &sub);
337 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
340 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
341 log_error("Failed to parse reply.");
346 dbus_message_iter_recurse(&sub, &sub2);
348 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
349 log_error("Failed to parse reply.");
354 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
355 log_error("Failed to parse reply.");
360 dbus_message_iter_recurse(&sub2, &sub3);
363 r = print_property(name, &sub3);
365 r = status_property_machine(name, &sub3, &machine_info);
368 log_error("Failed to parse reply.");
372 dbus_message_iter_next(&sub);
375 if (!show_properties)
376 print_machine_status_info(&machine_info);
385 static int show(DBusConnection *bus, char **args, unsigned n) {
386 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
390 bool show_properties, new_line = false;
395 dbus_error_init(&error);
397 show_properties = !strstr(args[0], "status");
399 pager_open_if_enabled();
401 if (show_properties && n <= 1) {
402 /* If not argument is specified inspect the manager
405 ret = show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line);
409 for (i = 1; i < n; i++) {
410 const char *path = NULL;
412 ret = bus_method_call_with_reply(
414 "org.freedesktop.machine1",
415 "/org/freedesktop/machine1",
416 "org.freedesktop.machine1.Manager",
420 DBUS_TYPE_STRING, &args[i],
425 if (!dbus_message_get_args(reply, &error,
426 DBUS_TYPE_OBJECT_PATH, &path,
427 DBUS_TYPE_INVALID)) {
428 log_error("Failed to parse reply: %s", bus_error_message(&error));
433 r = show_one(args[0], bus, path, show_properties, &new_line);
439 dbus_error_free(&error);
444 static int kill_machine(DBusConnection *bus, char **args, unsigned n) {
450 arg_kill_who = "all";
452 for (i = 1; i < n; i++) {
455 r = bus_method_call_with_reply (
457 "org.freedesktop.machine1",
458 "/org/freedesktop/machine1",
459 "org.freedesktop.machine1.Manager",
463 DBUS_TYPE_STRING, &args[i],
464 DBUS_TYPE_STRING, &arg_kill_who,
465 DBUS_TYPE_INT32, &arg_signal,
474 static int terminate_machine(DBusConnection *bus, char **args, unsigned n) {
479 for (i = 1; i < n; i++) {
482 r = bus_method_call_with_reply (
484 "org.freedesktop.machine1",
485 "/org/freedesktop/machine1",
486 "org.freedesktop.machine1.Manager",
490 DBUS_TYPE_STRING, &args[i],
499 static int help(void) {
501 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
502 "Send control commands to or query the virtual machine and container registration manager.\n\n"
503 " -h --help Show this help\n"
504 " --version Show package version\n"
505 " -p --property=NAME Show only properties by this name\n"
506 " -a --all Show all properties, including empty ones\n"
507 " --kill-who=WHO Who to send signal to\n"
508 " -l --full Do not ellipsize output\n"
509 " -s --signal=SIGNAL Which signal to send\n"
510 " --no-ask-password Don't prompt for password\n"
511 " -H --host=[USER@]HOST Show information for remote host\n"
512 " -P --privileged Acquire privileges before execution\n"
513 " --no-pager Do not pipe output into a pager\n\n"
515 " list List running VMs and containers\n"
516 " status [NAME...] Show VM/container status\n"
517 " show[NAME...] Show properties of one or more VMs/containers\n"
518 " terminate [NAME...] Terminate one or more VMs/containers\n"
519 " kill [NAME...] Send signal to processes of a VM/container\n",
520 program_invocation_short_name);
525 static int parse_argv(int argc, char *argv[]) {
534 static const struct option options[] = {
535 { "help", no_argument, NULL, 'h' },
536 { "version", no_argument, NULL, ARG_VERSION },
537 { "property", required_argument, NULL, 'p' },
538 { "all", no_argument, NULL, 'a' },
539 { "full", no_argument, NULL, 'l' },
540 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
541 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
542 { "signal", required_argument, NULL, 's' },
543 { "host", required_argument, NULL, 'H' },
544 { "privileged", no_argument, NULL, 'P' },
545 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
554 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
563 puts(PACKAGE_STRING);
564 puts(SYSTEMD_FEATURES);
570 l = strv_append(arg_property, optarg);
574 strv_free(arg_property);
577 /* If the user asked for a particular
578 * property, show it to him, even if it is
596 case ARG_NO_ASK_PASSWORD:
597 arg_ask_password = false;
601 arg_kill_who = optarg;
605 arg_signal = signal_from_string_try_harder(optarg);
606 if (arg_signal < 0) {
607 log_error("Failed to parse signal string %s.", optarg);
613 arg_transport = TRANSPORT_POLKIT;
617 arg_transport = TRANSPORT_SSH;
618 parse_user_at_host(optarg, &arg_user, &arg_host);
625 log_error("Unknown option code %c", c);
633 static int machinectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
635 static const struct {
643 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
645 { "list", LESS, 1, list_machines },
646 { "status", MORE, 2, show },
647 { "show", MORE, 1, show },
648 { "terminate", MORE, 2, terminate_machine },
649 { "kill", MORE, 2, kill_machine },
659 left = argc - optind;
662 /* Special rule: no arguments means "list-sessions" */
665 if (streq(argv[optind], "help")) {
670 for (i = 0; i < ELEMENTSOF(verbs); i++)
671 if (streq(argv[optind], verbs[i].verb))
674 if (i >= ELEMENTSOF(verbs)) {
675 log_error("Unknown operation %s", argv[optind]);
680 switch (verbs[i].argc_cmp) {
683 if (left != verbs[i].argc) {
684 log_error("Invalid number of arguments.");
691 if (left < verbs[i].argc) {
692 log_error("Too few arguments.");
699 if (left > verbs[i].argc) {
700 log_error("Too many arguments.");
707 assert_not_reached("Unknown comparison operator.");
711 log_error("Failed to get D-Bus connection: %s", error->message);
715 return verbs[i].dispatch(bus, argv + optind, left);
718 int main(int argc, char*argv[]) {
719 int r, retval = EXIT_FAILURE;
720 DBusConnection *bus = NULL;
723 dbus_error_init(&error);
725 setlocale(LC_ALL, "");
726 log_parse_environment();
729 r = parse_argv(argc, argv);
733 retval = EXIT_SUCCESS;
737 if (arg_transport == TRANSPORT_NORMAL)
738 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
739 else if (arg_transport == TRANSPORT_POLKIT)
740 bus_connect_system_polkit(&bus, &error);
741 else if (arg_transport == TRANSPORT_SSH)
742 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
744 assert_not_reached("Uh, invalid transport...");
746 r = machinectl_main(bus, argc, argv, &error);
747 retval = r < 0 ? EXIT_FAILURE : r;
751 dbus_connection_flush(bus);
752 dbus_connection_close(bus);
753 dbus_connection_unref(bus);
756 dbus_error_free(&error);
759 strv_free(arg_property);