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