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