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