]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
util-lib: split out IO related calls to io-util.[ch]
[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
de1c301e 24#include "sd-bus.h"
3f6fd1ba 25
2b5c5383 26#include "bus-dump.h"
3f6fd1ba 27#include "bus-internal.h"
781fa938 28#include "bus-signature.h"
79f34de9 29#include "bus-type.h"
3f6fd1ba 30#include "bus-util.h"
a1ad3767 31#include "busctl-introspect.h"
4f5dd394 32#include "escape.h"
3ffd4af2 33#include "fd-util.h"
3f6fd1ba
LP
34#include "log.h"
35#include "pager.h"
36#include "path-util.h"
37#include "set.h"
38#include "strv.h"
288a74cc 39#include "terminal-util.h"
3f6fd1ba 40#include "util.h"
de1c301e 41
1f849790 42static bool arg_no_pager = false;
17d47d8d 43static bool arg_legend = true;
1f849790 44static char *arg_address = NULL;
56e61788
LP
45static bool arg_unique = false;
46static bool arg_acquired = false;
47static bool arg_activatable = false;
48static bool arg_show_machine = false;
1f849790 49static char **arg_matches = NULL;
d75edbd6
LP
50static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
51static char *arg_host = NULL;
52static bool arg_user = false;
1f70b087 53static size_t arg_snaplen = 4096;
d9130355 54static bool arg_list = false;
781fa938 55static bool arg_quiet = false;
1fc55609 56static bool arg_verbose = false;
38051578
LP
57static bool arg_expect_reply = true;
58static bool arg_auto_start = true;
59static bool arg_allow_interactive_authorization = true;
40ed1a45 60static bool arg_augment_creds = true;
a44b1081 61static usec_t arg_timeout = 0;
1f849790
LP
62
63static void pager_open_if_enabled(void) {
64
65 /* Cache result before we open the pager */
66 if (arg_no_pager)
67 return;
68
69 pager_open(false);
70}
71
1d58a1fe
LP
72#define NAME_IS_ACQUIRED INT_TO_PTR(1)
73#define NAME_IS_ACTIVATABLE INT_TO_PTR(2)
74
1f849790 75static int list_bus_names(sd_bus *bus, char **argv) {
71f2ab46 76 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
77 _cleanup_free_ char **merged = NULL;
78 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
79 char **i;
80 int r;
89ffcd2a 81 size_t max_i = 0;
5e2f14e6
LP
82 unsigned n = 0;
83 void *v;
84 char *k;
85 Iterator iterator;
de1c301e 86
1f849790 87 assert(bus);
de1c301e 88
d9130355
LP
89 if (!arg_unique && !arg_acquired && !arg_activatable)
90 arg_unique = arg_acquired = arg_activatable = true;
91
56e61788 92 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
23bbb0de
MS
93 if (r < 0)
94 return log_error_errno(r, "Failed to list names: %m");
de1c301e 95
1f849790
LP
96 pager_open_if_enabled();
97
d5099efc 98 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
99 if (!names)
100 return log_oom();
89ffcd2a 101
5e2f14e6 102 STRV_FOREACH(i, acquired) {
71f2ab46
LP
103 max_i = MAX(max_i, strlen(*i));
104
1d58a1fe 105 r = hashmap_put(names, *i, NAME_IS_ACQUIRED);
23bbb0de
MS
106 if (r < 0)
107 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
108 }
109
110 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
111 max_i = MAX(max_i, strlen(*i));
112
1d58a1fe 113 r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE);
23bbb0de
MS
114 if (r < 0 && r != -EEXIST)
115 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
116 }
117
118 merged = new(char*, hashmap_size(names) + 1);
119 HASHMAP_FOREACH_KEY(v, k, names, iterator)
120 merged[n++] = k;
121
122 merged[n] = NULL;
123 strv_sort(merged);
124
17d47d8d
TA
125 if (arg_legend) {
126 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
455971c1 127 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
89ffcd2a 128
17d47d8d
TA
129 if (arg_show_machine)
130 puts(" MACHINE");
131 else
132 putchar('\n');
133 }
a4297f08 134
5e2f14e6
LP
135 STRV_FOREACH(i, merged) {
136 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
137 sd_id128_t mid;
71f2ab46 138
1d58a1fe 139 if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) {
5e2f14e6 140 /* Activatable */
71f2ab46 141
5e2f14e6 142 printf("%-*s", (int) max_i, *i);
56e61788
LP
143 printf(" - - - (activatable) - - ");
144 if (arg_show_machine)
5e2f14e6 145 puts(" -");
56e61788
LP
146 else
147 putchar('\n');
71f2ab46
LP
148 continue;
149
5e2f14e6 150 }
de1c301e 151
56e61788
LP
152 if (!arg_unique && (*i)[0] == ':')
153 continue;
154
155 if (!arg_acquired && (*i)[0] != ':')
1f849790 156 continue;
89ffcd2a
LP
157
158 printf("%-*s", (int) max_i, *i);
de1c301e 159
40ed1a45
LP
160 r = sd_bus_get_name_creds(
161 bus, *i,
162 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) |
05bae4a6 163 SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
40ed1a45
LP
164 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
165 SD_BUS_CREDS_DESCRIPTION, &creds);
89ffcd2a 166 if (r >= 0) {
14008e4e 167 const char *unique, *session, *unit, *cn;
5b12334d
LP
168 pid_t pid;
169 uid_t uid;
de1c301e 170
5b12334d
LP
171 r = sd_bus_creds_get_pid(creds, &pid);
172 if (r >= 0) {
173 const char *comm = NULL;
de1c301e 174
5b12334d 175 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 176
5b12334d
LP
177 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
178 } else
a4297f08 179 fputs(" - - ", stdout);
89ffcd2a 180
05bae4a6 181 r = sd_bus_creds_get_euid(creds, &uid);
5b12334d
LP
182 if (r >= 0) {
183 _cleanup_free_ char *u = NULL;
89ffcd2a 184
5b12334d
LP
185 u = uid_to_name(uid);
186 if (!u)
187 return log_oom();
89ffcd2a 188
5b12334d
LP
189 if (strlen(u) > 16)
190 u[16] = 0;
191
192 printf(" %-16s", u);
193 } else
a4297f08 194 fputs(" - ", stdout);
89ffcd2a 195
49b832c5
LP
196 r = sd_bus_creds_get_unique_name(creds, &unique);
197 if (r >= 0)
56e61788
LP
198 printf(" %-13s", unique);
199 else
200 fputs(" - ", stdout);
201
202 r = sd_bus_creds_get_unit(creds, &unit);
203 if (r >= 0) {
204 _cleanup_free_ char *e;
205
206 e = ellipsize(unit, 25, 100);
207 if (!e)
208 return log_oom();
209
210 printf(" %-25s", e);
211 } else
212 fputs(" - ", stdout);
213
214 r = sd_bus_creds_get_session(creds, &session);
215 if (r >= 0)
216 printf(" %-10s", session);
a4297f08 217 else
56e61788 218 fputs(" - ", stdout);
7b0b392f 219
455971c1 220 r = sd_bus_creds_get_description(creds, &cn);
14008e4e
LP
221 if (r >= 0)
222 printf(" %-19s", cn);
223 else
224 fputs(" - ", stdout);
225
7b0b392f 226 } else
14008e4e 227 printf(" - - - - - - - ");
a4297f08 228
56e61788 229 if (arg_show_machine) {
056f95d0 230 r = sd_bus_get_name_machine_id(bus, *i, &mid);
a4297f08
LP
231 if (r >= 0) {
232 char m[SD_ID128_STRING_MAX];
233 printf(" %s\n", sd_id128_to_string(mid, m));
234 } else
235 puts(" -");
56e61788
LP
236 } else
237 putchar('\n');
de1c301e
LP
238 }
239
1f849790
LP
240 return 0;
241}
242
d9130355
LP
243static void print_subtree(const char *prefix, const char *path, char **l) {
244 const char *vertical, *space;
245 char **n;
246
247 /* We assume the list is sorted. Let's first skip over the
248 * entry we are looking at. */
249 for (;;) {
250 if (!*l)
251 return;
252
253 if (!streq(*l, path))
254 break;
255
256 l++;
257 }
258
63c372cb
LP
259 vertical = strjoina(prefix, draw_special_char(DRAW_TREE_VERTICAL));
260 space = strjoina(prefix, draw_special_char(DRAW_TREE_SPACE));
d9130355
LP
261
262 for (;;) {
263 bool has_more = false;
264
265 if (!*l || !path_startswith(*l, path))
266 break;
267
268 n = l + 1;
269 for (;;) {
270 if (!*n || !path_startswith(*n, path))
271 break;
272
273 if (!path_startswith(*n, *l)) {
274 has_more = true;
275 break;
276 }
277
278 n++;
279 }
280
281 printf("%s%s%s\n", prefix, draw_special_char(has_more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), *l);
282
283 print_subtree(has_more ? vertical : space, *l, l);
284 l = n;
285 }
286}
287
288static void print_tree(const char *prefix, char **l) {
289
290 pager_open_if_enabled();
291
292 prefix = strempty(prefix);
293
294 if (arg_list) {
295 char **i;
296
297 STRV_FOREACH(i, l)
298 printf("%s%s\n", prefix, *i);
299 return;
300 }
301
56c8b52d
LP
302 if (strv_isempty(l)) {
303 printf("No objects discovered.\n");
304 return;
305 }
306
307 if (streq(l[0], "/") && !l[1]) {
308 printf("Only root object discovered.\n");
309 return;
310 }
d9130355
LP
311
312 print_subtree(prefix, "/", l);
313}
314
a1ad3767
LP
315static int on_path(const char *path, void *userdata) {
316 Set *paths = userdata;
d9130355
LP
317 int r;
318
a1ad3767 319 assert(paths);
d9130355 320
a1ad3767
LP
321 r = set_put_strdup(paths, path);
322 if (r < 0)
323 return log_oom();
d9130355 324
a1ad3767 325 return 0;
d9130355
LP
326}
327
73fc23c0 328static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) {
0171da06 329 static const XMLIntrospectOps ops = {
a1ad3767
LP
330 .on_path = on_path,
331 };
332
d9130355 333 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
a1ad3767
LP
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
335 const char *xml;
d9130355
LP
336 int r;
337
338 r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
339 if (r < 0) {
73fc23c0
LP
340 if (many)
341 printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r));
342 else
343 log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r));
d9130355
LP
344 return r;
345 }
346
347 r = sd_bus_message_read(reply, "s", &xml);
348 if (r < 0)
349 return bus_log_parse_error(r);
350
a1ad3767 351 return parse_xml_introspect(path, xml, &ops, paths);
d9130355
LP
352}
353
73fc23c0 354static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) {
d9130355
LP
355 _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
356 _cleanup_free_ char **l = NULL;
357 char *m;
358 int r;
359
360 paths = set_new(&string_hash_ops);
361 if (!paths)
362 return log_oom();
363
364 done = set_new(&string_hash_ops);
365 if (!done)
366 return log_oom();
367
368 failed = set_new(&string_hash_ops);
369 if (!failed)
370 return log_oom();
371
372 m = strdup("/");
373 if (!m)
374 return log_oom();
375
376 r = set_put(paths, m);
377 if (r < 0) {
378 free(m);
379 return log_oom();
380 }
381
382 for (;;) {
383 _cleanup_free_ char *p = NULL;
384 int q;
385
386 p = set_steal_first(paths);
387 if (!p)
388 break;
389
390 if (set_contains(done, p) ||
391 set_contains(failed, p))
392 continue;
393
73fc23c0 394 q = find_nodes(bus, service, p, paths, many);
d9130355
LP
395 if (q < 0) {
396 if (r >= 0)
397 r = q;
398
399 q = set_put(failed, p);
400 } else
401 q = set_put(done, p);
402
403 if (q < 0)
404 return log_oom();
405
406 assert(q != 0);
407 p = NULL;
408 }
409
73fc23c0
LP
410 pager_open_if_enabled();
411
d9130355
LP
412 l = set_get_strv(done);
413 if (!l)
414 return log_oom();
415
416 strv_sort(l);
417 print_tree(prefix, l);
418
419 fflush(stdout);
420
421 return r;
422}
423
424static int tree(sd_bus *bus, char **argv) {
425 char **i;
426 int r = 0;
427
428 if (!arg_unique && !arg_acquired)
429 arg_acquired = true;
430
431 if (strv_length(argv) <= 1) {
432 _cleanup_strv_free_ char **names = NULL;
56c8b52d 433 bool not_first = false;
d9130355
LP
434
435 r = sd_bus_list_names(bus, &names, NULL);
23bbb0de
MS
436 if (r < 0)
437 return log_error_errno(r, "Failed to get name list: %m");
d9130355
LP
438
439 pager_open_if_enabled();
440
441 STRV_FOREACH(i, names) {
442 int q;
443
444 if (!arg_unique && (*i)[0] == ':')
445 continue;
446
447 if (!arg_acquired && (*i)[0] == ':')
448 continue;
449
450 if (not_first)
451 printf("\n");
452
1fc464f6 453 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
d9130355 454
73fc23c0 455 q = tree_one(bus, *i, NULL, true);
d9130355
LP
456 if (q < 0 && r >= 0)
457 r = q;
458
459 not_first = true;
460 }
461 } else {
d9130355
LP
462 STRV_FOREACH(i, argv+1) {
463 int q;
464
465 if (i > argv+1)
466 printf("\n");
467
73fc23c0
LP
468 if (argv[2]) {
469 pager_open_if_enabled();
1fc464f6 470 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
73fc23c0 471 }
d9130355 472
73fc23c0 473 q = tree_one(bus, *i, NULL, !!argv[2]);
d9130355
LP
474 if (q < 0 && r >= 0)
475 r = q;
476 }
477 }
478
479 return r;
480}
481
1fc55609
LP
482static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) {
483 int r;
484
485 for (;;) {
486 const char *contents = NULL;
487 char type;
488 union {
489 uint8_t u8;
490 uint16_t u16;
491 int16_t s16;
492 uint32_t u32;
493 int32_t s32;
494 uint64_t u64;
495 int64_t s64;
496 double d64;
497 const char *string;
498 int i;
499 } basic;
500
501 r = sd_bus_message_peek_type(m, &type, &contents);
502 if (r <= 0)
503 return r;
504
505 if (bus_type_is_container(type) > 0) {
506
507 r = sd_bus_message_enter_container(m, type, contents);
508 if (r < 0)
509 return r;
510
511 if (type == SD_BUS_TYPE_ARRAY) {
512 unsigned n = 0;
513
514 /* count array entries */
515 for (;;) {
516
517 r = sd_bus_message_skip(m, contents);
518 if (r < 0)
519 return r;
520 if (r == 0)
521 break;
522
523 n++;
524 }
525
526 r = sd_bus_message_rewind(m, false);
527 if (r < 0)
528 return r;
529
530 if (needs_space)
531 fputc(' ', f);
532
533 fprintf(f, "%u", n);
534 } else if (type == SD_BUS_TYPE_VARIANT) {
535
536 if (needs_space)
537 fputc(' ', f);
538
539 fprintf(f, "%s", contents);
540 }
541
1ed24c61 542 r = format_cmdline(m, f, needs_space || IN_SET(type, SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_VARIANT));
1fc55609
LP
543 if (r < 0)
544 return r;
545
546 r = sd_bus_message_exit_container(m);
547 if (r < 0)
548 return r;
549
550 continue;
551 }
552
553 r = sd_bus_message_read_basic(m, type, &basic);
554 if (r < 0)
555 return r;
556
557 if (needs_space)
558 fputc(' ', f);
559
560 switch (type) {
561 case SD_BUS_TYPE_BYTE:
562 fprintf(f, "%u", basic.u8);
563 break;
564
565 case SD_BUS_TYPE_BOOLEAN:
566 fputs(true_false(basic.i), f);
567 break;
568
569 case SD_BUS_TYPE_INT16:
570 fprintf(f, "%i", basic.s16);
571 break;
572
573 case SD_BUS_TYPE_UINT16:
574 fprintf(f, "%u", basic.u16);
575 break;
576
577 case SD_BUS_TYPE_INT32:
578 fprintf(f, "%i", basic.s32);
579 break;
580
581 case SD_BUS_TYPE_UINT32:
582 fprintf(f, "%u", basic.u32);
583 break;
584
585 case SD_BUS_TYPE_INT64:
586 fprintf(f, "%" PRIi64, basic.s64);
587 break;
588
589 case SD_BUS_TYPE_UINT64:
590 fprintf(f, "%" PRIu64, basic.u64);
591 break;
592
593 case SD_BUS_TYPE_DOUBLE:
594 fprintf(f, "%g", basic.d64);
595 break;
596
597 case SD_BUS_TYPE_STRING:
598 case SD_BUS_TYPE_OBJECT_PATH:
599 case SD_BUS_TYPE_SIGNATURE: {
600 _cleanup_free_ char *b = NULL;
601
602 b = cescape(basic.string);
603 if (!b)
604 return -ENOMEM;
605
606 fprintf(f, "\"%s\"", b);
607 break;
608 }
609
610 case SD_BUS_TYPE_UNIX_FD:
611 fprintf(f, "%i", basic.i);
612 break;
613
614 default:
615 assert_not_reached("Unknown basic type.");
616 }
617
1ed24c61 618 needs_space = true;
1fc55609
LP
619 }
620}
621
0171da06
LP
622typedef struct Member {
623 const char *type;
624 char *interface;
625 char *name;
626 char *signature;
627 char *result;
1fc55609 628 char *value;
0171da06
LP
629 bool writable;
630 uint64_t flags;
631} Member;
632
b826ab58 633static void member_hash_func(const void *p, struct siphash *state) {
0171da06 634 const Member *m = p;
1e2527a6 635 uint64_t arity = 1;
0171da06
LP
636
637 assert(m);
638 assert(m->type);
639
b826ab58 640 string_hash_func(m->type, state);
0171da06 641
1e2527a6
TG
642 arity += !!m->name + !!m->interface;
643
644 uint64_hash_func(&arity, state);
645
0171da06 646 if (m->name)
b826ab58 647 string_hash_func(m->name, state);
0171da06
LP
648
649 if (m->interface)
b826ab58 650 string_hash_func(m->interface, state);
0171da06
LP
651}
652
653static int member_compare_func(const void *a, const void *b) {
654 const Member *x = a, *y = b;
655 int d;
656
657 assert(x);
658 assert(y);
659 assert(x->type);
660 assert(y->type);
661
c030a850
NK
662 d = strcmp_ptr(x->interface, y->interface);
663 if (d != 0)
664 return d;
0171da06
LP
665
666 d = strcmp(x->type, y->type);
667 if (d != 0)
668 return d;
669
c030a850 670 return strcmp_ptr(x->name, y->name);
0171da06
LP
671}
672
673static int member_compare_funcp(const void *a, const void *b) {
674 const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b;
675
676 return member_compare_func(*x, *y);
677}
678
679static void member_free(Member *m) {
680 if (!m)
681 return;
682
683 free(m->interface);
684 free(m->name);
685 free(m->signature);
686 free(m->result);
1fc55609 687 free(m->value);
0171da06
LP
688 free(m);
689}
690
691DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
692
693static void member_set_free(Set *s) {
694 Member *m;
695
696 while ((m = set_steal_first(s)))
697 member_free(m);
698
699 set_free(s);
700}
701
702DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
703
704static int on_interface(const char *interface, uint64_t flags, void *userdata) {
705 _cleanup_(member_freep) Member *m;
706 Set *members = userdata;
707 int r;
708
709 assert(interface);
710 assert(members);
711
712 m = new0(Member, 1);
713 if (!m)
714 return log_oom();
715
716 m->type = "interface";
717 m->flags = flags;
718
719 r = free_and_strdup(&m->interface, interface);
720 if (r < 0)
721 return log_oom();
722
723 r = set_put(members, m);
724 if (r <= 0) {
725 log_error("Duplicate interface");
726 return -EINVAL;
727 }
728
729 m = NULL;
730 return 0;
731}
732
733static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
734 _cleanup_(member_freep) Member *m;
735 Set *members = userdata;
736 int r;
737
738 assert(interface);
739 assert(name);
740
741 m = new0(Member, 1);
742 if (!m)
743 return log_oom();
744
745 m->type = "method";
746 m->flags = flags;
747
748 r = free_and_strdup(&m->interface, interface);
749 if (r < 0)
750 return log_oom();
751
752 r = free_and_strdup(&m->name, name);
753 if (r < 0)
754 return log_oom();
755
756 r = free_and_strdup(&m->signature, signature);
757 if (r < 0)
758 return log_oom();
759
760 r = free_and_strdup(&m->result, result);
761 if (r < 0)
762 return log_oom();
763
764 r = set_put(members, m);
765 if (r <= 0) {
766 log_error("Duplicate method");
767 return -EINVAL;
768 }
769
770 m = NULL;
771 return 0;
772}
773
774static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
775 _cleanup_(member_freep) Member *m;
776 Set *members = userdata;
777 int r;
778
779 assert(interface);
780 assert(name);
781
782 m = new0(Member, 1);
783 if (!m)
784 return log_oom();
785
786 m->type = "signal";
787 m->flags = flags;
788
789 r = free_and_strdup(&m->interface, interface);
790 if (r < 0)
791 return log_oom();
792
793 r = free_and_strdup(&m->name, name);
794 if (r < 0)
795 return log_oom();
796
797 r = free_and_strdup(&m->signature, signature);
798 if (r < 0)
799 return log_oom();
800
801 r = set_put(members, m);
802 if (r <= 0) {
803 log_error("Duplicate signal");
804 return -EINVAL;
805 }
806
807 m = NULL;
808 return 0;
809}
810
811static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
812 _cleanup_(member_freep) Member *m;
813 Set *members = userdata;
814 int r;
815
816 assert(interface);
817 assert(name);
818
819 m = new0(Member, 1);
820 if (!m)
821 return log_oom();
822
823 m->type = "property";
824 m->flags = flags;
825 m->writable = writable;
826
827 r = free_and_strdup(&m->interface, interface);
828 if (r < 0)
829 return log_oom();
830
831 r = free_and_strdup(&m->name, name);
832 if (r < 0)
833 return log_oom();
834
835 r = free_and_strdup(&m->signature, signature);
836 if (r < 0)
837 return log_oom();
838
839 r = set_put(members, m);
840 if (r <= 0) {
841 log_error("Duplicate property");
842 return -EINVAL;
843 }
844
845 m = NULL;
846 return 0;
847}
848
849static const char *strdash(const char *x) {
850 return isempty(x) ? "-" : x;
851}
852
853static int introspect(sd_bus *bus, char **argv) {
854 static const struct hash_ops member_hash_ops = {
855 .hash = member_hash_func,
856 .compare = member_compare_func,
857 };
858
859 static const XMLIntrospectOps ops = {
860 .on_interface = on_interface,
861 .on_method = on_method,
862 .on_signal = on_signal,
863 .on_property = on_property,
864 };
865
866 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
867 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
868 _cleanup_(member_set_freep) Set *members = NULL;
869 Iterator i;
870 Member *m;
871 const char *xml;
872 int r;
873 unsigned name_width, type_width, signature_width, result_width;
874 Member **sorted = NULL;
4f44c03e 875 unsigned k = 0, j, n_args;
0171da06 876
4f44c03e
LP
877 n_args = strv_length(argv);
878 if (n_args < 3) {
0171da06
LP
879 log_error("Requires service and object path argument.");
880 return -EINVAL;
881 }
882
4f44c03e
LP
883 if (n_args > 4) {
884 log_error("Too many arguments.");
885 return -EINVAL;
886 }
887
0171da06
LP
888 members = set_new(&member_hash_ops);
889 if (!members)
890 return log_oom();
891
892 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
893 if (r < 0) {
894 log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
895 return r;
896 }
897
898 r = sd_bus_message_read(reply, "s", &xml);
899 if (r < 0)
900 return bus_log_parse_error(r);
901
1fc55609 902 /* First, get list of all properties */
0171da06
LP
903 r = parse_xml_introspect(argv[2], xml, &ops, members);
904 if (r < 0)
905 return r;
906
1fc55609
LP
907 /* Second, find the current values for them */
908 SET_FOREACH(m, members, i) {
909
910 if (!streq(m->type, "property"))
911 continue;
912
913 if (m->value)
914 continue;
915
4f44c03e
LP
916 if (argv[3] && !streq(argv[3], m->interface))
917 continue;
918
1fc55609
LP
919 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
920 if (r < 0) {
921 log_error("%s", bus_error_message(&error, r));
922 return r;
923 }
924
925 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
926 if (r < 0)
927 return bus_log_parse_error(r);
928
929 for (;;) {
930 Member *z;
931 _cleanup_free_ char *buf = NULL;
932 _cleanup_fclose_ FILE *mf = NULL;
933 size_t sz = 0;
934 const char *name;
935
936 r = sd_bus_message_enter_container(reply, 'e', "sv");
937 if (r < 0)
938 return bus_log_parse_error(r);
939
940 if (r == 0)
941 break;
942
943 r = sd_bus_message_read(reply, "s", &name);
944 if (r < 0)
945 return bus_log_parse_error(r);
946
947 r = sd_bus_message_enter_container(reply, 'v', NULL);
948 if (r < 0)
949 return bus_log_parse_error(r);
950
951 mf = open_memstream(&buf, &sz);
952 if (!mf)
953 return log_oom();
954
955 r = format_cmdline(reply, mf, false);
956 if (r < 0)
957 return bus_log_parse_error(r);
958
959 fclose(mf);
960 mf = NULL;
961
962 z = set_get(members, &((Member) {
963 .type = "property",
964 .interface = m->interface,
965 .name = (char*) name }));
966 if (z) {
967 free(z->value);
968 z->value = buf;
969 buf = NULL;
970 }
971
972 r = sd_bus_message_exit_container(reply);
973 if (r < 0)
974 return bus_log_parse_error(r);
975
976 r = sd_bus_message_exit_container(reply);
977 if (r < 0)
978 return bus_log_parse_error(r);
979 }
980
981 r = sd_bus_message_exit_container(reply);
982 if (r < 0)
983 return bus_log_parse_error(r);
984 }
985
0171da06
LP
986 pager_open_if_enabled();
987
988 name_width = strlen("NAME");
989 type_width = strlen("TYPE");
990 signature_width = strlen("SIGNATURE");
1fc55609 991 result_width = strlen("RESULT/VALUE");
0171da06
LP
992
993 sorted = newa(Member*, set_size(members));
994
995 SET_FOREACH(m, members, i) {
4f44c03e
LP
996
997 if (argv[3] && !streq(argv[3], m->interface))
998 continue;
999
0171da06
LP
1000 if (m->interface)
1001 name_width = MAX(name_width, strlen(m->interface));
1002 if (m->name)
1003 name_width = MAX(name_width, strlen(m->name) + 1);
1004 if (m->type)
1005 type_width = MAX(type_width, strlen(m->type));
1006 if (m->signature)
1007 signature_width = MAX(signature_width, strlen(m->signature));
1008 if (m->result)
1009 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1010 if (m->value)
1011 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1012
1013 sorted[k++] = m;
1014 }
1015
1fc55609
LP
1016 if (result_width > 40)
1017 result_width = 40;
1018
0171da06
LP
1019 qsort(sorted, k, sizeof(Member*), member_compare_funcp);
1020
1fc55609
LP
1021 if (arg_legend) {
1022 printf("%-*s %-*s %-*s %-*s %s\n",
1023 (int) name_width, "NAME",
1024 (int) type_width, "TYPE",
1025 (int) signature_width, "SIGNATURE",
1026 (int) result_width, "RESULT/VALUE",
1027 "FLAGS");
1028 }
0171da06
LP
1029
1030 for (j = 0; j < k; j++) {
1fc55609
LP
1031 _cleanup_free_ char *ellipsized = NULL;
1032 const char *rv;
0171da06
LP
1033 bool is_interface;
1034
1035 m = sorted[j];
1036
4f44c03e
LP
1037 if (argv[3] && !streq(argv[3], m->interface))
1038 continue;
1039
0171da06
LP
1040 is_interface = streq(m->type, "interface");
1041
4f44c03e
LP
1042 if (argv[3] && is_interface)
1043 continue;
1044
1fc55609
LP
1045 if (m->value) {
1046 ellipsized = ellipsize(m->value, result_width, 100);
1047 if (!ellipsized)
1048 return log_oom();
1049
1050 rv = ellipsized;
1051 } else
1052 rv = strdash(m->result);
1053
0171da06
LP
1054 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1055 is_interface ? ansi_highlight() : "",
1056 is_interface ? "" : ".",
1057 - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1fc464f6 1058 is_interface ? ansi_normal() : "",
0171da06
LP
1059 (int) type_width, strdash(m->type),
1060 (int) signature_width, strdash(m->signature),
1fc55609 1061 (int) result_width, rv,
0171da06
LP
1062 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1063 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1064 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1065 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1066 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1067 m->writable ? " writable" : "");
1068 }
1069
1070 return 0;
1071}
1072
1f70b087 1073static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 1074 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1075}
1076
1077static int message_pcap(sd_bus_message *m, FILE *f) {
1078 return bus_message_pcap_frame(m, arg_snaplen, f);
1079}
1080
1081static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
b51f299a 1082 bool added_something = false;
1f849790
LP
1083 char **i;
1084 int r;
1085
1086 STRV_FOREACH(i, argv+1) {
1087 _cleanup_free_ char *m = NULL;
1088
1089 if (!service_name_is_valid(*i)) {
1090 log_error("Invalid service name '%s'", *i);
1091 return -EINVAL;
1092 }
1093
1094 m = strjoin("sender='", *i, "'", NULL);
1095 if (!m)
1096 return log_oom();
1097
19befb2d 1098 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
23bbb0de
MS
1099 if (r < 0)
1100 return log_error_errno(r, "Failed to add match: %m");
b51f299a 1101
f6d1e6cb
LU
1102 free(m);
1103 m = strjoin("destination='", *i, "'", NULL);
1104 if (!m)
1105 return log_oom();
1106
1107 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
1108 if (r < 0)
1109 return log_error_errno(r, "Failed to add match: %m");
1110
b51f299a 1111 added_something = true;
1f849790
LP
1112 }
1113
1114 STRV_FOREACH(i, arg_matches) {
19befb2d 1115 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
23bbb0de
MS
1116 if (r < 0)
1117 return log_error_errno(r, "Failed to add match: %m");
b51f299a
LP
1118
1119 added_something = true;
1120 }
1121
1122 if (!added_something) {
19befb2d 1123 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
23bbb0de
MS
1124 if (r < 0)
1125 return log_error_errno(r, "Failed to add match: %m");
1f849790
LP
1126 }
1127
92d66625
LP
1128 log_info("Monitoring bus message stream.");
1129
1f849790
LP
1130 for (;;) {
1131 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1132
1133 r = sd_bus_process(bus, &m);
23bbb0de
MS
1134 if (r < 0)
1135 return log_error_errno(r, "Failed to process bus: %m");
1f849790
LP
1136
1137 if (m) {
1f70b087 1138 dump(m, stdout);
1d44f758 1139 fflush(stdout);
92d66625
LP
1140
1141 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
1142 log_info("Connection terminated, exiting.");
1143 return 0;
1144 }
1145
1f849790
LP
1146 continue;
1147 }
1148
1149 if (r > 0)
1150 continue;
1151
1152 r = sd_bus_wait(bus, (uint64_t) -1);
23bbb0de
MS
1153 if (r < 0)
1154 return log_error_errno(r, "Failed to wait for bus: %m");
1f849790 1155 }
95c4fe82 1156}
1f849790 1157
1f70b087
LP
1158static int capture(sd_bus *bus, char *argv[]) {
1159 int r;
1160
1161 if (isatty(fileno(stdout)) > 0) {
1162 log_error("Refusing to write message data to console, please redirect output to a file.");
1163 return -EINVAL;
1164 }
1165
1166 bus_pcap_header(arg_snaplen, stdout);
1167
1168 r = monitor(bus, argv, message_pcap);
1169 if (r < 0)
1170 return r;
1171
1172 if (ferror(stdout)) {
1173 log_error("Couldn't write capture file.");
1174 return -EIO;
1175 }
1176
1177 return r;
1178}
1179
95c4fe82
LP
1180static int status(sd_bus *bus, char *argv[]) {
1181 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
1182 pid_t pid;
1183 int r;
1184
1185 assert(bus);
1186
2e9efd22
LP
1187 if (strv_length(argv) > 2) {
1188 log_error("Expects no or one argument.");
95c4fe82
LP
1189 return -EINVAL;
1190 }
1191
2e9efd22
LP
1192 if (argv[1]) {
1193 r = parse_pid(argv[1], &pid);
1194 if (r < 0)
1195 r = sd_bus_get_name_creds(
1196 bus,
1197 argv[1],
1198 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1199 &creds);
1200 else
1201 r = sd_bus_creds_new_from_pid(
1202 &creds,
1203 pid,
1204 _SD_BUS_CREDS_ALL);
3acc1daf 1205 } else {
5b820358 1206 const char *scope, *address;
5c302692 1207 sd_id128_t bus_id;
3acc1daf 1208
5b820358
LP
1209 r = sd_bus_get_address(bus, &address);
1210 if (r >= 0)
1fc464f6 1211 printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal());
5b820358 1212
3acc1daf
LP
1213 r = sd_bus_get_scope(bus, &scope);
1214 if (r >= 0)
1fc464f6 1215 printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal());
3acc1daf 1216
5c302692
LP
1217 r = sd_bus_get_bus_id(bus, &bus_id);
1218 if (r >= 0)
1fc464f6 1219 printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
5c302692 1220
2e9efd22 1221 r = sd_bus_get_owner_creds(
40ed1a45 1222 bus,
40ed1a45
LP
1223 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1224 &creds);
3acc1daf 1225 }
95c4fe82 1226
23bbb0de
MS
1227 if (r < 0)
1228 return log_error_errno(r, "Failed to get credentials: %m");
95c4fe82 1229
d0b2babf 1230 bus_creds_dump(creds, NULL, false);
95c4fe82 1231 return 0;
1f849790
LP
1232}
1233
781fa938
LP
1234static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1235 char **p;
1236 int r;
1237
1238 assert(m);
1239 assert(signature);
1240 assert(x);
1241
1242 p = *x;
1243
1244 for (;;) {
1245 const char *v;
1246 char t;
1247
1248 t = *signature;
1249 v = *p;
1250
1251 if (t == 0)
1252 break;
1253 if (!v) {
1254 log_error("Too few parameters for signature.");
1255 return -EINVAL;
1256 }
1257
1258 signature++;
1259 p++;
1260
1261 switch (t) {
1262
1263 case SD_BUS_TYPE_BOOLEAN:
1264
1265 r = parse_boolean(v);
1266 if (r < 0) {
1267 log_error("Failed to parse as boolean: %s", v);
1268 return r;
1269 }
1270
1271 r = sd_bus_message_append_basic(m, t, &r);
1272 break;
1273
1274 case SD_BUS_TYPE_BYTE: {
1275 uint8_t z;
1276
1277 r = safe_atou8(v, &z);
1278 if (r < 0) {
1279 log_error("Failed to parse as byte (unsigned 8bit integer): %s", v);
1280 return r;
1281 }
1282
1283 r = sd_bus_message_append_basic(m, t, &z);
1284 break;
1285 }
1286
1287 case SD_BUS_TYPE_INT16: {
1288 int16_t z;
1289
1290 r = safe_atoi16(v, &z);
1291 if (r < 0) {
1292 log_error("Failed to parse as signed 16bit integer: %s", v);
1293 return r;
1294 }
1295
1296 r = sd_bus_message_append_basic(m, t, &z);
1297 break;
1298 }
1299
1300 case SD_BUS_TYPE_UINT16: {
1301 uint16_t z;
1302
1303 r = safe_atou16(v, &z);
1304 if (r < 0) {
1305 log_error("Failed to parse as unsigned 16bit integer: %s", v);
1306 return r;
1307 }
1308
1309 r = sd_bus_message_append_basic(m, t, &z);
1310 break;
1311 }
1312
1313 case SD_BUS_TYPE_INT32: {
1314 int32_t z;
1315
1316 r = safe_atoi32(v, &z);
1317 if (r < 0) {
1318 log_error("Failed to parse as signed 32bit integer: %s", v);
1319 return r;
1320 }
1321
1322 r = sd_bus_message_append_basic(m, t, &z);
1323 break;
1324 }
1325
1326 case SD_BUS_TYPE_UINT32: {
1327 uint32_t z;
1328
1329 r = safe_atou32(v, &z);
1330 if (r < 0) {
1331 log_error("Failed to parse as unsigned 32bit integer: %s", v);
1332 return r;
1333 }
1334
1335 r = sd_bus_message_append_basic(m, t, &z);
1336 break;
1337 }
1338
1339 case SD_BUS_TYPE_INT64: {
1340 int64_t z;
1341
1342 r = safe_atoi64(v, &z);
1343 if (r < 0) {
1344 log_error("Failed to parse as signed 64bit integer: %s", v);
1345 return r;
1346 }
1347
1348 r = sd_bus_message_append_basic(m, t, &z);
1349 break;
1350 }
1351
1352 case SD_BUS_TYPE_UINT64: {
1353 uint64_t z;
1354
1355 r = safe_atou64(v, &z);
1356 if (r < 0) {
1357 log_error("Failed to parse as unsigned 64bit integer: %s", v);
1358 return r;
1359 }
1360
1361 r = sd_bus_message_append_basic(m, t, &z);
1362 break;
1363 }
1364
1365
1366 case SD_BUS_TYPE_DOUBLE: {
1367 double z;
1368
1369 r = safe_atod(v, &z);
1370 if (r < 0) {
1371 log_error("Failed to parse as double precision floating point: %s", v);
1372 return r;
1373 }
1374
1375 r = sd_bus_message_append_basic(m, t, &z);
1376 break;
1377 }
1378
1379 case SD_BUS_TYPE_STRING:
1380 case SD_BUS_TYPE_OBJECT_PATH:
1381 case SD_BUS_TYPE_SIGNATURE:
1382
1383 r = sd_bus_message_append_basic(m, t, v);
1384 break;
1385
1386 case SD_BUS_TYPE_ARRAY: {
1387 uint32_t n;
1388 size_t k;
1389
1390 r = safe_atou32(v, &n);
1391 if (r < 0) {
1392 log_error("Failed to parse number of array entries: %s", v);
1393 return r;
1394 }
1395
1396 r = signature_element_length(signature, &k);
1397 if (r < 0) {
1398 log_error("Invalid array signature.");
1399 return r;
1400 }
1401
1402 {
1403 unsigned i;
1404 char s[k + 1];
1405 memcpy(s, signature, k);
1406 s[k] = 0;
1407
1408 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1409 if (r < 0)
1410 return bus_log_create_error(r);
1411
1412 for (i = 0; i < n; i++) {
1413 r = message_append_cmdline(m, s, &p);
1414 if (r < 0)
1415 return r;
1416 }
1417 }
1418
1419 signature += k;
1420
1421 r = sd_bus_message_close_container(m);
1422 break;
1423 }
1424
1425 case SD_BUS_TYPE_VARIANT:
1426 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1427 if (r < 0)
1428 return bus_log_create_error(r);
1429
1430 r = message_append_cmdline(m, v, &p);
1431 if (r < 0)
1432 return r;
1433
1434 r = sd_bus_message_close_container(m);
1435 break;
1436
1437 case SD_BUS_TYPE_STRUCT_BEGIN:
1438 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1439 size_t k;
1440
1441 signature--;
1442 p--;
1443
1444 r = signature_element_length(signature, &k);
1445 if (r < 0) {
1446 log_error("Invalid struct/dict entry signature.");
1447 return r;
1448 }
1449
1450 {
1451 char s[k-1];
1452 memcpy(s, signature + 1, k - 2);
1453 s[k - 2] = 0;
1454
1455 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1456 if (r < 0)
1457 return bus_log_create_error(r);
1458
1459 r = message_append_cmdline(m, s, &p);
1460 if (r < 0)
1461 return r;
1462 }
1463
1464 signature += k;
1465
1466 r = sd_bus_message_close_container(m);
1467 break;
1468 }
1469
1470 case SD_BUS_TYPE_UNIX_FD:
1471 log_error("UNIX file descriptor not supported as type.");
1472 return -EINVAL;
1473
1474 default:
1475 log_error("Unknown signature type %c.", t);
1476 return -EINVAL;
1477 }
1478
1479 if (r < 0)
1480 return bus_log_create_error(r);
1481 }
1482
1483 *x = p;
1484 return 0;
1485}
1486
1487static int call(sd_bus *bus, char *argv[]) {
1488 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1489 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1490 int r;
1491
1492 assert(bus);
1493
1494 if (strv_length(argv) < 5) {
1495 log_error("Expects at least four arguments.");
1496 return -EINVAL;
1497 }
1498
1499 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
1500 if (r < 0)
1501 return bus_log_create_error(r);
781fa938 1502
38051578
LP
1503 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
1504 if (r < 0)
1505 return bus_log_create_error(r);
1506
1507 r = sd_bus_message_set_auto_start(m, arg_auto_start);
1508 if (r < 0)
1509 return bus_log_create_error(r);
1510
1511 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
1512 if (r < 0)
1513 return bus_log_create_error(r);
1514
781fa938
LP
1515 if (!isempty(argv[5])) {
1516 char **p;
1517
1518 p = argv+6;
1519
1520 r = message_append_cmdline(m, argv[5], &p);
1521 if (r < 0)
1522 return r;
1523
1524 if (*p) {
1525 log_error("Too many parameters for signature.");
1526 return -EINVAL;
1527 }
1528 }
1529
38051578
LP
1530 if (!arg_expect_reply) {
1531 r = sd_bus_send(bus, m, NULL);
1532 if (r < 0) {
1533 log_error("Failed to send message.");
1534 return r;
1535 }
1536
1537 return 0;
1538 }
1539
a44b1081 1540 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
781fa938
LP
1541 if (r < 0) {
1542 log_error("%s", bus_error_message(&error, r));
1543 return r;
1544 }
1545
1546 r = sd_bus_message_is_empty(reply);
1547 if (r < 0)
1548 return bus_log_parse_error(r);
1fc55609 1549
781fa938 1550 if (r == 0 && !arg_quiet) {
1fc55609
LP
1551
1552 if (arg_verbose) {
1553 pager_open_if_enabled();
1554
1555 r = bus_message_dump(reply, stdout, 0);
1556 if (r < 0)
1557 return r;
1558 } else {
1559
1560 fputs(sd_bus_message_get_signature(reply, true), stdout);
1561 fputc(' ', stdout);
1562
1563 r = format_cmdline(reply, stdout, false);
1564 if (r < 0)
1565 return bus_log_parse_error(r);
1566
1567 fputc('\n', stdout);
1568 }
d55192ad
LP
1569 }
1570
1571 return 0;
1572}
1573
1574static int get_property(sd_bus *bus, char *argv[]) {
1575 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1576 unsigned n;
1fc55609 1577 char **i;
d55192ad
LP
1578 int r;
1579
1580 assert(bus);
1581
1582 n = strv_length(argv);
1fc55609
LP
1583 if (n < 5) {
1584 log_error("Expects at least four arguments.");
d55192ad
LP
1585 return -EINVAL;
1586 }
1587
1fc55609 1588 STRV_FOREACH(i, argv + 4) {
d55192ad 1589 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1fc55609
LP
1590 const char *contents = NULL;
1591 char type;
d55192ad 1592
1fc55609 1593 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
d55192ad
LP
1594 if (r < 0) {
1595 log_error("%s", bus_error_message(&error, r));
1596 return r;
1597 }
1598
1fc55609 1599 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
1600 if (r < 0)
1601 return bus_log_parse_error(r);
1602
1fc55609
LP
1603 r = sd_bus_message_enter_container(reply, 'v', contents);
1604 if (r < 0)
1605 return bus_log_parse_error(r);
d55192ad 1606
1fc55609 1607 if (arg_verbose) {
d55192ad 1608 pager_open_if_enabled();
d55192ad 1609
1fc55609 1610 r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 1611 if (r < 0)
1fc55609
LP
1612 return r;
1613 } else {
1614 fputs(contents, stdout);
1615 fputc(' ', stdout);
d55192ad 1616
1fc55609 1617 r = format_cmdline(reply, stdout, false);
d55192ad
LP
1618 if (r < 0)
1619 return bus_log_parse_error(r);
1620
1fc55609 1621 fputc('\n', stdout);
d55192ad
LP
1622 }
1623
1624 r = sd_bus_message_exit_container(reply);
1625 if (r < 0)
1626 return bus_log_parse_error(r);
1fc55609 1627 }
d55192ad 1628
1fc55609
LP
1629 return 0;
1630}
d55192ad 1631
1fc55609
LP
1632static int set_property(sd_bus *bus, char *argv[]) {
1633 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1634 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1635 unsigned n;
1636 char **p;
1637 int r;
d55192ad 1638
1fc55609 1639 assert(bus);
d55192ad 1640
1fc55609
LP
1641 n = strv_length(argv);
1642 if (n < 6) {
1643 log_error("Expects at least five arguments.");
1644 return -EINVAL;
1645 }
d55192ad 1646
1fc55609
LP
1647 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
1648 if (r < 0)
1649 return bus_log_create_error(r);
d55192ad 1650
1fc55609
LP
1651 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
1652 if (r < 0)
1653 return bus_log_create_error(r);
d55192ad 1654
1fc55609
LP
1655 r = sd_bus_message_open_container(m, 'v', argv[5]);
1656 if (r < 0)
1657 return bus_log_create_error(r);
1658
1659 p = argv+6;
1660 r = message_append_cmdline(m, argv[5], &p);
1661 if (r < 0)
1662 return r;
1663
1664 r = sd_bus_message_close_container(m);
1665 if (r < 0)
1666 return bus_log_create_error(r);
1667
1668 if (*p) {
1669 log_error("Too many parameters for signature.");
1670 return -EINVAL;
1671 }
1672
a44b1081 1673 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
1fc55609
LP
1674 if (r < 0) {
1675 log_error("%s", bus_error_message(&error, r));
1676 return r;
781fa938
LP
1677 }
1678
1679 return 0;
1680}
1681
1f849790 1682static int help(void) {
1f849790
LP
1683 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1684 "Introspect the bus.\n\n"
d75edbd6
LP
1685 " -h --help Show this help\n"
1686 " --version Show package version\n"
a86a47ce 1687 " --no-pager Do not pipe output into a pager\n"
17d47d8d 1688 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
1689 " --system Connect to system bus\n"
1690 " --user Connect to user bus\n"
1691 " -H --host=[USER@]HOST Operate on remote host\n"
1692 " -M --machine=CONTAINER Operate on local container\n"
1693 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
1694 " --show-machine Show machine ID column in list\n"
1695 " --unique Only show unique names\n"
1696 " --acquired Only show acquired names\n"
1697 " --activatable Only show activatable names\n"
d9130355 1698 " --match=MATCH Only show matching messages\n"
d28ebe27 1699 " --size=SIZE Maximum length of captured packet\n"
781fa938 1700 " --list Don't show tree, but simple object path list\n"
1fc55609 1701 " --quiet Don't show method call reply\n"
38051578
LP
1702 " --verbose Show result values in long format\n"
1703 " --expect-reply=BOOL Expect a method call reply\n"
1704 " --auto-start=BOOL Auto-start destination service\n"
1705 " --allow-interactive-authorization=BOOL\n"
a44b1081 1706 " Allow interactive authorization for operation\n"
40ed1a45
LP
1707 " --timeout=SECS Maximum time to wait for method call completion\n"
1708 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n"
1f849790 1709 "Commands:\n"
d75edbd6 1710 " list List bus names\n"
2e9efd22 1711 " status [SERVICE] Show bus service, process or bus owner credentials\n"
d94fe1f1 1712 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1713 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 1714 " tree [SERVICE...] Show object tree of service\n"
4f44c03e 1715 " introspect SERVICE OBJECT [INTERFACE]\n"
0171da06 1716 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 1717 " Call a method\n"
1fc55609 1718 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 1719 " Get property value\n"
1fc55609
LP
1720 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
1721 " Set property value\n"
601185b4
ZJS
1722 " help Show this help\n"
1723 , program_invocation_short_name);
1f849790
LP
1724
1725 return 0;
1726}
1727
1728static int parse_argv(int argc, char *argv[]) {
1729
1730 enum {
1731 ARG_VERSION = 0x100,
1732 ARG_NO_PAGER,
17d47d8d 1733 ARG_NO_LEGEND,
1f849790
LP
1734 ARG_SYSTEM,
1735 ARG_USER,
1736 ARG_ADDRESS,
1737 ARG_MATCH,
56e61788
LP
1738 ARG_SHOW_MACHINE,
1739 ARG_UNIQUE,
1740 ARG_ACQUIRED,
1f70b087
LP
1741 ARG_ACTIVATABLE,
1742 ARG_SIZE,
d9130355 1743 ARG_LIST,
1fc55609 1744 ARG_VERBOSE,
38051578
LP
1745 ARG_EXPECT_REPLY,
1746 ARG_AUTO_START,
1747 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 1748 ARG_TIMEOUT,
40ed1a45 1749 ARG_AUGMENT_CREDS,
1f849790
LP
1750 };
1751
1752 static const struct option options[] = {
56e61788
LP
1753 { "help", no_argument, NULL, 'h' },
1754 { "version", no_argument, NULL, ARG_VERSION },
1755 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1756 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1757 { "system", no_argument, NULL, ARG_SYSTEM },
1758 { "user", no_argument, NULL, ARG_USER },
1759 { "address", required_argument, NULL, ARG_ADDRESS },
1760 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1761 { "unique", no_argument, NULL, ARG_UNIQUE },
1762 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1763 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1764 { "match", required_argument, NULL, ARG_MATCH },
1765 { "host", required_argument, NULL, 'H' },
1766 { "machine", required_argument, NULL, 'M' },
1f70b087 1767 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1768 { "list", no_argument, NULL, ARG_LIST },
781fa938 1769 { "quiet", no_argument, NULL, 'q' },
1fc55609 1770 { "verbose", no_argument, NULL, ARG_VERBOSE },
38051578
LP
1771 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
1772 { "auto-start", required_argument, NULL, ARG_AUTO_START },
1773 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
a44b1081 1774 { "timeout", required_argument, NULL, ARG_TIMEOUT },
40ed1a45 1775 { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS},
eb9da376 1776 {},
1f849790
LP
1777 };
1778
1f70b087 1779 int c, r;
1f849790
LP
1780
1781 assert(argc >= 0);
1782 assert(argv);
1783
781fa938 1784 while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
1f849790
LP
1785
1786 switch (c) {
1787
1788 case 'h':
1789 return help();
1790
1791 case ARG_VERSION:
3f6fd1ba 1792 return version();
1f849790
LP
1793
1794 case ARG_NO_PAGER:
1795 arg_no_pager = true;
1796 break;
1797
17d47d8d
TA
1798 case ARG_NO_LEGEND:
1799 arg_legend = false;
1800 break;
1801
1f849790
LP
1802 case ARG_USER:
1803 arg_user = true;
1804 break;
1805
1806 case ARG_SYSTEM:
1807 arg_user = false;
1808 break;
1809
1810 case ARG_ADDRESS:
1811 arg_address = optarg;
1812 break;
1813
56e61788
LP
1814 case ARG_SHOW_MACHINE:
1815 arg_show_machine = true;
1816 break;
1817
1818 case ARG_UNIQUE:
1819 arg_unique = true;
1f849790
LP
1820 break;
1821
56e61788
LP
1822 case ARG_ACQUIRED:
1823 arg_acquired = true;
1824 break;
1825
1826 case ARG_ACTIVATABLE:
1827 arg_activatable = true;
a4297f08
LP
1828 break;
1829
1f849790
LP
1830 case ARG_MATCH:
1831 if (strv_extend(&arg_matches, optarg) < 0)
1832 return log_oom();
1833 break;
1834
1f70b087 1835 case ARG_SIZE: {
59f448cf 1836 uint64_t sz;
1f70b087 1837
59f448cf 1838 r = parse_size(optarg, 1024, &sz);
1f70b087
LP
1839 if (r < 0) {
1840 log_error("Failed to parse size: %s", optarg);
1841 return r;
1842 }
1843
59f448cf 1844 if ((uint64_t) (size_t) sz != sz) {
1f70b087
LP
1845 log_error("Size out of range.");
1846 return -E2BIG;
1847 }
1848
59f448cf 1849 arg_snaplen = (size_t) sz;
1f70b087
LP
1850 break;
1851 }
1852
d9130355
LP
1853 case ARG_LIST:
1854 arg_list = true;
1855 break;
1856
d75edbd6
LP
1857 case 'H':
1858 arg_transport = BUS_TRANSPORT_REMOTE;
1859 arg_host = optarg;
1860 break;
1861
1862 case 'M':
de33fc62 1863 arg_transport = BUS_TRANSPORT_MACHINE;
d75edbd6
LP
1864 arg_host = optarg;
1865 break;
1866
781fa938
LP
1867 case 'q':
1868 arg_quiet = true;
1869 break;
1870
1fc55609
LP
1871 case ARG_VERBOSE:
1872 arg_verbose = true;
1873 break;
1874
38051578
LP
1875 case ARG_EXPECT_REPLY:
1876 r = parse_boolean(optarg);
1877 if (r < 0) {
1878 log_error("Failed to parse --expect-reply= parameter.");
1879 return r;
1880 }
1881
1882 arg_expect_reply = !!r;
1883 break;
1884
1885
1886 case ARG_AUTO_START:
1887 r = parse_boolean(optarg);
1888 if (r < 0) {
1889 log_error("Failed to parse --auto-start= parameter.");
1890 return r;
1891 }
1892
1893 arg_auto_start = !!r;
1894 break;
1895
1896
1897 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
1898 r = parse_boolean(optarg);
1899 if (r < 0) {
1900 log_error("Failed to parse --allow-interactive-authorization= parameter.");
1901 return r;
1902 }
1903
1904 arg_allow_interactive_authorization = !!r;
1905 break;
1906
a44b1081
LP
1907 case ARG_TIMEOUT:
1908 r = parse_sec(optarg, &arg_timeout);
1909 if (r < 0) {
1910 log_error("Failed to parse --timeout= parameter.");
1911 return r;
1912 }
1913
1914 break;
1915
40ed1a45
LP
1916 case ARG_AUGMENT_CREDS:
1917 r = parse_boolean(optarg);
1918 if (r < 0) {
1919 log_error("Failed to parse --augment-creds= parameter.");
1920 return r;
1921 }
1922
1923 arg_augment_creds = !!r;
1924 break;
1925
1f849790
LP
1926 case '?':
1927 return -EINVAL;
1928
1929 default:
eb9da376 1930 assert_not_reached("Unhandled option");
1f849790 1931 }
1f849790
LP
1932
1933 return 1;
1934}
1935
1936static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
1937 assert(bus);
1938
1939 if (optind >= argc ||
1940 streq(argv[optind], "list"))
1941 return list_bus_names(bus, argv + optind);
1942
1943 if (streq(argv[optind], "monitor"))
1f70b087
LP
1944 return monitor(bus, argv + optind, message_dump);
1945
1946 if (streq(argv[optind], "capture"))
1947 return capture(bus, argv + optind);
1f849790 1948
95c4fe82
LP
1949 if (streq(argv[optind], "status"))
1950 return status(bus, argv + optind);
d9130355
LP
1951
1952 if (streq(argv[optind], "tree"))
1953 return tree(bus, argv + optind);
781fa938 1954
0171da06
LP
1955 if (streq(argv[optind], "introspect"))
1956 return introspect(bus, argv + optind);
1957
781fa938
LP
1958 if (streq(argv[optind], "call"))
1959 return call(bus, argv + optind);
d55192ad
LP
1960
1961 if (streq(argv[optind], "get-property"))
1962 return get_property(bus, argv + optind);
95c4fe82 1963
1fc55609
LP
1964 if (streq(argv[optind], "set-property"))
1965 return set_property(bus, argv + optind);
1966
1f849790
LP
1967 if (streq(argv[optind], "help"))
1968 return help();
1969
1970 log_error("Unknown command '%s'", argv[optind]);
1971 return -EINVAL;
1972}
1973
1974int main(int argc, char *argv[]) {
03976f7b 1975 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
1f849790
LP
1976 int r;
1977
1978 log_parse_environment();
1979 log_open();
1980
1981 r = parse_argv(argc, argv);
1982 if (r <= 0)
1983 goto finish;
1984
09365592
LP
1985 r = sd_bus_new(&bus);
1986 if (r < 0) {
da927ba9 1987 log_error_errno(r, "Failed to allocate bus: %m");
09365592
LP
1988 goto finish;
1989 }
1990
1f70b087
LP
1991 if (streq_ptr(argv[optind], "monitor") ||
1992 streq_ptr(argv[optind], "capture")) {
09365592
LP
1993
1994 r = sd_bus_set_monitor(bus, true);
1f849790 1995 if (r < 0) {
da927ba9 1996 log_error_errno(r, "Failed to set monitor mode: %m");
1f849790
LP
1997 goto finish;
1998 }
d0ce7734 1999
b5dae4c7 2000 r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL);
d0ce7734 2001 if (r < 0) {
da927ba9 2002 log_error_errno(r, "Failed to enable credentials: %m");
d0ce7734
LP
2003 goto finish;
2004 }
2005
2006 r = sd_bus_negotiate_timestamp(bus, true);
2007 if (r < 0) {
da927ba9 2008 log_error_errno(r, "Failed to enable timestamps: %m");
d0ce7734
LP
2009 goto finish;
2010 }
2011
2012 r = sd_bus_negotiate_fds(bus, true);
2013 if (r < 0) {
da927ba9 2014 log_error_errno(r, "Failed to enable fds: %m");
d0ce7734
LP
2015 goto finish;
2016 }
09365592 2017 }
1f849790 2018
8d87d4a9
DH
2019 r = sd_bus_set_bus_client(bus, true);
2020 if (r < 0) {
2021 log_error_errno(r, "Failed to set bus client: %m");
2022 goto finish;
2023 }
2024
09365592 2025 if (arg_address)
1f849790 2026 r = sd_bus_set_address(bus, arg_address);
09365592
LP
2027 else {
2028 switch (arg_transport) {
1f849790 2029
09365592 2030 case BUS_TRANSPORT_LOCAL:
3acc1daf
LP
2031 if (arg_user) {
2032 bus->is_user = true;
09365592 2033 r = bus_set_address_user(bus);
3acc1daf
LP
2034 } else {
2035 bus->is_system = true;
09365592 2036 r = bus_set_address_system(bus);
3acc1daf 2037 }
09365592
LP
2038 break;
2039
2040 case BUS_TRANSPORT_REMOTE:
2041 r = bus_set_address_system_remote(bus, arg_host);
2042 break;
2043
de33fc62
LP
2044 case BUS_TRANSPORT_MACHINE:
2045 r = bus_set_address_system_machine(bus, arg_host);
09365592
LP
2046 break;
2047
2048 default:
2049 assert_not_reached("Hmm, unknown transport type.");
1f849790 2050 }
09365592
LP
2051 }
2052 if (r < 0) {
da927ba9 2053 log_error_errno(r, "Failed to set address: %m");
09365592
LP
2054 goto finish;
2055 }
1f849790 2056
09365592 2057 r = sd_bus_start(bus);
1f849790 2058 if (r < 0) {
da927ba9 2059 log_error_errno(r, "Failed to connect to bus: %m");
1f849790
LP
2060 goto finish;
2061 }
2062
2063 r = busctl_main(bus, argc, argv);
2064
2065finish:
2066 pager_close();
d75edbd6 2067
1f849790 2068 strv_free(arg_matches);
de1c301e 2069
de1c301e
LP
2070 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2071}