chiark / gitweb /
machined: correct how some properties are exported on the bus
[elogind.git] / src / machine / machinectl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <dbus/dbus.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <pwd.h>
28 #include <locale.h>
29
30 #include "log.h"
31 #include "util.h"
32 #include "macro.h"
33 #include "pager.h"
34 #include "dbus-common.h"
35 #include "build.h"
36 #include "strv.h"
37 #include "unit-name.h"
38 #include "cgroup-show.h"
39 #include "cgroup-util.h"
40 #include "spawn-polkit-agent.h"
41
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 enum transport {
49         TRANSPORT_NORMAL,
50         TRANSPORT_SSH,
51         TRANSPORT_POLKIT
52 } arg_transport = TRANSPORT_NORMAL;
53 static bool arg_ask_password = true;
54 static char *arg_host = NULL;
55 static char *arg_user = NULL;
56
57 static void pager_open_if_enabled(void) {
58
59         /* Cache result before we open the pager */
60         if (arg_no_pager)
61                 return;
62
63         pager_open(false);
64 }
65
66 static int list_machines(DBusConnection *bus, char **args, unsigned n) {
67         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
68         DBusMessageIter iter, sub, sub2;
69         unsigned k = 0;
70         int r;
71
72         pager_open_if_enabled();
73
74         r = bus_method_call_with_reply (
75                         bus,
76                         "org.freedesktop.machine1",
77                         "/org/freedesktop/machine1",
78                         "org.freedesktop.machine1.Manager",
79                         "ListMachines",
80                         &reply,
81                         NULL,
82                         DBUS_TYPE_INVALID);
83         if (r)
84                 return r;
85
86         if (!dbus_message_iter_init(reply, &iter) ||
87             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
88             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
89                 log_error("Failed to parse reply.");
90                 return -EIO;
91         }
92
93         dbus_message_iter_recurse(&iter, &sub);
94
95         if (on_tty())
96                 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
97
98         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
99                 const char *name, *class, *service, *object;
100
101                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
102                         log_error("Failed to parse reply.");
103                         return -EIO;
104                 }
105
106                 dbus_message_iter_recurse(&sub, &sub2);
107
108                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
109                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &class, true) < 0 ||
110                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &service, true) < 0 ||
111                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
112                         log_error("Failed to parse reply.");
113                         return -EIO;
114                 }
115
116                 printf("%-32s %-9s %-16s\n", name, class, service);
117
118                 k++;
119
120                 dbus_message_iter_next(&sub);
121         }
122
123         if (on_tty())
124                 printf("\n%u machines listed.\n", k);
125
126         return 0;
127 }
128
129 static int show_scope_cgroup(DBusConnection *bus, const char *unit, pid_t leader) {
130         const char *interface = "org.freedesktop.systemd1.Scope";
131         const char *property = "ControlGroup";
132         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
133         _cleanup_free_ char *path = NULL;
134         DBusMessageIter iter, sub;
135         const char *cgroup;
136         DBusError error;
137         int r, output_flags;
138         unsigned c;
139
140         assert(bus);
141         assert(unit);
142
143         if (arg_transport == TRANSPORT_SSH)
144                 return 0;
145
146         path = unit_dbus_path_from_name(unit);
147         if (!path)
148                 return log_oom();
149
150         r = bus_method_call_with_reply(
151                         bus,
152                         "org.freedesktop.systemd1",
153                         path,
154                         "org.freedesktop.DBus.Properties",
155                         "Get",
156                         &reply,
157                         &error,
158                         DBUS_TYPE_STRING, &interface,
159                         DBUS_TYPE_STRING, &property,
160                         DBUS_TYPE_INVALID);
161         if (r < 0) {
162                 log_error("Failed to query ControlGroup: %s", bus_error(&error, r));
163                 dbus_error_free(&error);
164                 return r;
165         }
166
167         if (!dbus_message_iter_init(reply, &iter) ||
168             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
169                 log_error("Failed to parse reply.");
170                 return -EINVAL;
171         }
172
173         dbus_message_iter_recurse(&iter, &sub);
174         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
175                 log_error("Failed to parse reply.");
176                 return -EINVAL;
177         }
178
179         dbus_message_iter_get_basic(&sub, &cgroup);
180
181         if (isempty(cgroup))
182                 return 0;
183
184         if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
185                 return 0;
186
187         output_flags =
188                 arg_all * OUTPUT_SHOW_ALL |
189                 arg_full * OUTPUT_FULL_WIDTH;
190
191         c = columns();
192         if (c > 18)
193                 c -= 18;
194         else
195                 c = 0;
196
197         show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, false, &leader, leader > 0, output_flags);
198         return 0;
199 }
200
201 typedef struct MachineStatusInfo {
202         const char *name;
203         sd_id128_t id;
204         const char *class;
205         const char *service;
206         const char *scope;
207         const char *root_directory;
208         pid_t leader;
209         usec_t timestamp;
210 } MachineStatusInfo;
211
212 static void print_machine_status_info(DBusConnection *bus, MachineStatusInfo *i) {
213         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
214         char since2[FORMAT_TIMESTAMP_MAX], *s2;
215         assert(i);
216
217         fputs(strna(i->name), stdout);
218
219         if (!sd_id128_equal(i->id, SD_ID128_NULL))
220                 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
221         else
222                 putchar('\n');
223
224         s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
225         s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
226
227         if (s1)
228                 printf("\t   Since: %s; %s\n", s2, s1);
229         else if (s2)
230                 printf("\t   Since: %s\n", s2);
231
232         if (i->leader > 0) {
233                 _cleanup_free_ char *t = NULL;
234
235                 printf("\t  Leader: %u", (unsigned) i->leader);
236
237                 get_process_comm(i->leader, &t);
238                 if (t)
239                         printf(" (%s)", t);
240
241                 putchar('\n');
242         }
243
244         if (i->service) {
245                 printf("\t Service: %s", i->service);
246
247                 if (i->class)
248                         printf("; class %s", i->class);
249
250                 putchar('\n');
251         } else if (i->class)
252                 printf("\t   Class: %s\n", i->class);
253
254         if (i->root_directory)
255                 printf("\t    Root: %s\n", i->root_directory);
256
257         if (i->scope) {
258                 printf("\t    Unit: %s\n", i->scope);
259                 show_scope_cgroup(bus, i->scope, i->leader);
260         }
261 }
262
263 static int status_property_machine(const char *name, DBusMessageIter *iter, MachineStatusInfo *i) {
264         assert(name);
265         assert(iter);
266         assert(i);
267
268         switch (dbus_message_iter_get_arg_type(iter)) {
269
270         case DBUS_TYPE_STRING: {
271                 const char *s;
272
273                 dbus_message_iter_get_basic(iter, &s);
274
275                 if (!isempty(s)) {
276                         if (streq(name, "Name"))
277                                 i->name = s;
278                         else if (streq(name, "Class"))
279                                 i->class = s;
280                         else if (streq(name, "Service"))
281                                 i->service = s;
282                         else if (streq(name, "Scope"))
283                                 i->scope = s;
284                         else if (streq(name, "RootDirectory"))
285                                 i->root_directory = s;
286                 }
287                 break;
288         }
289
290         case DBUS_TYPE_UINT32: {
291                 uint32_t u;
292
293                 dbus_message_iter_get_basic(iter, &u);
294
295                 if (streq(name, "Leader"))
296                         i->leader = (pid_t) u;
297
298                 break;
299         }
300
301         case DBUS_TYPE_UINT64: {
302                 uint64_t u;
303
304                 dbus_message_iter_get_basic(iter, &u);
305
306                 if (streq(name, "Timestamp"))
307                         i->timestamp = (usec_t) u;
308
309                 break;
310         }
311
312         case DBUS_TYPE_ARRAY: {
313                 DBusMessageIter sub;
314
315                 dbus_message_iter_recurse(iter, &sub);
316
317                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE && streq(name, "Id")) {
318                         void *v;
319                         int n;
320
321                         dbus_message_iter_get_fixed_array(&sub, &v, &n);
322                         if (n == 0)
323                                 i->id = SD_ID128_NULL;
324                         else if (n == 16)
325                                 memcpy(&i->id, v, n);
326                 }
327
328                 break;
329         }
330         }
331
332         return 0;
333 }
334
335 static int print_property(const char *name, DBusMessageIter *iter) {
336         assert(name);
337         assert(iter);
338
339         if (arg_property && !strv_find(arg_property, name))
340                 return 0;
341
342         if (generic_print_property(name, iter, arg_all) > 0)
343                 return 0;
344
345         if (arg_all)
346                 printf("%s=[unprintable]\n", name);
347
348         return 0;
349 }
350
351 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
352         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
353         const char *interface = "";
354         int r;
355         DBusMessageIter iter, sub, sub2, sub3;
356         MachineStatusInfo machine_info = {};
357
358         assert(path);
359         assert(new_line);
360
361         r = bus_method_call_with_reply(
362                         bus,
363                         "org.freedesktop.machine1",
364                         path,
365                         "org.freedesktop.DBus.Properties",
366                         "GetAll",
367                         &reply,
368                         NULL,
369                         DBUS_TYPE_STRING, &interface,
370                         DBUS_TYPE_INVALID);
371         if (r < 0)
372                 goto finish;
373
374         if (!dbus_message_iter_init(reply, &iter) ||
375             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
376             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
377                 log_error("Failed to parse reply.");
378                 r = -EIO;
379                 goto finish;
380         }
381
382         dbus_message_iter_recurse(&iter, &sub);
383
384         if (*new_line)
385                 printf("\n");
386
387         *new_line = true;
388
389         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
390                 const char *name;
391
392                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
393                         log_error("Failed to parse reply.");
394                         r = -EIO;
395                         goto finish;
396                 }
397
398                 dbus_message_iter_recurse(&sub, &sub2);
399
400                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
401                         log_error("Failed to parse reply.");
402                         r = -EIO;
403                         goto finish;
404                 }
405
406                 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
407                         log_error("Failed to parse reply.");
408                         r = -EIO;
409                         goto finish;
410                 }
411
412                 dbus_message_iter_recurse(&sub2, &sub3);
413
414                 if (show_properties)
415                         r = print_property(name, &sub3);
416                 else
417                         r = status_property_machine(name, &sub3, &machine_info);
418
419                 if (r < 0) {
420                         log_error("Failed to parse reply.");
421                         goto finish;
422                 }
423
424                 dbus_message_iter_next(&sub);
425         }
426
427         if (!show_properties)
428                 print_machine_status_info(bus, &machine_info);
429
430         r = 0;
431
432 finish:
433
434         return r;
435 }
436
437 static int show(DBusConnection *bus, char **args, unsigned n) {
438         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
439         int r, ret = 0;
440         DBusError error;
441         unsigned i;
442         bool show_properties, new_line = false;
443
444         assert(bus);
445         assert(args);
446
447         dbus_error_init(&error);
448
449         show_properties = !strstr(args[0], "status");
450
451         pager_open_if_enabled();
452
453         if (show_properties && n <= 1) {
454                 /* If not argument is specified inspect the manager
455                  * itself */
456
457                 ret = show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line);
458                 goto finish;
459         }
460
461         for (i = 1; i < n; i++) {
462                 const char *path = NULL;
463
464                 ret = bus_method_call_with_reply(
465                                 bus,
466                                 "org.freedesktop.machine1",
467                                 "/org/freedesktop/machine1",
468                                 "org.freedesktop.machine1.Manager",
469                                 "GetMachine",
470                                 &reply,
471                                 NULL,
472                                 DBUS_TYPE_STRING, &args[i],
473                                 DBUS_TYPE_INVALID);
474                 if (ret < 0)
475                         goto finish;
476
477                 if (!dbus_message_get_args(reply, &error,
478                                            DBUS_TYPE_OBJECT_PATH, &path,
479                                            DBUS_TYPE_INVALID)) {
480                         log_error("Failed to parse reply: %s", bus_error_message(&error));
481                         ret = -EIO;
482                         goto finish;
483                 }
484
485                 r = show_one(args[0], bus, path, show_properties, &new_line);
486                 if (r != 0)
487                         ret = r;
488         }
489
490 finish:
491         dbus_error_free(&error);
492
493         return ret;
494 }
495
496 static int kill_machine(DBusConnection *bus, char **args, unsigned n) {
497         unsigned i;
498
499         assert(args);
500
501         if (!arg_kill_who)
502                 arg_kill_who = "all";
503
504         for (i = 1; i < n; i++) {
505                 int r;
506
507                 r = bus_method_call_with_reply (
508                         bus,
509                         "org.freedesktop.machine1",
510                         "/org/freedesktop/machine1",
511                         "org.freedesktop.machine1.Manager",
512                         "KillMachine",
513                         NULL,
514                         NULL,
515                         DBUS_TYPE_STRING, &args[i],
516                         DBUS_TYPE_STRING, &arg_kill_who,
517                         DBUS_TYPE_INT32, &arg_signal,
518                         DBUS_TYPE_INVALID);
519                 if (r)
520                         return r;
521         }
522
523         return 0;
524 }
525
526 static int terminate_machine(DBusConnection *bus, char **args, unsigned n) {
527         unsigned i;
528
529         assert(args);
530
531         for (i = 1; i < n; i++) {
532                 int r;
533
534                 r = bus_method_call_with_reply (
535                         bus,
536                         "org.freedesktop.machine1",
537                         "/org/freedesktop/machine1",
538                         "org.freedesktop.machine1.Manager",
539                         "TerminateMachine",
540                         NULL,
541                         NULL,
542                         DBUS_TYPE_STRING, &args[i],
543                         DBUS_TYPE_INVALID);
544                 if (r)
545                         return r;
546         }
547
548         return 0;
549 }
550
551 static int help(void) {
552
553         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
554                "Send control commands to or query the virtual machine and container registration manager.\n\n"
555                "  -h --help              Show this help\n"
556                "     --version           Show package version\n"
557                "  -p --property=NAME     Show only properties by this name\n"
558                "  -a --all               Show all properties, including empty ones\n"
559                "     --kill-who=WHO      Who to send signal to\n"
560                "  -l --full              Do not ellipsize output\n"
561                "  -s --signal=SIGNAL     Which signal to send\n"
562                "     --no-ask-password   Don't prompt for password\n"
563                "  -H --host=[USER@]HOST  Show information for remote host\n"
564                "  -P --privileged        Acquire privileges before execution\n"
565                "     --no-pager          Do not pipe output into a pager\n\n"
566                "Commands:\n"
567                "  list                   List running VMs and containers\n"
568                "  status [NAME...]       Show VM/container status\n"
569                "  show [NAME...]         Show properties of one or more VMs/containers\n"
570                "  terminate [NAME...]    Terminate one or more VMs/containers\n"
571                "  kill [NAME...]         Send signal to processes of a VM/container\n",
572                program_invocation_short_name);
573
574         return 0;
575 }
576
577 static int parse_argv(int argc, char *argv[]) {
578
579         enum {
580                 ARG_VERSION = 0x100,
581                 ARG_NO_PAGER,
582                 ARG_KILL_WHO,
583                 ARG_NO_ASK_PASSWORD,
584         };
585
586         static const struct option options[] = {
587                 { "help",            no_argument,       NULL, 'h'                 },
588                 { "version",         no_argument,       NULL, ARG_VERSION         },
589                 { "property",        required_argument, NULL, 'p'                 },
590                 { "all",             no_argument,       NULL, 'a'                 },
591                 { "full",            no_argument,       NULL, 'l'                 },
592                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
593                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
594                 { "signal",          required_argument, NULL, 's'                 },
595                 { "host",            required_argument, NULL, 'H'                 },
596                 { "privileged",      no_argument,       NULL, 'P'                 },
597                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
598                 { NULL,              0,                 NULL, 0                   }
599         };
600
601         int c;
602
603         assert(argc >= 0);
604         assert(argv);
605
606         while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
607
608                 switch (c) {
609
610                 case 'h':
611                         help();
612                         return 0;
613
614                 case ARG_VERSION:
615                         puts(PACKAGE_STRING);
616                         puts(SYSTEMD_FEATURES);
617                         return 0;
618
619                 case 'p': {
620                         char **l;
621
622                         l = strv_append(arg_property, optarg);
623                         if (!l)
624                                 return -ENOMEM;
625
626                         strv_free(arg_property);
627                         arg_property = l;
628
629                         /* If the user asked for a particular
630                          * property, show it to him, even if it is
631                          * empty. */
632                         arg_all = true;
633                         break;
634                 }
635
636                 case 'a':
637                         arg_all = true;
638                         break;
639
640                 case 'l':
641                         arg_full = true;
642                         break;
643
644                 case ARG_NO_PAGER:
645                         arg_no_pager = true;
646                         break;
647
648                 case ARG_NO_ASK_PASSWORD:
649                         arg_ask_password = false;
650                         break;
651
652                 case ARG_KILL_WHO:
653                         arg_kill_who = optarg;
654                         break;
655
656                 case 's':
657                         arg_signal = signal_from_string_try_harder(optarg);
658                         if (arg_signal < 0) {
659                                 log_error("Failed to parse signal string %s.", optarg);
660                                 return -EINVAL;
661                         }
662                         break;
663
664                 case 'P':
665                         arg_transport = TRANSPORT_POLKIT;
666                         break;
667
668                 case 'H':
669                         arg_transport = TRANSPORT_SSH;
670                         parse_user_at_host(optarg, &arg_user, &arg_host);
671                         break;
672
673                 case '?':
674                         return -EINVAL;
675
676                 default:
677                         log_error("Unknown option code %c", c);
678                         return -EINVAL;
679                 }
680         }
681
682         return 1;
683 }
684
685 static int machinectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
686
687         static const struct {
688                 const char* verb;
689                 const enum {
690                         MORE,
691                         LESS,
692                         EQUAL
693                 } argc_cmp;
694                 const int argc;
695                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
696         } verbs[] = {
697                 { "list",                  LESS,   1, list_machines     },
698                 { "status",                MORE,   2, show              },
699                 { "show",                  MORE,   1, show              },
700                 { "terminate",             MORE,   2, terminate_machine },
701                 { "kill",                  MORE,   2, kill_machine      },
702         };
703
704         int left;
705         unsigned i;
706
707         assert(argc >= 0);
708         assert(argv);
709         assert(error);
710
711         left = argc - optind;
712
713         if (left <= 0)
714                 /* Special rule: no arguments means "list-sessions" */
715                 i = 0;
716         else {
717                 if (streq(argv[optind], "help")) {
718                         help();
719                         return 0;
720                 }
721
722                 for (i = 0; i < ELEMENTSOF(verbs); i++)
723                         if (streq(argv[optind], verbs[i].verb))
724                                 break;
725
726                 if (i >= ELEMENTSOF(verbs)) {
727                         log_error("Unknown operation %s", argv[optind]);
728                         return -EINVAL;
729                 }
730         }
731
732         switch (verbs[i].argc_cmp) {
733
734         case EQUAL:
735                 if (left != verbs[i].argc) {
736                         log_error("Invalid number of arguments.");
737                         return -EINVAL;
738                 }
739
740                 break;
741
742         case MORE:
743                 if (left < verbs[i].argc) {
744                         log_error("Too few arguments.");
745                         return -EINVAL;
746                 }
747
748                 break;
749
750         case LESS:
751                 if (left > verbs[i].argc) {
752                         log_error("Too many arguments.");
753                         return -EINVAL;
754                 }
755
756                 break;
757
758         default:
759                 assert_not_reached("Unknown comparison operator.");
760         }
761
762         if (!bus) {
763                 log_error("Failed to get D-Bus connection: %s", error->message);
764                 return -EIO;
765         }
766
767         return verbs[i].dispatch(bus, argv + optind, left);
768 }
769
770 int main(int argc, char*argv[]) {
771         int r, retval = EXIT_FAILURE;
772         DBusConnection *bus = NULL;
773         DBusError error;
774
775         dbus_error_init(&error);
776
777         setlocale(LC_ALL, "");
778         log_parse_environment();
779         log_open();
780
781         r = parse_argv(argc, argv);
782         if (r < 0)
783                 goto finish;
784         else if (r == 0) {
785                 retval = EXIT_SUCCESS;
786                 goto finish;
787         }
788
789         if (arg_transport == TRANSPORT_NORMAL)
790                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
791         else if (arg_transport == TRANSPORT_POLKIT)
792                 bus_connect_system_polkit(&bus, &error);
793         else if (arg_transport == TRANSPORT_SSH)
794                 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
795         else
796                 assert_not_reached("Uh, invalid transport...");
797
798         r = machinectl_main(bus, argc, argv, &error);
799         retval = r < 0 ? EXIT_FAILURE : r;
800
801 finish:
802         if (bus) {
803                 dbus_connection_flush(bus);
804                 dbus_connection_close(bus);
805                 dbus_connection_unref(bus);
806         }
807
808         dbus_error_free(&error);
809         dbus_shutdown();
810
811         strv_free(arg_property);
812
813         pager_close();
814
815         return retval;
816 }