chiark / gitweb /
manager: recheck unit paths on daemon reload
[elogind.git] / src / systemctl.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <getopt.h>
24 #include <stdbool.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/ioctl.h>
28 #include <termios.h>
29 #include <unistd.h>
30
31 #include <dbus/dbus.h>
32
33 #include "log.h"
34 #include "util.h"
35 #include "macro.h"
36 #include "set.h"
37
38 static const char *arg_type = NULL;
39 static bool arg_all = false;
40 static bool arg_replace = false;
41 static bool arg_session = false;
42 static bool arg_block = false;
43
44 static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
45
46         if (dbus_message_iter_get_arg_type(iter) != type)
47                 return -EIO;
48
49         dbus_message_iter_get_basic(iter, data);
50
51         if (!dbus_message_iter_next(iter) != !next)
52                 return -EIO;
53
54         return 0;
55 }
56
57 static int columns(void) {
58         static int parsed_columns = 0;
59         const char *e;
60
61         if (parsed_columns > 0)
62                 return parsed_columns;
63
64         if ((e = getenv("COLUMNS")))
65                 parsed_columns = atoi(e);
66
67         if (parsed_columns <= 0) {
68                 struct winsize ws;
69                 zero(ws);
70
71                 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
72                         parsed_columns = ws.ws_col;
73         }
74
75         if (parsed_columns <= 0)
76                 parsed_columns = 80;
77
78         return parsed_columns;
79 }
80
81 static int list_units(DBusConnection *bus, char **args, unsigned n) {
82         DBusMessage *m = NULL, *reply = NULL;
83         DBusError error;
84         int r;
85         DBusMessageIter iter, sub, sub2;
86         unsigned k = 0;
87
88         dbus_error_init(&error);
89
90         if (!(m = dbus_message_new_method_call(
91                               "org.freedesktop.systemd1",
92                               "/org/freedesktop/systemd1",
93                               "org.freedesktop.systemd1.Manager",
94                               "ListUnits"))) {
95                 log_error("Could not allocate message.");
96                 return -ENOMEM;
97         }
98
99         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
100                 log_error("Failed to issue method call: %s", error.message);
101                 r = -EIO;
102                 goto finish;
103         }
104
105         if (!dbus_message_iter_init(reply, &iter) ||
106             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
107             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
108                 log_error("Failed to parse reply.");
109                 r = -EIO;
110                 goto finish;
111         }
112
113         dbus_message_iter_recurse(&iter, &sub);
114
115         printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
116
117         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
118                 const char *id, *description, *load_state, *active_state, *sub_state, *unit_state, *job_type, *job_path, *dot;
119                 uint32_t job_id;
120
121                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
122                         log_error("Failed to parse reply.");
123                         r = -EIO;
124                         goto finish;
125                 }
126
127                 dbus_message_iter_recurse(&sub, &sub2);
128
129                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
130                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
131                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
132                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
133                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
134                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_state, true) < 0 ||
135                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &job_id, true) < 0 ||
136                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &job_type, true) < 0 ||
137                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, false) < 0) {
138                         log_error("Failed to parse reply.");
139                         r = -EIO;
140                         goto finish;
141                 }
142
143                 if ((!arg_type || ((dot = strrchr(id, '.')) &&
144                                    streq(dot+1, arg_type))) &&
145                     (arg_all || !streq(active_state, "inactive"))) {
146
147                         int a = 0, b = 0;
148
149                         printf("%-45s %-6s %-12s %-12s%n", id, load_state, active_state, sub_state, &a);
150
151                         if (job_id != 0)
152                                 printf(" %-15s%n", job_type, &b);
153                         else
154                                 b = 1 + 15;
155
156                         if (a + b + 2 < columns()) {
157                                 if (job_id == 0)
158                                         printf("                ");
159
160                                 printf("%.*s", columns() - a - b - 2, description);
161                         }
162
163                         fputs("\n", stdout);
164                         k++;
165                 }
166
167                 dbus_message_iter_next(&sub);
168         }
169
170         if (arg_all)
171                 printf("\n%u units listed.\n", k);
172         else
173                 printf("\n%u live units listed. Pass --all to see dead units, too.\n", k);
174
175         r = 0;
176
177 finish:
178         if (m)
179                 dbus_message_unref(m);
180
181         if (reply)
182                 dbus_message_unref(reply);
183
184         dbus_error_free(&error);
185
186         return r;
187 }
188
189 static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
190         DBusMessage *m = NULL, *reply = NULL;
191         DBusError error;
192         int r;
193         DBusMessageIter iter, sub, sub2;
194         unsigned k = 0;
195
196         dbus_error_init(&error);
197
198         if (!(m = dbus_message_new_method_call(
199                               "org.freedesktop.systemd1",
200                               "/org/freedesktop/systemd1",
201                               "org.freedesktop.systemd1.Manager",
202                               "ListJobs"))) {
203                 log_error("Could not allocate message.");
204                 return -ENOMEM;
205         }
206
207         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
208                 log_error("Failed to issue method call: %s", error.message);
209                 r = -EIO;
210                 goto finish;
211         }
212
213         if (!dbus_message_iter_init(reply, &iter) ||
214             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
215             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
216                 log_error("Failed to parse reply.");
217                 r = -EIO;
218                 goto finish;
219         }
220
221         dbus_message_iter_recurse(&iter, &sub);
222
223         printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
224
225         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
226                 const char *name, *type, *state, *job_path, *unit_path;
227                 uint32_t id;
228
229                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
230                         log_error("Failed to parse reply.");
231                         r = -EIO;
232                         goto finish;
233                 }
234
235                 dbus_message_iter_recurse(&sub, &sub2);
236
237                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
238                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
239                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
240                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
241                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
242                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
243                         log_error("Failed to parse reply.");
244                         r = -EIO;
245                         goto finish;
246                 }
247
248                 printf("%4u %-45s %-17s %-7s\n", id, name, type, state);
249                 k++;
250
251                 dbus_message_iter_next(&sub);
252         }
253
254         printf("\n%u jobs listed.\n", k);
255         r = 0;
256
257 finish:
258         if (m)
259                 dbus_message_unref(m);
260
261         if (reply)
262                 dbus_message_unref(reply);
263
264         dbus_error_free(&error);
265
266         return r;
267 }
268
269 static int load_unit(DBusConnection *bus, char **args, unsigned n) {
270         DBusMessage *m = NULL, *reply = NULL;
271         DBusError error;
272         int r;
273         unsigned i;
274
275         dbus_error_init(&error);
276
277         for (i = 1; i < n; i++) {
278
279                 if (!(m = dbus_message_new_method_call(
280                                       "org.freedesktop.systemd1",
281                                       "/org/freedesktop/systemd1",
282                                       "org.freedesktop.systemd1.Manager",
283                                       "LoadUnit"))) {
284                         log_error("Could not allocate message.");
285                         r = -ENOMEM;
286                         goto finish;
287                 }
288
289                 if (!dbus_message_append_args(m,
290                                               DBUS_TYPE_STRING, &args[i],
291                                               DBUS_TYPE_INVALID)) {
292                         log_error("Could not append arguments to message.");
293                         r = -ENOMEM;
294                         goto finish;
295                 }
296
297                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
298                         log_error("Failed to issue method call: %s", error.message);
299                         r = -EIO;
300                         goto finish;
301                 }
302
303                 dbus_message_unref(m);
304                 dbus_message_unref(reply);
305
306                 m = reply = NULL;
307         }
308
309         r = 0;
310
311 finish:
312         if (m)
313                 dbus_message_unref(m);
314
315         if (reply)
316                 dbus_message_unref(reply);
317
318         dbus_error_free(&error);
319
320         return r;
321 }
322
323 static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
324         DBusMessage *m = NULL, *reply = NULL;
325         DBusError error;
326         int r;
327         unsigned i;
328
329         dbus_error_init(&error);
330
331         for (i = 1; i < n; i++) {
332                 unsigned id;
333                 const char *path;
334
335                 if (!(m = dbus_message_new_method_call(
336                                       "org.freedesktop.systemd1",
337                                       "/org/freedesktop/systemd1",
338                                       "org.freedesktop.systemd1.Manager",
339                                       "GetJob"))) {
340                         log_error("Could not allocate message.");
341                         r = -ENOMEM;
342                         goto finish;
343                 }
344
345                 if ((r = safe_atou(args[i], &id)) < 0) {
346                         log_error("Failed to parse job id: %s", strerror(-r));
347                         goto finish;
348                 }
349
350                 assert_cc(sizeof(uint32_t) == sizeof(id));
351                 if (!dbus_message_append_args(m,
352                                               DBUS_TYPE_UINT32, &id,
353                                               DBUS_TYPE_INVALID)) {
354                         log_error("Could not append arguments to message.");
355                         r = -ENOMEM;
356                         goto finish;
357                 }
358
359                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
360                         log_error("Failed to issue method call: %s", error.message);
361                         r = -EIO;
362                         goto finish;
363                 }
364
365                 if (!dbus_message_get_args(reply, &error,
366                                            DBUS_TYPE_OBJECT_PATH, &path,
367                                            DBUS_TYPE_INVALID)) {
368                         log_error("Failed to parse reply: %s", error.message);
369                         r = -EIO;
370                         goto finish;
371                 }
372
373                 dbus_message_unref(m);
374                 if (!(m = dbus_message_new_method_call(
375                                       "org.freedesktop.systemd1",
376                                       path,
377                                       "org.freedesktop.systemd1.Job",
378                                       "Cancel"))) {
379                         log_error("Could not allocate message.");
380                         r = -ENOMEM;
381                         goto finish;
382                 }
383
384                 dbus_message_unref(reply);
385                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
386                         log_error("Failed to issue method call: %s", error.message);
387                         r = -EIO;
388                         goto finish;
389                 }
390
391                 dbus_message_unref(m);
392                 dbus_message_unref(reply);
393                 m = reply = NULL;
394         }
395
396         r = 0;
397
398 finish:
399         if (m)
400                 dbus_message_unref(m);
401
402         if (reply)
403                 dbus_message_unref(reply);
404
405         dbus_error_free(&error);
406
407         return r;
408 }
409
410 static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
411         DBusError error;
412         Set *s = data;
413
414         assert(connection);
415         assert(message);
416         assert(s);
417
418         dbus_error_init(&error);
419
420         /* log_debug("Got D-Bus request: %s.%s() on %s", */
421         /*           dbus_message_get_interface(message), */
422         /*           dbus_message_get_member(message), */
423         /*           dbus_message_get_path(message)); */
424
425         if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
426                 log_error("Warning! D-Bus connection terminated.");
427                 dbus_connection_close(connection);
428
429         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
430                 uint32_t id;
431                 const char *path;
432
433                 if (!dbus_message_get_args(message, &error,
434                                            DBUS_TYPE_UINT32, &id,
435                                            DBUS_TYPE_OBJECT_PATH, &path,
436                                            DBUS_TYPE_INVALID))
437                         log_error("Failed to parse message: %s", error.message);
438                 else {
439                         char *p;
440
441                         if ((p = set_remove(s, (char*) path)))
442                                 free(p);
443                 }
444         }
445
446         dbus_error_free(&error);
447         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
448 }
449
450 static int enable_wait_for_jobs(DBusConnection *bus) {
451         DBusError error;
452         DBusMessage *m = NULL, *reply = NULL;
453         int r;
454
455         assert(bus);
456
457         dbus_error_init(&error);
458
459         dbus_bus_add_match(bus,
460                            "type='signal',"
461                            "sender='org.freedesktop.systemd1',"
462                            "interface='org.freedesktop.systemd1.Manager',"
463                            "member='JobRemoved',"
464                            "path='/org/freedesktop/systemd1'",
465                            &error);
466
467         if (dbus_error_is_set(&error)) {
468                 log_error("Failed to add match: %s", error.message);
469                 r = -EIO;
470                 goto finish;
471         }
472
473         if (!(m = dbus_message_new_method_call(
474                               "org.freedesktop.systemd1",
475                               "/org/freedesktop/systemd1",
476                               "org.freedesktop.systemd1.Manager",
477                               "Subscribe"))) {
478                 log_error("Could not allocate message.");
479                 r = -ENOMEM;
480                 goto finish;
481         }
482
483         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
484                 log_error("Failed to issue method call: %s", error.message);
485                 r = -EIO;
486                 goto finish;
487         }
488
489         r = 0;
490
491 finish:
492         /* This is slightly dirty, since we don't undo the match registrations. */
493
494         if (m)
495                 dbus_message_unref(m);
496
497         if (reply)
498                 dbus_message_unref(reply);
499
500         dbus_error_free(&error);
501
502         return r;
503 }
504
505 static int wait_for_jobs(DBusConnection *bus, Set *s) {
506         int r;
507
508         assert(bus);
509         assert(s);
510
511         if (!dbus_connection_add_filter(bus, wait_filter, s, NULL)) {
512                 log_error("Failed to add filter.");
513                 r = -ENOMEM;
514                 goto finish;
515         }
516
517         while (!set_isempty(s) &&
518                dbus_connection_read_write_dispatch(bus, -1))
519                 ;
520
521         r = 0;
522
523 finish:
524         /* This is slightly dirty, since we don't undo the filter registration. */
525
526         return r;
527 }
528
529 static int start_unit(DBusConnection *bus, char **args, unsigned n) {
530         DBusMessage *m = NULL, *reply = NULL;
531         DBusError error;
532         int r;
533         unsigned i;
534         const char *method, *mode;
535         char *p = NULL;
536         Set *s = NULL;
537
538         dbus_error_init(&error);
539
540         method =
541                 streq(args[0], "start")  ? "StartUnit" :
542                 streq(args[0], "stop")   ? "StopUnit" :
543                 streq(args[0], "reload") ? "ReloadUnit" :
544                                            "RestartUnit";
545
546         mode = arg_replace ? "replace" : "fail";
547
548         if (arg_block) {
549                 if ((r = enable_wait_for_jobs(bus)) < 0) {
550                         log_error("Could not watch jobs: %s", strerror(-r));
551                         goto finish;
552                 }
553         }
554
555         for (i = 1; i < n; i++) {
556
557                 if (!(m = dbus_message_new_method_call(
558                                       "org.freedesktop.systemd1",
559                                       "/org/freedesktop/systemd1",
560                                       "org.freedesktop.systemd1.Manager",
561                                       method))) {
562                         log_error("Could not allocate message.");
563                         r = -ENOMEM;
564                         goto finish;
565                 }
566
567                 if (!dbus_message_append_args(m,
568                                               DBUS_TYPE_STRING, &args[i],
569                                               DBUS_TYPE_STRING, &mode,
570                                               DBUS_TYPE_INVALID)) {
571                         log_error("Could not append arguments to message.");
572                         r = -ENOMEM;
573                         goto finish;
574                 }
575
576                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
577                         log_error("Failed to issue method call: %s", error.message);
578                         r = -EIO;
579                         goto finish;
580                 }
581
582                 if (arg_block) {
583                         const char *path;
584
585                         if (!dbus_message_get_args(reply, &error,
586                                                    DBUS_TYPE_OBJECT_PATH, &path,
587                                                    DBUS_TYPE_INVALID)) {
588                                 log_error("Failed to parse reply: %s", error.message);
589                                 r = -EIO;
590                                 goto finish;
591                         }
592
593                         if (!s)
594                                 if (!(s = set_new(string_hash_func, string_compare_func))) {
595                                         log_error("Failed to allocate set.");
596                                         r = -ENOMEM;
597                                         goto finish;
598                                 }
599
600                         if (!(p = strdup(path))) {
601                                 log_error("Failed to duplicate path.");
602                                 r = -ENOMEM;
603                                 goto finish;
604                         }
605
606                         if ((r = set_put(s, p)) < 0) {
607                                 log_error("Failed to add path to set.");
608                                 goto finish;
609                         }
610                         p = NULL;
611                 }
612
613                 dbus_message_unref(m);
614                 dbus_message_unref(reply);
615
616                 m = reply = NULL;
617         }
618
619         if (arg_block)
620                 r = wait_for_jobs(bus, s);
621         else
622                 r = 0;
623
624 finish:
625         free(p);
626
627         if (s)
628                 set_free_free(s);
629
630         if (m)
631                 dbus_message_unref(m);
632
633         if (reply)
634                 dbus_message_unref(reply);
635
636         dbus_error_free(&error);
637
638         return r;
639 }
640
641 static int isolate_unit(DBusConnection *bus, char **args, unsigned n) {
642         DBusMessage *m = NULL, *reply = NULL;
643         DBusError error;
644         int r;
645         const char *mode = "isolate";
646         char *p = NULL;
647         Set *s = NULL;
648
649         dbus_error_init(&error);
650
651         if (arg_block) {
652                 if ((r = enable_wait_for_jobs(bus)) < 0) {
653                         log_error("Could not watch jobs: %s", strerror(-r));
654                         goto finish;
655                 }
656         }
657
658         if (!(m = dbus_message_new_method_call(
659                               "org.freedesktop.systemd1",
660                               "/org/freedesktop/systemd1",
661                               "org.freedesktop.systemd1.Manager",
662                               "StartUnit"))) {
663                 log_error("Could not allocate message.");
664                 r = -ENOMEM;
665                 goto finish;
666         }
667
668         if (!dbus_message_append_args(m,
669                                       DBUS_TYPE_STRING, &args[1],
670                                       DBUS_TYPE_STRING, &mode,
671                                       DBUS_TYPE_INVALID)) {
672                 log_error("Could not append arguments to message.");
673                 r = -ENOMEM;
674                 goto finish;
675         }
676
677         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
678                 log_error("Failed to issue method call: %s", error.message);
679                 r = -EIO;
680                 goto finish;
681         }
682
683         if (arg_block) {
684                 const char *path;
685
686                 if (!dbus_message_get_args(reply, &error,
687                                            DBUS_TYPE_OBJECT_PATH, &path,
688                                            DBUS_TYPE_INVALID)) {
689                         log_error("Failed to parse reply: %s", error.message);
690                         r = -EIO;
691                         goto finish;
692                 }
693
694                 if (!(s = set_new(string_hash_func, string_compare_func))) {
695                         log_error("Failed to allocate set.");
696                         r = -ENOMEM;
697                         goto finish;
698                 }
699
700                 if (!(p = strdup(path))) {
701                         log_error("Failed to duplicate path.");
702                         r = -ENOMEM;
703                         goto finish;
704                 }
705
706                 if ((r = set_put(s, p)) < 0) {
707                         log_error("Failed to add path to set.");
708                         goto finish;
709                 }
710                 p = NULL;
711
712                 r = wait_for_jobs(bus, s);
713
714         } else
715                 r = 0;
716
717 finish:
718         free(p);
719
720         if (s)
721                 set_free_free(s);
722
723         if (m)
724                 dbus_message_unref(m);
725
726         if (reply)
727                 dbus_message_unref(reply);
728
729         dbus_error_free(&error);
730
731         return r;
732 }
733
734 static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) {
735         DBusError error;
736         DBusMessage *m = NULL, *reply = NULL;
737
738         assert(connection);
739         assert(message);
740
741         dbus_error_init(&error);
742
743         /* log_debug("Got D-Bus request: %s.%s() on %s", */
744         /*           dbus_message_get_interface(message), */
745         /*           dbus_message_get_member(message), */
746         /*           dbus_message_get_path(message)); */
747
748         if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
749                 log_error("Warning! D-Bus connection terminated.");
750                 dbus_connection_close(connection);
751
752         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitNew") ||
753                    dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
754                 const char *id, *path;
755
756                 if (!dbus_message_get_args(message, &error,
757                                            DBUS_TYPE_STRING, &id,
758                                            DBUS_TYPE_OBJECT_PATH, &path,
759                                            DBUS_TYPE_INVALID))
760                         log_error("Failed to parse message: %s", error.message);
761                 else if (streq(dbus_message_get_member(message), "UnitNew"))
762                         printf("Unit %s added.\n", id);
763                 else
764                         printf("Unit %s removed.\n", id);
765
766         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew") ||
767                    dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
768                 uint32_t id;
769                 const char *path;
770
771                 if (!dbus_message_get_args(message, &error,
772                                            DBUS_TYPE_UINT32, &id,
773                                            DBUS_TYPE_OBJECT_PATH, &path,
774                                            DBUS_TYPE_INVALID))
775                         log_error("Failed to parse message: %s", error.message);
776                 else if (streq(dbus_message_get_member(message), "JobNew"))
777                         printf("Job %u added.\n", id);
778                 else
779                         printf("Job %u removed.\n", id);
780
781
782         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Unit", "Changed") ||
783                    dbus_message_is_signal(message, "org.freedesktop.systemd1.Job", "Changed")) {
784
785                 const char *path, *interface, *property = "Id";
786                 DBusMessageIter iter, sub;
787
788                 path = dbus_message_get_path(message);
789                 interface = dbus_message_get_interface(message);
790
791                 if (!(m = dbus_message_new_method_call(
792                               "org.freedesktop.systemd1",
793                               path,
794                               "org.freedesktop.DBus.Properties",
795                               "Get"))) {
796                         log_error("Could not allocate message.");
797                         goto oom;
798                 }
799
800                 if (!dbus_message_append_args(m,
801                                               DBUS_TYPE_STRING, &interface,
802                                               DBUS_TYPE_STRING, &property,
803                                               DBUS_TYPE_INVALID)) {
804                         log_error("Could not append arguments to message.");
805                         goto finish;
806                 }
807
808                 if (!(reply = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
809                         log_error("Failed to issue method call: %s", error.message);
810                         goto finish;
811                 }
812
813                 if (!dbus_message_iter_init(reply, &iter) ||
814                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
815                         log_error("Failed to parse reply.");
816                         goto finish;
817                 }
818
819                 dbus_message_iter_recurse(&iter, &sub);
820
821                 if (streq(interface, "org.freedesktop.systemd1.Unit")) {
822                         const char *id;
823
824                         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
825                                 log_error("Failed to parse reply.");
826                                 goto finish;
827                         }
828
829                         dbus_message_iter_get_basic(&sub, &id);
830                         printf("Unit %s changed.\n", id);
831                 } else {
832                         uint32_t id;
833
834                         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)  {
835                                 log_error("Failed to parse reply.");
836                                 goto finish;
837                         }
838
839                         dbus_message_iter_get_basic(&sub, &id);
840                         printf("Job %u changed.\n", id);
841                 }
842         }
843
844 finish:
845         if (m)
846                 dbus_message_unref(m);
847
848         if (reply)
849                 dbus_message_unref(reply);
850
851         dbus_error_free(&error);
852         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
853
854 oom:
855         if (m)
856                 dbus_message_unref(m);
857
858         if (reply)
859                 dbus_message_unref(reply);
860
861         dbus_error_free(&error);
862         return DBUS_HANDLER_RESULT_NEED_MEMORY;
863 }
864
865 static int monitor(DBusConnection *bus, char **args, unsigned n) {
866         DBusMessage *m = NULL, *reply = NULL;
867         DBusError error;
868         int r;
869
870         dbus_error_init(&error);
871
872         dbus_bus_add_match(bus,
873                            "type='signal',"
874                            "sender='org.freedesktop.systemd1',"
875                            "interface='org.freedesktop.systemd1.Manager',"
876                            "path='/org/freedesktop/systemd1'",
877                            &error);
878
879         if (dbus_error_is_set(&error)) {
880                 log_error("Failed to add match: %s", error.message);
881                 r = -EIO;
882                 goto finish;
883         }
884
885         dbus_bus_add_match(bus,
886                            "type='signal',"
887                            "sender='org.freedesktop.systemd1',"
888                            "interface='org.freedesktop.systemd1.Unit',"
889                            "member='Changed'",
890                            &error);
891
892         if (dbus_error_is_set(&error)) {
893                 log_error("Failed to add match: %s", error.message);
894                 r = -EIO;
895                 goto finish;
896         }
897
898         dbus_bus_add_match(bus,
899                            "type='signal',"
900                            "sender='org.freedesktop.systemd1',"
901                            "interface='org.freedesktop.systemd1.Job',"
902                            "member='Changed'",
903                            &error);
904
905         if (dbus_error_is_set(&error)) {
906                 log_error("Failed to add match: %s", error.message);
907                 r = -EIO;
908                 goto finish;
909         }
910
911         if (!dbus_connection_add_filter(bus, monitor_filter, NULL, NULL)) {
912                 log_error("Failed to add filter.");
913                 r = -ENOMEM;
914                 goto finish;
915         }
916
917         if (!(m = dbus_message_new_method_call(
918                               "org.freedesktop.systemd1",
919                               "/org/freedesktop/systemd1",
920                               "org.freedesktop.systemd1.Manager",
921                               "Subscribe"))) {
922                 log_error("Could not allocate message.");
923                 r = -ENOMEM;
924                 goto finish;
925         }
926
927         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
928                 log_error("Failed to issue method call: %s", error.message);
929                 r = -EIO;
930                 goto finish;
931         }
932
933         while (dbus_connection_read_write_dispatch(bus, -1))
934                 ;
935
936         r = 0;
937
938 finish:
939
940         /* This is slightly dirty, since we don't undo the filter or the matches. */
941
942         if (m)
943                 dbus_message_unref(m);
944
945         if (reply)
946                 dbus_message_unref(reply);
947
948         dbus_error_free(&error);
949
950         return r;
951 }
952
953 static int dump(DBusConnection *bus, char **args, unsigned n) {
954         DBusMessage *m = NULL, *reply = NULL;
955         DBusError error;
956         int r;
957         const char *text;
958
959         dbus_error_init(&error);
960
961         if (!(m = dbus_message_new_method_call(
962                               "org.freedesktop.systemd1",
963                               "/org/freedesktop/systemd1",
964                               "org.freedesktop.systemd1.Manager",
965                               "Dump"))) {
966                 log_error("Could not allocate message.");
967                 return -ENOMEM;
968         }
969
970         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
971                 log_error("Failed to issue method call: %s", error.message);
972                 r = -EIO;
973                 goto finish;
974         }
975
976         if (!dbus_message_get_args(reply, &error,
977                                    DBUS_TYPE_STRING, &text,
978                                    DBUS_TYPE_INVALID)) {
979                 log_error("Failed to parse reply: %s", error.message);
980                 r = -EIO;
981                 goto finish;
982         }
983
984         fputs(text, stdout);
985
986         r = 0;
987
988 finish:
989         if (m)
990                 dbus_message_unref(m);
991
992         if (reply)
993                 dbus_message_unref(reply);
994
995         dbus_error_free(&error);
996
997         return r;
998 }
999
1000 static int snapshot(DBusConnection *bus, char **args, unsigned n) {
1001         DBusMessage *m = NULL, *reply = NULL;
1002         DBusError error;
1003         int r;
1004         const char *name = "", *path, *id;
1005         dbus_bool_t cleanup = FALSE;
1006         DBusMessageIter iter, sub;
1007         const char
1008                 *interface = "org.freedesktop.systemd1.Unit",
1009                 *property = "Id";
1010
1011         dbus_error_init(&error);
1012
1013         if (!(m = dbus_message_new_method_call(
1014                               "org.freedesktop.systemd1",
1015                               "/org/freedesktop/systemd1",
1016                               "org.freedesktop.systemd1.Manager",
1017                               "CreateSnapshot"))) {
1018                 log_error("Could not allocate message.");
1019                 return -ENOMEM;
1020         }
1021
1022         if (n > 1)
1023                 name = args[1];
1024
1025         if (!dbus_message_append_args(m,
1026                                       DBUS_TYPE_STRING, &name,
1027                                       DBUS_TYPE_BOOLEAN, &cleanup,
1028                                       DBUS_TYPE_INVALID)) {
1029                 log_error("Could not append arguments to message.");
1030                 r = -ENOMEM;
1031                 goto finish;
1032         }
1033
1034         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1035                 log_error("Failed to issue method call: %s", error.message);
1036                 r = -EIO;
1037                 goto finish;
1038         }
1039
1040         if (!dbus_message_get_args(reply, &error,
1041                                    DBUS_TYPE_OBJECT_PATH, &path,
1042                                    DBUS_TYPE_INVALID)) {
1043                 log_error("Failed to parse reply: %s", error.message);
1044                 r = -EIO;
1045                 goto finish;
1046         }
1047
1048         dbus_message_unref(m);
1049         if (!(m = dbus_message_new_method_call(
1050                               "org.freedesktop.systemd1",
1051                               path,
1052                               "org.freedesktop.DBus.Properties",
1053                               "Get"))) {
1054                 log_error("Could not allocate message.");
1055                 return -ENOMEM;
1056         }
1057
1058         if (!dbus_message_append_args(m,
1059                                       DBUS_TYPE_STRING, &interface,
1060                                       DBUS_TYPE_STRING, &property,
1061                                       DBUS_TYPE_INVALID)) {
1062                 log_error("Could not append arguments to message.");
1063                 r = -ENOMEM;
1064                 goto finish;
1065         }
1066
1067         dbus_message_unref(reply);
1068         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1069                 log_error("Failed to issue method call: %s", error.message);
1070                 r = -EIO;
1071                 goto finish;
1072         }
1073
1074         if (!dbus_message_iter_init(reply, &iter) ||
1075             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
1076                 log_error("Failed to parse reply.");
1077                 r = -EIO;
1078                 goto finish;
1079         }
1080
1081         dbus_message_iter_recurse(&iter, &sub);
1082
1083         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
1084                 log_error("Failed to parse reply.");
1085                 r = -EIO;
1086                 goto finish;
1087         }
1088
1089         dbus_message_iter_get_basic(&sub, &id);
1090         puts(id);
1091         r = 0;
1092
1093 finish:
1094         if (m)
1095                 dbus_message_unref(m);
1096
1097         if (reply)
1098                 dbus_message_unref(reply);
1099
1100         dbus_error_free(&error);
1101
1102         return r;
1103 }
1104
1105 static int clear_jobs(DBusConnection *bus, char **args, unsigned n) {
1106         DBusMessage *m = NULL, *reply = NULL;
1107         DBusError error;
1108         int r;
1109         const char *method;
1110
1111         dbus_error_init(&error);
1112
1113         method =
1114                 streq(args[0], "clear-jobs")    ? "ClearJobs" :
1115                 streq(args[0], "daemon-reload") ? "Reload" :
1116                 streq(args[0], "daemon-reexec") ? "Reexecute" :
1117                                                   "Exit";
1118
1119         if (!(m = dbus_message_new_method_call(
1120                               "org.freedesktop.systemd1",
1121                               "/org/freedesktop/systemd1",
1122                               "org.freedesktop.systemd1.Manager",
1123                               method))) {
1124                 log_error("Could not allocate message.");
1125                 return -ENOMEM;
1126         }
1127
1128         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1129                 log_error("Failed to issue method call: %s", error.message);
1130                 r = -EIO;
1131                 goto finish;
1132         }
1133
1134         r = 0;
1135
1136 finish:
1137         if (m)
1138                 dbus_message_unref(m);
1139
1140         if (reply)
1141                 dbus_message_unref(reply);
1142
1143         dbus_error_free(&error);
1144
1145         return r;
1146 }
1147
1148 static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
1149         DBusMessage *m = NULL, *reply = NULL;
1150         DBusError error;
1151         DBusMessageIter iter, sub, sub2;
1152         int r;
1153         const char
1154                 *interface = "org.freedesktop.systemd1.Manager",
1155                 *property = "Environment";
1156
1157         dbus_error_init(&error);
1158
1159         if (!(m = dbus_message_new_method_call(
1160                               "org.freedesktop.systemd1",
1161                               "/org/freedesktop/systemd1",
1162                               "org.freedesktop.DBus.Properties",
1163                               "Get"))) {
1164                 log_error("Could not allocate message.");
1165                 return -ENOMEM;
1166         }
1167
1168         if (!dbus_message_append_args(m,
1169                                       DBUS_TYPE_STRING, &interface,
1170                                       DBUS_TYPE_STRING, &property,
1171                                       DBUS_TYPE_INVALID)) {
1172                 log_error("Could not append arguments to message.");
1173                 r = -ENOMEM;
1174                 goto finish;
1175         }
1176
1177         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1178                 log_error("Failed to issue method call: %s", error.message);
1179                 r = -EIO;
1180                 goto finish;
1181         }
1182
1183         if (!dbus_message_iter_init(reply, &iter) ||
1184             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
1185                 log_error("Failed to parse reply.");
1186                 r = -EIO;
1187                 goto finish;
1188         }
1189
1190         dbus_message_iter_recurse(&iter, &sub);
1191
1192         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
1193             dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING)  {
1194                 log_error("Failed to parse reply.");
1195                 r = -EIO;
1196                 goto finish;
1197         }
1198
1199         dbus_message_iter_recurse(&sub, &sub2);
1200
1201         while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
1202                 const char *text;
1203
1204                 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
1205                         log_error("Failed to parse reply.");
1206                         r = -EIO;
1207                         goto finish;
1208                 }
1209
1210                 dbus_message_iter_get_basic(&sub2, &text);
1211                 printf("%s\n", text);
1212
1213                 dbus_message_iter_next(&sub2);
1214         }
1215
1216         r = 0;
1217
1218 finish:
1219         if (m)
1220                 dbus_message_unref(m);
1221
1222         if (reply)
1223                 dbus_message_unref(reply);
1224
1225         dbus_error_free(&error);
1226
1227         return r;
1228 }
1229
1230 static int set_environment(DBusConnection *bus, char **args, unsigned n) {
1231         DBusMessage *m = NULL, *reply = NULL;
1232         DBusError error;
1233         int r;
1234         const char *method;
1235         DBusMessageIter iter, sub;
1236         unsigned i;
1237
1238         dbus_error_init(&error);
1239
1240         method = streq(args[0], "set-environment")
1241                 ? "SetEnvironment"
1242                 : "UnsetEnvironment";
1243
1244         if (!(m = dbus_message_new_method_call(
1245                               "org.freedesktop.systemd1",
1246                               "/org/freedesktop/systemd1",
1247                               "org.freedesktop.systemd1.Manager",
1248                               method))) {
1249
1250                 log_error("Could not allocate message.");
1251                 return -ENOMEM;
1252         }
1253
1254         dbus_message_iter_init_append(m, &iter);
1255
1256         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
1257                 log_error("Could not append arguments to message.");
1258                 r = -ENOMEM;
1259                 goto finish;
1260         }
1261
1262         for (i = 1; i < n; i++)
1263                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
1264                         log_error("Could not append arguments to message.");
1265                         r = -ENOMEM;
1266                         goto finish;
1267                 }
1268
1269         if (!dbus_message_iter_close_container(&iter, &sub)) {
1270                 log_error("Could not append arguments to message.");
1271                 r = -ENOMEM;
1272                 goto finish;
1273         }
1274
1275         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1276                 log_error("Failed to issue method call: %s", error.message);
1277                 r = -EIO;
1278                 goto finish;
1279         }
1280
1281         r = 0;
1282
1283 finish:
1284         if (m)
1285                 dbus_message_unref(m);
1286
1287         if (reply)
1288                 dbus_message_unref(reply);
1289
1290         dbus_error_free(&error);
1291
1292         return r;
1293 }
1294
1295 static int help(void) {
1296
1297         printf("%s [options]\n\n"
1298                "  -h --help      Show this help\n"
1299                "  -t --type=TYPE List only units of a particular type\n"
1300                "  -a --all       Show all units, including dead ones\n"
1301                "     --replace   When installing a new job, replace existing conflicting ones\n"
1302                "     --system    Connect to system bus\n"
1303                "     --session   Connect to session bus\n"
1304                "     --block     Wait until operation finished\n\n"
1305                "Commands:\n"
1306                "  list-units                      List units\n"
1307                "  list-jobs                       List jobs\n"
1308                "  clear-jobs                      Cancel all jobs\n"
1309                "  load [NAME...]                  Load one or more units\n"
1310                "  cancel [JOB...]                 Cancel one or more jobs\n"
1311                "  start [NAME...]                 Start one or more units\n"
1312                "  stop [NAME...]                  Stop one or more units\n"
1313                "  restart [NAME...]               Restart one or more units\n"
1314                "  reload [NAME...]                Reload one or more units\n"
1315                "  isolate [NAME]                  Start one unit and stop all others\n"
1316                "  monitor                         Monitor unit/job changes\n"
1317                "  dump                            Dump server status\n"
1318                "  snapshot [NAME]                 Create a snapshot\n"
1319                "  daemon-reload                   Reload daemon configuration\n"
1320                "  daemon-reexecute                Reexecute daemon\n"
1321                "  daemon-exit                     Ask the daemon to quit\n"
1322                "  show-environment                Dump environment\n"
1323                "  set-environment [NAME=VALUE...] Set one or more environment variables\n"
1324                "  unset-environment [NAME...]     Unset one or more environment variables\n",
1325                __progname);
1326
1327         return 0;
1328 }
1329
1330 static int parse_argv(int argc, char *argv[]) {
1331
1332         enum {
1333                 ARG_REPLACE = 0x100,
1334                 ARG_SESSION,
1335                 ARG_SYSTEM,
1336                 ARG_BLOCK,
1337         };
1338
1339         static const struct option options[] = {
1340                 { "help",      no_argument,       NULL, 'h'         },
1341                 { "type",      required_argument, NULL, 't'         },
1342                 { "all",       no_argument,       NULL, 'a'         },
1343                 { "replace",   no_argument,       NULL, ARG_REPLACE },
1344                 { "session",   no_argument,       NULL, ARG_SESSION },
1345                 { "system",    no_argument,       NULL, ARG_SYSTEM  },
1346                 { "block",     no_argument,       NULL, ARG_BLOCK   }
1347         };
1348
1349         int c;
1350
1351         assert(argc >= 1);
1352         assert(argv);
1353
1354         while ((c = getopt_long(argc, argv, "hta", options, NULL)) >= 0) {
1355
1356                 switch (c) {
1357
1358                 case 'h':
1359                         help();
1360                         return 0;
1361
1362                 case 't':
1363                         arg_type = optarg;
1364                         break;
1365
1366                 case 'a':
1367                         arg_all = true;
1368                         break;
1369
1370                 case ARG_REPLACE:
1371                         arg_replace = true;
1372                         break;
1373
1374                 case ARG_SESSION:
1375                         arg_session = true;
1376                         break;
1377
1378                 case ARG_SYSTEM:
1379                         arg_session = false;
1380                         break;
1381
1382                 case ARG_BLOCK:
1383                         arg_block = true;
1384                         break;
1385
1386                 case '?':
1387                         return -EINVAL;
1388
1389                 default:
1390                         log_error("Unknown option code %c", c);
1391                         return -EINVAL;
1392                 }
1393         }
1394
1395         return 1;
1396 }
1397
1398 int main(int argc, char*argv[]) {
1399
1400         static const struct {
1401                 const char* verb;
1402                 const enum {
1403                         MORE,
1404                         LESS,
1405                         EQUAL
1406                 } argc_cmp;
1407                 const int argc;
1408                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1409         } verbs[] = {
1410                 { "list-units",        LESS,  1, list_units      },
1411                 { "list-jobs",         EQUAL, 1, list_jobs       },
1412                 { "clear-jobs",        EQUAL, 1, clear_jobs      },
1413                 { "load",              MORE,  2, load_unit       },
1414                 { "cancel",            MORE,  2, cancel_job      },
1415                 { "start",             MORE,  2, start_unit      },
1416                 { "stop",              MORE,  2, start_unit      },
1417                 { "reload",            MORE,  2, start_unit      },
1418                 { "restart",           MORE,  2, start_unit      },
1419                 { "isolate",           EQUAL, 2, isolate_unit    },
1420                 { "monitor",           EQUAL, 1, monitor         },
1421                 { "dump",              EQUAL, 1, dump            },
1422                 { "snapshot",          LESS,  2, snapshot        },
1423                 { "daemon-reload",     EQUAL, 1, clear_jobs      },
1424                 { "daemon-reexec",     EQUAL, 1, clear_jobs      },
1425                 { "daemon-exit",       EQUAL, 1, clear_jobs      },
1426                 { "show-environment",  EQUAL, 1, show_enviroment },
1427                 { "set-environment",   MORE,  2, set_environment },
1428                 { "unset-environment", MORE,  2, set_environment },
1429         };
1430
1431         int r, retval = 1, left;
1432         unsigned i;
1433         DBusConnection *bus = NULL;
1434         DBusError error;
1435
1436         dbus_error_init(&error);
1437
1438         log_set_target(LOG_TARGET_CONSOLE);
1439         log_parse_environment();
1440
1441         if ((r = parse_argv(argc, argv)) < 0)
1442                 goto finish;
1443         else if (r == 0) {
1444                 retval = 0;
1445                 goto finish;
1446         }
1447
1448         left = argc - optind;
1449
1450         if (left <= 0)
1451                 /* Special rule: no arguments means "list-units" */
1452                 i = 0;
1453         else {
1454                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1455                         if (streq(argv[optind], verbs[i].verb))
1456                                 break;
1457
1458                 if (i >= ELEMENTSOF(verbs)) {
1459                         log_error("Unknown operation %s", argv[optind]);
1460                         goto finish;
1461                 }
1462         }
1463
1464         switch (verbs[i].argc_cmp) {
1465
1466         case EQUAL:
1467                 if (left != verbs[i].argc) {
1468                         log_error("Invalid number of arguments.");
1469                         goto finish;
1470                 }
1471
1472                 break;
1473
1474         case MORE:
1475                 if (left < verbs[i].argc) {
1476                         log_error("Too few arguments.");
1477                         goto finish;
1478                 }
1479
1480                 break;
1481
1482         case LESS:
1483                 if (left > verbs[i].argc) {
1484                         log_error("Too many arguments.");
1485                         goto finish;
1486                 }
1487
1488                 break;
1489
1490         default:
1491                 assert_not_reached("Unknown comparison operator.");
1492         }
1493
1494         if (!(bus = dbus_bus_get(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) {
1495                 log_error("Failed to get D-Bus connection: %s", error.message);
1496                 goto finish;
1497         }
1498
1499         dbus_connection_set_exit_on_disconnect(bus, FALSE);
1500
1501         retval = verbs[i].dispatch(bus, argv + optind, left) < 0;
1502
1503 finish:
1504
1505         if (bus)
1506                 dbus_connection_unref(bus);
1507
1508         dbus_shutdown();
1509
1510         return retval;
1511 }