]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
sd-bus: create clean error when a property Set() call with incorrect signature is...
[thirdparty/systemd.git] / src / libsystemd / sd-bus / busctl.c
CommitLineData
de1c301e
LP
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
1f849790
LP
22#include <getopt.h>
23
89ffcd2a
LP
24#include "strv.h"
25#include "util.h"
26#include "log.h"
1f849790
LP
27#include "build.h"
28#include "pager.h"
d9130355
LP
29#include "xml.h"
30#include "path-util.h"
89ffcd2a 31
de1c301e 32#include "sd-bus.h"
89ffcd2a
LP
33#include "bus-message.h"
34#include "bus-internal.h"
40ca29a1 35#include "bus-util.h"
2b5c5383 36#include "bus-dump.h"
781fa938 37#include "bus-signature.h"
a1ad3767 38#include "busctl-introspect.h"
de1c301e 39
1f849790 40static bool arg_no_pager = false;
17d47d8d 41static bool arg_legend = true;
1f849790 42static char *arg_address = NULL;
56e61788
LP
43static bool arg_unique = false;
44static bool arg_acquired = false;
45static bool arg_activatable = false;
46static bool arg_show_machine = false;
1f849790 47static char **arg_matches = NULL;
d75edbd6
LP
48static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
49static char *arg_host = NULL;
50static bool arg_user = false;
1f70b087 51static size_t arg_snaplen = 4096;
d9130355 52static bool arg_list = false;
781fa938 53static bool arg_quiet = false;
1f849790
LP
54
55static void pager_open_if_enabled(void) {
56
57 /* Cache result before we open the pager */
58 if (arg_no_pager)
59 return;
60
61 pager_open(false);
62}
63
64static int list_bus_names(sd_bus *bus, char **argv) {
71f2ab46 65 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
66 _cleanup_free_ char **merged = NULL;
67 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
68 char **i;
69 int r;
89ffcd2a 70 size_t max_i = 0;
5e2f14e6
LP
71 unsigned n = 0;
72 void *v;
73 char *k;
74 Iterator iterator;
de1c301e 75
1f849790 76 assert(bus);
de1c301e 77
d9130355
LP
78 if (!arg_unique && !arg_acquired && !arg_activatable)
79 arg_unique = arg_acquired = arg_activatable = true;
80
56e61788 81 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
de1c301e
LP
82 if (r < 0) {
83 log_error("Failed to list names: %s", strerror(-r));
1f849790 84 return r;
de1c301e
LP
85 }
86
1f849790
LP
87 pager_open_if_enabled();
88
d5099efc 89 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
90 if (!names)
91 return log_oom();
89ffcd2a 92
5e2f14e6 93 STRV_FOREACH(i, acquired) {
71f2ab46
LP
94 max_i = MAX(max_i, strlen(*i));
95
5e2f14e6
LP
96 r = hashmap_put(names, *i, INT_TO_PTR(1));
97 if (r < 0) {
98 log_error("Failed to add to hashmap: %s", strerror(-r));
99 return r;
100 }
101 }
102
103 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
104 max_i = MAX(max_i, strlen(*i));
105
5e2f14e6
LP
106 r = hashmap_put(names, *i, INT_TO_PTR(2));
107 if (r < 0 && r != -EEXIST) {
108 log_error("Failed to add to hashmap: %s", strerror(-r));
109 return r;
110 }
111 }
112
113 merged = new(char*, hashmap_size(names) + 1);
114 HASHMAP_FOREACH_KEY(v, k, names, iterator)
115 merged[n++] = k;
116
117 merged[n] = NULL;
118 strv_sort(merged);
119
17d47d8d
TA
120 if (arg_legend) {
121 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
455971c1 122 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
89ffcd2a 123
17d47d8d
TA
124 if (arg_show_machine)
125 puts(" MACHINE");
126 else
127 putchar('\n');
128 }
a4297f08 129
5e2f14e6
LP
130 STRV_FOREACH(i, merged) {
131 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
132 sd_id128_t mid;
71f2ab46 133
5e2f14e6
LP
134 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
135 /* Activatable */
71f2ab46 136
5e2f14e6 137 printf("%-*s", (int) max_i, *i);
56e61788
LP
138 printf(" - - - (activatable) - - ");
139 if (arg_show_machine)
5e2f14e6 140 puts(" -");
56e61788
LP
141 else
142 putchar('\n');
71f2ab46
LP
143 continue;
144
5e2f14e6 145 }
de1c301e 146
56e61788
LP
147 if (!arg_unique && (*i)[0] == ':')
148 continue;
149
150 if (!arg_acquired && (*i)[0] != ':')
1f849790 151 continue;
89ffcd2a
LP
152
153 printf("%-*s", (int) max_i, *i);
de1c301e 154
056f95d0 155 r = sd_bus_get_name_creds(bus, *i,
14008e4e
LP
156 SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
157 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
455971c1 158 SD_BUS_CREDS_DESCRIPTION, &creds);
89ffcd2a 159 if (r >= 0) {
14008e4e 160 const char *unique, *session, *unit, *cn;
5b12334d
LP
161 pid_t pid;
162 uid_t uid;
de1c301e 163
5b12334d
LP
164 r = sd_bus_creds_get_pid(creds, &pid);
165 if (r >= 0) {
166 const char *comm = NULL;
de1c301e 167
5b12334d 168 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 169
5b12334d
LP
170 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
171 } else
a4297f08 172 fputs(" - - ", stdout);
89ffcd2a 173
5b12334d
LP
174 r = sd_bus_creds_get_uid(creds, &uid);
175 if (r >= 0) {
176 _cleanup_free_ char *u = NULL;
89ffcd2a 177
5b12334d
LP
178 u = uid_to_name(uid);
179 if (!u)
180 return log_oom();
89ffcd2a 181
5b12334d
LP
182 if (strlen(u) > 16)
183 u[16] = 0;
184
185 printf(" %-16s", u);
186 } else
a4297f08 187 fputs(" - ", stdout);
89ffcd2a 188
49b832c5
LP
189 r = sd_bus_creds_get_unique_name(creds, &unique);
190 if (r >= 0)
56e61788
LP
191 printf(" %-13s", unique);
192 else
193 fputs(" - ", stdout);
194
195 r = sd_bus_creds_get_unit(creds, &unit);
196 if (r >= 0) {
197 _cleanup_free_ char *e;
198
199 e = ellipsize(unit, 25, 100);
200 if (!e)
201 return log_oom();
202
203 printf(" %-25s", e);
204 } else
205 fputs(" - ", stdout);
206
207 r = sd_bus_creds_get_session(creds, &session);
208 if (r >= 0)
209 printf(" %-10s", session);
a4297f08 210 else
56e61788 211 fputs(" - ", stdout);
7b0b392f 212
455971c1 213 r = sd_bus_creds_get_description(creds, &cn);
14008e4e
LP
214 if (r >= 0)
215 printf(" %-19s", cn);
216 else
217 fputs(" - ", stdout);
218
7b0b392f 219 } else
14008e4e 220 printf(" - - - - - - - ");
a4297f08 221
56e61788 222 if (arg_show_machine) {
056f95d0 223 r = sd_bus_get_name_machine_id(bus, *i, &mid);
a4297f08
LP
224 if (r >= 0) {
225 char m[SD_ID128_STRING_MAX];
226 printf(" %s\n", sd_id128_to_string(mid, m));
227 } else
228 puts(" -");
56e61788
LP
229 } else
230 putchar('\n');
de1c301e
LP
231 }
232
1f849790
LP
233 return 0;
234}
235
d9130355
LP
236static void print_subtree(const char *prefix, const char *path, char **l) {
237 const char *vertical, *space;
238 char **n;
239
240 /* We assume the list is sorted. Let's first skip over the
241 * entry we are looking at. */
242 for (;;) {
243 if (!*l)
244 return;
245
246 if (!streq(*l, path))
247 break;
248
249 l++;
250 }
251
252 vertical = strappenda(prefix, draw_special_char(DRAW_TREE_VERTICAL));
253 space = strappenda(prefix, draw_special_char(DRAW_TREE_SPACE));
254
255 for (;;) {
256 bool has_more = false;
257
258 if (!*l || !path_startswith(*l, path))
259 break;
260
261 n = l + 1;
262 for (;;) {
263 if (!*n || !path_startswith(*n, path))
264 break;
265
266 if (!path_startswith(*n, *l)) {
267 has_more = true;
268 break;
269 }
270
271 n++;
272 }
273
274 printf("%s%s%s\n", prefix, draw_special_char(has_more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), *l);
275
276 print_subtree(has_more ? vertical : space, *l, l);
277 l = n;
278 }
279}
280
281static void print_tree(const char *prefix, char **l) {
282
283 pager_open_if_enabled();
284
285 prefix = strempty(prefix);
286
287 if (arg_list) {
288 char **i;
289
290 STRV_FOREACH(i, l)
291 printf("%s%s\n", prefix, *i);
292 return;
293 }
294
56c8b52d
LP
295 if (strv_isempty(l)) {
296 printf("No objects discovered.\n");
297 return;
298 }
299
300 if (streq(l[0], "/") && !l[1]) {
301 printf("Only root object discovered.\n");
302 return;
303 }
d9130355
LP
304
305 print_subtree(prefix, "/", l);
306}
307
a1ad3767
LP
308static int on_path(const char *path, void *userdata) {
309 Set *paths = userdata;
d9130355
LP
310 int r;
311
a1ad3767 312 assert(paths);
d9130355 313
a1ad3767
LP
314 r = set_put_strdup(paths, path);
315 if (r < 0)
316 return log_oom();
d9130355 317
a1ad3767 318 return 0;
d9130355
LP
319}
320
73fc23c0 321static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) {
0171da06 322 static const XMLIntrospectOps ops = {
a1ad3767
LP
323 .on_path = on_path,
324 };
325
d9130355 326 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
a1ad3767
LP
327 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
328 const char *xml;
d9130355
LP
329 int r;
330
331 r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
332 if (r < 0) {
73fc23c0
LP
333 if (many)
334 printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r));
335 else
336 log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r));
d9130355
LP
337 return r;
338 }
339
340 r = sd_bus_message_read(reply, "s", &xml);
341 if (r < 0)
342 return bus_log_parse_error(r);
343
a1ad3767 344 return parse_xml_introspect(path, xml, &ops, paths);
d9130355
LP
345}
346
73fc23c0 347static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) {
d9130355
LP
348 _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
349 _cleanup_free_ char **l = NULL;
350 char *m;
351 int r;
352
353 paths = set_new(&string_hash_ops);
354 if (!paths)
355 return log_oom();
356
357 done = set_new(&string_hash_ops);
358 if (!done)
359 return log_oom();
360
361 failed = set_new(&string_hash_ops);
362 if (!failed)
363 return log_oom();
364
365 m = strdup("/");
366 if (!m)
367 return log_oom();
368
369 r = set_put(paths, m);
370 if (r < 0) {
371 free(m);
372 return log_oom();
373 }
374
375 for (;;) {
376 _cleanup_free_ char *p = NULL;
377 int q;
378
379 p = set_steal_first(paths);
380 if (!p)
381 break;
382
383 if (set_contains(done, p) ||
384 set_contains(failed, p))
385 continue;
386
73fc23c0 387 q = find_nodes(bus, service, p, paths, many);
d9130355
LP
388 if (q < 0) {
389 if (r >= 0)
390 r = q;
391
392 q = set_put(failed, p);
393 } else
394 q = set_put(done, p);
395
396 if (q < 0)
397 return log_oom();
398
399 assert(q != 0);
400 p = NULL;
401 }
402
73fc23c0
LP
403 pager_open_if_enabled();
404
d9130355
LP
405 l = set_get_strv(done);
406 if (!l)
407 return log_oom();
408
409 strv_sort(l);
410 print_tree(prefix, l);
411
412 fflush(stdout);
413
414 return r;
415}
416
417static int tree(sd_bus *bus, char **argv) {
418 char **i;
419 int r = 0;
420
421 if (!arg_unique && !arg_acquired)
422 arg_acquired = true;
423
424 if (strv_length(argv) <= 1) {
425 _cleanup_strv_free_ char **names = NULL;
56c8b52d 426 bool not_first = false;
d9130355
LP
427
428 r = sd_bus_list_names(bus, &names, NULL);
429 if (r < 0) {
430 log_error("Failed to get name list: %s", strerror(-r));
431 return r;
432 }
433
434 pager_open_if_enabled();
435
436 STRV_FOREACH(i, names) {
437 int q;
438
439 if (!arg_unique && (*i)[0] == ':')
440 continue;
441
442 if (!arg_acquired && (*i)[0] == ':')
443 continue;
444
445 if (not_first)
446 printf("\n");
447
56c8b52d 448 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off());
d9130355 449
73fc23c0 450 q = tree_one(bus, *i, NULL, true);
d9130355
LP
451 if (q < 0 && r >= 0)
452 r = q;
453
454 not_first = true;
455 }
456 } else {
d9130355
LP
457 STRV_FOREACH(i, argv+1) {
458 int q;
459
460 if (i > argv+1)
461 printf("\n");
462
73fc23c0
LP
463 if (argv[2]) {
464 pager_open_if_enabled();
56c8b52d 465 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off());
73fc23c0 466 }
d9130355 467
73fc23c0 468 q = tree_one(bus, *i, NULL, !!argv[2]);
d9130355
LP
469 if (q < 0 && r >= 0)
470 r = q;
471 }
472 }
473
474 return r;
475}
476
0171da06
LP
477typedef struct Member {
478 const char *type;
479 char *interface;
480 char *name;
481 char *signature;
482 char *result;
483 bool writable;
484 uint64_t flags;
485} Member;
486
487static unsigned long member_hash_func(const void *p, const uint8_t hash_key[]) {
488 const Member *m = p;
489 unsigned long ul;
490
491 assert(m);
492 assert(m->type);
493
494 ul = string_hash_func(m->type, hash_key);
495
496 if (m->name)
497 ul ^= string_hash_func(m->name, hash_key);
498
499 if (m->interface)
500 ul ^= string_hash_func(m->interface, hash_key);
501
502 return ul;
503}
504
505static int member_compare_func(const void *a, const void *b) {
506 const Member *x = a, *y = b;
507 int d;
508
509 assert(x);
510 assert(y);
511 assert(x->type);
512 assert(y->type);
513
514 if (!x->interface && y->interface)
515 return -1;
516 if (x->interface && !y->interface)
517 return 1;
518 if (x->interface && y->interface) {
519 d = strcmp(x->interface, y->interface);
520 if (d != 0)
521 return d;
522 }
523
524 d = strcmp(x->type, y->type);
525 if (d != 0)
526 return d;
527
528 if (!x->name && y->name)
529 return -1;
530 if (x->name && !y->name)
531 return 1;
532 if (x->name && y->name)
533 return strcmp(x->name, y->name);
534
535 return 0;
536}
537
538static int member_compare_funcp(const void *a, const void *b) {
539 const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b;
540
541 return member_compare_func(*x, *y);
542}
543
544static void member_free(Member *m) {
545 if (!m)
546 return;
547
548 free(m->interface);
549 free(m->name);
550 free(m->signature);
551 free(m->result);
552 free(m);
553}
554
555DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
556
557static void member_set_free(Set *s) {
558 Member *m;
559
560 while ((m = set_steal_first(s)))
561 member_free(m);
562
563 set_free(s);
564}
565
566DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
567
568static int on_interface(const char *interface, uint64_t flags, void *userdata) {
569 _cleanup_(member_freep) Member *m;
570 Set *members = userdata;
571 int r;
572
573 assert(interface);
574 assert(members);
575
576 m = new0(Member, 1);
577 if (!m)
578 return log_oom();
579
580 m->type = "interface";
581 m->flags = flags;
582
583 r = free_and_strdup(&m->interface, interface);
584 if (r < 0)
585 return log_oom();
586
587 r = set_put(members, m);
588 if (r <= 0) {
589 log_error("Duplicate interface");
590 return -EINVAL;
591 }
592
593 m = NULL;
594 return 0;
595}
596
597static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
598 _cleanup_(member_freep) Member *m;
599 Set *members = userdata;
600 int r;
601
602 assert(interface);
603 assert(name);
604
605 m = new0(Member, 1);
606 if (!m)
607 return log_oom();
608
609 m->type = "method";
610 m->flags = flags;
611
612 r = free_and_strdup(&m->interface, interface);
613 if (r < 0)
614 return log_oom();
615
616 r = free_and_strdup(&m->name, name);
617 if (r < 0)
618 return log_oom();
619
620 r = free_and_strdup(&m->signature, signature);
621 if (r < 0)
622 return log_oom();
623
624 r = free_and_strdup(&m->result, result);
625 if (r < 0)
626 return log_oom();
627
628 r = set_put(members, m);
629 if (r <= 0) {
630 log_error("Duplicate method");
631 return -EINVAL;
632 }
633
634 m = NULL;
635 return 0;
636}
637
638static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
639 _cleanup_(member_freep) Member *m;
640 Set *members = userdata;
641 int r;
642
643 assert(interface);
644 assert(name);
645
646 m = new0(Member, 1);
647 if (!m)
648 return log_oom();
649
650 m->type = "signal";
651 m->flags = flags;
652
653 r = free_and_strdup(&m->interface, interface);
654 if (r < 0)
655 return log_oom();
656
657 r = free_and_strdup(&m->name, name);
658 if (r < 0)
659 return log_oom();
660
661 r = free_and_strdup(&m->signature, signature);
662 if (r < 0)
663 return log_oom();
664
665 r = set_put(members, m);
666 if (r <= 0) {
667 log_error("Duplicate signal");
668 return -EINVAL;
669 }
670
671 m = NULL;
672 return 0;
673}
674
675static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
676 _cleanup_(member_freep) Member *m;
677 Set *members = userdata;
678 int r;
679
680 assert(interface);
681 assert(name);
682
683 m = new0(Member, 1);
684 if (!m)
685 return log_oom();
686
687 m->type = "property";
688 m->flags = flags;
689 m->writable = writable;
690
691 r = free_and_strdup(&m->interface, interface);
692 if (r < 0)
693 return log_oom();
694
695 r = free_and_strdup(&m->name, name);
696 if (r < 0)
697 return log_oom();
698
699 r = free_and_strdup(&m->signature, signature);
700 if (r < 0)
701 return log_oom();
702
703 r = set_put(members, m);
704 if (r <= 0) {
705 log_error("Duplicate property");
706 return -EINVAL;
707 }
708
709 m = NULL;
710 return 0;
711}
712
713static const char *strdash(const char *x) {
714 return isempty(x) ? "-" : x;
715}
716
717static int introspect(sd_bus *bus, char **argv) {
718 static const struct hash_ops member_hash_ops = {
719 .hash = member_hash_func,
720 .compare = member_compare_func,
721 };
722
723 static const XMLIntrospectOps ops = {
724 .on_interface = on_interface,
725 .on_method = on_method,
726 .on_signal = on_signal,
727 .on_property = on_property,
728 };
729
730 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
731 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
732 _cleanup_(member_set_freep) Set *members = NULL;
733 Iterator i;
734 Member *m;
735 const char *xml;
736 int r;
737 unsigned name_width, type_width, signature_width, result_width;
738 Member **sorted = NULL;
739 unsigned k = 0, j;
740
741 if (strv_length(argv) != 3) {
742 log_error("Requires service and object path argument.");
743 return -EINVAL;
744 }
745
746 members = set_new(&member_hash_ops);
747 if (!members)
748 return log_oom();
749
750 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
751 if (r < 0) {
752 log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
753 return r;
754 }
755
756 r = sd_bus_message_read(reply, "s", &xml);
757 if (r < 0)
758 return bus_log_parse_error(r);
759
760 r = parse_xml_introspect(argv[2], xml, &ops, members);
761 if (r < 0)
762 return r;
763
764 pager_open_if_enabled();
765
766 name_width = strlen("NAME");
767 type_width = strlen("TYPE");
768 signature_width = strlen("SIGNATURE");
769 result_width = strlen("RESULT");
770
771 sorted = newa(Member*, set_size(members));
772
773 SET_FOREACH(m, members, i) {
774 if (m->interface)
775 name_width = MAX(name_width, strlen(m->interface));
776 if (m->name)
777 name_width = MAX(name_width, strlen(m->name) + 1);
778 if (m->type)
779 type_width = MAX(type_width, strlen(m->type));
780 if (m->signature)
781 signature_width = MAX(signature_width, strlen(m->signature));
782 if (m->result)
783 result_width = MAX(result_width, strlen(m->result));
784
785 sorted[k++] = m;
786 }
787
788 assert(k == set_size(members));
789 qsort(sorted, k, sizeof(Member*), member_compare_funcp);
790
791 printf("%-*s %-*s %-*s %-*s %s\n",
792 (int) name_width, "NAME",
793 (int) type_width, "TYPE",
794 (int) signature_width, "SIGNATURE",
795 (int) result_width, "RESULT",
796 "FLAGS");
797
798 for (j = 0; j < k; j++) {
799 bool is_interface;
800
801 m = sorted[j];
802
803 is_interface = streq(m->type, "interface");
804
805 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
806 is_interface ? ansi_highlight() : "",
807 is_interface ? "" : ".",
808 - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name),
809 is_interface ? ansi_highlight_off() : "",
810 (int) type_width, strdash(m->type),
811 (int) signature_width, strdash(m->signature),
812 (int) result_width, strdash(m->result),
813 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
814 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
815 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
816 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
817 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
818 m->writable ? " writable" : "");
819 }
820
821 return 0;
822}
823
1f70b087 824static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 825 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
826}
827
828static int message_pcap(sd_bus_message *m, FILE *f) {
829 return bus_message_pcap_frame(m, arg_snaplen, f);
830}
831
832static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
b51f299a 833 bool added_something = false;
1f849790
LP
834 char **i;
835 int r;
836
837 STRV_FOREACH(i, argv+1) {
838 _cleanup_free_ char *m = NULL;
839
840 if (!service_name_is_valid(*i)) {
841 log_error("Invalid service name '%s'", *i);
842 return -EINVAL;
843 }
844
845 m = strjoin("sender='", *i, "'", NULL);
846 if (!m)
847 return log_oom();
848
19befb2d 849 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
1f849790
LP
850 if (r < 0) {
851 log_error("Failed to add match: %s", strerror(-r));
852 return r;
853 }
b51f299a
LP
854
855 added_something = true;
1f849790
LP
856 }
857
858 STRV_FOREACH(i, arg_matches) {
19befb2d 859 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
1f849790
LP
860 if (r < 0) {
861 log_error("Failed to add match: %s", strerror(-r));
862 return r;
863 }
b51f299a
LP
864
865 added_something = true;
866 }
867
868 if (!added_something) {
19befb2d 869 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
b51f299a
LP
870 if (r < 0) {
871 log_error("Failed to add match: %s", strerror(-r));
872 return r;
873 }
1f849790
LP
874 }
875
876 for (;;) {
877 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
878
879 r = sd_bus_process(bus, &m);
880 if (r < 0) {
881 log_error("Failed to process bus: %s", strerror(-r));
882 return r;
883 }
884
885 if (m) {
1f70b087 886 dump(m, stdout);
1f849790
LP
887 continue;
888 }
889
890 if (r > 0)
891 continue;
892
893 r = sd_bus_wait(bus, (uint64_t) -1);
894 if (r < 0) {
895 log_error("Failed to wait for bus: %s", strerror(-r));
896 return r;
897 }
898 }
95c4fe82 899}
1f849790 900
1f70b087
LP
901static int capture(sd_bus *bus, char *argv[]) {
902 int r;
903
904 if (isatty(fileno(stdout)) > 0) {
905 log_error("Refusing to write message data to console, please redirect output to a file.");
906 return -EINVAL;
907 }
908
909 bus_pcap_header(arg_snaplen, stdout);
910
911 r = monitor(bus, argv, message_pcap);
912 if (r < 0)
913 return r;
914
915 if (ferror(stdout)) {
916 log_error("Couldn't write capture file.");
917 return -EIO;
918 }
919
920 return r;
921}
922
95c4fe82
LP
923static int status(sd_bus *bus, char *argv[]) {
924 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
925 pid_t pid;
926 int r;
927
928 assert(bus);
929
930 if (strv_length(argv) != 2) {
931 log_error("Expects one argument.");
932 return -EINVAL;
933 }
934
935 r = parse_pid(argv[1], &pid);
936 if (r < 0)
056f95d0 937 r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
95c4fe82 938 else
151b9b96 939 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
95c4fe82
LP
940
941 if (r < 0) {
942 log_error("Failed to get credentials: %s", strerror(-r));
943 return r;
944 }
945
d0b2babf 946 bus_creds_dump(creds, NULL, false);
95c4fe82 947 return 0;
1f849790
LP
948}
949
781fa938
LP
950static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
951 char **p;
952 int r;
953
954 assert(m);
955 assert(signature);
956 assert(x);
957
958 p = *x;
959
960 for (;;) {
961 const char *v;
962 char t;
963
964 t = *signature;
965 v = *p;
966
967 if (t == 0)
968 break;
969 if (!v) {
970 log_error("Too few parameters for signature.");
971 return -EINVAL;
972 }
973
974 signature++;
975 p++;
976
977 switch (t) {
978
979 case SD_BUS_TYPE_BOOLEAN:
980
981 r = parse_boolean(v);
982 if (r < 0) {
983 log_error("Failed to parse as boolean: %s", v);
984 return r;
985 }
986
987 r = sd_bus_message_append_basic(m, t, &r);
988 break;
989
990 case SD_BUS_TYPE_BYTE: {
991 uint8_t z;
992
993 r = safe_atou8(v, &z);
994 if (r < 0) {
995 log_error("Failed to parse as byte (unsigned 8bit integer): %s", v);
996 return r;
997 }
998
999 r = sd_bus_message_append_basic(m, t, &z);
1000 break;
1001 }
1002
1003 case SD_BUS_TYPE_INT16: {
1004 int16_t z;
1005
1006 r = safe_atoi16(v, &z);
1007 if (r < 0) {
1008 log_error("Failed to parse as signed 16bit integer: %s", v);
1009 return r;
1010 }
1011
1012 r = sd_bus_message_append_basic(m, t, &z);
1013 break;
1014 }
1015
1016 case SD_BUS_TYPE_UINT16: {
1017 uint16_t z;
1018
1019 r = safe_atou16(v, &z);
1020 if (r < 0) {
1021 log_error("Failed to parse as unsigned 16bit integer: %s", v);
1022 return r;
1023 }
1024
1025 r = sd_bus_message_append_basic(m, t, &z);
1026 break;
1027 }
1028
1029 case SD_BUS_TYPE_INT32: {
1030 int32_t z;
1031
1032 r = safe_atoi32(v, &z);
1033 if (r < 0) {
1034 log_error("Failed to parse as signed 32bit integer: %s", v);
1035 return r;
1036 }
1037
1038 r = sd_bus_message_append_basic(m, t, &z);
1039 break;
1040 }
1041
1042 case SD_BUS_TYPE_UINT32: {
1043 uint32_t z;
1044
1045 r = safe_atou32(v, &z);
1046 if (r < 0) {
1047 log_error("Failed to parse as unsigned 32bit integer: %s", v);
1048 return r;
1049 }
1050
1051 r = sd_bus_message_append_basic(m, t, &z);
1052 break;
1053 }
1054
1055 case SD_BUS_TYPE_INT64: {
1056 int64_t z;
1057
1058 r = safe_atoi64(v, &z);
1059 if (r < 0) {
1060 log_error("Failed to parse as signed 64bit integer: %s", v);
1061 return r;
1062 }
1063
1064 r = sd_bus_message_append_basic(m, t, &z);
1065 break;
1066 }
1067
1068 case SD_BUS_TYPE_UINT64: {
1069 uint64_t z;
1070
1071 r = safe_atou64(v, &z);
1072 if (r < 0) {
1073 log_error("Failed to parse as unsigned 64bit integer: %s", v);
1074 return r;
1075 }
1076
1077 r = sd_bus_message_append_basic(m, t, &z);
1078 break;
1079 }
1080
1081
1082 case SD_BUS_TYPE_DOUBLE: {
1083 double z;
1084
1085 r = safe_atod(v, &z);
1086 if (r < 0) {
1087 log_error("Failed to parse as double precision floating point: %s", v);
1088 return r;
1089 }
1090
1091 r = sd_bus_message_append_basic(m, t, &z);
1092 break;
1093 }
1094
1095 case SD_BUS_TYPE_STRING:
1096 case SD_BUS_TYPE_OBJECT_PATH:
1097 case SD_BUS_TYPE_SIGNATURE:
1098
1099 r = sd_bus_message_append_basic(m, t, v);
1100 break;
1101
1102 case SD_BUS_TYPE_ARRAY: {
1103 uint32_t n;
1104 size_t k;
1105
1106 r = safe_atou32(v, &n);
1107 if (r < 0) {
1108 log_error("Failed to parse number of array entries: %s", v);
1109 return r;
1110 }
1111
1112 r = signature_element_length(signature, &k);
1113 if (r < 0) {
1114 log_error("Invalid array signature.");
1115 return r;
1116 }
1117
1118 {
1119 unsigned i;
1120 char s[k + 1];
1121 memcpy(s, signature, k);
1122 s[k] = 0;
1123
1124 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1125 if (r < 0)
1126 return bus_log_create_error(r);
1127
1128 for (i = 0; i < n; i++) {
1129 r = message_append_cmdline(m, s, &p);
1130 if (r < 0)
1131 return r;
1132 }
1133 }
1134
1135 signature += k;
1136
1137 r = sd_bus_message_close_container(m);
1138 break;
1139 }
1140
1141 case SD_BUS_TYPE_VARIANT:
1142 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1143 if (r < 0)
1144 return bus_log_create_error(r);
1145
1146 r = message_append_cmdline(m, v, &p);
1147 if (r < 0)
1148 return r;
1149
1150 r = sd_bus_message_close_container(m);
1151 break;
1152
1153 case SD_BUS_TYPE_STRUCT_BEGIN:
1154 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1155 size_t k;
1156
1157 signature--;
1158 p--;
1159
1160 r = signature_element_length(signature, &k);
1161 if (r < 0) {
1162 log_error("Invalid struct/dict entry signature.");
1163 return r;
1164 }
1165
1166 {
1167 char s[k-1];
1168 memcpy(s, signature + 1, k - 2);
1169 s[k - 2] = 0;
1170
1171 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1172 if (r < 0)
1173 return bus_log_create_error(r);
1174
1175 r = message_append_cmdline(m, s, &p);
1176 if (r < 0)
1177 return r;
1178 }
1179
1180 signature += k;
1181
1182 r = sd_bus_message_close_container(m);
1183 break;
1184 }
1185
1186 case SD_BUS_TYPE_UNIX_FD:
1187 log_error("UNIX file descriptor not supported as type.");
1188 return -EINVAL;
1189
1190 default:
1191 log_error("Unknown signature type %c.", t);
1192 return -EINVAL;
1193 }
1194
1195 if (r < 0)
1196 return bus_log_create_error(r);
1197 }
1198
1199 *x = p;
1200 return 0;
1201}
1202
1203static int call(sd_bus *bus, char *argv[]) {
1204 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1205 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1206 int r;
1207
1208 assert(bus);
1209
1210 if (strv_length(argv) < 5) {
1211 log_error("Expects at least four arguments.");
1212 return -EINVAL;
1213 }
1214
1215 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
1216 if (r < 0) {
1217 log_error("Failed to prepare bus message: %s", strerror(-r));
1218 return r;
1219 }
1220
1221 if (!isempty(argv[5])) {
1222 char **p;
1223
1224 p = argv+6;
1225
1226 r = message_append_cmdline(m, argv[5], &p);
1227 if (r < 0)
1228 return r;
1229
1230 if (*p) {
1231 log_error("Too many parameters for signature.");
1232 return -EINVAL;
1233 }
1234 }
1235
1236 r = sd_bus_call(bus, m, 0, &error, &reply);
1237 if (r < 0) {
1238 log_error("%s", bus_error_message(&error, r));
1239 return r;
1240 }
1241
1242 r = sd_bus_message_is_empty(reply);
1243 if (r < 0)
1244 return bus_log_parse_error(r);
1245 if (r == 0 && !arg_quiet) {
1246 pager_open_if_enabled();
d55192ad
LP
1247 bus_message_dump(reply, stdout, 0);
1248 }
1249
1250 return 0;
1251}
1252
1253static int get_property(sd_bus *bus, char *argv[]) {
1254 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1255 unsigned n;
1256 int r;
1257
1258 assert(bus);
1259
1260 n = strv_length(argv);
1261 if (n < 3) {
1262 log_error("Expects at least three arguments.");
1263 return -EINVAL;
1264 }
1265
1266 if (n < 5) {
1267 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1268 bool not_first = false;
1269
1270 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", strempty(argv[3]));
1271 if (r < 0) {
1272 log_error("%s", bus_error_message(&error, r));
1273 return r;
1274 }
1275
1276 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
1277 if (r < 0)
1278 return bus_log_parse_error(r);
1279
1280 for (;;) {
1281 const char *name;
1282
1283 r = sd_bus_message_enter_container(reply, 'e', "sv");
1284 if (r < 0)
1285 return bus_log_parse_error(r);
1286
1287 if (r == 0)
1288 break;
1289
1290 r = sd_bus_message_read(reply, "s", &name);
1291 if (r < 0)
1292 return bus_log_parse_error(r);
1293
1294 if (not_first)
1295 printf("\n");
1296
1297 printf("Property %s:\n", name);
1298
1299 r = sd_bus_message_enter_container(reply, 'v', NULL);
1300 if (r < 0)
1301 return bus_log_parse_error(r);
1302
1303 pager_open_if_enabled();
1304 bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
1305
1306 r = sd_bus_message_exit_container(reply);
1307 if (r < 0)
1308 return bus_log_parse_error(r);
1309
1310 r = sd_bus_message_exit_container(reply);
1311 if (r < 0)
1312 return bus_log_parse_error(r);
1313
1314 not_first = true;
1315 }
1316
1317 r = sd_bus_message_exit_container(reply);
1318 if (r < 0)
1319 return bus_log_parse_error(r);
1320 } else {
1321 char **i;
1322
1323 STRV_FOREACH(i, argv + 4) {
1324 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1325
1326 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
1327 if (r < 0) {
1328 log_error("%s", bus_error_message(&error, r));
1329 return r;
1330 }
1331
1332 r = sd_bus_message_enter_container(reply, 'v', NULL);
1333 if (r < 0)
1334 return bus_log_parse_error(r);
1335
1336 if (i > argv + 4)
1337 printf("\n");
1338
1339 if (argv[5])
1340 printf("Property %s:\n", *i);
1341
1342 pager_open_if_enabled();
1343 bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
1344
1345 r = sd_bus_message_exit_container(reply);
1346 if (r < 0)
1347 return bus_log_parse_error(r);
1348 }
781fa938
LP
1349 }
1350
1351 return 0;
1352}
1353
1f849790 1354static int help(void) {
1f849790
LP
1355 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1356 "Introspect the bus.\n\n"
d75edbd6
LP
1357 " -h --help Show this help\n"
1358 " --version Show package version\n"
a86a47ce 1359 " --no-pager Do not pipe output into a pager\n"
17d47d8d 1360 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
1361 " --system Connect to system bus\n"
1362 " --user Connect to user bus\n"
1363 " -H --host=[USER@]HOST Operate on remote host\n"
1364 " -M --machine=CONTAINER Operate on local container\n"
1365 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
1366 " --show-machine Show machine ID column in list\n"
1367 " --unique Only show unique names\n"
1368 " --acquired Only show acquired names\n"
1369 " --activatable Only show activatable names\n"
d9130355 1370 " --match=MATCH Only show matching messages\n"
781fa938
LP
1371 " --list Don't show tree, but simple object path list\n"
1372 " --quiet Don't show method call reply\n\n"
1f849790 1373 "Commands:\n"
d75edbd6 1374 " list List bus names\n"
0171da06 1375 " status SERVICE Show service name status\n"
d94fe1f1 1376 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1377 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06
LP
1378 " tree [SERVICE...] Show object tree of service\n"
1379 " introspect SERVICE PATH\n"
1380 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 1381 " Call a method\n"
0171da06 1382 " get-property SERVICE OBJECT [INTERFACE [PROPERTY...]]\n"
d55192ad 1383 " Get property value\n"
601185b4
ZJS
1384 " help Show this help\n"
1385 , program_invocation_short_name);
1f849790
LP
1386
1387 return 0;
1388}
1389
1390static int parse_argv(int argc, char *argv[]) {
1391
1392 enum {
1393 ARG_VERSION = 0x100,
1394 ARG_NO_PAGER,
17d47d8d 1395 ARG_NO_LEGEND,
1f849790
LP
1396 ARG_SYSTEM,
1397 ARG_USER,
1398 ARG_ADDRESS,
1399 ARG_MATCH,
56e61788
LP
1400 ARG_SHOW_MACHINE,
1401 ARG_UNIQUE,
1402 ARG_ACQUIRED,
1f70b087
LP
1403 ARG_ACTIVATABLE,
1404 ARG_SIZE,
d9130355 1405 ARG_LIST,
1f849790
LP
1406 };
1407
1408 static const struct option options[] = {
56e61788
LP
1409 { "help", no_argument, NULL, 'h' },
1410 { "version", no_argument, NULL, ARG_VERSION },
1411 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1412 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1413 { "system", no_argument, NULL, ARG_SYSTEM },
1414 { "user", no_argument, NULL, ARG_USER },
1415 { "address", required_argument, NULL, ARG_ADDRESS },
1416 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1417 { "unique", no_argument, NULL, ARG_UNIQUE },
1418 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1419 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1420 { "match", required_argument, NULL, ARG_MATCH },
1421 { "host", required_argument, NULL, 'H' },
1422 { "machine", required_argument, NULL, 'M' },
1f70b087 1423 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1424 { "list", no_argument, NULL, ARG_LIST },
781fa938 1425 { "quiet", no_argument, NULL, 'q' },
eb9da376 1426 {},
1f849790
LP
1427 };
1428
1f70b087 1429 int c, r;
1f849790
LP
1430
1431 assert(argc >= 0);
1432 assert(argv);
1433
781fa938 1434 while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
1f849790
LP
1435
1436 switch (c) {
1437
1438 case 'h':
1439 return help();
1440
1441 case ARG_VERSION:
1442 puts(PACKAGE_STRING);
1443 puts(SYSTEMD_FEATURES);
1444 return 0;
1445
1446 case ARG_NO_PAGER:
1447 arg_no_pager = true;
1448 break;
1449
17d47d8d
TA
1450 case ARG_NO_LEGEND:
1451 arg_legend = false;
1452 break;
1453
1f849790
LP
1454 case ARG_USER:
1455 arg_user = true;
1456 break;
1457
1458 case ARG_SYSTEM:
1459 arg_user = false;
1460 break;
1461
1462 case ARG_ADDRESS:
1463 arg_address = optarg;
1464 break;
1465
56e61788
LP
1466 case ARG_SHOW_MACHINE:
1467 arg_show_machine = true;
1468 break;
1469
1470 case ARG_UNIQUE:
1471 arg_unique = true;
1f849790
LP
1472 break;
1473
56e61788
LP
1474 case ARG_ACQUIRED:
1475 arg_acquired = true;
1476 break;
1477
1478 case ARG_ACTIVATABLE:
1479 arg_activatable = true;
a4297f08
LP
1480 break;
1481
1f849790
LP
1482 case ARG_MATCH:
1483 if (strv_extend(&arg_matches, optarg) < 0)
1484 return log_oom();
1485 break;
1486
1f70b087
LP
1487 case ARG_SIZE: {
1488 off_t o;
1489
1490 r = parse_size(optarg, 0, &o);
1491 if (r < 0) {
1492 log_error("Failed to parse size: %s", optarg);
1493 return r;
1494 }
1495
1496 if ((off_t) (size_t) o != o) {
1497 log_error("Size out of range.");
1498 return -E2BIG;
1499 }
1500
1501 arg_snaplen = (size_t) o;
1502 break;
1503 }
1504
d9130355
LP
1505 case ARG_LIST:
1506 arg_list = true;
1507 break;
1508
d75edbd6
LP
1509 case 'H':
1510 arg_transport = BUS_TRANSPORT_REMOTE;
1511 arg_host = optarg;
1512 break;
1513
1514 case 'M':
1515 arg_transport = BUS_TRANSPORT_CONTAINER;
1516 arg_host = optarg;
1517 break;
1518
781fa938
LP
1519 case 'q':
1520 arg_quiet = true;
1521 break;
1522
1f849790
LP
1523 case '?':
1524 return -EINVAL;
1525
1526 default:
eb9da376 1527 assert_not_reached("Unhandled option");
1f849790 1528 }
1f849790
LP
1529
1530 return 1;
1531}
1532
1533static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
1534 assert(bus);
1535
1536 if (optind >= argc ||
1537 streq(argv[optind], "list"))
1538 return list_bus_names(bus, argv + optind);
1539
1540 if (streq(argv[optind], "monitor"))
1f70b087
LP
1541 return monitor(bus, argv + optind, message_dump);
1542
1543 if (streq(argv[optind], "capture"))
1544 return capture(bus, argv + optind);
1f849790 1545
95c4fe82
LP
1546 if (streq(argv[optind], "status"))
1547 return status(bus, argv + optind);
d9130355
LP
1548
1549 if (streq(argv[optind], "tree"))
1550 return tree(bus, argv + optind);
781fa938 1551
0171da06
LP
1552 if (streq(argv[optind], "introspect"))
1553 return introspect(bus, argv + optind);
1554
781fa938
LP
1555 if (streq(argv[optind], "call"))
1556 return call(bus, argv + optind);
d55192ad
LP
1557
1558 if (streq(argv[optind], "get-property"))
1559 return get_property(bus, argv + optind);
95c4fe82 1560
1f849790
LP
1561 if (streq(argv[optind], "help"))
1562 return help();
1563
1564 log_error("Unknown command '%s'", argv[optind]);
1565 return -EINVAL;
1566}
1567
1568int main(int argc, char *argv[]) {
24996861 1569 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1f849790
LP
1570 int r;
1571
1572 log_parse_environment();
1573 log_open();
1574
1575 r = parse_argv(argc, argv);
1576 if (r <= 0)
1577 goto finish;
1578
09365592
LP
1579 r = sd_bus_new(&bus);
1580 if (r < 0) {
1581 log_error("Failed to allocate bus: %s", strerror(-r));
1582 goto finish;
1583 }
1584
1f70b087
LP
1585 if (streq_ptr(argv[optind], "monitor") ||
1586 streq_ptr(argv[optind], "capture")) {
09365592
LP
1587
1588 r = sd_bus_set_monitor(bus, true);
1f849790 1589 if (r < 0) {
09365592 1590 log_error("Failed to set monitor mode: %s", strerror(-r));
1f849790
LP
1591 goto finish;
1592 }
d0ce7734
LP
1593
1594 r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL);
1595 if (r < 0) {
1596 log_error("Failed to enable credentials: %s", strerror(-r));
1597 goto finish;
1598 }
1599
1600 r = sd_bus_negotiate_timestamp(bus, true);
1601 if (r < 0) {
1602 log_error("Failed to enable timestamps: %s", strerror(-r));
1603 goto finish;
1604 }
1605
1606 r = sd_bus_negotiate_fds(bus, true);
1607 if (r < 0) {
1608 log_error("Failed to enable fds: %s", strerror(-r));
1609 goto finish;
1610 }
09365592 1611 }
1f849790 1612
09365592 1613 if (arg_address)
1f849790 1614 r = sd_bus_set_address(bus, arg_address);
09365592
LP
1615 else {
1616 switch (arg_transport) {
1f849790 1617
09365592
LP
1618 case BUS_TRANSPORT_LOCAL:
1619 if (arg_user)
1620 r = bus_set_address_user(bus);
1621 else
1622 r = bus_set_address_system(bus);
1623 break;
1624
1625 case BUS_TRANSPORT_REMOTE:
1626 r = bus_set_address_system_remote(bus, arg_host);
1627 break;
1628
1629 case BUS_TRANSPORT_CONTAINER:
1630 r = bus_set_address_system_container(bus, arg_host);
1631 break;
1632
1633 default:
1634 assert_not_reached("Hmm, unknown transport type.");
1f849790 1635 }
09365592
LP
1636 }
1637 if (r < 0) {
1638 log_error("Failed to set address: %s", strerror(-r));
1639 goto finish;
1640 }
1f849790 1641
09365592
LP
1642 r = sd_bus_set_bus_client(bus, true);
1643 if (r < 0) {
1644 log_error("Failed to set bus client: %s", strerror(-r));
1645 goto finish;
1646 }
1f849790 1647
09365592 1648 r = sd_bus_start(bus);
1f849790
LP
1649 if (r < 0) {
1650 log_error("Failed to connect to bus: %s", strerror(-r));
1651 goto finish;
1652 }
1653
1654 r = busctl_main(bus, argc, argv);
1655
1656finish:
1657 pager_close();
d75edbd6 1658
1f849790 1659 strv_free(arg_matches);
de1c301e 1660
de1c301e
LP
1661 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1662}