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 {
53 } arg_transport = TRANSPORT_LOCAL;
54 static bool arg_ask_password = true;
55 static char *arg_host = 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) {
97 printf("%-32s %-9s %-16s\n", name, class, service);
104 r = sd_bus_message_exit_container(reply);
109 printf("\n%u machines listed.\n", k);
114 log_error("Failed to parse reply: %s", strerror(-r));
118 static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
119 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
120 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
121 _cleanup_free_ char *path = NULL;
129 if (arg_transport == TRANSPORT_REMOTE)
132 path = unit_dbus_path_from_name(unit);
136 r = sd_bus_get_property(
138 "org.freedesktop.systemd1",
140 "org.freedesktop.systemd1.Scope",
146 log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r));
150 r = sd_bus_message_read(reply, "s", &cgroup);
152 log_error("Failed to parse reply: %s", strerror(-r));
159 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
163 arg_all * OUTPUT_SHOW_ALL |
164 arg_full * OUTPUT_FULL_WIDTH;
172 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
176 typedef struct MachineStatusInfo {
182 const char *root_directory;
187 static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
188 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
189 char since2[FORMAT_TIMESTAMP_MAX], *s2;
192 fputs(strna(i->name), stdout);
194 if (!sd_id128_equal(i->id, SD_ID128_NULL))
195 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
199 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
200 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
203 printf("\t Since: %s; %s\n", s2, s1);
205 printf("\t Since: %s\n", s2);
208 _cleanup_free_ char *t = NULL;
210 printf("\t Leader: %u", (unsigned) i->leader);
212 get_process_comm(i->leader, &t);
220 printf("\t Service: %s", i->service);
223 printf("; class %s", i->class);
227 printf("\t Class: %s\n", i->class);
229 if (i->root_directory)
230 printf("\t Root: %s\n", i->root_directory);
233 printf("\t Unit: %s\n", i->scope);
234 show_scope_cgroup(bus, i->scope, i->leader);
238 static int status_property_machine(const char *name, sd_bus_message *property, MachineStatusInfo *i) {
240 const char *contents;
247 r = sd_bus_message_peek_type(property, &type, &contents);
249 log_error("Could not determine type of message: %s", strerror(-r));
255 case SD_BUS_TYPE_STRING: {
258 sd_bus_message_read_basic(property, type, &s);
261 if (streq(name, "Name"))
263 else if (streq(name, "Class"))
265 else if (streq(name, "Service"))
267 else if (streq(name, "Scope"))
269 else if (streq(name, "RootDirectory"))
270 i->root_directory = s;
275 case SD_BUS_TYPE_UINT32: {
278 sd_bus_message_read_basic(property, type, &u);
280 if (streq(name, "Leader"))
281 i->leader = (pid_t) u;
286 case SD_BUS_TYPE_UINT64: {
289 sd_bus_message_read_basic(property, type, &u);
291 if (streq(name, "Timestamp"))
292 i->timestamp = (usec_t) u;
297 case SD_BUS_TYPE_ARRAY: {
298 if (streq(contents, "y") && streq(name, "Id")) {
302 sd_bus_message_read_array(property, SD_BUS_TYPE_BYTE, &v, &n);
304 i->id = SD_ID128_NULL;
306 memcpy(&i->id, v, n);
316 static int print_property(const char *name, sd_bus_message *reply) {
320 if (arg_property && !strv_find(arg_property, name))
323 if (bus_generic_print_property(name, reply, arg_all) > 0)
327 printf("%s=[unprintable]\n", name);
332 static int show_one(const char *verb, sd_bus *bus, const char *path, bool show_properties, bool *new_line) {
333 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
336 MachineStatusInfo machine_info = {};
341 r = sd_bus_call_method(
343 "org.freedesktop.machine1",
345 "org.freedesktop.DBus.Properties",
351 log_error("Could not get properties: %s", bus_error_message(&error, -r));
361 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
365 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
367 const char *contents;
369 r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name);
373 r = sd_bus_message_peek_type(reply, NULL, &contents);
377 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
382 r = print_property(name, reply);
384 r = status_property_machine(name, reply, &machine_info);
388 r = sd_bus_message_exit_container(reply);
392 r = sd_bus_message_exit_container(reply);
399 r = sd_bus_message_exit_container(reply);
403 if (!show_properties)
404 print_machine_status_info(bus, &machine_info);
409 log_error("Failed to parse reply: %s", strerror(-r));
413 static int show(sd_bus *bus, char **args, unsigned n) {
414 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
415 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
418 bool show_properties, new_line = false;
423 show_properties = !strstr(args[0], "status");
425 pager_open_if_enabled();
427 if (show_properties && n <= 1) {
429 /* If no argument is specified inspect the manager
432 return show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line);
435 for (i = 1; i < n; i++) {
436 const char *path = NULL;
438 r = sd_bus_call_method(
440 "org.freedesktop.machine1",
441 "/org/freedesktop/machine1",
442 "org.freedesktop.machine1.Manager",
448 log_error("Could not get path to machine: %s", bus_error_message(&error, -r));
452 r = sd_bus_message_read(reply, "o", &path);
454 log_error("Failed to parse reply: %s", strerror(-r));
458 r = show_one(args[0], bus, path, show_properties, &new_line);
466 static int kill_machine(sd_bus *bus, char **args, unsigned n) {
467 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
473 arg_kill_who = "all";
475 for (i = 1; i < n; i++) {
478 r = sd_bus_call_method(
480 "org.freedesktop.machine1",
481 "/org/freedesktop/machine1",
482 "org.freedesktop.machine1.Manager",
486 "ssi", args[i], arg_kill_who, arg_signal);
488 log_error("Could not kill machine: %s", bus_error_message(&error, -r));
496 static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
497 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
502 for (i = 1; i < n; i++) {
505 r = sd_bus_call_method(
507 "org.freedesktop.machine1",
508 "/org/freedesktop/machine1",
509 "org.freedesktop.machine1.Manager",
515 log_error("Could not terminate machine: %s", bus_error_message(&error, -r));
523 static int help(void) {
525 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
526 "Send control commands to or query the virtual machine and container registration manager.\n\n"
527 " -h --help Show this help\n"
528 " --version Show package version\n"
529 " --no-pager Do not pipe output into a pager\n"
530 " --no-ask-password Don't prompt for password\n"
531 " -H --host=[USER@]HOST Operate on remote host\n"
532 " -M --machine=CONTAINER Operate on local container\n"
533 " -p --property=NAME Show only properties by this name\n"
534 " -a --all Show all properties, including empty ones\n"
535 " -l --full Do not ellipsize output\n"
536 " --kill-who=WHO Who to send signal to\n"
537 " -s --signal=SIGNAL Which signal to send\n\n"
539 " list List running VMs and containers\n"
540 " status [NAME...] Show VM/container status\n"
541 " show [NAME...] Show properties of one or more VMs/containers\n"
542 " terminate [NAME...] Terminate one or more VMs/containers\n"
543 " kill [NAME...] Send signal to processes of a VM/container\n",
544 program_invocation_short_name);
549 static int parse_argv(int argc, char *argv[]) {
558 static const struct option options[] = {
559 { "help", no_argument, NULL, 'h' },
560 { "version", no_argument, NULL, ARG_VERSION },
561 { "property", required_argument, NULL, 'p' },
562 { "all", no_argument, NULL, 'a' },
563 { "full", no_argument, NULL, 'l' },
564 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
565 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
566 { "signal", required_argument, NULL, 's' },
567 { "host", required_argument, NULL, 'H' },
568 { "machine", required_argument, NULL, 'M' },
569 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
578 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
587 puts(PACKAGE_STRING);
588 puts(SYSTEMD_FEATURES);
592 r = strv_extend(&arg_property, optarg);
596 /* If the user asked for a particular
597 * property, show it to him, even if it is
614 case ARG_NO_ASK_PASSWORD:
615 arg_ask_password = false;
619 arg_kill_who = optarg;
623 arg_signal = signal_from_string_try_harder(optarg);
624 if (arg_signal < 0) {
625 log_error("Failed to parse signal string %s.", optarg);
631 arg_transport = TRANSPORT_REMOTE;
636 arg_transport = TRANSPORT_CONTAINER;
644 log_error("Unknown option code %c", c);
652 static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) {
654 static const struct {
662 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
664 { "list", LESS, 1, list_machines },
665 { "status", MORE, 2, show },
666 { "show", MORE, 1, show },
667 { "terminate", MORE, 2, terminate_machine },
668 { "kill", MORE, 2, kill_machine },
677 left = argc - optind;
680 /* Special rule: no arguments means "list-sessions" */
683 if (streq(argv[optind], "help")) {
688 for (i = 0; i < ELEMENTSOF(verbs); i++)
689 if (streq(argv[optind], verbs[i].verb))
692 if (i >= ELEMENTSOF(verbs)) {
693 log_error("Unknown operation %s", argv[optind]);
698 switch (verbs[i].argc_cmp) {
701 if (left != verbs[i].argc) {
702 log_error("Invalid number of arguments.");
709 if (left < verbs[i].argc) {
710 log_error("Too few arguments.");
717 if (left > verbs[i].argc) {
718 log_error("Too many arguments.");
725 assert_not_reached("Unknown comparison operator.");
729 log_error("Failed to get D-Bus connection: %s", strerror(-r));
733 return verbs[i].dispatch(bus, argv + optind, left);
736 int main(int argc, char*argv[]) {
737 int r, ret = EXIT_FAILURE;
738 _cleanup_bus_unref_ sd_bus *bus = NULL;
740 setlocale(LC_ALL, "");
741 log_parse_environment();
744 r = parse_argv(argc, argv);
752 if (arg_transport == TRANSPORT_LOCAL)
753 r = sd_bus_open_system(&bus);
754 else if (arg_transport == TRANSPORT_REMOTE)
755 r = sd_bus_open_system_remote(arg_host, &bus);
756 else if (arg_transport == TRANSPORT_CONTAINER)
757 r = sd_bus_open_system_container(arg_host, &bus);
759 assert_not_reached("Uh, invalid transport...");
761 log_error("Failed to connect to machined: %s", strerror(-r));
766 r = machinectl_main(bus, argc, argv, r);
767 ret = r < 0 ? EXIT_FAILURE : r;
770 strv_free(arg_property);