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 { "privileged", no_argument, NULL, 'P' },
576 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
585 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
594 puts(PACKAGE_STRING);
595 puts(SYSTEMD_FEATURES);
601 l = strv_append(arg_property, optarg);
605 strv_free(arg_property);
608 /* If the user asked for a particular
609 * property, show it to him, even if it is
627 case ARG_NO_ASK_PASSWORD:
628 arg_ask_password = false;
632 arg_kill_who = optarg;
636 arg_signal = signal_from_string_try_harder(optarg);
637 if (arg_signal < 0) {
638 log_error("Failed to parse signal string %s.", optarg);
644 arg_transport = TRANSPORT_SSH;
645 parse_user_at_host(optarg, &arg_user, &arg_host);
652 log_error("Unknown option code %c", c);
660 static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) {
662 static const struct {
670 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
672 { "list", LESS, 1, list_machines },
673 { "status", MORE, 2, show },
674 { "show", MORE, 1, show },
675 { "terminate", MORE, 2, terminate_machine },
676 { "kill", MORE, 2, kill_machine },
685 left = argc - optind;
688 /* Special rule: no arguments means "list-sessions" */
691 if (streq(argv[optind], "help")) {
696 for (i = 0; i < ELEMENTSOF(verbs); i++)
697 if (streq(argv[optind], verbs[i].verb))
700 if (i >= ELEMENTSOF(verbs)) {
701 log_error("Unknown operation %s", argv[optind]);
706 switch (verbs[i].argc_cmp) {
709 if (left != verbs[i].argc) {
710 log_error("Invalid number of arguments.");
717 if (left < verbs[i].argc) {
718 log_error("Too few arguments.");
725 if (left > verbs[i].argc) {
726 log_error("Too many arguments.");
733 assert_not_reached("Unknown comparison operator.");
737 log_error("Failed to get D-Bus connection: %s", strerror(-r));
741 return verbs[i].dispatch(bus, argv + optind, left);
744 int main(int argc, char*argv[]) {
745 int r, retval = EXIT_FAILURE;
746 _cleanup_bus_unref_ sd_bus *bus = NULL;
748 setlocale(LC_ALL, "");
749 log_parse_environment();
752 r = parse_argv(argc, argv);
756 retval = EXIT_SUCCESS;
760 if (arg_transport == TRANSPORT_NORMAL)
761 r = sd_bus_open_system(&bus);
762 else if (arg_transport == TRANSPORT_SSH)
763 r = bus_connect_system_ssh(arg_host, &bus);
765 assert_not_reached("Uh, invalid transport...");
767 retval = EXIT_FAILURE;
771 r = machinectl_main(bus, argc, argv, r);
772 retval = r < 0 ? EXIT_FAILURE : r;
775 strv_free(arg_property);