]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
hashmap: refactor hash_func
[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"
3f6fd1ba
LP
32#include "log.h"
33#include "pager.h"
34#include "path-util.h"
35#include "set.h"
36#include "strv.h"
288a74cc 37#include "terminal-util.h"
3f6fd1ba 38#include "util.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
1fc464f6 451 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
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();
1fc464f6 468 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
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
b826ab58 631static void member_hash_func(const void *p, struct siphash *state) {
0171da06 632 const Member *m = p;
0171da06
LP
633
634 assert(m);
635 assert(m->type);
636
b826ab58 637 string_hash_func(m->type, state);
0171da06
LP
638
639 if (m->name)
b826ab58 640 string_hash_func(m->name, state);
0171da06
LP
641
642 if (m->interface)
b826ab58 643 string_hash_func(m->interface, state);
0171da06
LP
644}
645
646static int member_compare_func(const void *a, const void *b) {
647 const Member *x = a, *y = b;
648 int d;
649
650 assert(x);
651 assert(y);
652 assert(x->type);
653 assert(y->type);
654
c030a850
NK
655 d = strcmp_ptr(x->interface, y->interface);
656 if (d != 0)
657 return d;
0171da06
LP
658
659 d = strcmp(x->type, y->type);
660 if (d != 0)
661 return d;
662
c030a850 663 return strcmp_ptr(x->name, y->name);
0171da06
LP
664}
665
666static int member_compare_funcp(const void *a, const void *b) {
667 const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b;
668
669 return member_compare_func(*x, *y);
670}
671
672static void member_free(Member *m) {
673 if (!m)
674 return;
675
676 free(m->interface);
677 free(m->name);
678 free(m->signature);
679 free(m->result);
1fc55609 680 free(m->value);
0171da06
LP
681 free(m);
682}
683
684DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
685
686static void member_set_free(Set *s) {
687 Member *m;
688
689 while ((m = set_steal_first(s)))
690 member_free(m);
691
692 set_free(s);
693}
694
695DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
696
697static int on_interface(const char *interface, uint64_t flags, void *userdata) {
698 _cleanup_(member_freep) Member *m;
699 Set *members = userdata;
700 int r;
701
702 assert(interface);
703 assert(members);
704
705 m = new0(Member, 1);
706 if (!m)
707 return log_oom();
708
709 m->type = "interface";
710 m->flags = flags;
711
712 r = free_and_strdup(&m->interface, interface);
713 if (r < 0)
714 return log_oom();
715
716 r = set_put(members, m);
717 if (r <= 0) {
718 log_error("Duplicate interface");
719 return -EINVAL;
720 }
721
722 m = NULL;
723 return 0;
724}
725
726static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
727 _cleanup_(member_freep) Member *m;
728 Set *members = userdata;
729 int r;
730
731 assert(interface);
732 assert(name);
733
734 m = new0(Member, 1);
735 if (!m)
736 return log_oom();
737
738 m->type = "method";
739 m->flags = flags;
740
741 r = free_and_strdup(&m->interface, interface);
742 if (r < 0)
743 return log_oom();
744
745 r = free_and_strdup(&m->name, name);
746 if (r < 0)
747 return log_oom();
748
749 r = free_and_strdup(&m->signature, signature);
750 if (r < 0)
751 return log_oom();
752
753 r = free_and_strdup(&m->result, result);
754 if (r < 0)
755 return log_oom();
756
757 r = set_put(members, m);
758 if (r <= 0) {
759 log_error("Duplicate method");
760 return -EINVAL;
761 }
762
763 m = NULL;
764 return 0;
765}
766
767static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
768 _cleanup_(member_freep) Member *m;
769 Set *members = userdata;
770 int r;
771
772 assert(interface);
773 assert(name);
774
775 m = new0(Member, 1);
776 if (!m)
777 return log_oom();
778
779 m->type = "signal";
780 m->flags = flags;
781
782 r = free_and_strdup(&m->interface, interface);
783 if (r < 0)
784 return log_oom();
785
786 r = free_and_strdup(&m->name, name);
787 if (r < 0)
788 return log_oom();
789
790 r = free_and_strdup(&m->signature, signature);
791 if (r < 0)
792 return log_oom();
793
794 r = set_put(members, m);
795 if (r <= 0) {
796 log_error("Duplicate signal");
797 return -EINVAL;
798 }
799
800 m = NULL;
801 return 0;
802}
803
804static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
805 _cleanup_(member_freep) Member *m;
806 Set *members = userdata;
807 int r;
808
809 assert(interface);
810 assert(name);
811
812 m = new0(Member, 1);
813 if (!m)
814 return log_oom();
815
816 m->type = "property";
817 m->flags = flags;
818 m->writable = writable;
819
820 r = free_and_strdup(&m->interface, interface);
821 if (r < 0)
822 return log_oom();
823
824 r = free_and_strdup(&m->name, name);
825 if (r < 0)
826 return log_oom();
827
828 r = free_and_strdup(&m->signature, signature);
829 if (r < 0)
830 return log_oom();
831
832 r = set_put(members, m);
833 if (r <= 0) {
834 log_error("Duplicate property");
835 return -EINVAL;
836 }
837
838 m = NULL;
839 return 0;
840}
841
842static const char *strdash(const char *x) {
843 return isempty(x) ? "-" : x;
844}
845
846static int introspect(sd_bus *bus, char **argv) {
847 static const struct hash_ops member_hash_ops = {
848 .hash = member_hash_func,
849 .compare = member_compare_func,
850 };
851
852 static const XMLIntrospectOps ops = {
853 .on_interface = on_interface,
854 .on_method = on_method,
855 .on_signal = on_signal,
856 .on_property = on_property,
857 };
858
859 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
860 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
861 _cleanup_(member_set_freep) Set *members = NULL;
862 Iterator i;
863 Member *m;
864 const char *xml;
865 int r;
866 unsigned name_width, type_width, signature_width, result_width;
867 Member **sorted = NULL;
4f44c03e 868 unsigned k = 0, j, n_args;
0171da06 869
4f44c03e
LP
870 n_args = strv_length(argv);
871 if (n_args < 3) {
0171da06
LP
872 log_error("Requires service and object path argument.");
873 return -EINVAL;
874 }
875
4f44c03e
LP
876 if (n_args > 4) {
877 log_error("Too many arguments.");
878 return -EINVAL;
879 }
880
0171da06
LP
881 members = set_new(&member_hash_ops);
882 if (!members)
883 return log_oom();
884
885 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
886 if (r < 0) {
887 log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
888 return r;
889 }
890
891 r = sd_bus_message_read(reply, "s", &xml);
892 if (r < 0)
893 return bus_log_parse_error(r);
894
1fc55609 895 /* First, get list of all properties */
0171da06
LP
896 r = parse_xml_introspect(argv[2], xml, &ops, members);
897 if (r < 0)
898 return r;
899
1fc55609
LP
900 /* Second, find the current values for them */
901 SET_FOREACH(m, members, i) {
902
903 if (!streq(m->type, "property"))
904 continue;
905
906 if (m->value)
907 continue;
908
4f44c03e
LP
909 if (argv[3] && !streq(argv[3], m->interface))
910 continue;
911
1fc55609
LP
912 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
913 if (r < 0) {
914 log_error("%s", bus_error_message(&error, r));
915 return r;
916 }
917
918 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
919 if (r < 0)
920 return bus_log_parse_error(r);
921
922 for (;;) {
923 Member *z;
924 _cleanup_free_ char *buf = NULL;
925 _cleanup_fclose_ FILE *mf = NULL;
926 size_t sz = 0;
927 const char *name;
928
929 r = sd_bus_message_enter_container(reply, 'e', "sv");
930 if (r < 0)
931 return bus_log_parse_error(r);
932
933 if (r == 0)
934 break;
935
936 r = sd_bus_message_read(reply, "s", &name);
937 if (r < 0)
938 return bus_log_parse_error(r);
939
940 r = sd_bus_message_enter_container(reply, 'v', NULL);
941 if (r < 0)
942 return bus_log_parse_error(r);
943
944 mf = open_memstream(&buf, &sz);
945 if (!mf)
946 return log_oom();
947
948 r = format_cmdline(reply, mf, false);
949 if (r < 0)
950 return bus_log_parse_error(r);
951
952 fclose(mf);
953 mf = NULL;
954
955 z = set_get(members, &((Member) {
956 .type = "property",
957 .interface = m->interface,
958 .name = (char*) name }));
959 if (z) {
960 free(z->value);
961 z->value = buf;
962 buf = NULL;
963 }
964
965 r = sd_bus_message_exit_container(reply);
966 if (r < 0)
967 return bus_log_parse_error(r);
968
969 r = sd_bus_message_exit_container(reply);
970 if (r < 0)
971 return bus_log_parse_error(r);
972 }
973
974 r = sd_bus_message_exit_container(reply);
975 if (r < 0)
976 return bus_log_parse_error(r);
977 }
978
0171da06
LP
979 pager_open_if_enabled();
980
981 name_width = strlen("NAME");
982 type_width = strlen("TYPE");
983 signature_width = strlen("SIGNATURE");
1fc55609 984 result_width = strlen("RESULT/VALUE");
0171da06
LP
985
986 sorted = newa(Member*, set_size(members));
987
988 SET_FOREACH(m, members, i) {
4f44c03e
LP
989
990 if (argv[3] && !streq(argv[3], m->interface))
991 continue;
992
0171da06
LP
993 if (m->interface)
994 name_width = MAX(name_width, strlen(m->interface));
995 if (m->name)
996 name_width = MAX(name_width, strlen(m->name) + 1);
997 if (m->type)
998 type_width = MAX(type_width, strlen(m->type));
999 if (m->signature)
1000 signature_width = MAX(signature_width, strlen(m->signature));
1001 if (m->result)
1002 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1003 if (m->value)
1004 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1005
1006 sorted[k++] = m;
1007 }
1008
1fc55609
LP
1009 if (result_width > 40)
1010 result_width = 40;
1011
0171da06
LP
1012 qsort(sorted, k, sizeof(Member*), member_compare_funcp);
1013
1fc55609
LP
1014 if (arg_legend) {
1015 printf("%-*s %-*s %-*s %-*s %s\n",
1016 (int) name_width, "NAME",
1017 (int) type_width, "TYPE",
1018 (int) signature_width, "SIGNATURE",
1019 (int) result_width, "RESULT/VALUE",
1020 "FLAGS");
1021 }
0171da06
LP
1022
1023 for (j = 0; j < k; j++) {
1fc55609
LP
1024 _cleanup_free_ char *ellipsized = NULL;
1025 const char *rv;
0171da06
LP
1026 bool is_interface;
1027
1028 m = sorted[j];
1029
4f44c03e
LP
1030 if (argv[3] && !streq(argv[3], m->interface))
1031 continue;
1032
0171da06
LP
1033 is_interface = streq(m->type, "interface");
1034
4f44c03e
LP
1035 if (argv[3] && is_interface)
1036 continue;
1037
1fc55609
LP
1038 if (m->value) {
1039 ellipsized = ellipsize(m->value, result_width, 100);
1040 if (!ellipsized)
1041 return log_oom();
1042
1043 rv = ellipsized;
1044 } else
1045 rv = strdash(m->result);
1046
0171da06
LP
1047 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1048 is_interface ? ansi_highlight() : "",
1049 is_interface ? "" : ".",
1050 - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1fc464f6 1051 is_interface ? ansi_normal() : "",
0171da06
LP
1052 (int) type_width, strdash(m->type),
1053 (int) signature_width, strdash(m->signature),
1fc55609 1054 (int) result_width, rv,
0171da06
LP
1055 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1056 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1057 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1058 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1059 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1060 m->writable ? " writable" : "");
1061 }
1062
1063 return 0;
1064}
1065
1f70b087 1066static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 1067 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1068}
1069
1070static int message_pcap(sd_bus_message *m, FILE *f) {
1071 return bus_message_pcap_frame(m, arg_snaplen, f);
1072}
1073
1074static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
b51f299a 1075 bool added_something = false;
1f849790
LP
1076 char **i;
1077 int r;
1078
1079 STRV_FOREACH(i, argv+1) {
1080 _cleanup_free_ char *m = NULL;
1081
1082 if (!service_name_is_valid(*i)) {
1083 log_error("Invalid service name '%s'", *i);
1084 return -EINVAL;
1085 }
1086
1087 m = strjoin("sender='", *i, "'", NULL);
1088 if (!m)
1089 return log_oom();
1090
19befb2d 1091 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
23bbb0de
MS
1092 if (r < 0)
1093 return log_error_errno(r, "Failed to add match: %m");
b51f299a 1094
f6d1e6cb
LU
1095 free(m);
1096 m = strjoin("destination='", *i, "'", NULL);
1097 if (!m)
1098 return log_oom();
1099
1100 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
1101 if (r < 0)
1102 return log_error_errno(r, "Failed to add match: %m");
1103
b51f299a 1104 added_something = true;
1f849790
LP
1105 }
1106
1107 STRV_FOREACH(i, arg_matches) {
19befb2d 1108 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
23bbb0de
MS
1109 if (r < 0)
1110 return log_error_errno(r, "Failed to add match: %m");
b51f299a
LP
1111
1112 added_something = true;
1113 }
1114
1115 if (!added_something) {
19befb2d 1116 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
23bbb0de
MS
1117 if (r < 0)
1118 return log_error_errno(r, "Failed to add match: %m");
1f849790
LP
1119 }
1120
92d66625
LP
1121 log_info("Monitoring bus message stream.");
1122
1f849790
LP
1123 for (;;) {
1124 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1125
1126 r = sd_bus_process(bus, &m);
23bbb0de
MS
1127 if (r < 0)
1128 return log_error_errno(r, "Failed to process bus: %m");
1f849790
LP
1129
1130 if (m) {
1f70b087 1131 dump(m, stdout);
1d44f758 1132 fflush(stdout);
92d66625
LP
1133
1134 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
1135 log_info("Connection terminated, exiting.");
1136 return 0;
1137 }
1138
1f849790
LP
1139 continue;
1140 }
1141
1142 if (r > 0)
1143 continue;
1144
1145 r = sd_bus_wait(bus, (uint64_t) -1);
23bbb0de
MS
1146 if (r < 0)
1147 return log_error_errno(r, "Failed to wait for bus: %m");
1f849790 1148 }
95c4fe82 1149}
1f849790 1150
1f70b087
LP
1151static int capture(sd_bus *bus, char *argv[]) {
1152 int r;
1153
1154 if (isatty(fileno(stdout)) > 0) {
1155 log_error("Refusing to write message data to console, please redirect output to a file.");
1156 return -EINVAL;
1157 }
1158
1159 bus_pcap_header(arg_snaplen, stdout);
1160
1161 r = monitor(bus, argv, message_pcap);
1162 if (r < 0)
1163 return r;
1164
1165 if (ferror(stdout)) {
1166 log_error("Couldn't write capture file.");
1167 return -EIO;
1168 }
1169
1170 return r;
1171}
1172
95c4fe82
LP
1173static int status(sd_bus *bus, char *argv[]) {
1174 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
1175 pid_t pid;
1176 int r;
1177
1178 assert(bus);
1179
2e9efd22
LP
1180 if (strv_length(argv) > 2) {
1181 log_error("Expects no or one argument.");
95c4fe82
LP
1182 return -EINVAL;
1183 }
1184
2e9efd22
LP
1185 if (argv[1]) {
1186 r = parse_pid(argv[1], &pid);
1187 if (r < 0)
1188 r = sd_bus_get_name_creds(
1189 bus,
1190 argv[1],
1191 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1192 &creds);
1193 else
1194 r = sd_bus_creds_new_from_pid(
1195 &creds,
1196 pid,
1197 _SD_BUS_CREDS_ALL);
3acc1daf 1198 } else {
5b820358 1199 const char *scope, *address;
5c302692 1200 sd_id128_t bus_id;
3acc1daf 1201
5b820358
LP
1202 r = sd_bus_get_address(bus, &address);
1203 if (r >= 0)
1fc464f6 1204 printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal());
5b820358 1205
3acc1daf
LP
1206 r = sd_bus_get_scope(bus, &scope);
1207 if (r >= 0)
1fc464f6 1208 printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal());
3acc1daf 1209
5c302692
LP
1210 r = sd_bus_get_bus_id(bus, &bus_id);
1211 if (r >= 0)
1fc464f6 1212 printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
5c302692 1213
2e9efd22 1214 r = sd_bus_get_owner_creds(
40ed1a45 1215 bus,
40ed1a45
LP
1216 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1217 &creds);
3acc1daf 1218 }
95c4fe82 1219
23bbb0de
MS
1220 if (r < 0)
1221 return log_error_errno(r, "Failed to get credentials: %m");
95c4fe82 1222
d0b2babf 1223 bus_creds_dump(creds, NULL, false);
95c4fe82 1224 return 0;
1f849790
LP
1225}
1226
781fa938
LP
1227static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1228 char **p;
1229 int r;
1230
1231 assert(m);
1232 assert(signature);
1233 assert(x);
1234
1235 p = *x;
1236
1237 for (;;) {
1238 const char *v;
1239 char t;
1240
1241 t = *signature;
1242 v = *p;
1243
1244 if (t == 0)
1245 break;
1246 if (!v) {
1247 log_error("Too few parameters for signature.");
1248 return -EINVAL;
1249 }
1250
1251 signature++;
1252 p++;
1253
1254 switch (t) {
1255
1256 case SD_BUS_TYPE_BOOLEAN:
1257
1258 r = parse_boolean(v);
1259 if (r < 0) {
1260 log_error("Failed to parse as boolean: %s", v);
1261 return r;
1262 }
1263
1264 r = sd_bus_message_append_basic(m, t, &r);
1265 break;
1266
1267 case SD_BUS_TYPE_BYTE: {
1268 uint8_t z;
1269
1270 r = safe_atou8(v, &z);
1271 if (r < 0) {
1272 log_error("Failed to parse as byte (unsigned 8bit integer): %s", v);
1273 return r;
1274 }
1275
1276 r = sd_bus_message_append_basic(m, t, &z);
1277 break;
1278 }
1279
1280 case SD_BUS_TYPE_INT16: {
1281 int16_t z;
1282
1283 r = safe_atoi16(v, &z);
1284 if (r < 0) {
1285 log_error("Failed to parse as signed 16bit integer: %s", v);
1286 return r;
1287 }
1288
1289 r = sd_bus_message_append_basic(m, t, &z);
1290 break;
1291 }
1292
1293 case SD_BUS_TYPE_UINT16: {
1294 uint16_t z;
1295
1296 r = safe_atou16(v, &z);
1297 if (r < 0) {
1298 log_error("Failed to parse as unsigned 16bit integer: %s", v);
1299 return r;
1300 }
1301
1302 r = sd_bus_message_append_basic(m, t, &z);
1303 break;
1304 }
1305
1306 case SD_BUS_TYPE_INT32: {
1307 int32_t z;
1308
1309 r = safe_atoi32(v, &z);
1310 if (r < 0) {
1311 log_error("Failed to parse as signed 32bit integer: %s", v);
1312 return r;
1313 }
1314
1315 r = sd_bus_message_append_basic(m, t, &z);
1316 break;
1317 }
1318
1319 case SD_BUS_TYPE_UINT32: {
1320 uint32_t z;
1321
1322 r = safe_atou32(v, &z);
1323 if (r < 0) {
1324 log_error("Failed to parse as unsigned 32bit integer: %s", v);
1325 return r;
1326 }
1327
1328 r = sd_bus_message_append_basic(m, t, &z);
1329 break;
1330 }
1331
1332 case SD_BUS_TYPE_INT64: {
1333 int64_t z;
1334
1335 r = safe_atoi64(v, &z);
1336 if (r < 0) {
1337 log_error("Failed to parse as signed 64bit integer: %s", v);
1338 return r;
1339 }
1340
1341 r = sd_bus_message_append_basic(m, t, &z);
1342 break;
1343 }
1344
1345 case SD_BUS_TYPE_UINT64: {
1346 uint64_t z;
1347
1348 r = safe_atou64(v, &z);
1349 if (r < 0) {
1350 log_error("Failed to parse as unsigned 64bit integer: %s", v);
1351 return r;
1352 }
1353
1354 r = sd_bus_message_append_basic(m, t, &z);
1355 break;
1356 }
1357
1358
1359 case SD_BUS_TYPE_DOUBLE: {
1360 double z;
1361
1362 r = safe_atod(v, &z);
1363 if (r < 0) {
1364 log_error("Failed to parse as double precision floating point: %s", v);
1365 return r;
1366 }
1367
1368 r = sd_bus_message_append_basic(m, t, &z);
1369 break;
1370 }
1371
1372 case SD_BUS_TYPE_STRING:
1373 case SD_BUS_TYPE_OBJECT_PATH:
1374 case SD_BUS_TYPE_SIGNATURE:
1375
1376 r = sd_bus_message_append_basic(m, t, v);
1377 break;
1378
1379 case SD_BUS_TYPE_ARRAY: {
1380 uint32_t n;
1381 size_t k;
1382
1383 r = safe_atou32(v, &n);
1384 if (r < 0) {
1385 log_error("Failed to parse number of array entries: %s", v);
1386 return r;
1387 }
1388
1389 r = signature_element_length(signature, &k);
1390 if (r < 0) {
1391 log_error("Invalid array signature.");
1392 return r;
1393 }
1394
1395 {
1396 unsigned i;
1397 char s[k + 1];
1398 memcpy(s, signature, k);
1399 s[k] = 0;
1400
1401 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1402 if (r < 0)
1403 return bus_log_create_error(r);
1404
1405 for (i = 0; i < n; i++) {
1406 r = message_append_cmdline(m, s, &p);
1407 if (r < 0)
1408 return r;
1409 }
1410 }
1411
1412 signature += k;
1413
1414 r = sd_bus_message_close_container(m);
1415 break;
1416 }
1417
1418 case SD_BUS_TYPE_VARIANT:
1419 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1420 if (r < 0)
1421 return bus_log_create_error(r);
1422
1423 r = message_append_cmdline(m, v, &p);
1424 if (r < 0)
1425 return r;
1426
1427 r = sd_bus_message_close_container(m);
1428 break;
1429
1430 case SD_BUS_TYPE_STRUCT_BEGIN:
1431 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1432 size_t k;
1433
1434 signature--;
1435 p--;
1436
1437 r = signature_element_length(signature, &k);
1438 if (r < 0) {
1439 log_error("Invalid struct/dict entry signature.");
1440 return r;
1441 }
1442
1443 {
1444 char s[k-1];
1445 memcpy(s, signature + 1, k - 2);
1446 s[k - 2] = 0;
1447
1448 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1449 if (r < 0)
1450 return bus_log_create_error(r);
1451
1452 r = message_append_cmdline(m, s, &p);
1453 if (r < 0)
1454 return r;
1455 }
1456
1457 signature += k;
1458
1459 r = sd_bus_message_close_container(m);
1460 break;
1461 }
1462
1463 case SD_BUS_TYPE_UNIX_FD:
1464 log_error("UNIX file descriptor not supported as type.");
1465 return -EINVAL;
1466
1467 default:
1468 log_error("Unknown signature type %c.", t);
1469 return -EINVAL;
1470 }
1471
1472 if (r < 0)
1473 return bus_log_create_error(r);
1474 }
1475
1476 *x = p;
1477 return 0;
1478}
1479
1480static int call(sd_bus *bus, char *argv[]) {
1481 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1482 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1483 int r;
1484
1485 assert(bus);
1486
1487 if (strv_length(argv) < 5) {
1488 log_error("Expects at least four arguments.");
1489 return -EINVAL;
1490 }
1491
1492 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
1493 if (r < 0)
1494 return bus_log_create_error(r);
781fa938 1495
38051578
LP
1496 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
1497 if (r < 0)
1498 return bus_log_create_error(r);
1499
1500 r = sd_bus_message_set_auto_start(m, arg_auto_start);
1501 if (r < 0)
1502 return bus_log_create_error(r);
1503
1504 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
1505 if (r < 0)
1506 return bus_log_create_error(r);
1507
781fa938
LP
1508 if (!isempty(argv[5])) {
1509 char **p;
1510
1511 p = argv+6;
1512
1513 r = message_append_cmdline(m, argv[5], &p);
1514 if (r < 0)
1515 return r;
1516
1517 if (*p) {
1518 log_error("Too many parameters for signature.");
1519 return -EINVAL;
1520 }
1521 }
1522
38051578
LP
1523 if (!arg_expect_reply) {
1524 r = sd_bus_send(bus, m, NULL);
1525 if (r < 0) {
1526 log_error("Failed to send message.");
1527 return r;
1528 }
1529
1530 return 0;
1531 }
1532
a44b1081 1533 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
781fa938
LP
1534 if (r < 0) {
1535 log_error("%s", bus_error_message(&error, r));
1536 return r;
1537 }
1538
1539 r = sd_bus_message_is_empty(reply);
1540 if (r < 0)
1541 return bus_log_parse_error(r);
1fc55609 1542
781fa938 1543 if (r == 0 && !arg_quiet) {
1fc55609
LP
1544
1545 if (arg_verbose) {
1546 pager_open_if_enabled();
1547
1548 r = bus_message_dump(reply, stdout, 0);
1549 if (r < 0)
1550 return r;
1551 } else {
1552
1553 fputs(sd_bus_message_get_signature(reply, true), stdout);
1554 fputc(' ', stdout);
1555
1556 r = format_cmdline(reply, stdout, false);
1557 if (r < 0)
1558 return bus_log_parse_error(r);
1559
1560 fputc('\n', stdout);
1561 }
d55192ad
LP
1562 }
1563
1564 return 0;
1565}
1566
1567static int get_property(sd_bus *bus, char *argv[]) {
1568 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1569 unsigned n;
1fc55609 1570 char **i;
d55192ad
LP
1571 int r;
1572
1573 assert(bus);
1574
1575 n = strv_length(argv);
1fc55609
LP
1576 if (n < 5) {
1577 log_error("Expects at least four arguments.");
d55192ad
LP
1578 return -EINVAL;
1579 }
1580
1fc55609 1581 STRV_FOREACH(i, argv + 4) {
d55192ad 1582 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1fc55609
LP
1583 const char *contents = NULL;
1584 char type;
d55192ad 1585
1fc55609 1586 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
d55192ad
LP
1587 if (r < 0) {
1588 log_error("%s", bus_error_message(&error, r));
1589 return r;
1590 }
1591
1fc55609 1592 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
1593 if (r < 0)
1594 return bus_log_parse_error(r);
1595
1fc55609
LP
1596 r = sd_bus_message_enter_container(reply, 'v', contents);
1597 if (r < 0)
1598 return bus_log_parse_error(r);
d55192ad 1599
1fc55609 1600 if (arg_verbose) {
d55192ad 1601 pager_open_if_enabled();
d55192ad 1602
1fc55609 1603 r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 1604 if (r < 0)
1fc55609
LP
1605 return r;
1606 } else {
1607 fputs(contents, stdout);
1608 fputc(' ', stdout);
d55192ad 1609
1fc55609 1610 r = format_cmdline(reply, stdout, false);
d55192ad
LP
1611 if (r < 0)
1612 return bus_log_parse_error(r);
1613
1fc55609 1614 fputc('\n', stdout);
d55192ad
LP
1615 }
1616
1617 r = sd_bus_message_exit_container(reply);
1618 if (r < 0)
1619 return bus_log_parse_error(r);
1fc55609 1620 }
d55192ad 1621
1fc55609
LP
1622 return 0;
1623}
d55192ad 1624
1fc55609
LP
1625static int set_property(sd_bus *bus, char *argv[]) {
1626 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1627 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1628 unsigned n;
1629 char **p;
1630 int r;
d55192ad 1631
1fc55609 1632 assert(bus);
d55192ad 1633
1fc55609
LP
1634 n = strv_length(argv);
1635 if (n < 6) {
1636 log_error("Expects at least five arguments.");
1637 return -EINVAL;
1638 }
d55192ad 1639
1fc55609
LP
1640 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
1641 if (r < 0)
1642 return bus_log_create_error(r);
d55192ad 1643
1fc55609
LP
1644 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
1645 if (r < 0)
1646 return bus_log_create_error(r);
d55192ad 1647
1fc55609
LP
1648 r = sd_bus_message_open_container(m, 'v', argv[5]);
1649 if (r < 0)
1650 return bus_log_create_error(r);
1651
1652 p = argv+6;
1653 r = message_append_cmdline(m, argv[5], &p);
1654 if (r < 0)
1655 return r;
1656
1657 r = sd_bus_message_close_container(m);
1658 if (r < 0)
1659 return bus_log_create_error(r);
1660
1661 if (*p) {
1662 log_error("Too many parameters for signature.");
1663 return -EINVAL;
1664 }
1665
a44b1081 1666 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
1fc55609
LP
1667 if (r < 0) {
1668 log_error("%s", bus_error_message(&error, r));
1669 return r;
781fa938
LP
1670 }
1671
1672 return 0;
1673}
1674
1f849790 1675static int help(void) {
1f849790
LP
1676 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1677 "Introspect the bus.\n\n"
d75edbd6
LP
1678 " -h --help Show this help\n"
1679 " --version Show package version\n"
a86a47ce 1680 " --no-pager Do not pipe output into a pager\n"
17d47d8d 1681 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
1682 " --system Connect to system bus\n"
1683 " --user Connect to user bus\n"
1684 " -H --host=[USER@]HOST Operate on remote host\n"
1685 " -M --machine=CONTAINER Operate on local container\n"
1686 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
1687 " --show-machine Show machine ID column in list\n"
1688 " --unique Only show unique names\n"
1689 " --acquired Only show acquired names\n"
1690 " --activatable Only show activatable names\n"
d9130355 1691 " --match=MATCH Only show matching messages\n"
d28ebe27 1692 " --size=SIZE Maximum length of captured packet\n"
781fa938 1693 " --list Don't show tree, but simple object path list\n"
1fc55609 1694 " --quiet Don't show method call reply\n"
38051578
LP
1695 " --verbose Show result values in long format\n"
1696 " --expect-reply=BOOL Expect a method call reply\n"
1697 " --auto-start=BOOL Auto-start destination service\n"
1698 " --allow-interactive-authorization=BOOL\n"
a44b1081 1699 " Allow interactive authorization for operation\n"
40ed1a45
LP
1700 " --timeout=SECS Maximum time to wait for method call completion\n"
1701 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n"
1f849790 1702 "Commands:\n"
d75edbd6 1703 " list List bus names\n"
2e9efd22 1704 " status [SERVICE] Show bus service, process or bus owner credentials\n"
d94fe1f1 1705 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1706 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 1707 " tree [SERVICE...] Show object tree of service\n"
4f44c03e 1708 " introspect SERVICE OBJECT [INTERFACE]\n"
0171da06 1709 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 1710 " Call a method\n"
1fc55609 1711 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 1712 " Get property value\n"
1fc55609
LP
1713 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
1714 " Set property value\n"
601185b4
ZJS
1715 " help Show this help\n"
1716 , program_invocation_short_name);
1f849790
LP
1717
1718 return 0;
1719}
1720
1721static int parse_argv(int argc, char *argv[]) {
1722
1723 enum {
1724 ARG_VERSION = 0x100,
1725 ARG_NO_PAGER,
17d47d8d 1726 ARG_NO_LEGEND,
1f849790
LP
1727 ARG_SYSTEM,
1728 ARG_USER,
1729 ARG_ADDRESS,
1730 ARG_MATCH,
56e61788
LP
1731 ARG_SHOW_MACHINE,
1732 ARG_UNIQUE,
1733 ARG_ACQUIRED,
1f70b087
LP
1734 ARG_ACTIVATABLE,
1735 ARG_SIZE,
d9130355 1736 ARG_LIST,
1fc55609 1737 ARG_VERBOSE,
38051578
LP
1738 ARG_EXPECT_REPLY,
1739 ARG_AUTO_START,
1740 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 1741 ARG_TIMEOUT,
40ed1a45 1742 ARG_AUGMENT_CREDS,
1f849790
LP
1743 };
1744
1745 static const struct option options[] = {
56e61788
LP
1746 { "help", no_argument, NULL, 'h' },
1747 { "version", no_argument, NULL, ARG_VERSION },
1748 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1749 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1750 { "system", no_argument, NULL, ARG_SYSTEM },
1751 { "user", no_argument, NULL, ARG_USER },
1752 { "address", required_argument, NULL, ARG_ADDRESS },
1753 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1754 { "unique", no_argument, NULL, ARG_UNIQUE },
1755 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1756 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1757 { "match", required_argument, NULL, ARG_MATCH },
1758 { "host", required_argument, NULL, 'H' },
1759 { "machine", required_argument, NULL, 'M' },
1f70b087 1760 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1761 { "list", no_argument, NULL, ARG_LIST },
781fa938 1762 { "quiet", no_argument, NULL, 'q' },
1fc55609 1763 { "verbose", no_argument, NULL, ARG_VERBOSE },
38051578
LP
1764 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
1765 { "auto-start", required_argument, NULL, ARG_AUTO_START },
1766 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
a44b1081 1767 { "timeout", required_argument, NULL, ARG_TIMEOUT },
40ed1a45 1768 { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS},
eb9da376 1769 {},
1f849790
LP
1770 };
1771
1f70b087 1772 int c, r;
1f849790
LP
1773
1774 assert(argc >= 0);
1775 assert(argv);
1776
781fa938 1777 while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
1f849790
LP
1778
1779 switch (c) {
1780
1781 case 'h':
1782 return help();
1783
1784 case ARG_VERSION:
3f6fd1ba 1785 return version();
1f849790
LP
1786
1787 case ARG_NO_PAGER:
1788 arg_no_pager = true;
1789 break;
1790
17d47d8d
TA
1791 case ARG_NO_LEGEND:
1792 arg_legend = false;
1793 break;
1794
1f849790
LP
1795 case ARG_USER:
1796 arg_user = true;
1797 break;
1798
1799 case ARG_SYSTEM:
1800 arg_user = false;
1801 break;
1802
1803 case ARG_ADDRESS:
1804 arg_address = optarg;
1805 break;
1806
56e61788
LP
1807 case ARG_SHOW_MACHINE:
1808 arg_show_machine = true;
1809 break;
1810
1811 case ARG_UNIQUE:
1812 arg_unique = true;
1f849790
LP
1813 break;
1814
56e61788
LP
1815 case ARG_ACQUIRED:
1816 arg_acquired = true;
1817 break;
1818
1819 case ARG_ACTIVATABLE:
1820 arg_activatable = true;
a4297f08
LP
1821 break;
1822
1f849790
LP
1823 case ARG_MATCH:
1824 if (strv_extend(&arg_matches, optarg) < 0)
1825 return log_oom();
1826 break;
1827
1f70b087 1828 case ARG_SIZE: {
59f448cf 1829 uint64_t sz;
1f70b087 1830
59f448cf 1831 r = parse_size(optarg, 1024, &sz);
1f70b087
LP
1832 if (r < 0) {
1833 log_error("Failed to parse size: %s", optarg);
1834 return r;
1835 }
1836
59f448cf 1837 if ((uint64_t) (size_t) sz != sz) {
1f70b087
LP
1838 log_error("Size out of range.");
1839 return -E2BIG;
1840 }
1841
59f448cf 1842 arg_snaplen = (size_t) sz;
1f70b087
LP
1843 break;
1844 }
1845
d9130355
LP
1846 case ARG_LIST:
1847 arg_list = true;
1848 break;
1849
d75edbd6
LP
1850 case 'H':
1851 arg_transport = BUS_TRANSPORT_REMOTE;
1852 arg_host = optarg;
1853 break;
1854
1855 case 'M':
de33fc62 1856 arg_transport = BUS_TRANSPORT_MACHINE;
d75edbd6
LP
1857 arg_host = optarg;
1858 break;
1859
781fa938
LP
1860 case 'q':
1861 arg_quiet = true;
1862 break;
1863
1fc55609
LP
1864 case ARG_VERBOSE:
1865 arg_verbose = true;
1866 break;
1867
38051578
LP
1868 case ARG_EXPECT_REPLY:
1869 r = parse_boolean(optarg);
1870 if (r < 0) {
1871 log_error("Failed to parse --expect-reply= parameter.");
1872 return r;
1873 }
1874
1875 arg_expect_reply = !!r;
1876 break;
1877
1878
1879 case ARG_AUTO_START:
1880 r = parse_boolean(optarg);
1881 if (r < 0) {
1882 log_error("Failed to parse --auto-start= parameter.");
1883 return r;
1884 }
1885
1886 arg_auto_start = !!r;
1887 break;
1888
1889
1890 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
1891 r = parse_boolean(optarg);
1892 if (r < 0) {
1893 log_error("Failed to parse --allow-interactive-authorization= parameter.");
1894 return r;
1895 }
1896
1897 arg_allow_interactive_authorization = !!r;
1898 break;
1899
a44b1081
LP
1900 case ARG_TIMEOUT:
1901 r = parse_sec(optarg, &arg_timeout);
1902 if (r < 0) {
1903 log_error("Failed to parse --timeout= parameter.");
1904 return r;
1905 }
1906
1907 break;
1908
40ed1a45
LP
1909 case ARG_AUGMENT_CREDS:
1910 r = parse_boolean(optarg);
1911 if (r < 0) {
1912 log_error("Failed to parse --augment-creds= parameter.");
1913 return r;
1914 }
1915
1916 arg_augment_creds = !!r;
1917 break;
1918
1f849790
LP
1919 case '?':
1920 return -EINVAL;
1921
1922 default:
eb9da376 1923 assert_not_reached("Unhandled option");
1f849790 1924 }
1f849790
LP
1925
1926 return 1;
1927}
1928
1929static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
1930 assert(bus);
1931
1932 if (optind >= argc ||
1933 streq(argv[optind], "list"))
1934 return list_bus_names(bus, argv + optind);
1935
1936 if (streq(argv[optind], "monitor"))
1f70b087
LP
1937 return monitor(bus, argv + optind, message_dump);
1938
1939 if (streq(argv[optind], "capture"))
1940 return capture(bus, argv + optind);
1f849790 1941
95c4fe82
LP
1942 if (streq(argv[optind], "status"))
1943 return status(bus, argv + optind);
d9130355
LP
1944
1945 if (streq(argv[optind], "tree"))
1946 return tree(bus, argv + optind);
781fa938 1947
0171da06
LP
1948 if (streq(argv[optind], "introspect"))
1949 return introspect(bus, argv + optind);
1950
781fa938
LP
1951 if (streq(argv[optind], "call"))
1952 return call(bus, argv + optind);
d55192ad
LP
1953
1954 if (streq(argv[optind], "get-property"))
1955 return get_property(bus, argv + optind);
95c4fe82 1956
1fc55609
LP
1957 if (streq(argv[optind], "set-property"))
1958 return set_property(bus, argv + optind);
1959
1f849790
LP
1960 if (streq(argv[optind], "help"))
1961 return help();
1962
1963 log_error("Unknown command '%s'", argv[optind]);
1964 return -EINVAL;
1965}
1966
1967int main(int argc, char *argv[]) {
03976f7b 1968 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
1f849790
LP
1969 int r;
1970
1971 log_parse_environment();
1972 log_open();
1973
1974 r = parse_argv(argc, argv);
1975 if (r <= 0)
1976 goto finish;
1977
09365592
LP
1978 r = sd_bus_new(&bus);
1979 if (r < 0) {
da927ba9 1980 log_error_errno(r, "Failed to allocate bus: %m");
09365592
LP
1981 goto finish;
1982 }
1983
1f70b087
LP
1984 if (streq_ptr(argv[optind], "monitor") ||
1985 streq_ptr(argv[optind], "capture")) {
09365592
LP
1986
1987 r = sd_bus_set_monitor(bus, true);
1f849790 1988 if (r < 0) {
da927ba9 1989 log_error_errno(r, "Failed to set monitor mode: %m");
1f849790
LP
1990 goto finish;
1991 }
d0ce7734 1992
b5dae4c7 1993 r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL);
d0ce7734 1994 if (r < 0) {
da927ba9 1995 log_error_errno(r, "Failed to enable credentials: %m");
d0ce7734
LP
1996 goto finish;
1997 }
1998
1999 r = sd_bus_negotiate_timestamp(bus, true);
2000 if (r < 0) {
da927ba9 2001 log_error_errno(r, "Failed to enable timestamps: %m");
d0ce7734
LP
2002 goto finish;
2003 }
2004
2005 r = sd_bus_negotiate_fds(bus, true);
2006 if (r < 0) {
da927ba9 2007 log_error_errno(r, "Failed to enable fds: %m");
d0ce7734
LP
2008 goto finish;
2009 }
09365592 2010 }
1f849790 2011
8d87d4a9
DH
2012 r = sd_bus_set_bus_client(bus, true);
2013 if (r < 0) {
2014 log_error_errno(r, "Failed to set bus client: %m");
2015 goto finish;
2016 }
2017
09365592 2018 if (arg_address)
1f849790 2019 r = sd_bus_set_address(bus, arg_address);
09365592
LP
2020 else {
2021 switch (arg_transport) {
1f849790 2022
09365592 2023 case BUS_TRANSPORT_LOCAL:
3acc1daf
LP
2024 if (arg_user) {
2025 bus->is_user = true;
09365592 2026 r = bus_set_address_user(bus);
3acc1daf
LP
2027 } else {
2028 bus->is_system = true;
09365592 2029 r = bus_set_address_system(bus);
3acc1daf 2030 }
09365592
LP
2031 break;
2032
2033 case BUS_TRANSPORT_REMOTE:
2034 r = bus_set_address_system_remote(bus, arg_host);
2035 break;
2036
de33fc62
LP
2037 case BUS_TRANSPORT_MACHINE:
2038 r = bus_set_address_system_machine(bus, arg_host);
09365592
LP
2039 break;
2040
2041 default:
2042 assert_not_reached("Hmm, unknown transport type.");
1f849790 2043 }
09365592
LP
2044 }
2045 if (r < 0) {
da927ba9 2046 log_error_errno(r, "Failed to set address: %m");
09365592
LP
2047 goto finish;
2048 }
1f849790 2049
09365592 2050 r = sd_bus_start(bus);
1f849790 2051 if (r < 0) {
da927ba9 2052 log_error_errno(r, "Failed to connect to bus: %m");
1f849790
LP
2053 goto finish;
2054 }
2055
2056 r = busctl_main(bus, argc, argv);
2057
2058finish:
2059 pager_close();
d75edbd6 2060
1f849790 2061 strv_free(arg_matches);
de1c301e 2062
de1c301e
LP
2063 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2064}