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