]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
sd-bus: when we synthesize messages, initialize timestamps ourselves
[thirdparty/systemd.git] / src / libsystemd / sd-bus / busctl.c
CommitLineData
de1c301e
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
1f849790
LP
22#include <getopt.h>
23
89ffcd2a
LP
24#include "strv.h"
25#include "util.h"
26#include "log.h"
1f849790
LP
27#include "build.h"
28#include "pager.h"
d9130355
LP
29#include "xml.h"
30#include "path-util.h"
3c70e3bb 31#include "set.h"
89ffcd2a 32
de1c301e 33#include "sd-bus.h"
89ffcd2a
LP
34#include "bus-message.h"
35#include "bus-internal.h"
40ca29a1 36#include "bus-util.h"
2b5c5383 37#include "bus-dump.h"
781fa938 38#include "bus-signature.h"
79f34de9 39#include "bus-type.h"
a1ad3767 40#include "busctl-introspect.h"
de1c301e 41
1f849790 42static bool arg_no_pager = false;
17d47d8d 43static bool arg_legend = true;
1f849790 44static char *arg_address = NULL;
56e61788
LP
45static bool arg_unique = false;
46static bool arg_acquired = false;
47static bool arg_activatable = false;
48static bool arg_show_machine = false;
1f849790 49static char **arg_matches = NULL;
d75edbd6
LP
50static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
51static char *arg_host = NULL;
52static bool arg_user = false;
1f70b087 53static size_t arg_snaplen = 4096;
d9130355 54static bool arg_list = false;
781fa938 55static bool arg_quiet = false;
1fc55609 56static bool arg_verbose = false;
38051578
LP
57static bool arg_expect_reply = true;
58static bool arg_auto_start = true;
59static bool arg_allow_interactive_authorization = true;
40ed1a45 60static bool arg_augment_creds = true;
a44b1081 61static usec_t arg_timeout = 0;
1f849790
LP
62
63static void pager_open_if_enabled(void) {
64
65 /* Cache result before we open the pager */
66 if (arg_no_pager)
67 return;
68
69 pager_open(false);
70}
71
1d58a1fe
LP
72#define NAME_IS_ACQUIRED INT_TO_PTR(1)
73#define NAME_IS_ACTIVATABLE INT_TO_PTR(2)
74
1f849790 75static int list_bus_names(sd_bus *bus, char **argv) {
71f2ab46 76 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
77 _cleanup_free_ char **merged = NULL;
78 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
79 char **i;
80 int r;
89ffcd2a 81 size_t max_i = 0;
5e2f14e6
LP
82 unsigned n = 0;
83 void *v;
84 char *k;
85 Iterator iterator;
de1c301e 86
1f849790 87 assert(bus);
de1c301e 88
d9130355
LP
89 if (!arg_unique && !arg_acquired && !arg_activatable)
90 arg_unique = arg_acquired = arg_activatable = true;
91
56e61788 92 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
23bbb0de
MS
93 if (r < 0)
94 return log_error_errno(r, "Failed to list names: %m");
de1c301e 95
1f849790
LP
96 pager_open_if_enabled();
97
d5099efc 98 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
99 if (!names)
100 return log_oom();
89ffcd2a 101
5e2f14e6 102 STRV_FOREACH(i, acquired) {
71f2ab46
LP
103 max_i = MAX(max_i, strlen(*i));
104
1d58a1fe 105 r = hashmap_put(names, *i, NAME_IS_ACQUIRED);
23bbb0de
MS
106 if (r < 0)
107 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
108 }
109
110 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
111 max_i = MAX(max_i, strlen(*i));
112
1d58a1fe 113 r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE);
23bbb0de
MS
114 if (r < 0 && r != -EEXIST)
115 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
116 }
117
118 merged = new(char*, hashmap_size(names) + 1);
119 HASHMAP_FOREACH_KEY(v, k, names, iterator)
120 merged[n++] = k;
121
122 merged[n] = NULL;
123 strv_sort(merged);
124
17d47d8d
TA
125 if (arg_legend) {
126 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
455971c1 127 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
89ffcd2a 128
17d47d8d
TA
129 if (arg_show_machine)
130 puts(" MACHINE");
131 else
132 putchar('\n');
133 }
a4297f08 134
5e2f14e6
LP
135 STRV_FOREACH(i, merged) {
136 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
137 sd_id128_t mid;
71f2ab46 138
1d58a1fe 139 if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) {
5e2f14e6 140 /* Activatable */
71f2ab46 141
5e2f14e6 142 printf("%-*s", (int) max_i, *i);
56e61788
LP
143 printf(" - - - (activatable) - - ");
144 if (arg_show_machine)
5e2f14e6 145 puts(" -");
56e61788
LP
146 else
147 putchar('\n');
71f2ab46
LP
148 continue;
149
5e2f14e6 150 }
de1c301e 151
56e61788
LP
152 if (!arg_unique && (*i)[0] == ':')
153 continue;
154
155 if (!arg_acquired && (*i)[0] != ':')
1f849790 156 continue;
89ffcd2a
LP
157
158 printf("%-*s", (int) max_i, *i);
de1c301e 159
40ed1a45
LP
160 r = sd_bus_get_name_creds(
161 bus, *i,
162 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) |
163 SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
164 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
165 SD_BUS_CREDS_DESCRIPTION, &creds);
89ffcd2a 166 if (r >= 0) {
14008e4e 167 const char *unique, *session, *unit, *cn;
5b12334d
LP
168 pid_t pid;
169 uid_t uid;
de1c301e 170
5b12334d
LP
171 r = sd_bus_creds_get_pid(creds, &pid);
172 if (r >= 0) {
173 const char *comm = NULL;
de1c301e 174
5b12334d 175 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 176
5b12334d
LP
177 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
178 } else
a4297f08 179 fputs(" - - ", stdout);
89ffcd2a 180
5b12334d
LP
181 r = sd_bus_creds_get_uid(creds, &uid);
182 if (r >= 0) {
183 _cleanup_free_ char *u = NULL;
89ffcd2a 184
5b12334d
LP
185 u = uid_to_name(uid);
186 if (!u)
187 return log_oom();
89ffcd2a 188
5b12334d
LP
189 if (strlen(u) > 16)
190 u[16] = 0;
191
192 printf(" %-16s", u);
193 } else
a4297f08 194 fputs(" - ", stdout);
89ffcd2a 195
49b832c5
LP
196 r = sd_bus_creds_get_unique_name(creds, &unique);
197 if (r >= 0)
56e61788
LP
198 printf(" %-13s", unique);
199 else
200 fputs(" - ", stdout);
201
202 r = sd_bus_creds_get_unit(creds, &unit);
203 if (r >= 0) {
204 _cleanup_free_ char *e;
205
206 e = ellipsize(unit, 25, 100);
207 if (!e)
208 return log_oom();
209
210 printf(" %-25s", e);
211 } else
212 fputs(" - ", stdout);
213
214 r = sd_bus_creds_get_session(creds, &session);
215 if (r >= 0)
216 printf(" %-10s", session);
a4297f08 217 else
56e61788 218 fputs(" - ", stdout);
7b0b392f 219
455971c1 220 r = sd_bus_creds_get_description(creds, &cn);
14008e4e
LP
221 if (r >= 0)
222 printf(" %-19s", cn);
223 else
224 fputs(" - ", stdout);
225
7b0b392f 226 } else
14008e4e 227 printf(" - - - - - - - ");
a4297f08 228
56e61788 229 if (arg_show_machine) {
056f95d0 230 r = sd_bus_get_name_machine_id(bus, *i, &mid);
a4297f08
LP
231 if (r >= 0) {
232 char m[SD_ID128_STRING_MAX];
233 printf(" %s\n", sd_id128_to_string(mid, m));
234 } else
235 puts(" -");
56e61788
LP
236 } else
237 putchar('\n');
de1c301e
LP
238 }
239
1f849790
LP
240 return 0;
241}
242
d9130355
LP
243static void print_subtree(const char *prefix, const char *path, char **l) {
244 const char *vertical, *space;
245 char **n;
246
247 /* We assume the list is sorted. Let's first skip over the
248 * entry we are looking at. */
249 for (;;) {
250 if (!*l)
251 return;
252
253 if (!streq(*l, path))
254 break;
255
256 l++;
257 }
258
259 vertical = strappenda(prefix, draw_special_char(DRAW_TREE_VERTICAL));
260 space = strappenda(prefix, draw_special_char(DRAW_TREE_SPACE));
261
262 for (;;) {
263 bool has_more = false;
264
265 if (!*l || !path_startswith(*l, path))
266 break;
267
268 n = l + 1;
269 for (;;) {
270 if (!*n || !path_startswith(*n, path))
271 break;
272
273 if (!path_startswith(*n, *l)) {
274 has_more = true;
275 break;
276 }
277
278 n++;
279 }
280
281 printf("%s%s%s\n", prefix, draw_special_char(has_more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), *l);
282
283 print_subtree(has_more ? vertical : space, *l, l);
284 l = n;
285 }
286}
287
288static void print_tree(const char *prefix, char **l) {
289
290 pager_open_if_enabled();
291
292 prefix = strempty(prefix);
293
294 if (arg_list) {
295 char **i;
296
297 STRV_FOREACH(i, l)
298 printf("%s%s\n", prefix, *i);
299 return;
300 }
301
56c8b52d
LP
302 if (strv_isempty(l)) {
303 printf("No objects discovered.\n");
304 return;
305 }
306
307 if (streq(l[0], "/") && !l[1]) {
308 printf("Only root object discovered.\n");
309 return;
310 }
d9130355
LP
311
312 print_subtree(prefix, "/", l);
313}
314
a1ad3767
LP
315static int on_path(const char *path, void *userdata) {
316 Set *paths = userdata;
d9130355
LP
317 int r;
318
a1ad3767 319 assert(paths);
d9130355 320
a1ad3767
LP
321 r = set_put_strdup(paths, path);
322 if (r < 0)
323 return log_oom();
d9130355 324
a1ad3767 325 return 0;
d9130355
LP
326}
327
73fc23c0 328static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) {
0171da06 329 static const XMLIntrospectOps ops = {
a1ad3767
LP
330 .on_path = on_path,
331 };
332
d9130355 333 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
a1ad3767
LP
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
335 const char *xml;
d9130355
LP
336 int r;
337
338 r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
339 if (r < 0) {
73fc23c0
LP
340 if (many)
341 printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r));
342 else
343 log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r));
d9130355
LP
344 return r;
345 }
346
347 r = sd_bus_message_read(reply, "s", &xml);
348 if (r < 0)
349 return bus_log_parse_error(r);
350
a1ad3767 351 return parse_xml_introspect(path, xml, &ops, paths);
d9130355
LP
352}
353
73fc23c0 354static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) {
d9130355
LP
355 _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
356 _cleanup_free_ char **l = NULL;
357 char *m;
358 int r;
359
360 paths = set_new(&string_hash_ops);
361 if (!paths)
362 return log_oom();
363
364 done = set_new(&string_hash_ops);
365 if (!done)
366 return log_oom();
367
368 failed = set_new(&string_hash_ops);
369 if (!failed)
370 return log_oom();
371
372 m = strdup("/");
373 if (!m)
374 return log_oom();
375
376 r = set_put(paths, m);
377 if (r < 0) {
378 free(m);
379 return log_oom();
380 }
381
382 for (;;) {
383 _cleanup_free_ char *p = NULL;
384 int q;
385
386 p = set_steal_first(paths);
387 if (!p)
388 break;
389
390 if (set_contains(done, p) ||
391 set_contains(failed, p))
392 continue;
393
73fc23c0 394 q = find_nodes(bus, service, p, paths, many);
d9130355
LP
395 if (q < 0) {
396 if (r >= 0)
397 r = q;
398
399 q = set_put(failed, p);
400 } else
401 q = set_put(done, p);
402
403 if (q < 0)
404 return log_oom();
405
406 assert(q != 0);
407 p = NULL;
408 }
409
73fc23c0
LP
410 pager_open_if_enabled();
411
d9130355
LP
412 l = set_get_strv(done);
413 if (!l)
414 return log_oom();
415
416 strv_sort(l);
417 print_tree(prefix, l);
418
419 fflush(stdout);
420
421 return r;
422}
423
424static int tree(sd_bus *bus, char **argv) {
425 char **i;
426 int r = 0;
427
428 if (!arg_unique && !arg_acquired)
429 arg_acquired = true;
430
431 if (strv_length(argv) <= 1) {
432 _cleanup_strv_free_ char **names = NULL;
56c8b52d 433 bool not_first = false;
d9130355
LP
434
435 r = sd_bus_list_names(bus, &names, NULL);
23bbb0de
MS
436 if (r < 0)
437 return log_error_errno(r, "Failed to get name list: %m");
d9130355
LP
438
439 pager_open_if_enabled();
440
441 STRV_FOREACH(i, names) {
442 int q;
443
444 if (!arg_unique && (*i)[0] == ':')
445 continue;
446
447 if (!arg_acquired && (*i)[0] == ':')
448 continue;
449
450 if (not_first)
451 printf("\n");
452
56c8b52d 453 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off());
d9130355 454
73fc23c0 455 q = tree_one(bus, *i, NULL, true);
d9130355
LP
456 if (q < 0 && r >= 0)
457 r = q;
458
459 not_first = true;
460 }
461 } else {
d9130355
LP
462 STRV_FOREACH(i, argv+1) {
463 int q;
464
465 if (i > argv+1)
466 printf("\n");
467
73fc23c0
LP
468 if (argv[2]) {
469 pager_open_if_enabled();
56c8b52d 470 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off());
73fc23c0 471 }
d9130355 472
73fc23c0 473 q = tree_one(bus, *i, NULL, !!argv[2]);
d9130355
LP
474 if (q < 0 && r >= 0)
475 r = q;
476 }
477 }
478
479 return r;
480}
481
1fc55609
LP
482static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) {
483 int r;
484
485 for (;;) {
486 const char *contents = NULL;
487 char type;
488 union {
489 uint8_t u8;
490 uint16_t u16;
491 int16_t s16;
492 uint32_t u32;
493 int32_t s32;
494 uint64_t u64;
495 int64_t s64;
496 double d64;
497 const char *string;
498 int i;
499 } basic;
500
501 r = sd_bus_message_peek_type(m, &type, &contents);
502 if (r <= 0)
503 return r;
504
505 if (bus_type_is_container(type) > 0) {
506
507 r = sd_bus_message_enter_container(m, type, contents);
508 if (r < 0)
509 return r;
510
511 if (type == SD_BUS_TYPE_ARRAY) {
512 unsigned n = 0;
513
514 /* count array entries */
515 for (;;) {
516
517 r = sd_bus_message_skip(m, contents);
518 if (r < 0)
519 return r;
520 if (r == 0)
521 break;
522
523 n++;
524 }
525
526 r = sd_bus_message_rewind(m, false);
527 if (r < 0)
528 return r;
529
530 if (needs_space)
531 fputc(' ', f);
532
533 fprintf(f, "%u", n);
534 } else if (type == SD_BUS_TYPE_VARIANT) {
535
536 if (needs_space)
537 fputc(' ', f);
538
539 fprintf(f, "%s", contents);
540 }
541
1ed24c61 542 r = format_cmdline(m, f, needs_space || IN_SET(type, SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_VARIANT));
1fc55609
LP
543 if (r < 0)
544 return r;
545
546 r = sd_bus_message_exit_container(m);
547 if (r < 0)
548 return r;
549
550 continue;
551 }
552
553 r = sd_bus_message_read_basic(m, type, &basic);
554 if (r < 0)
555 return r;
556
557 if (needs_space)
558 fputc(' ', f);
559
560 switch (type) {
561 case SD_BUS_TYPE_BYTE:
562 fprintf(f, "%u", basic.u8);
563 break;
564
565 case SD_BUS_TYPE_BOOLEAN:
566 fputs(true_false(basic.i), f);
567 break;
568
569 case SD_BUS_TYPE_INT16:
570 fprintf(f, "%i", basic.s16);
571 break;
572
573 case SD_BUS_TYPE_UINT16:
574 fprintf(f, "%u", basic.u16);
575 break;
576
577 case SD_BUS_TYPE_INT32:
578 fprintf(f, "%i", basic.s32);
579 break;
580
581 case SD_BUS_TYPE_UINT32:
582 fprintf(f, "%u", basic.u32);
583 break;
584
585 case SD_BUS_TYPE_INT64:
586 fprintf(f, "%" PRIi64, basic.s64);
587 break;
588
589 case SD_BUS_TYPE_UINT64:
590 fprintf(f, "%" PRIu64, basic.u64);
591 break;
592
593 case SD_BUS_TYPE_DOUBLE:
594 fprintf(f, "%g", basic.d64);
595 break;
596
597 case SD_BUS_TYPE_STRING:
598 case SD_BUS_TYPE_OBJECT_PATH:
599 case SD_BUS_TYPE_SIGNATURE: {
600 _cleanup_free_ char *b = NULL;
601
602 b = cescape(basic.string);
603 if (!b)
604 return -ENOMEM;
605
606 fprintf(f, "\"%s\"", b);
607 break;
608 }
609
610 case SD_BUS_TYPE_UNIX_FD:
611 fprintf(f, "%i", basic.i);
612 break;
613
614 default:
615 assert_not_reached("Unknown basic type.");
616 }
617
1ed24c61 618 needs_space = true;
1fc55609
LP
619 }
620}
621
0171da06
LP
622typedef struct Member {
623 const char *type;
624 char *interface;
625 char *name;
626 char *signature;
627 char *result;
1fc55609 628 char *value;
0171da06
LP
629 bool writable;
630 uint64_t flags;
631} Member;
632
633static unsigned long member_hash_func(const void *p, const uint8_t hash_key[]) {
634 const Member *m = p;
635 unsigned long ul;
636
637 assert(m);
638 assert(m->type);
639
640 ul = string_hash_func(m->type, hash_key);
641
642 if (m->name)
643 ul ^= string_hash_func(m->name, hash_key);
644
645 if (m->interface)
646 ul ^= string_hash_func(m->interface, hash_key);
647
648 return ul;
649}
650
651static int member_compare_func(const void *a, const void *b) {
652 const Member *x = a, *y = b;
653 int d;
654
655 assert(x);
656 assert(y);
657 assert(x->type);
658 assert(y->type);
659
660 if (!x->interface && y->interface)
661 return -1;
662 if (x->interface && !y->interface)
663 return 1;
664 if (x->interface && y->interface) {
665 d = strcmp(x->interface, y->interface);
666 if (d != 0)
667 return d;
668 }
669
670 d = strcmp(x->type, y->type);
671 if (d != 0)
672 return d;
673
674 if (!x->name && y->name)
675 return -1;
676 if (x->name && !y->name)
677 return 1;
678 if (x->name && y->name)
679 return strcmp(x->name, y->name);
680
681 return 0;
682}
683
684static int member_compare_funcp(const void *a, const void *b) {
685 const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b;
686
687 return member_compare_func(*x, *y);
688}
689
690static void member_free(Member *m) {
691 if (!m)
692 return;
693
694 free(m->interface);
695 free(m->name);
696 free(m->signature);
697 free(m->result);
1fc55609 698 free(m->value);
0171da06
LP
699 free(m);
700}
701
702DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
703
704static void member_set_free(Set *s) {
705 Member *m;
706
707 while ((m = set_steal_first(s)))
708 member_free(m);
709
710 set_free(s);
711}
712
713DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
714
715static int on_interface(const char *interface, uint64_t flags, void *userdata) {
716 _cleanup_(member_freep) Member *m;
717 Set *members = userdata;
718 int r;
719
720 assert(interface);
721 assert(members);
722
723 m = new0(Member, 1);
724 if (!m)
725 return log_oom();
726
727 m->type = "interface";
728 m->flags = flags;
729
730 r = free_and_strdup(&m->interface, interface);
731 if (r < 0)
732 return log_oom();
733
734 r = set_put(members, m);
735 if (r <= 0) {
736 log_error("Duplicate interface");
737 return -EINVAL;
738 }
739
740 m = NULL;
741 return 0;
742}
743
744static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
745 _cleanup_(member_freep) Member *m;
746 Set *members = userdata;
747 int r;
748
749 assert(interface);
750 assert(name);
751
752 m = new0(Member, 1);
753 if (!m)
754 return log_oom();
755
756 m->type = "method";
757 m->flags = flags;
758
759 r = free_and_strdup(&m->interface, interface);
760 if (r < 0)
761 return log_oom();
762
763 r = free_and_strdup(&m->name, name);
764 if (r < 0)
765 return log_oom();
766
767 r = free_and_strdup(&m->signature, signature);
768 if (r < 0)
769 return log_oom();
770
771 r = free_and_strdup(&m->result, result);
772 if (r < 0)
773 return log_oom();
774
775 r = set_put(members, m);
776 if (r <= 0) {
777 log_error("Duplicate method");
778 return -EINVAL;
779 }
780
781 m = NULL;
782 return 0;
783}
784
785static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
786 _cleanup_(member_freep) Member *m;
787 Set *members = userdata;
788 int r;
789
790 assert(interface);
791 assert(name);
792
793 m = new0(Member, 1);
794 if (!m)
795 return log_oom();
796
797 m->type = "signal";
798 m->flags = flags;
799
800 r = free_and_strdup(&m->interface, interface);
801 if (r < 0)
802 return log_oom();
803
804 r = free_and_strdup(&m->name, name);
805 if (r < 0)
806 return log_oom();
807
808 r = free_and_strdup(&m->signature, signature);
809 if (r < 0)
810 return log_oom();
811
812 r = set_put(members, m);
813 if (r <= 0) {
814 log_error("Duplicate signal");
815 return -EINVAL;
816 }
817
818 m = NULL;
819 return 0;
820}
821
822static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
823 _cleanup_(member_freep) Member *m;
824 Set *members = userdata;
825 int r;
826
827 assert(interface);
828 assert(name);
829
830 m = new0(Member, 1);
831 if (!m)
832 return log_oom();
833
834 m->type = "property";
835 m->flags = flags;
836 m->writable = writable;
837
838 r = free_and_strdup(&m->interface, interface);
839 if (r < 0)
840 return log_oom();
841
842 r = free_and_strdup(&m->name, name);
843 if (r < 0)
844 return log_oom();
845
846 r = free_and_strdup(&m->signature, signature);
847 if (r < 0)
848 return log_oom();
849
850 r = set_put(members, m);
851 if (r <= 0) {
852 log_error("Duplicate property");
853 return -EINVAL;
854 }
855
856 m = NULL;
857 return 0;
858}
859
860static const char *strdash(const char *x) {
861 return isempty(x) ? "-" : x;
862}
863
864static int introspect(sd_bus *bus, char **argv) {
865 static const struct hash_ops member_hash_ops = {
866 .hash = member_hash_func,
867 .compare = member_compare_func,
868 };
869
870 static const XMLIntrospectOps ops = {
871 .on_interface = on_interface,
872 .on_method = on_method,
873 .on_signal = on_signal,
874 .on_property = on_property,
875 };
876
877 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
878 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
879 _cleanup_(member_set_freep) Set *members = NULL;
880 Iterator i;
881 Member *m;
882 const char *xml;
883 int r;
884 unsigned name_width, type_width, signature_width, result_width;
885 Member **sorted = NULL;
4f44c03e 886 unsigned k = 0, j, n_args;
0171da06 887
4f44c03e
LP
888 n_args = strv_length(argv);
889 if (n_args < 3) {
0171da06
LP
890 log_error("Requires service and object path argument.");
891 return -EINVAL;
892 }
893
4f44c03e
LP
894 if (n_args > 4) {
895 log_error("Too many arguments.");
896 return -EINVAL;
897 }
898
0171da06
LP
899 members = set_new(&member_hash_ops);
900 if (!members)
901 return log_oom();
902
903 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
904 if (r < 0) {
905 log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
906 return r;
907 }
908
909 r = sd_bus_message_read(reply, "s", &xml);
910 if (r < 0)
911 return bus_log_parse_error(r);
912
1fc55609 913 /* First, get list of all properties */
0171da06
LP
914 r = parse_xml_introspect(argv[2], xml, &ops, members);
915 if (r < 0)
916 return r;
917
1fc55609
LP
918 /* Second, find the current values for them */
919 SET_FOREACH(m, members, i) {
920
921 if (!streq(m->type, "property"))
922 continue;
923
924 if (m->value)
925 continue;
926
4f44c03e
LP
927 if (argv[3] && !streq(argv[3], m->interface))
928 continue;
929
1fc55609
LP
930 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
931 if (r < 0) {
932 log_error("%s", bus_error_message(&error, r));
933 return r;
934 }
935
936 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
937 if (r < 0)
938 return bus_log_parse_error(r);
939
940 for (;;) {
941 Member *z;
942 _cleanup_free_ char *buf = NULL;
943 _cleanup_fclose_ FILE *mf = NULL;
944 size_t sz = 0;
945 const char *name;
946
947 r = sd_bus_message_enter_container(reply, 'e', "sv");
948 if (r < 0)
949 return bus_log_parse_error(r);
950
951 if (r == 0)
952 break;
953
954 r = sd_bus_message_read(reply, "s", &name);
955 if (r < 0)
956 return bus_log_parse_error(r);
957
958 r = sd_bus_message_enter_container(reply, 'v', NULL);
959 if (r < 0)
960 return bus_log_parse_error(r);
961
962 mf = open_memstream(&buf, &sz);
963 if (!mf)
964 return log_oom();
965
966 r = format_cmdline(reply, mf, false);
967 if (r < 0)
968 return bus_log_parse_error(r);
969
970 fclose(mf);
971 mf = NULL;
972
973 z = set_get(members, &((Member) {
974 .type = "property",
975 .interface = m->interface,
976 .name = (char*) name }));
977 if (z) {
978 free(z->value);
979 z->value = buf;
980 buf = NULL;
981 }
982
983 r = sd_bus_message_exit_container(reply);
984 if (r < 0)
985 return bus_log_parse_error(r);
986
987 r = sd_bus_message_exit_container(reply);
988 if (r < 0)
989 return bus_log_parse_error(r);
990 }
991
992 r = sd_bus_message_exit_container(reply);
993 if (r < 0)
994 return bus_log_parse_error(r);
995 }
996
0171da06
LP
997 pager_open_if_enabled();
998
999 name_width = strlen("NAME");
1000 type_width = strlen("TYPE");
1001 signature_width = strlen("SIGNATURE");
1fc55609 1002 result_width = strlen("RESULT/VALUE");
0171da06
LP
1003
1004 sorted = newa(Member*, set_size(members));
1005
1006 SET_FOREACH(m, members, i) {
4f44c03e
LP
1007
1008 if (argv[3] && !streq(argv[3], m->interface))
1009 continue;
1010
0171da06
LP
1011 if (m->interface)
1012 name_width = MAX(name_width, strlen(m->interface));
1013 if (m->name)
1014 name_width = MAX(name_width, strlen(m->name) + 1);
1015 if (m->type)
1016 type_width = MAX(type_width, strlen(m->type));
1017 if (m->signature)
1018 signature_width = MAX(signature_width, strlen(m->signature));
1019 if (m->result)
1020 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1021 if (m->value)
1022 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1023
1024 sorted[k++] = m;
1025 }
1026
1fc55609
LP
1027 if (result_width > 40)
1028 result_width = 40;
1029
0171da06
LP
1030 qsort(sorted, k, sizeof(Member*), member_compare_funcp);
1031
1fc55609
LP
1032 if (arg_legend) {
1033 printf("%-*s %-*s %-*s %-*s %s\n",
1034 (int) name_width, "NAME",
1035 (int) type_width, "TYPE",
1036 (int) signature_width, "SIGNATURE",
1037 (int) result_width, "RESULT/VALUE",
1038 "FLAGS");
1039 }
0171da06
LP
1040
1041 for (j = 0; j < k; j++) {
1fc55609
LP
1042 _cleanup_free_ char *ellipsized = NULL;
1043 const char *rv;
0171da06
LP
1044 bool is_interface;
1045
1046 m = sorted[j];
1047
4f44c03e
LP
1048 if (argv[3] && !streq(argv[3], m->interface))
1049 continue;
1050
0171da06
LP
1051 is_interface = streq(m->type, "interface");
1052
4f44c03e
LP
1053 if (argv[3] && is_interface)
1054 continue;
1055
1fc55609
LP
1056 if (m->value) {
1057 ellipsized = ellipsize(m->value, result_width, 100);
1058 if (!ellipsized)
1059 return log_oom();
1060
1061 rv = ellipsized;
1062 } else
1063 rv = strdash(m->result);
1064
0171da06
LP
1065 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1066 is_interface ? ansi_highlight() : "",
1067 is_interface ? "" : ".",
1068 - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1069 is_interface ? ansi_highlight_off() : "",
1070 (int) type_width, strdash(m->type),
1071 (int) signature_width, strdash(m->signature),
1fc55609 1072 (int) result_width, rv,
0171da06
LP
1073 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1074 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1075 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1076 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1077 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1078 m->writable ? " writable" : "");
1079 }
1080
1081 return 0;
1082}
1083
1f70b087 1084static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 1085 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1086}
1087
1088static int message_pcap(sd_bus_message *m, FILE *f) {
1089 return bus_message_pcap_frame(m, arg_snaplen, f);
1090}
1091
1092static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
b51f299a 1093 bool added_something = false;
1f849790
LP
1094 char **i;
1095 int r;
1096
1097 STRV_FOREACH(i, argv+1) {
1098 _cleanup_free_ char *m = NULL;
1099
1100 if (!service_name_is_valid(*i)) {
1101 log_error("Invalid service name '%s'", *i);
1102 return -EINVAL;
1103 }
1104
1105 m = strjoin("sender='", *i, "'", NULL);
1106 if (!m)
1107 return log_oom();
1108
19befb2d 1109 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
23bbb0de
MS
1110 if (r < 0)
1111 return log_error_errno(r, "Failed to add match: %m");
b51f299a
LP
1112
1113 added_something = true;
1f849790
LP
1114 }
1115
1116 STRV_FOREACH(i, arg_matches) {
19befb2d 1117 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
23bbb0de
MS
1118 if (r < 0)
1119 return log_error_errno(r, "Failed to add match: %m");
b51f299a
LP
1120
1121 added_something = true;
1122 }
1123
1124 if (!added_something) {
19befb2d 1125 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
23bbb0de
MS
1126 if (r < 0)
1127 return log_error_errno(r, "Failed to add match: %m");
1f849790
LP
1128 }
1129
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);
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)
1204 printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_highlight_off());
1205
3acc1daf
LP
1206 r = sd_bus_get_scope(bus, &scope);
1207 if (r >= 0)
5b820358 1208 printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_highlight_off());
3acc1daf 1209
5c302692
LP
1210 r = sd_bus_get_bus_id(bus, &bus_id);
1211 if (r >= 0)
1212 printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_highlight_off());
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"
781fa938 1692 " --list Don't show tree, but simple object path list\n"
1fc55609 1693 " --quiet Don't show method call reply\n"
38051578
LP
1694 " --verbose Show result values in long format\n"
1695 " --expect-reply=BOOL Expect a method call reply\n"
1696 " --auto-start=BOOL Auto-start destination service\n"
1697 " --allow-interactive-authorization=BOOL\n"
a44b1081 1698 " Allow interactive authorization for operation\n"
40ed1a45
LP
1699 " --timeout=SECS Maximum time to wait for method call completion\n"
1700 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n"
1f849790 1701 "Commands:\n"
d75edbd6 1702 " list List bus names\n"
2e9efd22 1703 " status [SERVICE] Show bus service, process or bus owner credentials\n"
d94fe1f1 1704 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1705 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 1706 " tree [SERVICE...] Show object tree of service\n"
4f44c03e 1707 " introspect SERVICE OBJECT [INTERFACE]\n"
0171da06 1708 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 1709 " Call a method\n"
1fc55609 1710 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 1711 " Get property value\n"
1fc55609
LP
1712 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
1713 " Set property value\n"
601185b4
ZJS
1714 " help Show this help\n"
1715 , program_invocation_short_name);
1f849790
LP
1716
1717 return 0;
1718}
1719
1720static int parse_argv(int argc, char *argv[]) {
1721
1722 enum {
1723 ARG_VERSION = 0x100,
1724 ARG_NO_PAGER,
17d47d8d 1725 ARG_NO_LEGEND,
1f849790
LP
1726 ARG_SYSTEM,
1727 ARG_USER,
1728 ARG_ADDRESS,
1729 ARG_MATCH,
56e61788
LP
1730 ARG_SHOW_MACHINE,
1731 ARG_UNIQUE,
1732 ARG_ACQUIRED,
1f70b087
LP
1733 ARG_ACTIVATABLE,
1734 ARG_SIZE,
d9130355 1735 ARG_LIST,
1fc55609 1736 ARG_VERBOSE,
38051578
LP
1737 ARG_EXPECT_REPLY,
1738 ARG_AUTO_START,
1739 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 1740 ARG_TIMEOUT,
40ed1a45 1741 ARG_AUGMENT_CREDS,
1f849790
LP
1742 };
1743
1744 static const struct option options[] = {
56e61788
LP
1745 { "help", no_argument, NULL, 'h' },
1746 { "version", no_argument, NULL, ARG_VERSION },
1747 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1748 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1749 { "system", no_argument, NULL, ARG_SYSTEM },
1750 { "user", no_argument, NULL, ARG_USER },
1751 { "address", required_argument, NULL, ARG_ADDRESS },
1752 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1753 { "unique", no_argument, NULL, ARG_UNIQUE },
1754 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1755 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1756 { "match", required_argument, NULL, ARG_MATCH },
1757 { "host", required_argument, NULL, 'H' },
1758 { "machine", required_argument, NULL, 'M' },
1f70b087 1759 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1760 { "list", no_argument, NULL, ARG_LIST },
781fa938 1761 { "quiet", no_argument, NULL, 'q' },
1fc55609 1762 { "verbose", no_argument, NULL, ARG_VERBOSE },
38051578
LP
1763 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
1764 { "auto-start", required_argument, NULL, ARG_AUTO_START },
1765 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
a44b1081 1766 { "timeout", required_argument, NULL, ARG_TIMEOUT },
40ed1a45 1767 { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS},
eb9da376 1768 {},
1f849790
LP
1769 };
1770
1f70b087 1771 int c, r;
1f849790
LP
1772
1773 assert(argc >= 0);
1774 assert(argv);
1775
781fa938 1776 while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
1f849790
LP
1777
1778 switch (c) {
1779
1780 case 'h':
1781 return help();
1782
1783 case ARG_VERSION:
1784 puts(PACKAGE_STRING);
1785 puts(SYSTEMD_FEATURES);
1786 return 0;
1787
1788 case ARG_NO_PAGER:
1789 arg_no_pager = true;
1790 break;
1791
17d47d8d
TA
1792 case ARG_NO_LEGEND:
1793 arg_legend = false;
1794 break;
1795
1f849790
LP
1796 case ARG_USER:
1797 arg_user = true;
1798 break;
1799
1800 case ARG_SYSTEM:
1801 arg_user = false;
1802 break;
1803
1804 case ARG_ADDRESS:
1805 arg_address = optarg;
1806 break;
1807
56e61788
LP
1808 case ARG_SHOW_MACHINE:
1809 arg_show_machine = true;
1810 break;
1811
1812 case ARG_UNIQUE:
1813 arg_unique = true;
1f849790
LP
1814 break;
1815
56e61788
LP
1816 case ARG_ACQUIRED:
1817 arg_acquired = true;
1818 break;
1819
1820 case ARG_ACTIVATABLE:
1821 arg_activatable = true;
a4297f08
LP
1822 break;
1823
1f849790
LP
1824 case ARG_MATCH:
1825 if (strv_extend(&arg_matches, optarg) < 0)
1826 return log_oom();
1827 break;
1828
1f70b087
LP
1829 case ARG_SIZE: {
1830 off_t o;
1831
1832 r = parse_size(optarg, 0, &o);
1833 if (r < 0) {
1834 log_error("Failed to parse size: %s", optarg);
1835 return r;
1836 }
1837
1838 if ((off_t) (size_t) o != o) {
1839 log_error("Size out of range.");
1840 return -E2BIG;
1841 }
1842
1843 arg_snaplen = (size_t) o;
1844 break;
1845 }
1846
d9130355
LP
1847 case ARG_LIST:
1848 arg_list = true;
1849 break;
1850
d75edbd6
LP
1851 case 'H':
1852 arg_transport = BUS_TRANSPORT_REMOTE;
1853 arg_host = optarg;
1854 break;
1855
1856 case 'M':
de33fc62 1857 arg_transport = BUS_TRANSPORT_MACHINE;
d75edbd6
LP
1858 arg_host = optarg;
1859 break;
1860
781fa938
LP
1861 case 'q':
1862 arg_quiet = true;
1863 break;
1864
1fc55609
LP
1865 case ARG_VERBOSE:
1866 arg_verbose = true;
1867 break;
1868
38051578
LP
1869 case ARG_EXPECT_REPLY:
1870 r = parse_boolean(optarg);
1871 if (r < 0) {
1872 log_error("Failed to parse --expect-reply= parameter.");
1873 return r;
1874 }
1875
1876 arg_expect_reply = !!r;
1877 break;
1878
1879
1880 case ARG_AUTO_START:
1881 r = parse_boolean(optarg);
1882 if (r < 0) {
1883 log_error("Failed to parse --auto-start= parameter.");
1884 return r;
1885 }
1886
1887 arg_auto_start = !!r;
1888 break;
1889
1890
1891 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
1892 r = parse_boolean(optarg);
1893 if (r < 0) {
1894 log_error("Failed to parse --allow-interactive-authorization= parameter.");
1895 return r;
1896 }
1897
1898 arg_allow_interactive_authorization = !!r;
1899 break;
1900
a44b1081
LP
1901 case ARG_TIMEOUT:
1902 r = parse_sec(optarg, &arg_timeout);
1903 if (r < 0) {
1904 log_error("Failed to parse --timeout= parameter.");
1905 return r;
1906 }
1907
1908 break;
1909
40ed1a45
LP
1910 case ARG_AUGMENT_CREDS:
1911 r = parse_boolean(optarg);
1912 if (r < 0) {
1913 log_error("Failed to parse --augment-creds= parameter.");
1914 return r;
1915 }
1916
1917 arg_augment_creds = !!r;
1918 break;
1919
1f849790
LP
1920 case '?':
1921 return -EINVAL;
1922
1923 default:
eb9da376 1924 assert_not_reached("Unhandled option");
1f849790 1925 }
1f849790
LP
1926
1927 return 1;
1928}
1929
1930static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
1931 assert(bus);
1932
1933 if (optind >= argc ||
1934 streq(argv[optind], "list"))
1935 return list_bus_names(bus, argv + optind);
1936
1937 if (streq(argv[optind], "monitor"))
1f70b087
LP
1938 return monitor(bus, argv + optind, message_dump);
1939
1940 if (streq(argv[optind], "capture"))
1941 return capture(bus, argv + optind);
1f849790 1942
95c4fe82
LP
1943 if (streq(argv[optind], "status"))
1944 return status(bus, argv + optind);
d9130355
LP
1945
1946 if (streq(argv[optind], "tree"))
1947 return tree(bus, argv + optind);
781fa938 1948
0171da06
LP
1949 if (streq(argv[optind], "introspect"))
1950 return introspect(bus, argv + optind);
1951
781fa938
LP
1952 if (streq(argv[optind], "call"))
1953 return call(bus, argv + optind);
d55192ad
LP
1954
1955 if (streq(argv[optind], "get-property"))
1956 return get_property(bus, argv + optind);
95c4fe82 1957
1fc55609
LP
1958 if (streq(argv[optind], "set-property"))
1959 return set_property(bus, argv + optind);
1960
1f849790
LP
1961 if (streq(argv[optind], "help"))
1962 return help();
1963
1964 log_error("Unknown command '%s'", argv[optind]);
1965 return -EINVAL;
1966}
1967
1968int main(int argc, char *argv[]) {
24996861 1969 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1f849790
LP
1970 int r;
1971
1972 log_parse_environment();
1973 log_open();
1974
1975 r = parse_argv(argc, argv);
1976 if (r <= 0)
1977 goto finish;
1978
09365592
LP
1979 r = sd_bus_new(&bus);
1980 if (r < 0) {
da927ba9 1981 log_error_errno(r, "Failed to allocate bus: %m");
09365592
LP
1982 goto finish;
1983 }
1984
1f70b087
LP
1985 if (streq_ptr(argv[optind], "monitor") ||
1986 streq_ptr(argv[optind], "capture")) {
09365592
LP
1987
1988 r = sd_bus_set_monitor(bus, true);
1f849790 1989 if (r < 0) {
da927ba9 1990 log_error_errno(r, "Failed to set monitor mode: %m");
1f849790
LP
1991 goto finish;
1992 }
d0ce7734 1993
b5dae4c7 1994 r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL);
d0ce7734 1995 if (r < 0) {
da927ba9 1996 log_error_errno(r, "Failed to enable credentials: %m");
d0ce7734
LP
1997 goto finish;
1998 }
1999
2000 r = sd_bus_negotiate_timestamp(bus, true);
2001 if (r < 0) {
da927ba9 2002 log_error_errno(r, "Failed to enable timestamps: %m");
d0ce7734
LP
2003 goto finish;
2004 }
2005
2006 r = sd_bus_negotiate_fds(bus, true);
2007 if (r < 0) {
da927ba9 2008 log_error_errno(r, "Failed to enable fds: %m");
d0ce7734
LP
2009 goto finish;
2010 }
09365592 2011 }
1f849790 2012
09365592 2013 if (arg_address)
1f849790 2014 r = sd_bus_set_address(bus, arg_address);
09365592 2015 else {
b18ec7e2
LP
2016 r = sd_bus_set_bus_client(bus, true);
2017 if (r < 0) {
da927ba9 2018 log_error_errno(r, "Failed to set bus client: %m");
b18ec7e2
LP
2019 goto finish;
2020 }
2021
09365592 2022 switch (arg_transport) {
1f849790 2023
09365592 2024 case BUS_TRANSPORT_LOCAL:
3acc1daf
LP
2025 if (arg_user) {
2026 bus->is_user = true;
09365592 2027 r = bus_set_address_user(bus);
3acc1daf
LP
2028 } else {
2029 bus->is_system = true;
09365592 2030 r = bus_set_address_system(bus);
3acc1daf 2031 }
09365592
LP
2032 break;
2033
2034 case BUS_TRANSPORT_REMOTE:
2035 r = bus_set_address_system_remote(bus, arg_host);
2036 break;
2037
de33fc62
LP
2038 case BUS_TRANSPORT_MACHINE:
2039 r = bus_set_address_system_machine(bus, arg_host);
09365592
LP
2040 break;
2041
2042 default:
2043 assert_not_reached("Hmm, unknown transport type.");
1f849790 2044 }
09365592
LP
2045 }
2046 if (r < 0) {
da927ba9 2047 log_error_errno(r, "Failed to set address: %m");
09365592
LP
2048 goto finish;
2049 }
1f849790 2050
09365592 2051 r = sd_bus_start(bus);
1f849790 2052 if (r < 0) {
da927ba9 2053 log_error_errno(r, "Failed to connect to bus: %m");
1f849790
LP
2054 goto finish;
2055 }
2056
2057 r = busctl_main(bus, argc, argv);
2058
2059finish:
2060 pager_close();
d75edbd6 2061
1f849790 2062 strv_free(arg_matches);
de1c301e 2063
de1c301e
LP
2064 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2065}