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/>.
36 #include "bus-error.h"
39 #include "unit-name.h"
40 #include "cgroup-show.h"
41 #include "cgroup-util.h"
43 static char **arg_property = NULL;
44 static bool arg_all = false;
45 static bool arg_full = false;
46 static bool arg_no_pager = false;
47 static const char *arg_kill_who = NULL;
48 static int arg_signal = SIGTERM;
49 static enum transport {
52 } arg_transport = TRANSPORT_NORMAL;
53 static bool arg_ask_password = true;
54 static char *arg_host = NULL;
55 static char *arg_user = NULL;
57 static void pager_open_if_enabled(void) {
59 /* Cache result before we open the pager */
66 static int list_machines(sd_bus *bus, char **args, unsigned n) {
67 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
68 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
69 const char *name, *class, *service, *object;
73 pager_open_if_enabled();
75 r = sd_bus_call_method(
77 "org.freedesktop.machine1",
78 "/org/freedesktop/machine1",
79 "org.freedesktop.machine1.Manager",
85 log_error("Could not get machines: %s", bus_error_message(&error, -r));
90 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
92 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
96 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
100 printf("%-32s %-9s %-16s\n", name, class, service);
107 r = sd_bus_message_exit_container(reply);
112 printf("\n%u machines listed.\n", k);
117 log_error("Failed to parse reply: %s", strerror(-r));
121 static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
122 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
123 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
124 _cleanup_free_ char *path = NULL;
132 if (arg_transport == TRANSPORT_SSH)
135 path = unit_dbus_path_from_name(unit);
139 r = sd_bus_call_method(
141 "org.freedesktop.systemd1",
143 "org.freedesktop.DBus.Properties",
148 "org.freedesktop.systemd1.Scope",
151 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
155 r = sd_bus_message_read(reply, "v", "s", &cgroup);
157 log_error("Failed to parse reply: %s", strerror(-r));
164 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
168 arg_all * OUTPUT_SHOW_ALL |
169 arg_full * OUTPUT_FULL_WIDTH;
177 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
181 typedef struct MachineStatusInfo {
187 const char *root_directory;
192 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
193 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
194 char since2[FORMAT_TIMESTAMP_MAX], *s2;
197 fputs(strna(i->name), stdout);
199 if (!sd_id128_equal(i->id, SD_ID128_NULL))
200 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
204 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
205 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
208 printf("\t Since: %s; %s\n", s2, s1);
210 printf("\t Since: %s\n", s2);
213 _cleanup_free_ char *t = NULL;
215 printf("\t Leader: %u", (unsigned) i->leader);
217 get_process_comm(i->leader, &t);
225 printf("\t Service: %s", i->service);
228 printf("; class %s", i->class);
232 printf("\t Class: %s\n", i->class);
234 if (i->root_directory)
235 printf("\t Root: %s\n", i->root_directory);
238 printf("\t Unit: %s\n", i->scope);
239 show_scope_cgroup(bus, i->scope, i->leader);
243 static int status_property_machine(const char *name, sd_bus_message *property, MachineStatusInfo *i) {
245 const char *contents;
252 r = sd_bus_message_peek_type(property, &type, &contents);
254 log_error("Could not determine type of message: %s", strerror(-r));
260 case SD_BUS_TYPE_STRING: {
263 sd_bus_message_read_basic(property, type, &s);
266 if (streq(name, "Name"))
268 else if (streq(name, "Class"))
270 else if (streq(name, "Service"))
272 else if (streq(name, "Scope"))
274 else if (streq(name, "RootDirectory"))
275 i->root_directory = s;
280 case SD_BUS_TYPE_UINT32: {
283 sd_bus_message_read_basic(property, type, &u);
285 if (streq(name, "Leader"))
286 i->leader = (pid_t) u;
291 case SD_BUS_TYPE_UINT64: {
294 sd_bus_message_read_basic(property, type, &u);
296 if (streq(name, "Timestamp"))
297 i->timestamp = (usec_t) u;
302 case SD_BUS_TYPE_ARRAY: {
303 if (streq(contents, "y") && streq(name, "Id")) {
307 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, &v, &n);
309 i->id = SD_ID128_NULL;
311 memcpy(&i->id, v, n);
321 static int print_property(const char *name, sd_bus_message *reply) {
325 if (arg_property && !strv_find(arg_property, name))
328 if (bus_generic_print_property(name, reply, arg_all) > 0)
332 printf("%s=[unprintable]\n", name);
337 static int show_one(const char *verb, sd_bus *bus, const char *path, bool show_properties, bool *new_line) {
338 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
339 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
341 MachineStatusInfo machine_info = {};
346 r = sd_bus_call_method(
348 "org.freedesktop.machine1",
350 "org.freedesktop.DBus.Properties",
356 log_error("Could not get properties: %s", bus_error_message(&error, -r));
366 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
370 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
372 const char *contents;
377 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
381 r = sd_bus_message_peek_type(reply, NULL, &contents);
385 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
390 r = print_property(name, reply);
392 r = status_property_machine(name, reply, &machine_info);
396 r = sd_bus_message_exit_container(reply);
400 r = sd_bus_message_exit_container(reply);
407 r = sd_bus_message_exit_container(reply);
411 if (!show_properties)
412 print_machine_status_info(bus, &machine_info);
417 log_error("Failed to parse reply: %s", strerror(-r));
421 static int show(sd_bus *bus, char **args, unsigned n) {
422 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
423 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
426 bool show_properties, new_line = false;
431 show_properties = !strstr(args[0], "status");
433 pager_open_if_enabled();
435 if (show_properties && n <= 1) {
437 /* If no argument is specified inspect the manager
440 return show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line);
443 for (i = 1; i < n; i++) {
444 const char *path = NULL;
446 r = sd_bus_call_method(
448 "org.freedesktop.machine1",
449 "/org/freedesktop/machine1",
450 "org.freedesktop.machine1.Manager",
456 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
460 r = sd_bus_message_read(reply, "o", &path);
462 log_error("Failed to parse reply: %s", strerror(-r));
466 r = show_one(args[0], bus, path, show_properties, &new_line);
474 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
475 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
481 arg_kill_who = "all";
483 for (i = 1; i < n; i++) {
486 r = sd_bus_call_method(
488 "org.freedesktop.machine1",
489 "/org/freedesktop/machine1",
490 "org.freedesktop.machine1.Manager",
494 "ssi", args[i], arg_kill_who, arg_signal);
496 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
504 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
505 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
510 for (i = 1; i < n; i++) {
513 r = sd_bus_call_method(
515 "org.freedesktop.machine1",
516 "/org/freedesktop/machine1",
517 "org.freedesktop.machine1.Manager",
523 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
531 static int help(void) {
533 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
534 "Send control commands to or query the virtual machine and container registration manager.\n\n"
535 " -h --help Show this help\n"
536 " --version Show package version\n"
537 " -p --property=NAME Show only properties by this name\n"
538 " -a --all Show all properties, including empty ones\n"
539 " --kill-who=WHO Who to send signal to\n"
540 " -l --full Do not ellipsize output\n"
541 " -s --signal=SIGNAL Which signal to send\n"
542 " --no-ask-password Don't prompt for password\n"
543 " -H --host=[USER@]HOST Show information for remote host\n"
544 " --no-pager Do not pipe output into a pager\n\n"
546 " list List running VMs and containers\n"
547 " status [NAME...] Show VM/container status\n"
548 " show [NAME...] Show properties of one or more VMs/containers\n"
549 " terminate [NAME...] Terminate one or more VMs/containers\n"
550 " kill [NAME...] Send signal to processes of a VM/container\n",
551 program_invocation_short_name);
556 static int parse_argv(int argc, char *argv[]) {
565 static const struct option options[] = {
566 { "help", no_argument, NULL, 'h' },
567 { "version", no_argument, NULL, ARG_VERSION },
568 { "property", required_argument, NULL, 'p' },
569 { "all", no_argument, NULL, 'a' },
570 { "full", no_argument, NULL, 'l' },
571 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
572 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
573 { "signal", required_argument, NULL, 's' },
574 { "host", required_argument, NULL, 'H' },
575 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
584 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
593 puts(PACKAGE_STRING);
594 puts(SYSTEMD_FEATURES);
600 l = strv_append(arg_property, optarg);
604 strv_free(arg_property);
607 /* If the user asked for a particular
608 * property, show it to him, even if it is
626 case ARG_NO_ASK_PASSWORD:
627 arg_ask_password = false;
631 arg_kill_who = optarg;
635 arg_signal = signal_from_string_try_harder(optarg);
636 if (arg_signal < 0) {
637 log_error("Failed to parse signal string %s.", optarg);
643 arg_transport = TRANSPORT_SSH;
644 parse_user_at_host(optarg, &arg_user, &arg_host);
651 log_error("Unknown option code %c", c);
659 static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) {
661 static const struct {
669 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
671 { "list", LESS, 1, list_machines },
672 { "status", MORE, 2, show },
673 { "show", MORE, 1, show },
674 { "terminate", MORE, 2, terminate_machine },
675 { "kill", MORE, 2, kill_machine },
684 left = argc - optind;
687 /* Special rule: no arguments means "list-sessions" */
690 if (streq(argv[optind], "help")) {
695 for (i = 0; i < ELEMENTSOF(verbs); i++)
696 if (streq(argv[optind], verbs[i].verb))
699 if (i >= ELEMENTSOF(verbs)) {
700 log_error("Unknown operation %s", argv[optind]);
705 switch (verbs[i].argc_cmp) {
708 if (left != verbs[i].argc) {
709 log_error("Invalid number of arguments.");
716 if (left < verbs[i].argc) {
717 log_error("Too few arguments.");
724 if (left > verbs[i].argc) {
725 log_error("Too many arguments.");
732 assert_not_reached("Unknown comparison operator.");
736 log_error("Failed to get D-Bus connection: %s", strerror(-r));
740 return verbs[i].dispatch(bus, argv + optind, left);
743 int main(int argc, char*argv[]) {
744 int r, retval = EXIT_FAILURE;
745 _cleanup_bus_unref_ sd_bus *bus = NULL;
747 setlocale(LC_ALL, "");
748 log_parse_environment();
751 r = parse_argv(argc, argv);
755 retval = EXIT_SUCCESS;
759 if (arg_transport == TRANSPORT_NORMAL)
760 r = sd_bus_open_system(&bus);
761 else if (arg_transport == TRANSPORT_SSH)
762 r = bus_connect_system_ssh(arg_host, &bus);
764 assert_not_reached("Uh, invalid transport...");
766 retval = EXIT_FAILURE;
770 r = machinectl_main(bus, argc, argv, r);
771 retval = r < 0 ? EXIT_FAILURE : r;
774 strv_free(arg_property);