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