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/>.
35 #include "bus-error.h"
38 #include "unit-name.h"
39 #include "cgroup-show.h"
40 #include "cgroup-util.h"
42 static char **arg_property = NULL;
43 static bool arg_all = false;
44 static bool arg_full = false;
45 static bool arg_no_pager = false;
46 static const char *arg_kill_who = NULL;
47 static int arg_signal = SIGTERM;
48 static bool arg_ask_password = true;
49 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
50 static char *arg_host = NULL;
52 static void pager_open_if_enabled(void) {
54 /* Cache result before we open the pager */
61 static int list_machines(sd_bus *bus, char **args, unsigned n) {
62 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
63 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
64 const char *name, *class, *service, *object;
68 pager_open_if_enabled();
70 r = sd_bus_call_method(
72 "org.freedesktop.machine1",
73 "/org/freedesktop/machine1",
74 "org.freedesktop.machine1.Manager",
80 log_error("Could not get machines: %s", bus_error_message(&error, -r));
85 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
87 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssso)");
91 while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) {
92 printf("%-32s %-9s %-16s\n", name, class, service);
99 r = sd_bus_message_exit_container(reply);
104 printf("\n%u machines listed.\n", k);
109 log_error("Failed to parse reply: %s", strerror(-r));
113 static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
114 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
115 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
116 _cleanup_free_ char *path = NULL;
124 if (arg_transport == BUS_TRANSPORT_REMOTE)
127 path = unit_dbus_path_from_name(unit);
131 r = sd_bus_get_property(
133 "org.freedesktop.systemd1",
135 "org.freedesktop.systemd1.Scope",
141 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
145 r = sd_bus_message_read(reply, "s", &cgroup);
147 log_error("Failed to parse reply: %s", strerror(-r));
154 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
158 arg_all * OUTPUT_SHOW_ALL |
159 arg_full * OUTPUT_FULL_WIDTH;
167 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
171 typedef struct MachineStatusInfo {
177 const char *root_directory;
182 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
183 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
184 char since2[FORMAT_TIMESTAMP_MAX], *s2;
187 fputs(strna(i->name), stdout);
189 if (!sd_id128_equal(i->id, SD_ID128_NULL))
190 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
194 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
195 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
198 printf("\t Since: %s; %s\n", s2, s1);
200 printf("\t Since: %s\n", s2);
203 _cleanup_free_ char *t = NULL;
205 printf("\t Leader: %u", (unsigned) i->leader);
207 get_process_comm(i->leader, &t);
215 printf("\t Service: %s", i->service);
218 printf("; class %s", i->class);
222 printf("\t Class: %s\n", i->class);
224 if (i->root_directory)
225 printf("\t Root: %s\n", i->root_directory);
228 printf("\t Unit: %s\n", i->scope);
229 show_scope_cgroup(bus, i->scope, i->leader);
233 static int status_property_machine(const char *name, sd_bus_message *property, MachineStatusInfo *i) {
235 const char *contents;
242 r = sd_bus_message_peek_type(property, &type, &contents);
244 log_error("Could not determine type of message: %s", strerror(-r));
250 case SD_BUS_TYPE_STRING: {
253 sd_bus_message_read_basic(property, type, &s);
256 if (streq(name, "Name"))
258 else if (streq(name, "Class"))
260 else if (streq(name, "Service"))
262 else if (streq(name, "Scope"))
264 else if (streq(name, "RootDirectory"))
265 i->root_directory = s;
270 case SD_BUS_TYPE_UINT32: {
273 sd_bus_message_read_basic(property, type, &u);
275 if (streq(name, "Leader"))
276 i->leader = (pid_t) u;
281 case SD_BUS_TYPE_UINT64: {
284 sd_bus_message_read_basic(property, type, &u);
286 if (streq(name, "Timestamp"))
287 i->timestamp = (usec_t) u;
292 case SD_BUS_TYPE_ARRAY: {
293 if (streq(contents, "y") && streq(name, "Id")) {
297 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, &v, &n);
299 i->id = SD_ID128_NULL;
301 memcpy(&i->id, v, n);
311 static int print_property(const char *name, sd_bus_message *reply) {
315 if (arg_property && !strv_find(arg_property, name))
318 if (bus_generic_print_property(name, reply, arg_all) > 0)
322 printf("%s=[unprintable]\n", name);
327 static int show_one(const char *verb, sd_bus *bus, const char *path, bool show_properties, bool *new_line) {
328 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
329 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
331 MachineStatusInfo machine_info = {};
336 r = sd_bus_call_method(
338 "org.freedesktop.machine1",
340 "org.freedesktop.DBus.Properties",
346 log_error("Could not get properties: %s", bus_error_message(&error, -r));
356 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
360 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
362 const char *contents;
364 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
368 r = sd_bus_message_peek_type(reply, NULL, &contents);
372 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
377 r = print_property(name, reply);
379 r = status_property_machine(name, reply, &machine_info);
383 r = sd_bus_message_exit_container(reply);
387 r = sd_bus_message_exit_container(reply);
394 r = sd_bus_message_exit_container(reply);
398 if (!show_properties)
399 print_machine_status_info(bus, &machine_info);
404 log_error("Failed to parse reply: %s", strerror(-r));
408 static int show(sd_bus *bus, char **args, unsigned n) {
409 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
410 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
413 bool show_properties, new_line = false;
418 show_properties = !strstr(args[0], "status");
420 pager_open_if_enabled();
422 if (show_properties && n <= 1) {
424 /* If no argument is specified inspect the manager
427 return show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line);
430 for (i = 1; i < n; i++) {
431 const char *path = NULL;
433 r = sd_bus_call_method(
435 "org.freedesktop.machine1",
436 "/org/freedesktop/machine1",
437 "org.freedesktop.machine1.Manager",
443 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
447 r = sd_bus_message_read(reply, "o", &path);
449 log_error("Failed to parse reply: %s", strerror(-r));
453 r = show_one(args[0], bus, path, show_properties, &new_line);
461 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
462 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
468 arg_kill_who = "all";
470 for (i = 1; i < n; i++) {
473 r = sd_bus_call_method(
475 "org.freedesktop.machine1",
476 "/org/freedesktop/machine1",
477 "org.freedesktop.machine1.Manager",
481 "ssi", args[i], arg_kill_who, arg_signal);
483 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
491 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
492 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
497 for (i = 1; i < n; i++) {
500 r = sd_bus_call_method(
502 "org.freedesktop.machine1",
503 "/org/freedesktop/machine1",
504 "org.freedesktop.machine1.Manager",
510 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
518 static int help(void) {
520 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
521 "Send control commands to or query the virtual machine and container registration manager.\n\n"
522 " -h --help Show this help\n"
523 " --version Show package version\n"
524 " --no-pager Do not pipe output into a pager\n"
525 " --no-ask-password Don't prompt for password\n"
526 " -H --host=[USER@]HOST Operate on remote host\n"
527 " -M --machine=CONTAINER Operate on local container\n"
528 " -p --property=NAME Show only properties by this name\n"
529 " -a --all Show all properties, including empty ones\n"
530 " -l --full Do not ellipsize output\n"
531 " --kill-who=WHO Who to send signal to\n"
532 " -s --signal=SIGNAL Which signal to send\n\n"
534 " list List running VMs and containers\n"
535 " status [NAME...] Show VM/container status\n"
536 " show [NAME...] Show properties of one or more VMs/containers\n"
537 " terminate [NAME...] Terminate one or more VMs/containers\n"
538 " kill [NAME...] Send signal to processes of a VM/container\n",
539 program_invocation_short_name);
544 static int parse_argv(int argc, char *argv[]) {
553 static const struct option options[] = {
554 { "help", no_argument, NULL, 'h' },
555 { "version", no_argument, NULL, ARG_VERSION },
556 { "property", required_argument, NULL, 'p' },
557 { "all", no_argument, NULL, 'a' },
558 { "full", no_argument, NULL, 'l' },
559 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
560 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
561 { "signal", required_argument, NULL, 's' },
562 { "host", required_argument, NULL, 'H' },
563 { "machine", required_argument, NULL, 'M' },
564 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
573 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
582 puts(PACKAGE_STRING);
583 puts(SYSTEMD_FEATURES);
587 r = strv_extend(&arg_property, optarg);
591 /* If the user asked for a particular
592 * property, show it to him, even if it is
609 case ARG_NO_ASK_PASSWORD:
610 arg_ask_password = false;
614 arg_kill_who = optarg;
618 arg_signal = signal_from_string_try_harder(optarg);
619 if (arg_signal < 0) {
620 log_error("Failed to parse signal string %s.", optarg);
626 arg_transport = BUS_TRANSPORT_REMOTE;
631 arg_transport = BUS_TRANSPORT_CONTAINER;
639 log_error("Unknown option code %c", c);
647 static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) {
649 static const struct {
657 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
659 { "list", LESS, 1, list_machines },
660 { "status", MORE, 2, show },
661 { "show", MORE, 1, show },
662 { "terminate", MORE, 2, terminate_machine },
663 { "kill", MORE, 2, kill_machine },
672 left = argc - optind;
675 /* Special rule: no arguments means "list-sessions" */
678 if (streq(argv[optind], "help")) {
683 for (i = 0; i < ELEMENTSOF(verbs); i++)
684 if (streq(argv[optind], verbs[i].verb))
687 if (i >= ELEMENTSOF(verbs)) {
688 log_error("Unknown operation %s", argv[optind]);
693 switch (verbs[i].argc_cmp) {
696 if (left != verbs[i].argc) {
697 log_error("Invalid number of arguments.");
704 if (left < verbs[i].argc) {
705 log_error("Too few arguments.");
712 if (left > verbs[i].argc) {
713 log_error("Too many arguments.");
720 assert_not_reached("Unknown comparison operator.");
724 log_error("Failed to get D-Bus connection: %s", strerror(-r));
728 return verbs[i].dispatch(bus, argv + optind, left);
731 int main(int argc, char*argv[]) {
732 int r, ret = EXIT_FAILURE;
733 _cleanup_bus_unref_ sd_bus *bus = NULL;
735 setlocale(LC_ALL, "");
736 log_parse_environment();
739 r = parse_argv(argc, argv);
747 r = bus_open_transport(arg_transport, arg_host, false, &bus);
749 log_error("Failed to create bus connection: %s", strerror(-r));
754 r = machinectl_main(bus, argc, argv, r);
755 ret = r < 0 ? EXIT_FAILURE : r;
758 strv_free(arg_property);