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