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