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