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