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