]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/busctl/busctl.c
hash-funcs: make basic hash_ops typesafe
[thirdparty/systemd.git] / src / busctl / busctl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
de1c301e 2
1f849790 3#include <getopt.h>
0d536673 4#include <stdio_ext.h>
1f849790 5
de1c301e 6#include "sd-bus.h"
3f6fd1ba 7
b5efdb8a 8#include "alloc-util.h"
2b5c5383 9#include "bus-dump.h"
3f6fd1ba 10#include "bus-internal.h"
2de62253 11#include "bus-message.h"
781fa938 12#include "bus-signature.h"
79f34de9 13#include "bus-type.h"
3f6fd1ba 14#include "bus-util.h"
a1ad3767 15#include "busctl-introspect.h"
4f5dd394 16#include "escape.h"
3ffd4af2 17#include "fd-util.h"
6b0f5484 18#include "fileio.h"
9cebb234 19#include "json.h"
8752c575 20#include "locale-util.h"
3f6fd1ba 21#include "log.h"
360f3dc2 22#include "main-func.h"
3f6fd1ba 23#include "pager.h"
6bedfcbb 24#include "parse-util.h"
3f6fd1ba 25#include "path-util.h"
294bf0c3 26#include "pretty-print.h"
3f6fd1ba
LP
27#include "set.h"
28#include "strv.h"
288a74cc 29#include "terminal-util.h"
b1d4f8e1 30#include "user-util.h"
3f6fd1ba 31#include "util.h"
9bb31a0c 32#include "verbs.h"
de1c301e 33
9cebb234
LP
34static enum {
35 JSON_OFF,
36 JSON_SHORT,
37 JSON_PRETTY,
38} arg_json = JSON_OFF;
0221d68a 39static PagerFlags arg_pager_flags = 0;
17d47d8d 40static bool arg_legend = true;
5e4c5bde 41static const char *arg_address = NULL;
56e61788
LP
42static bool arg_unique = false;
43static bool arg_acquired = false;
44static bool arg_activatable = false;
45static bool arg_show_machine = false;
1f849790 46static char **arg_matches = NULL;
d75edbd6 47static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
02bf91ee 48static const char *arg_host = NULL;
d75edbd6 49static bool arg_user = false;
1f70b087 50static size_t arg_snaplen = 4096;
d9130355 51static bool arg_list = false;
781fa938 52static bool arg_quiet = false;
1fc55609 53static bool arg_verbose = false;
38051578
LP
54static bool arg_expect_reply = true;
55static bool arg_auto_start = true;
56static bool arg_allow_interactive_authorization = true;
40ed1a45 57static bool arg_augment_creds = true;
56d820b6 58static bool arg_watch_bind = false;
a44b1081 59static usec_t arg_timeout = 0;
1f849790 60
360f3dc2
YW
61STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep);
62
1d58a1fe
LP
63#define NAME_IS_ACQUIRED INT_TO_PTR(1)
64#define NAME_IS_ACTIVATABLE INT_TO_PTR(2)
65
2de62253
LP
66static int json_transform_message(sd_bus_message *m, JsonVariant **ret);
67static void json_dump_with_flags(JsonVariant *v, FILE *f);
68
9bb31a0c
YW
69static int acquire_bus(bool set_monitor, sd_bus **ret) {
70 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
71 int r;
72
73 r = sd_bus_new(&bus);
74 if (r < 0)
75 return log_error_errno(r, "Failed to allocate bus: %m");
76
77 if (set_monitor) {
78 r = sd_bus_set_monitor(bus, true);
79 if (r < 0)
80 return log_error_errno(r, "Failed to set monitor mode: %m");
81
82 r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL);
83 if (r < 0)
84 return log_error_errno(r, "Failed to enable credentials: %m");
85
86 r = sd_bus_negotiate_timestamp(bus, true);
87 if (r < 0)
88 return log_error_errno(r, "Failed to enable timestamps: %m");
89
90 r = sd_bus_negotiate_fds(bus, true);
91 if (r < 0)
92 return log_error_errno(r, "Failed to enable fds: %m");
93 }
94
95 r = sd_bus_set_bus_client(bus, true);
96 if (r < 0)
97 return log_error_errno(r, "Failed to set bus client: %m");
98
99 r = sd_bus_set_watch_bind(bus, arg_watch_bind);
100 if (r < 0)
101 return log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", yes_no(arg_watch_bind));
102
103 if (arg_address)
104 r = sd_bus_set_address(bus, arg_address);
105 else {
106 switch (arg_transport) {
107
108 case BUS_TRANSPORT_LOCAL:
109 if (arg_user) {
110 bus->is_user = true;
111 r = bus_set_address_user(bus);
112 } else {
113 bus->is_system = true;
114 r = bus_set_address_system(bus);
115 }
116 break;
117
118 case BUS_TRANSPORT_REMOTE:
119 r = bus_set_address_system_remote(bus, arg_host);
120 break;
121
122 case BUS_TRANSPORT_MACHINE:
123 r = bus_set_address_system_machine(bus, arg_host);
124 break;
125
126 default:
127 assert_not_reached("Hmm, unknown transport type.");
128 }
129 }
130 if (r < 0)
131 return log_error_errno(r, "Failed to set address: %m");
132
133 r = sd_bus_start(bus);
134 if (r < 0)
135 return log_error_errno(r, "Failed to connect to bus: %m");
136
1cc6c93a 137 *ret = TAKE_PTR(bus);
9bb31a0c
YW
138
139 return 0;
140}
141
142static int list_bus_names(int argc, char **argv, void *userdata) {
143 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
71f2ab46 144 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
145 _cleanup_free_ char **merged = NULL;
146 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
147 char **i;
148 int r;
89ffcd2a 149 size_t max_i = 0;
5e2f14e6
LP
150 unsigned n = 0;
151 void *v;
152 char *k;
153 Iterator iterator;
de1c301e 154
d9130355
LP
155 if (!arg_unique && !arg_acquired && !arg_activatable)
156 arg_unique = arg_acquired = arg_activatable = true;
157
9bb31a0c
YW
158 r = acquire_bus(false, &bus);
159 if (r < 0)
160 return r;
161
56e61788 162 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
23bbb0de
MS
163 if (r < 0)
164 return log_error_errno(r, "Failed to list names: %m");
de1c301e 165
0221d68a 166 (void) pager_open(arg_pager_flags);
1f849790 167
d5099efc 168 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
169 if (!names)
170 return log_oom();
89ffcd2a 171
5e2f14e6 172 STRV_FOREACH(i, acquired) {
71f2ab46
LP
173 max_i = MAX(max_i, strlen(*i));
174
1d58a1fe 175 r = hashmap_put(names, *i, NAME_IS_ACQUIRED);
23bbb0de
MS
176 if (r < 0)
177 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
178 }
179
180 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
181 max_i = MAX(max_i, strlen(*i));
182
1d58a1fe 183 r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE);
23bbb0de
MS
184 if (r < 0 && r != -EEXIST)
185 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
186 }
187
188 merged = new(char*, hashmap_size(names) + 1);
cb05d2a5
YW
189 if (!merged)
190 return log_oom();
191
5e2f14e6
LP
192 HASHMAP_FOREACH_KEY(v, k, names, iterator)
193 merged[n++] = k;
194
195 merged[n] = NULL;
196 strv_sort(merged);
197
17d47d8d
TA
198 if (arg_legend) {
199 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
455971c1 200 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
89ffcd2a 201
17d47d8d
TA
202 if (arg_show_machine)
203 puts(" MACHINE");
204 else
205 putchar('\n');
206 }
a4297f08 207
5e2f14e6 208 STRV_FOREACH(i, merged) {
4afd3348 209 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
5e2f14e6 210 sd_id128_t mid;
71f2ab46 211
1d58a1fe 212 if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) {
5e2f14e6 213 /* Activatable */
71f2ab46 214
5e2f14e6 215 printf("%-*s", (int) max_i, *i);
56e61788
LP
216 printf(" - - - (activatable) - - ");
217 if (arg_show_machine)
5e2f14e6 218 puts(" -");
56e61788
LP
219 else
220 putchar('\n');
71f2ab46
LP
221 continue;
222
5e2f14e6 223 }
de1c301e 224
56e61788
LP
225 if (!arg_unique && (*i)[0] == ':')
226 continue;
227
228 if (!arg_acquired && (*i)[0] != ':')
1f849790 229 continue;
89ffcd2a
LP
230
231 printf("%-*s", (int) max_i, *i);
de1c301e 232
40ed1a45
LP
233 r = sd_bus_get_name_creds(
234 bus, *i,
235 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) |
05bae4a6 236 SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
40ed1a45
LP
237 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
238 SD_BUS_CREDS_DESCRIPTION, &creds);
89ffcd2a 239 if (r >= 0) {
14008e4e 240 const char *unique, *session, *unit, *cn;
5b12334d
LP
241 pid_t pid;
242 uid_t uid;
de1c301e 243
5b12334d
LP
244 r = sd_bus_creds_get_pid(creds, &pid);
245 if (r >= 0) {
246 const char *comm = NULL;
de1c301e 247
5b12334d 248 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 249
5b12334d
LP
250 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
251 } else
a4297f08 252 fputs(" - - ", stdout);
89ffcd2a 253
05bae4a6 254 r = sd_bus_creds_get_euid(creds, &uid);
5b12334d
LP
255 if (r >= 0) {
256 _cleanup_free_ char *u = NULL;
89ffcd2a 257
5b12334d
LP
258 u = uid_to_name(uid);
259 if (!u)
260 return log_oom();
89ffcd2a 261
5b12334d
LP
262 if (strlen(u) > 16)
263 u[16] = 0;
264
265 printf(" %-16s", u);
266 } else
a4297f08 267 fputs(" - ", stdout);
89ffcd2a 268
49b832c5
LP
269 r = sd_bus_creds_get_unique_name(creds, &unique);
270 if (r >= 0)
56e61788
LP
271 printf(" %-13s", unique);
272 else
273 fputs(" - ", stdout);
274
275 r = sd_bus_creds_get_unit(creds, &unit);
276 if (r >= 0) {
277 _cleanup_free_ char *e;
278
279 e = ellipsize(unit, 25, 100);
280 if (!e)
281 return log_oom();
282
283 printf(" %-25s", e);
284 } else
285 fputs(" - ", stdout);
286
287 r = sd_bus_creds_get_session(creds, &session);
288 if (r >= 0)
289 printf(" %-10s", session);
a4297f08 290 else
56e61788 291 fputs(" - ", stdout);
7b0b392f 292
455971c1 293 r = sd_bus_creds_get_description(creds, &cn);
14008e4e
LP
294 if (r >= 0)
295 printf(" %-19s", cn);
296 else
297 fputs(" - ", stdout);
298
7b0b392f 299 } else
14008e4e 300 printf(" - - - - - - - ");
a4297f08 301
56e61788 302 if (arg_show_machine) {
056f95d0 303 r = sd_bus_get_name_machine_id(bus, *i, &mid);
a4297f08
LP
304 if (r >= 0) {
305 char m[SD_ID128_STRING_MAX];
306 printf(" %s\n", sd_id128_to_string(mid, m));
307 } else
308 puts(" -");
56e61788
LP
309 } else
310 putchar('\n');
de1c301e
LP
311 }
312
1f849790
LP
313 return 0;
314}
315
d9130355
LP
316static void print_subtree(const char *prefix, const char *path, char **l) {
317 const char *vertical, *space;
318 char **n;
319
320 /* We assume the list is sorted. Let's first skip over the
321 * entry we are looking at. */
322 for (;;) {
323 if (!*l)
324 return;
325
326 if (!streq(*l, path))
327 break;
328
329 l++;
330 }
331
323b7dc9
ZJS
332 vertical = strjoina(prefix, special_glyph(TREE_VERTICAL));
333 space = strjoina(prefix, special_glyph(TREE_SPACE));
d9130355
LP
334
335 for (;;) {
336 bool has_more = false;
337
338 if (!*l || !path_startswith(*l, path))
339 break;
340
341 n = l + 1;
342 for (;;) {
343 if (!*n || !path_startswith(*n, path))
344 break;
345
346 if (!path_startswith(*n, *l)) {
347 has_more = true;
348 break;
349 }
350
351 n++;
352 }
353
323b7dc9 354 printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l);
d9130355
LP
355
356 print_subtree(has_more ? vertical : space, *l, l);
357 l = n;
358 }
359}
360
361static void print_tree(const char *prefix, char **l) {
362
d9130355
LP
363 prefix = strempty(prefix);
364
365 if (arg_list) {
366 char **i;
367
368 STRV_FOREACH(i, l)
369 printf("%s%s\n", prefix, *i);
370 return;
371 }
372
56c8b52d
LP
373 if (strv_isempty(l)) {
374 printf("No objects discovered.\n");
375 return;
376 }
377
378 if (streq(l[0], "/") && !l[1]) {
379 printf("Only root object discovered.\n");
380 return;
381 }
d9130355
LP
382
383 print_subtree(prefix, "/", l);
384}
385
a1ad3767
LP
386static int on_path(const char *path, void *userdata) {
387 Set *paths = userdata;
d9130355
LP
388 int r;
389
a1ad3767 390 assert(paths);
d9130355 391
a1ad3767
LP
392 r = set_put_strdup(paths, path);
393 if (r < 0)
394 return log_oom();
d9130355 395
a1ad3767 396 return 0;
d9130355
LP
397}
398
73fc23c0 399static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) {
0171da06 400 static const XMLIntrospectOps ops = {
a1ad3767
LP
401 .on_path = on_path,
402 };
403
4afd3348
LP
404 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
405 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a1ad3767 406 const char *xml;
d9130355
LP
407 int r;
408
409 r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
410 if (r < 0) {
73fc23c0
LP
411 if (many)
412 printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r));
413 else
48956c39 414 log_error_errno(r, "Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r));
d9130355
LP
415 return r;
416 }
417
418 r = sd_bus_message_read(reply, "s", &xml);
419 if (r < 0)
420 return bus_log_parse_error(r);
421
a1ad3767 422 return parse_xml_introspect(path, xml, &ops, paths);
d9130355
LP
423}
424
73fc23c0 425static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) {
d9130355
LP
426 _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
427 _cleanup_free_ char **l = NULL;
428 char *m;
429 int r;
430
431 paths = set_new(&string_hash_ops);
432 if (!paths)
433 return log_oom();
434
435 done = set_new(&string_hash_ops);
436 if (!done)
437 return log_oom();
438
439 failed = set_new(&string_hash_ops);
440 if (!failed)
441 return log_oom();
442
443 m = strdup("/");
444 if (!m)
445 return log_oom();
446
447 r = set_put(paths, m);
448 if (r < 0) {
449 free(m);
450 return log_oom();
451 }
452
453 for (;;) {
454 _cleanup_free_ char *p = NULL;
455 int q;
456
457 p = set_steal_first(paths);
458 if (!p)
459 break;
460
461 if (set_contains(done, p) ||
462 set_contains(failed, p))
463 continue;
464
73fc23c0 465 q = find_nodes(bus, service, p, paths, many);
d9130355
LP
466 if (q < 0) {
467 if (r >= 0)
468 r = q;
469
470 q = set_put(failed, p);
471 } else
472 q = set_put(done, p);
473
474 if (q < 0)
475 return log_oom();
476
477 assert(q != 0);
478 p = NULL;
479 }
480
0221d68a 481 (void) pager_open(arg_pager_flags);
73fc23c0 482
d9130355
LP
483 l = set_get_strv(done);
484 if (!l)
485 return log_oom();
486
487 strv_sort(l);
488 print_tree(prefix, l);
489
490 fflush(stdout);
491
492 return r;
493}
494
9bb31a0c
YW
495static int tree(int argc, char **argv, void *userdata) {
496 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d9130355
LP
497 char **i;
498 int r = 0;
499
500 if (!arg_unique && !arg_acquired)
501 arg_acquired = true;
502
9bb31a0c
YW
503 r = acquire_bus(false, &bus);
504 if (r < 0)
505 return r;
506
507 if (argc <= 1) {
d9130355 508 _cleanup_strv_free_ char **names = NULL;
56c8b52d 509 bool not_first = false;
d9130355
LP
510
511 r = sd_bus_list_names(bus, &names, NULL);
23bbb0de
MS
512 if (r < 0)
513 return log_error_errno(r, "Failed to get name list: %m");
d9130355 514
0221d68a 515 (void) pager_open(arg_pager_flags);
d9130355
LP
516
517 STRV_FOREACH(i, names) {
518 int q;
519
520 if (!arg_unique && (*i)[0] == ':')
521 continue;
522
523 if (!arg_acquired && (*i)[0] == ':')
524 continue;
525
526 if (not_first)
527 printf("\n");
528
1fc464f6 529 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
d9130355 530
73fc23c0 531 q = tree_one(bus, *i, NULL, true);
d9130355
LP
532 if (q < 0 && r >= 0)
533 r = q;
534
535 not_first = true;
536 }
537 } else {
d9130355
LP
538 STRV_FOREACH(i, argv+1) {
539 int q;
540
541 if (i > argv+1)
542 printf("\n");
543
73fc23c0 544 if (argv[2]) {
0221d68a 545 (void) pager_open(arg_pager_flags);
1fc464f6 546 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
73fc23c0 547 }
d9130355 548
73fc23c0 549 q = tree_one(bus, *i, NULL, !!argv[2]);
d9130355
LP
550 if (q < 0 && r >= 0)
551 r = q;
552 }
553 }
554
555 return r;
556}
557
1fc55609
LP
558static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) {
559 int r;
560
561 for (;;) {
562 const char *contents = NULL;
563 char type;
564 union {
565 uint8_t u8;
566 uint16_t u16;
567 int16_t s16;
568 uint32_t u32;
569 int32_t s32;
570 uint64_t u64;
571 int64_t s64;
572 double d64;
573 const char *string;
574 int i;
575 } basic;
576
577 r = sd_bus_message_peek_type(m, &type, &contents);
317f2fc9 578 if (r < 0)
1fc55609 579 return r;
317f2fc9
LP
580 if (r == 0)
581 return needs_space;
1fc55609
LP
582
583 if (bus_type_is_container(type) > 0) {
584
585 r = sd_bus_message_enter_container(m, type, contents);
586 if (r < 0)
587 return r;
588
589 if (type == SD_BUS_TYPE_ARRAY) {
590 unsigned n = 0;
591
592 /* count array entries */
593 for (;;) {
594
595 r = sd_bus_message_skip(m, contents);
596 if (r < 0)
597 return r;
598 if (r == 0)
599 break;
600
601 n++;
602 }
603
604 r = sd_bus_message_rewind(m, false);
605 if (r < 0)
606 return r;
607
608 if (needs_space)
609 fputc(' ', f);
610
611 fprintf(f, "%u", n);
317f2fc9
LP
612 needs_space = true;
613
1fc55609
LP
614 } else if (type == SD_BUS_TYPE_VARIANT) {
615
616 if (needs_space)
617 fputc(' ', f);
618
619 fprintf(f, "%s", contents);
317f2fc9 620 needs_space = true;
1fc55609
LP
621 }
622
317f2fc9 623 r = format_cmdline(m, f, needs_space);
1fc55609
LP
624 if (r < 0)
625 return r;
626
317f2fc9
LP
627 needs_space = r > 0;
628
1fc55609
LP
629 r = sd_bus_message_exit_container(m);
630 if (r < 0)
631 return r;
632
633 continue;
634 }
635
636 r = sd_bus_message_read_basic(m, type, &basic);
637 if (r < 0)
638 return r;
639
640 if (needs_space)
641 fputc(' ', f);
642
643 switch (type) {
644 case SD_BUS_TYPE_BYTE:
645 fprintf(f, "%u", basic.u8);
646 break;
647
648 case SD_BUS_TYPE_BOOLEAN:
649 fputs(true_false(basic.i), f);
650 break;
651
652 case SD_BUS_TYPE_INT16:
653 fprintf(f, "%i", basic.s16);
654 break;
655
656 case SD_BUS_TYPE_UINT16:
657 fprintf(f, "%u", basic.u16);
658 break;
659
660 case SD_BUS_TYPE_INT32:
661 fprintf(f, "%i", basic.s32);
662 break;
663
664 case SD_BUS_TYPE_UINT32:
665 fprintf(f, "%u", basic.u32);
666 break;
667
668 case SD_BUS_TYPE_INT64:
669 fprintf(f, "%" PRIi64, basic.s64);
670 break;
671
672 case SD_BUS_TYPE_UINT64:
673 fprintf(f, "%" PRIu64, basic.u64);
674 break;
675
676 case SD_BUS_TYPE_DOUBLE:
677 fprintf(f, "%g", basic.d64);
678 break;
679
680 case SD_BUS_TYPE_STRING:
681 case SD_BUS_TYPE_OBJECT_PATH:
682 case SD_BUS_TYPE_SIGNATURE: {
683 _cleanup_free_ char *b = NULL;
684
685 b = cescape(basic.string);
686 if (!b)
687 return -ENOMEM;
688
689 fprintf(f, "\"%s\"", b);
690 break;
691 }
692
693 case SD_BUS_TYPE_UNIX_FD:
694 fprintf(f, "%i", basic.i);
695 break;
696
697 default:
698 assert_not_reached("Unknown basic type.");
699 }
700
1ed24c61 701 needs_space = true;
1fc55609
LP
702 }
703}
704
0171da06
LP
705typedef struct Member {
706 const char *type;
707 char *interface;
708 char *name;
709 char *signature;
710 char *result;
1fc55609 711 char *value;
0171da06
LP
712 bool writable;
713 uint64_t flags;
714} Member;
715
b826ab58 716static void member_hash_func(const void *p, struct siphash *state) {
0171da06 717 const Member *m = p;
1e2527a6 718 uint64_t arity = 1;
0171da06
LP
719
720 assert(m);
721 assert(m->type);
722
b826ab58 723 string_hash_func(m->type, state);
0171da06 724
1e2527a6
TG
725 arity += !!m->name + !!m->interface;
726
727 uint64_hash_func(&arity, state);
728
0171da06 729 if (m->name)
b826ab58 730 string_hash_func(m->name, state);
0171da06
LP
731
732 if (m->interface)
b826ab58 733 string_hash_func(m->interface, state);
0171da06
LP
734}
735
dc5f9c6f 736static int member_compare_func(const Member *x, const Member *y) {
0171da06
LP
737 int d;
738
739 assert(x);
740 assert(y);
741 assert(x->type);
742 assert(y->type);
743
c030a850
NK
744 d = strcmp_ptr(x->interface, y->interface);
745 if (d != 0)
746 return d;
0171da06
LP
747
748 d = strcmp(x->type, y->type);
749 if (d != 0)
750 return d;
751
c030a850 752 return strcmp_ptr(x->name, y->name);
0171da06
LP
753}
754
93bab288
YW
755static int member_compare_funcp(Member * const *a, Member * const *b) {
756 return member_compare_func(*a, *b);
0171da06
LP
757}
758
759static void member_free(Member *m) {
760 if (!m)
761 return;
762
763 free(m->interface);
764 free(m->name);
765 free(m->signature);
766 free(m->result);
1fc55609 767 free(m->value);
0171da06
LP
768 free(m);
769}
770
771DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
772
773static void member_set_free(Set *s) {
224b0e7a 774 set_free_with_destructor(s, member_free);
0171da06
LP
775}
776
777DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
778
779static int on_interface(const char *interface, uint64_t flags, void *userdata) {
780 _cleanup_(member_freep) Member *m;
781 Set *members = userdata;
782 int r;
783
784 assert(interface);
785 assert(members);
786
787 m = new0(Member, 1);
788 if (!m)
789 return log_oom();
790
791 m->type = "interface";
792 m->flags = flags;
793
794 r = free_and_strdup(&m->interface, interface);
795 if (r < 0)
796 return log_oom();
797
798 r = set_put(members, m);
799 if (r <= 0) {
800 log_error("Duplicate interface");
801 return -EINVAL;
802 }
803
804 m = NULL;
805 return 0;
806}
807
808static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
809 _cleanup_(member_freep) Member *m;
810 Set *members = userdata;
811 int r;
812
813 assert(interface);
814 assert(name);
815
816 m = new0(Member, 1);
817 if (!m)
818 return log_oom();
819
820 m->type = "method";
821 m->flags = flags;
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 = free_and_strdup(&m->result, result);
836 if (r < 0)
837 return log_oom();
838
839 r = set_put(members, m);
840 if (r <= 0) {
841 log_error("Duplicate method");
842 return -EINVAL;
843 }
844
845 m = NULL;
846 return 0;
847}
848
849static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
850 _cleanup_(member_freep) Member *m;
851 Set *members = userdata;
852 int r;
853
854 assert(interface);
855 assert(name);
856
857 m = new0(Member, 1);
858 if (!m)
859 return log_oom();
860
861 m->type = "signal";
862 m->flags = flags;
863
864 r = free_and_strdup(&m->interface, interface);
865 if (r < 0)
866 return log_oom();
867
868 r = free_and_strdup(&m->name, name);
869 if (r < 0)
870 return log_oom();
871
872 r = free_and_strdup(&m->signature, signature);
873 if (r < 0)
874 return log_oom();
875
876 r = set_put(members, m);
877 if (r <= 0) {
878 log_error("Duplicate signal");
879 return -EINVAL;
880 }
881
882 m = NULL;
883 return 0;
884}
885
886static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
887 _cleanup_(member_freep) Member *m;
888 Set *members = userdata;
889 int r;
890
891 assert(interface);
892 assert(name);
893
894 m = new0(Member, 1);
895 if (!m)
896 return log_oom();
897
898 m->type = "property";
899 m->flags = flags;
900 m->writable = writable;
901
902 r = free_and_strdup(&m->interface, interface);
903 if (r < 0)
904 return log_oom();
905
906 r = free_and_strdup(&m->name, name);
907 if (r < 0)
908 return log_oom();
909
910 r = free_and_strdup(&m->signature, signature);
911 if (r < 0)
912 return log_oom();
913
914 r = set_put(members, m);
915 if (r <= 0) {
916 log_error("Duplicate property");
917 return -EINVAL;
918 }
919
920 m = NULL;
921 return 0;
922}
923
9bb31a0c 924static int introspect(int argc, char **argv, void *userdata) {
0171da06
LP
925 static const struct hash_ops member_hash_ops = {
926 .hash = member_hash_func,
dc5f9c6f 927 .compare = (__compar_fn_t) member_compare_func,
0171da06
LP
928 };
929
930 static const XMLIntrospectOps ops = {
931 .on_interface = on_interface,
932 .on_method = on_method,
933 .on_signal = on_signal,
934 .on_property = on_property,
935 };
936
9bb31a0c 937 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
9efebb65 938 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_xml = NULL;
4afd3348 939 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
0171da06 940 _cleanup_(member_set_freep) Set *members = NULL;
9bb31a0c
YW
941 unsigned name_width, type_width, signature_width, result_width, j, k = 0;
942 Member *m, **sorted = NULL;
0171da06 943 Iterator i;
0171da06
LP
944 const char *xml;
945 int r;
0171da06 946
9bb31a0c
YW
947 r = acquire_bus(false, &bus);
948 if (r < 0)
949 return r;
4f44c03e 950
0171da06
LP
951 members = set_new(&member_hash_ops);
952 if (!members)
953 return log_oom();
954
9efebb65 955 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply_xml, "");
e51faad3
LP
956 if (r < 0)
957 return log_error_errno(r, "Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
0171da06 958
9efebb65 959 r = sd_bus_message_read(reply_xml, "s", &xml);
0171da06
LP
960 if (r < 0)
961 return bus_log_parse_error(r);
962
1fc55609 963 /* First, get list of all properties */
0171da06
LP
964 r = parse_xml_introspect(argv[2], xml, &ops, members);
965 if (r < 0)
966 return r;
967
1fc55609
LP
968 /* Second, find the current values for them */
969 SET_FOREACH(m, members, i) {
9efebb65 970 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
971
972 if (!streq(m->type, "property"))
973 continue;
974
975 if (m->value)
976 continue;
977
4f44c03e
LP
978 if (argv[3] && !streq(argv[3], m->interface))
979 continue;
980
1fc55609 981 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
e51faad3
LP
982 if (r < 0)
983 return log_error_errno(r, "%s", bus_error_message(&error, r));
1fc55609
LP
984
985 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
986 if (r < 0)
987 return bus_log_parse_error(r);
988
989 for (;;) {
990 Member *z;
991 _cleanup_free_ char *buf = NULL;
992 _cleanup_fclose_ FILE *mf = NULL;
993 size_t sz = 0;
994 const char *name;
995
996 r = sd_bus_message_enter_container(reply, 'e', "sv");
997 if (r < 0)
998 return bus_log_parse_error(r);
999
1000 if (r == 0)
1001 break;
1002
1003 r = sd_bus_message_read(reply, "s", &name);
1004 if (r < 0)
1005 return bus_log_parse_error(r);
1006
1007 r = sd_bus_message_enter_container(reply, 'v', NULL);
1008 if (r < 0)
1009 return bus_log_parse_error(r);
1010
1011 mf = open_memstream(&buf, &sz);
1012 if (!mf)
1013 return log_oom();
1014
0d536673
LP
1015 (void) __fsetlocking(mf, FSETLOCKING_BYCALLER);
1016
1fc55609
LP
1017 r = format_cmdline(reply, mf, false);
1018 if (r < 0)
1019 return bus_log_parse_error(r);
1020
8d3b9edc 1021 mf = safe_fclose(mf);
1fc55609
LP
1022
1023 z = set_get(members, &((Member) {
1024 .type = "property",
1025 .interface = m->interface,
1026 .name = (char*) name }));
8d3b9edc
LP
1027 if (z)
1028 free_and_replace(z->value, buf);
1fc55609
LP
1029
1030 r = sd_bus_message_exit_container(reply);
1031 if (r < 0)
1032 return bus_log_parse_error(r);
1033
1034 r = sd_bus_message_exit_container(reply);
1035 if (r < 0)
1036 return bus_log_parse_error(r);
1037 }
1038
1039 r = sd_bus_message_exit_container(reply);
1040 if (r < 0)
1041 return bus_log_parse_error(r);
1042 }
1043
0221d68a 1044 (void) pager_open(arg_pager_flags);
0171da06 1045
fbd0b64f
LP
1046 name_width = STRLEN("NAME");
1047 type_width = STRLEN("TYPE");
1048 signature_width = STRLEN("SIGNATURE");
1049 result_width = STRLEN("RESULT/VALUE");
0171da06
LP
1050
1051 sorted = newa(Member*, set_size(members));
1052
1053 SET_FOREACH(m, members, i) {
4f44c03e
LP
1054
1055 if (argv[3] && !streq(argv[3], m->interface))
1056 continue;
1057
0171da06
LP
1058 if (m->interface)
1059 name_width = MAX(name_width, strlen(m->interface));
1060 if (m->name)
1061 name_width = MAX(name_width, strlen(m->name) + 1);
1062 if (m->type)
1063 type_width = MAX(type_width, strlen(m->type));
1064 if (m->signature)
1065 signature_width = MAX(signature_width, strlen(m->signature));
1066 if (m->result)
1067 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1068 if (m->value)
1069 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1070
1071 sorted[k++] = m;
1072 }
1073
1fc55609
LP
1074 if (result_width > 40)
1075 result_width = 40;
1076
93bab288 1077 typesafe_qsort(sorted, k, member_compare_funcp);
0171da06 1078
1fc55609
LP
1079 if (arg_legend) {
1080 printf("%-*s %-*s %-*s %-*s %s\n",
1081 (int) name_width, "NAME",
1082 (int) type_width, "TYPE",
1083 (int) signature_width, "SIGNATURE",
1084 (int) result_width, "RESULT/VALUE",
1085 "FLAGS");
1086 }
0171da06
LP
1087
1088 for (j = 0; j < k; j++) {
1fc55609
LP
1089 _cleanup_free_ char *ellipsized = NULL;
1090 const char *rv;
0171da06
LP
1091 bool is_interface;
1092
1093 m = sorted[j];
1094
4f44c03e
LP
1095 if (argv[3] && !streq(argv[3], m->interface))
1096 continue;
1097
0171da06
LP
1098 is_interface = streq(m->type, "interface");
1099
4f44c03e
LP
1100 if (argv[3] && is_interface)
1101 continue;
1102
1fc55609
LP
1103 if (m->value) {
1104 ellipsized = ellipsize(m->value, result_width, 100);
1105 if (!ellipsized)
1106 return log_oom();
1107
1108 rv = ellipsized;
1109 } else
c5984fe1 1110 rv = empty_to_dash(m->result);
1fc55609 1111
0171da06
LP
1112 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1113 is_interface ? ansi_highlight() : "",
1114 is_interface ? "" : ".",
c5984fe1 1115 - !is_interface + (int) name_width, empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1fc464f6 1116 is_interface ? ansi_normal() : "",
c5984fe1
YW
1117 (int) type_width, empty_to_dash(m->type),
1118 (int) signature_width, empty_to_dash(m->signature),
1fc55609 1119 (int) result_width, rv,
0171da06
LP
1120 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1121 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1122 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1123 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1124 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1125 m->writable ? " writable" : "");
1126 }
1127
1128 return 0;
1129}
1130
1f70b087 1131static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 1132 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1133}
1134
1135static int message_pcap(sd_bus_message *m, FILE *f) {
1136 return bus_message_pcap_frame(m, arg_snaplen, f);
1137}
1138
2de62253
LP
1139static int message_json(sd_bus_message *m, FILE *f) {
1140 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
1141 char e[2];
1142 int r;
1143
1144 r = json_transform_message(m, &v);
1145 if (r < 0)
1146 return r;
1147
1148 e[0] = m->header->endian;
1149 e[1] = 0;
1150
1151 r = json_build(&w, JSON_BUILD_OBJECT(
1152 JSON_BUILD_PAIR("type", JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))),
1153 JSON_BUILD_PAIR("endian", JSON_BUILD_STRING(e)),
1154 JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(m->header->flags)),
1155 JSON_BUILD_PAIR("version", JSON_BUILD_INTEGER(m->header->version)),
3919bc24 1156 JSON_BUILD_PAIR_CONDITION(m->priority != 0, "priority", JSON_BUILD_INTEGER(m->priority)),
2de62253 1157 JSON_BUILD_PAIR("cookie", JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))),
3919bc24
LP
1158 JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", JSON_BUILD_INTEGER(m->reply_cookie)),
1159 JSON_BUILD_PAIR_CONDITION(m->sender, "sender", JSON_BUILD_STRING(m->sender)),
1160 JSON_BUILD_PAIR_CONDITION(m->destination, "destination", JSON_BUILD_STRING(m->destination)),
1161 JSON_BUILD_PAIR_CONDITION(m->path, "path", JSON_BUILD_STRING(m->path)),
1162 JSON_BUILD_PAIR_CONDITION(m->interface, "interface", JSON_BUILD_STRING(m->interface)),
1163 JSON_BUILD_PAIR_CONDITION(m->member, "member", JSON_BUILD_STRING(m->member)),
1164 JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", JSON_BUILD_INTEGER(m->monotonic)),
1165 JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", JSON_BUILD_INTEGER(m->realtime)),
1166 JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", JSON_BUILD_INTEGER(m->seqnum)),
1167 JSON_BUILD_PAIR_CONDITION(m->error.name, "error_name", JSON_BUILD_STRING(m->error.name)),
2de62253
LP
1168 JSON_BUILD_PAIR("payload", JSON_BUILD_VARIANT(v))));
1169 if (r < 0)
1170 return log_error_errno(r, "Failed to build JSON object: %m");
1171
1172 json_dump_with_flags(w, f);
1173 return 0;
1174}
1175
9bb31a0c
YW
1176static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f)) {
1177 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
17fd7460
LU
1178 _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
1179 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1f849790 1180 char **i;
17fd7460 1181 uint32_t flags = 0;
f5938e8f
TG
1182 const char *unique_name;
1183 bool is_monitor = false;
1f849790
LP
1184 int r;
1185
9bb31a0c
YW
1186 r = acquire_bus(true, &bus);
1187 if (r < 0)
1188 return r;
1189
17fd7460
LU
1190 /* upgrade connection; it's not used for anything else after this call */
1191 r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor");
1192 if (r < 0)
1193 return bus_log_create_error(r);
1194
1195 r = sd_bus_message_open_container(message, 'a', "s");
1196 if (r < 0)
1197 return bus_log_create_error(r);
1198
1f849790
LP
1199 STRV_FOREACH(i, argv+1) {
1200 _cleanup_free_ char *m = NULL;
1201
1202 if (!service_name_is_valid(*i)) {
1203 log_error("Invalid service name '%s'", *i);
1204 return -EINVAL;
1205 }
1206
605405c6 1207 m = strjoin("sender='", *i, "'");
1f849790
LP
1208 if (!m)
1209 return log_oom();
1210
17fd7460 1211 r = sd_bus_message_append_basic(message, 's', m);
23bbb0de 1212 if (r < 0)
17fd7460 1213 return bus_log_create_error(r);
b51f299a 1214
f6d1e6cb 1215 free(m);
605405c6 1216 m = strjoin("destination='", *i, "'");
f6d1e6cb
LU
1217 if (!m)
1218 return log_oom();
1219
17fd7460 1220 r = sd_bus_message_append_basic(message, 's', m);
f6d1e6cb 1221 if (r < 0)
17fd7460 1222 return bus_log_create_error(r);
1f849790
LP
1223 }
1224
1225 STRV_FOREACH(i, arg_matches) {
17fd7460 1226 r = sd_bus_message_append_basic(message, 's', *i);
23bbb0de 1227 if (r < 0)
17fd7460 1228 return bus_log_create_error(r);
b51f299a
LP
1229 }
1230
17fd7460
LU
1231 r = sd_bus_message_close_container(message);
1232 if (r < 0)
1233 return bus_log_create_error(r);
1234
1235 r = sd_bus_message_append_basic(message, 'u', &flags);
1236 if (r < 0)
1237 return bus_log_create_error(r);
1238
1239 r = sd_bus_call(bus, message, arg_timeout, &error, NULL);
48956c39
LP
1240 if (r < 0)
1241 return log_error_errno(r, "%s", bus_error_message(&error, r));
1f849790 1242
f5938e8f
TG
1243 r = sd_bus_get_unique_name(bus, &unique_name);
1244 if (r < 0)
1245 return log_error_errno(r, "Failed to get unique name: %m");
1246
92d66625
LP
1247 log_info("Monitoring bus message stream.");
1248
1f849790 1249 for (;;) {
4afd3348 1250 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1f849790
LP
1251
1252 r = sd_bus_process(bus, &m);
23bbb0de
MS
1253 if (r < 0)
1254 return log_error_errno(r, "Failed to process bus: %m");
1f849790 1255
f5938e8f
TG
1256 if (!is_monitor) {
1257 const char *name;
1258
1259 /* wait until we lose our unique name */
1260 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameLost") <= 0)
1261 continue;
1262
1263 r = sd_bus_message_read(m, "s", &name);
1264 if (r < 0)
1265 return log_error_errno(r, "Failed to read lost name: %m");
1266
d27d4637
TG
1267 if (streq(name, unique_name))
1268 is_monitor = true;
f5938e8f 1269
d27d4637 1270 continue;
f5938e8f
TG
1271 }
1272
1f849790 1273 if (m) {
1f70b087 1274 dump(m, stdout);
1d44f758 1275 fflush(stdout);
92d66625
LP
1276
1277 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
1278 log_info("Connection terminated, exiting.");
1279 return 0;
1280 }
1281
1f849790
LP
1282 continue;
1283 }
1284
1285 if (r > 0)
1286 continue;
1287
1288 r = sd_bus_wait(bus, (uint64_t) -1);
23bbb0de
MS
1289 if (r < 0)
1290 return log_error_errno(r, "Failed to wait for bus: %m");
1f849790 1291 }
95c4fe82 1292}
1f849790 1293
9bb31a0c 1294static int verb_monitor(int argc, char **argv, void *userdata) {
2de62253 1295 return monitor(argc, argv, arg_json != JSON_OFF ? message_json : message_dump);
9bb31a0c
YW
1296}
1297
1298static int verb_capture(int argc, char **argv, void *userdata) {
1f70b087
LP
1299 int r;
1300
baaa35ad
ZJS
1301 if (isatty(fileno(stdout)) > 0)
1302 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1303 "Refusing to write message data to console, please redirect output to a file.");
1f70b087
LP
1304
1305 bus_pcap_header(arg_snaplen, stdout);
1306
9bb31a0c 1307 r = monitor(argc, argv, message_pcap);
1f70b087
LP
1308 if (r < 0)
1309 return r;
1310
6b0f5484
LP
1311 r = fflush_and_check(stdout);
1312 if (r < 0)
1313 return log_error_errno(r, "Couldn't write capture file: %m");
1f70b087
LP
1314
1315 return r;
1316}
1317
9bb31a0c
YW
1318static int status(int argc, char **argv, void *userdata) {
1319 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 1320 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
95c4fe82
LP
1321 pid_t pid;
1322 int r;
1323
9bb31a0c
YW
1324 r = acquire_bus(false, &bus);
1325 if (r < 0)
1326 return r;
95c4fe82 1327
9bb31a0c 1328 if (!isempty(argv[1])) {
2e9efd22
LP
1329 r = parse_pid(argv[1], &pid);
1330 if (r < 0)
1331 r = sd_bus_get_name_creds(
1332 bus,
1333 argv[1],
1334 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1335 &creds);
1336 else
1337 r = sd_bus_creds_new_from_pid(
1338 &creds,
1339 pid,
1340 _SD_BUS_CREDS_ALL);
3acc1daf 1341 } else {
5b820358 1342 const char *scope, *address;
5c302692 1343 sd_id128_t bus_id;
3acc1daf 1344
5b820358
LP
1345 r = sd_bus_get_address(bus, &address);
1346 if (r >= 0)
1fc464f6 1347 printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal());
5b820358 1348
3acc1daf
LP
1349 r = sd_bus_get_scope(bus, &scope);
1350 if (r >= 0)
1fc464f6 1351 printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal());
3acc1daf 1352
5c302692
LP
1353 r = sd_bus_get_bus_id(bus, &bus_id);
1354 if (r >= 0)
1fc464f6 1355 printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
5c302692 1356
2e9efd22 1357 r = sd_bus_get_owner_creds(
40ed1a45 1358 bus,
40ed1a45
LP
1359 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1360 &creds);
3acc1daf 1361 }
95c4fe82 1362
23bbb0de
MS
1363 if (r < 0)
1364 return log_error_errno(r, "Failed to get credentials: %m");
95c4fe82 1365
d0b2babf 1366 bus_creds_dump(creds, NULL, false);
95c4fe82 1367 return 0;
1f849790
LP
1368}
1369
781fa938
LP
1370static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1371 char **p;
1372 int r;
1373
1374 assert(m);
1375 assert(signature);
1376 assert(x);
1377
1378 p = *x;
1379
1380 for (;;) {
1381 const char *v;
1382 char t;
1383
1384 t = *signature;
1385 v = *p;
1386
1387 if (t == 0)
1388 break;
baaa35ad
ZJS
1389 if (!v)
1390 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1391 "Too few parameters for signature.");
781fa938
LP
1392
1393 signature++;
1394 p++;
1395
1396 switch (t) {
1397
1398 case SD_BUS_TYPE_BOOLEAN:
1399
1400 r = parse_boolean(v);
48956c39 1401 if (r < 0)
3d9942de 1402 return log_error_errno(r, "Failed to parse '%s' as boolean: %m", v);
781fa938
LP
1403
1404 r = sd_bus_message_append_basic(m, t, &r);
1405 break;
1406
1407 case SD_BUS_TYPE_BYTE: {
1408 uint8_t z;
1409
1410 r = safe_atou8(v, &z);
48956c39 1411 if (r < 0)
3d9942de 1412 return log_error_errno(r, "Failed to parse '%s' as byte (unsigned 8bit integer): %m", v);
781fa938
LP
1413
1414 r = sd_bus_message_append_basic(m, t, &z);
1415 break;
1416 }
1417
1418 case SD_BUS_TYPE_INT16: {
1419 int16_t z;
1420
1421 r = safe_atoi16(v, &z);
48956c39 1422 if (r < 0)
3d9942de 1423 return log_error_errno(r, "Failed to parse '%s' as signed 16bit integer: %m", v);
781fa938
LP
1424
1425 r = sd_bus_message_append_basic(m, t, &z);
1426 break;
1427 }
1428
1429 case SD_BUS_TYPE_UINT16: {
1430 uint16_t z;
1431
1432 r = safe_atou16(v, &z);
48956c39 1433 if (r < 0)
3d9942de 1434 return log_error_errno(r, "Failed to parse '%s' as unsigned 16bit integer: %m", v);
781fa938
LP
1435
1436 r = sd_bus_message_append_basic(m, t, &z);
1437 break;
1438 }
1439
1440 case SD_BUS_TYPE_INT32: {
1441 int32_t z;
1442
1443 r = safe_atoi32(v, &z);
48956c39 1444 if (r < 0)
3d9942de 1445 return log_error_errno(r, "Failed to parse '%s' as signed 32bit integer: %m", v);
781fa938
LP
1446
1447 r = sd_bus_message_append_basic(m, t, &z);
1448 break;
1449 }
1450
1451 case SD_BUS_TYPE_UINT32: {
1452 uint32_t z;
1453
1454 r = safe_atou32(v, &z);
48956c39 1455 if (r < 0)
3d9942de 1456 return log_error_errno(r, "Failed to parse '%s' as unsigned 32bit integer: %m", v);
781fa938
LP
1457
1458 r = sd_bus_message_append_basic(m, t, &z);
1459 break;
1460 }
1461
1462 case SD_BUS_TYPE_INT64: {
1463 int64_t z;
1464
1465 r = safe_atoi64(v, &z);
48956c39 1466 if (r < 0)
3d9942de 1467 return log_error_errno(r, "Failed to parse '%s' as signed 64bit integer: %m", v);
781fa938
LP
1468
1469 r = sd_bus_message_append_basic(m, t, &z);
1470 break;
1471 }
1472
1473 case SD_BUS_TYPE_UINT64: {
1474 uint64_t z;
1475
1476 r = safe_atou64(v, &z);
48956c39 1477 if (r < 0)
3d9942de 1478 return log_error_errno(r, "Failed to parse '%s' as unsigned 64bit integer: %m", v);
781fa938
LP
1479
1480 r = sd_bus_message_append_basic(m, t, &z);
1481 break;
1482 }
1483
781fa938
LP
1484 case SD_BUS_TYPE_DOUBLE: {
1485 double z;
1486
1487 r = safe_atod(v, &z);
48956c39 1488 if (r < 0)
3d9942de 1489 return log_error_errno(r, "Failed to parse '%s' as double precision floating point: %m", v);
781fa938
LP
1490
1491 r = sd_bus_message_append_basic(m, t, &z);
1492 break;
1493 }
1494
1495 case SD_BUS_TYPE_STRING:
1496 case SD_BUS_TYPE_OBJECT_PATH:
1497 case SD_BUS_TYPE_SIGNATURE:
1498
1499 r = sd_bus_message_append_basic(m, t, v);
1500 break;
1501
1502 case SD_BUS_TYPE_ARRAY: {
1503 uint32_t n;
1504 size_t k;
1505
1506 r = safe_atou32(v, &n);
48956c39 1507 if (r < 0)
3d9942de 1508 return log_error_errno(r, "Failed to parse '%s' number of array entries: %m", v);
781fa938
LP
1509
1510 r = signature_element_length(signature, &k);
48956c39 1511 if (r < 0)
3d9942de 1512 return log_error_errno(r, "Invalid array signature: %m");
781fa938
LP
1513
1514 {
1515 unsigned i;
1516 char s[k + 1];
1517 memcpy(s, signature, k);
1518 s[k] = 0;
1519
1520 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1521 if (r < 0)
1522 return bus_log_create_error(r);
1523
1524 for (i = 0; i < n; i++) {
1525 r = message_append_cmdline(m, s, &p);
1526 if (r < 0)
1527 return r;
1528 }
1529 }
1530
1531 signature += k;
1532
1533 r = sd_bus_message_close_container(m);
1534 break;
1535 }
1536
1537 case SD_BUS_TYPE_VARIANT:
1538 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1539 if (r < 0)
1540 return bus_log_create_error(r);
1541
1542 r = message_append_cmdline(m, v, &p);
1543 if (r < 0)
1544 return r;
1545
1546 r = sd_bus_message_close_container(m);
1547 break;
1548
1549 case SD_BUS_TYPE_STRUCT_BEGIN:
1550 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1551 size_t k;
1552
1553 signature--;
1554 p--;
1555
1556 r = signature_element_length(signature, &k);
48956c39 1557 if (r < 0)
3d9942de 1558 return log_error_errno(r, "Invalid struct/dict entry signature: %m");
781fa938
LP
1559
1560 {
1561 char s[k-1];
1562 memcpy(s, signature + 1, k - 2);
1563 s[k - 2] = 0;
1564
1565 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1566 if (r < 0)
1567 return bus_log_create_error(r);
1568
1569 r = message_append_cmdline(m, s, &p);
1570 if (r < 0)
1571 return r;
1572 }
1573
1574 signature += k;
1575
1576 r = sd_bus_message_close_container(m);
1577 break;
1578 }
1579
1580 case SD_BUS_TYPE_UNIX_FD:
baaa35ad
ZJS
1581 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1582 "UNIX file descriptor not supported as type.");
781fa938
LP
1583
1584 default:
baaa35ad
ZJS
1585 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1586 "Unknown signature type %c.", t);
781fa938
LP
1587 }
1588
1589 if (r < 0)
1590 return bus_log_create_error(r);
1591 }
1592
1593 *x = p;
1594 return 0;
1595}
1596
9cebb234
LP
1597static int json_transform_one(sd_bus_message *m, JsonVariant **ret);
1598
1599static int json_transform_array_or_struct(sd_bus_message *m, JsonVariant **ret) {
1600 size_t n_elements = 0, n_allocated = 0;
1601 JsonVariant **elements = NULL;
1602 int r;
1603
1604 assert(m);
1605 assert(ret);
1606
1607 for (;;) {
1608 r = sd_bus_message_at_end(m, false);
1609 if (r < 0) {
1610 bus_log_parse_error(r);
1611 goto finish;
1612 }
1613 if (r > 0)
1614 break;
1615
1616 if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 1)) {
1617 r = log_oom();
1618 goto finish;
1619 }
1620
1621 r = json_transform_one(m, elements + n_elements);
1622 if (r < 0)
1623 goto finish;
1624
1625 n_elements++;
1626 }
1627
1628 r = json_variant_new_array(ret, elements, n_elements);
1629
1630finish:
1631 json_variant_unref_many(elements, n_elements);
1632 free(elements);
1633
1634 return r;
1635}
1636
1637static int json_transform_variant(sd_bus_message *m, const char *contents, JsonVariant **ret) {
172e4806 1638 _cleanup_(json_variant_unrefp) JsonVariant *value = NULL;
9cebb234
LP
1639 int r;
1640
1641 assert(m);
1642 assert(contents);
1643 assert(ret);
1644
1645 r = json_transform_one(m, &value);
1646 if (r < 0)
1647 return r;
1648
1649 r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(contents)),
1650 JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(value))));
1651 if (r < 0)
1652 return log_oom();
1653
1654 return r;
1655}
1656
1657static int json_transform_dict_array(sd_bus_message *m, JsonVariant **ret) {
1658 size_t n_elements = 0, n_allocated = 0;
1659 JsonVariant **elements = NULL;
1660 int r;
1661
1662 assert(m);
1663 assert(ret);
1664
1665 for (;;) {
1666 const char *contents;
1667 char type;
1668
1669 r = sd_bus_message_at_end(m, false);
1670 if (r < 0) {
1671 bus_log_parse_error(r);
1672 goto finish;
1673 }
1674 if (r > 0)
1675 break;
1676
1677 r = sd_bus_message_peek_type(m, &type, &contents);
1678 if (r < 0)
1679 return r;
1680
1681 assert(type == 'e');
1682
1683 if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 2)) {
1684 r = log_oom();
1685 goto finish;
1686 }
1687
1688 r = sd_bus_message_enter_container(m, type, contents);
1689 if (r < 0) {
1690 bus_log_parse_error(r);
1691 goto finish;
1692 }
1693
1694 r = json_transform_one(m, elements + n_elements);
1695 if (r < 0)
1696 goto finish;
1697
1698 n_elements++;
1699
1700 r = json_transform_one(m, elements + n_elements);
1701 if (r < 0)
1702 goto finish;
1703
1704 n_elements++;
1705
1706 r = sd_bus_message_exit_container(m);
1707 if (r < 0) {
1708 bus_log_parse_error(r);
1709 goto finish;
1710 }
1711 }
1712
1713 r = json_variant_new_object(ret, elements, n_elements);
1714
1715finish:
1716 json_variant_unref_many(elements, n_elements);
1717 free(elements);
1718
1719 return r;
1720}
1721
1722static int json_transform_one(sd_bus_message *m, JsonVariant **ret) {
1723 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1724 const char *contents;
1725 char type;
1726 int r;
1727
1728 assert(m);
1729 assert(ret);
1730
1731 r = sd_bus_message_peek_type(m, &type, &contents);
1732 if (r < 0)
1733 return bus_log_parse_error(r);
1734
1735 switch (type) {
1736
1737 case SD_BUS_TYPE_BYTE: {
1738 uint8_t b;
1739
1740 r = sd_bus_message_read_basic(m, type, &b);
1741 if (r < 0)
1742 return bus_log_parse_error(r);
1743
1744 r = json_variant_new_unsigned(&v, b);
1745 if (r < 0)
1746 return log_error_errno(r, "Failed to transform byte: %m");
1747
1748 break;
1749 }
1750
1751 case SD_BUS_TYPE_BOOLEAN: {
1752 int b;
1753
1754 r = sd_bus_message_read_basic(m, type, &b);
1755 if (r < 0)
1756 return bus_log_parse_error(r);
1757
1758 r = json_variant_new_boolean(&v, b);
1759 if (r < 0)
1760 return log_error_errno(r, "Failed to transform boolean: %m");
1761
1762 break;
1763 }
1764
1765 case SD_BUS_TYPE_INT16: {
1766 int16_t b;
1767
1768 r = sd_bus_message_read_basic(m, type, &b);
1769 if (r < 0)
1770 return bus_log_parse_error(r);
1771
1772 r = json_variant_new_integer(&v, b);
1773 if (r < 0)
1774 return log_error_errno(r, "Failed to transform int16: %m");
1775
1776 break;
1777 }
1778
1779 case SD_BUS_TYPE_UINT16: {
1780 uint16_t b;
1781
1782 r = sd_bus_message_read_basic(m, type, &b);
1783 if (r < 0)
1784 return bus_log_parse_error(r);
1785
1786 r = json_variant_new_unsigned(&v, b);
1787 if (r < 0)
1788 return log_error_errno(r, "Failed to transform uint16: %m");
1789
1790 break;
1791 }
1792
1793 case SD_BUS_TYPE_INT32: {
1794 int32_t b;
1795
1796 r = sd_bus_message_read_basic(m, type, &b);
1797 if (r < 0)
1798 return bus_log_parse_error(r);
1799
1800 r = json_variant_new_integer(&v, b);
1801 if (r < 0)
1802 return log_error_errno(r, "Failed to transform int32: %m");
1803
1804 break;
1805 }
1806
1807 case SD_BUS_TYPE_UINT32: {
1808 uint32_t b;
1809
1810 r = sd_bus_message_read_basic(m, type, &b);
1811 if (r < 0)
1812 return bus_log_parse_error(r);
1813
1814 r = json_variant_new_unsigned(&v, b);
1815 if (r < 0)
1816 return log_error_errno(r, "Failed to transform uint32: %m");
1817
1818 break;
1819 }
1820
1821 case SD_BUS_TYPE_INT64: {
1822 int64_t b;
1823
1824 r = sd_bus_message_read_basic(m, type, &b);
1825 if (r < 0)
1826 return bus_log_parse_error(r);
1827
1828 r = json_variant_new_integer(&v, b);
1829 if (r < 0)
1830 return log_error_errno(r, "Failed to transform int64: %m");
1831
1832 break;
1833 }
1834
1835 case SD_BUS_TYPE_UINT64: {
1836 uint64_t b;
1837
1838 r = sd_bus_message_read_basic(m, type, &b);
1839 if (r < 0)
1840 return bus_log_parse_error(r);
1841
1842 r = json_variant_new_unsigned(&v, b);
1843 if (r < 0)
1844 return log_error_errno(r, "Failed to transform uint64: %m");
1845
1846 break;
1847 }
1848
1849 case SD_BUS_TYPE_DOUBLE: {
1850 double d;
1851
1852 r = sd_bus_message_read_basic(m, type, &d);
1853 if (r < 0)
1854 return bus_log_parse_error(r);
1855
1856 r = json_variant_new_real(&v, d);
1857 if (r < 0)
1858 return log_error_errno(r, "Failed to transform double: %m");
1859
1860 break;
1861 }
1862
1863 case SD_BUS_TYPE_STRING:
1864 case SD_BUS_TYPE_OBJECT_PATH:
1865 case SD_BUS_TYPE_SIGNATURE: {
1866 const char *s;
1867
1868 r = sd_bus_message_read_basic(m, type, &s);
1869 if (r < 0)
1870 return bus_log_parse_error(r);
1871
1872 r = json_variant_new_string(&v, s);
1873 if (r < 0)
1874 return log_error_errno(r, "Failed to transform double: %m");
1875
1876 break;
1877 }
1878
1879 case SD_BUS_TYPE_UNIX_FD:
1880 r = sd_bus_message_read_basic(m, type, NULL);
1881 if (r < 0)
1882 return bus_log_parse_error(r);
1883
1884 r = json_variant_new_null(&v);
1885 if (r < 0)
1886 return log_error_errno(r, "Failed to transform fd: %m");
1887
1888 break;
1889
1890 case SD_BUS_TYPE_ARRAY:
1891 case SD_BUS_TYPE_VARIANT:
1892 case SD_BUS_TYPE_STRUCT:
1893 r = sd_bus_message_enter_container(m, type, contents);
1894 if (r < 0)
1895 return bus_log_parse_error(r);
1896
1897 if (type == SD_BUS_TYPE_VARIANT)
1898 r = json_transform_variant(m, contents, &v);
1899 else if (type == SD_BUS_TYPE_ARRAY && contents[0] == '{')
1900 r = json_transform_dict_array(m, &v);
1901 else
1902 r = json_transform_array_or_struct(m, &v);
1903 if (r < 0)
1904 return r;
1905
1906 r = sd_bus_message_exit_container(m);
1907 if (r < 0)
1908 return bus_log_parse_error(r);
1909
1910 break;
1911
1912 default:
1913 assert_not_reached("Unexpected element type");
1914 }
1915
1916 *ret = TAKE_PTR(v);
1917 return 0;
1918}
1919
1920static int json_transform_message(sd_bus_message *m, JsonVariant **ret) {
1921 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1922 const char *type;
1923 int r;
1924
1925 assert(m);
1926 assert(ret);
1927
1928 assert_se(type = sd_bus_message_get_signature(m, false));
1929
1930 r = json_transform_array_or_struct(m, &v);
1931 if (r < 0)
1932 return r;
1933
1934 r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(type)),
1935 JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(v))));
1936 if (r < 0)
1937 return log_oom();
1938
1939 return 0;
1940}
1941
1942static void json_dump_with_flags(JsonVariant *v, FILE *f) {
1943
1944 json_variant_dump(v,
1945 (arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) |
1946 colors_enabled() * JSON_FORMAT_COLOR,
1947 f, NULL);
1948}
1949
9bb31a0c
YW
1950static int call(int argc, char **argv, void *userdata) {
1951 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348
LP
1952 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1953 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
781fa938
LP
1954 int r;
1955
9bb31a0c
YW
1956 r = acquire_bus(false, &bus);
1957 if (r < 0)
1958 return r;
781fa938
LP
1959
1960 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
1961 if (r < 0)
1962 return bus_log_create_error(r);
781fa938 1963
38051578
LP
1964 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
1965 if (r < 0)
1966 return bus_log_create_error(r);
1967
1968 r = sd_bus_message_set_auto_start(m, arg_auto_start);
1969 if (r < 0)
1970 return bus_log_create_error(r);
1971
1972 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
1973 if (r < 0)
1974 return bus_log_create_error(r);
1975
781fa938
LP
1976 if (!isempty(argv[5])) {
1977 char **p;
1978
1979 p = argv+6;
1980
1981 r = message_append_cmdline(m, argv[5], &p);
1982 if (r < 0)
1983 return r;
1984
1985 if (*p) {
1986 log_error("Too many parameters for signature.");
1987 return -EINVAL;
1988 }
1989 }
1990
38051578
LP
1991 if (!arg_expect_reply) {
1992 r = sd_bus_send(bus, m, NULL);
48956c39
LP
1993 if (r < 0)
1994 return log_error_errno(r, "Failed to send message: %m");
38051578
LP
1995
1996 return 0;
1997 }
1998
a44b1081 1999 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
48956c39
LP
2000 if (r < 0)
2001 return log_error_errno(r, "%s", bus_error_message(&error, r));
781fa938
LP
2002
2003 r = sd_bus_message_is_empty(reply);
2004 if (r < 0)
2005 return bus_log_parse_error(r);
1fc55609 2006
781fa938 2007 if (r == 0 && !arg_quiet) {
1fc55609 2008
9cebb234
LP
2009 if (arg_json != JSON_OFF) {
2010 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
2011
2012 if (arg_json != JSON_SHORT)
0221d68a 2013 (void) pager_open(arg_pager_flags);
9cebb234
LP
2014
2015 r = json_transform_message(reply, &v);
2016 if (r < 0)
2017 return r;
2018
2019 json_dump_with_flags(v, stdout);
2020
2021 } else if (arg_verbose) {
0221d68a 2022 (void) pager_open(arg_pager_flags);
1fc55609
LP
2023
2024 r = bus_message_dump(reply, stdout, 0);
2025 if (r < 0)
2026 return r;
2027 } else {
2028
2029 fputs(sd_bus_message_get_signature(reply, true), stdout);
2030 fputc(' ', stdout);
2031
2032 r = format_cmdline(reply, stdout, false);
2033 if (r < 0)
2034 return bus_log_parse_error(r);
2035
2036 fputc('\n', stdout);
2037 }
d55192ad
LP
2038 }
2039
2040 return 0;
2041}
2042
9bb31a0c
YW
2043static int get_property(int argc, char **argv, void *userdata) {
2044 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 2045 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609 2046 char **i;
d55192ad
LP
2047 int r;
2048
9bb31a0c
YW
2049 r = acquire_bus(false, &bus);
2050 if (r < 0)
2051 return r;
d55192ad 2052
1fc55609 2053 STRV_FOREACH(i, argv + 4) {
4afd3348 2054 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
2055 const char *contents = NULL;
2056 char type;
d55192ad 2057
1fc55609 2058 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
48956c39
LP
2059 if (r < 0)
2060 return log_error_errno(r, "%s", bus_error_message(&error, r));
d55192ad 2061
1fc55609 2062 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
2063 if (r < 0)
2064 return bus_log_parse_error(r);
2065
1fc55609
LP
2066 r = sd_bus_message_enter_container(reply, 'v', contents);
2067 if (r < 0)
2068 return bus_log_parse_error(r);
d55192ad 2069
9cebb234
LP
2070 if (arg_json != JSON_OFF) {
2071 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
2072
2073 if (arg_json != JSON_SHORT)
0221d68a 2074 (void) pager_open(arg_pager_flags);
9cebb234
LP
2075
2076 r = json_transform_variant(reply, contents, &v);
2077 if (r < 0)
2078 return r;
2079
2080 json_dump_with_flags(v, stdout);
2081
2de62253 2082 } else if (arg_verbose) {
0221d68a 2083 (void) pager_open(arg_pager_flags);
d55192ad 2084
1fc55609 2085 r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 2086 if (r < 0)
1fc55609
LP
2087 return r;
2088 } else {
2089 fputs(contents, stdout);
2090 fputc(' ', stdout);
d55192ad 2091
1fc55609 2092 r = format_cmdline(reply, stdout, false);
d55192ad
LP
2093 if (r < 0)
2094 return bus_log_parse_error(r);
2095
1fc55609 2096 fputc('\n', stdout);
d55192ad
LP
2097 }
2098
2099 r = sd_bus_message_exit_container(reply);
2100 if (r < 0)
2101 return bus_log_parse_error(r);
1fc55609 2102 }
d55192ad 2103
1fc55609
LP
2104 return 0;
2105}
d55192ad 2106
9bb31a0c
YW
2107static int set_property(int argc, char **argv, void *userdata) {
2108 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348
LP
2109 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2110 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609
LP
2111 char **p;
2112 int r;
d55192ad 2113
9bb31a0c
YW
2114 r = acquire_bus(false, &bus);
2115 if (r < 0)
2116 return r;
d55192ad 2117
1fc55609
LP
2118 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
2119 if (r < 0)
2120 return bus_log_create_error(r);
d55192ad 2121
1fc55609
LP
2122 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
2123 if (r < 0)
2124 return bus_log_create_error(r);
d55192ad 2125
1fc55609
LP
2126 r = sd_bus_message_open_container(m, 'v', argv[5]);
2127 if (r < 0)
2128 return bus_log_create_error(r);
2129
9bb31a0c 2130 p = argv + 6;
1fc55609
LP
2131 r = message_append_cmdline(m, argv[5], &p);
2132 if (r < 0)
2133 return r;
2134
2135 r = sd_bus_message_close_container(m);
2136 if (r < 0)
2137 return bus_log_create_error(r);
2138
2139 if (*p) {
2140 log_error("Too many parameters for signature.");
2141 return -EINVAL;
2142 }
2143
a44b1081 2144 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
48956c39
LP
2145 if (r < 0)
2146 return log_error_errno(r, "%s", bus_error_message(&error, r));
781fa938
LP
2147
2148 return 0;
2149}
2150
1f849790 2151static int help(void) {
37ec0fdd
LP
2152 _cleanup_free_ char *link = NULL;
2153 int r;
2154
2155 r = terminal_urlify_man("busctl", "1", &link);
2156 if (r < 0)
2157 return log_oom();
2158
1f849790
LP
2159 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2160 "Introspect the bus.\n\n"
d75edbd6
LP
2161 " -h --help Show this help\n"
2162 " --version Show package version\n"
a86a47ce 2163 " --no-pager Do not pipe output into a pager\n"
17d47d8d 2164 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
2165 " --system Connect to system bus\n"
2166 " --user Connect to user bus\n"
2167 " -H --host=[USER@]HOST Operate on remote host\n"
2168 " -M --machine=CONTAINER Operate on local container\n"
2169 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
2170 " --show-machine Show machine ID column in list\n"
2171 " --unique Only show unique names\n"
2172 " --acquired Only show acquired names\n"
2173 " --activatable Only show activatable names\n"
d9130355 2174 " --match=MATCH Only show matching messages\n"
d28ebe27 2175 " --size=SIZE Maximum length of captured packet\n"
781fa938 2176 " --list Don't show tree, but simple object path list\n"
e53fa805 2177 " -q --quiet Don't show method call reply\n"
38051578 2178 " --verbose Show result values in long format\n"
9cebb234
LP
2179 " --json=MODE Output as JSON\n"
2180 " -j Same as --json=pretty on tty, --json=short otherwise\n"
38051578
LP
2181 " --expect-reply=BOOL Expect a method call reply\n"
2182 " --auto-start=BOOL Auto-start destination service\n"
2183 " --allow-interactive-authorization=BOOL\n"
a44b1081 2184 " Allow interactive authorization for operation\n"
40ed1a45 2185 " --timeout=SECS Maximum time to wait for method call completion\n"
56d820b6
LP
2186 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n"
2187 " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n"
2188 " system\n\n"
1f849790 2189 "Commands:\n"
d75edbd6 2190 " list List bus names\n"
2e9efd22 2191 " status [SERVICE] Show bus service, process or bus owner credentials\n"
d94fe1f1 2192 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 2193 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 2194 " tree [SERVICE...] Show object tree of service\n"
4f44c03e 2195 " introspect SERVICE OBJECT [INTERFACE]\n"
0171da06 2196 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 2197 " Call a method\n"
1fc55609 2198 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 2199 " Get property value\n"
1fc55609
LP
2200 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
2201 " Set property value\n"
601185b4 2202 " help Show this help\n"
37ec0fdd
LP
2203 "\nSee the %s for details.\n"
2204 , program_invocation_short_name
2205 , link
2206 );
1f849790
LP
2207
2208 return 0;
2209}
2210
9bb31a0c
YW
2211static int verb_help(int argc, char **argv, void *userdata) {
2212 return help();
2213}
2214
1f849790
LP
2215static int parse_argv(int argc, char *argv[]) {
2216
2217 enum {
2218 ARG_VERSION = 0x100,
2219 ARG_NO_PAGER,
17d47d8d 2220 ARG_NO_LEGEND,
1f849790
LP
2221 ARG_SYSTEM,
2222 ARG_USER,
2223 ARG_ADDRESS,
2224 ARG_MATCH,
56e61788
LP
2225 ARG_SHOW_MACHINE,
2226 ARG_UNIQUE,
2227 ARG_ACQUIRED,
1f70b087
LP
2228 ARG_ACTIVATABLE,
2229 ARG_SIZE,
d9130355 2230 ARG_LIST,
1fc55609 2231 ARG_VERBOSE,
38051578
LP
2232 ARG_EXPECT_REPLY,
2233 ARG_AUTO_START,
2234 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 2235 ARG_TIMEOUT,
40ed1a45 2236 ARG_AUGMENT_CREDS,
56d820b6 2237 ARG_WATCH_BIND,
9cebb234 2238 ARG_JSON,
1f849790
LP
2239 };
2240
2241 static const struct option options[] = {
9cebb234
LP
2242 { "help", no_argument, NULL, 'h' },
2243 { "version", no_argument, NULL, ARG_VERSION },
2244 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2245 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2246 { "system", no_argument, NULL, ARG_SYSTEM },
2247 { "user", no_argument, NULL, ARG_USER },
2248 { "address", required_argument, NULL, ARG_ADDRESS },
2249 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
2250 { "unique", no_argument, NULL, ARG_UNIQUE },
2251 { "acquired", no_argument, NULL, ARG_ACQUIRED },
2252 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
2253 { "match", required_argument, NULL, ARG_MATCH },
2254 { "host", required_argument, NULL, 'H' },
2255 { "machine", required_argument, NULL, 'M' },
2256 { "size", required_argument, NULL, ARG_SIZE },
2257 { "list", no_argument, NULL, ARG_LIST },
2258 { "quiet", no_argument, NULL, 'q' },
2259 { "verbose", no_argument, NULL, ARG_VERBOSE },
2260 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
2261 { "auto-start", required_argument, NULL, ARG_AUTO_START },
38051578 2262 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
9cebb234
LP
2263 { "timeout", required_argument, NULL, ARG_TIMEOUT },
2264 { "augment-creds", required_argument, NULL, ARG_AUGMENT_CREDS },
2265 { "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
2266 { "json", required_argument, NULL, ARG_JSON },
eb9da376 2267 {},
1f849790
LP
2268 };
2269
1f70b087 2270 int c, r;
1f849790
LP
2271
2272 assert(argc >= 0);
2273 assert(argv);
2274
9cebb234 2275 while ((c = getopt_long(argc, argv, "hH:M:qj", options, NULL)) >= 0)
1f849790
LP
2276
2277 switch (c) {
2278
2279 case 'h':
2280 return help();
2281
2282 case ARG_VERSION:
3f6fd1ba 2283 return version();
1f849790
LP
2284
2285 case ARG_NO_PAGER:
0221d68a 2286 arg_pager_flags |= PAGER_DISABLE;
1f849790
LP
2287 break;
2288
17d47d8d
TA
2289 case ARG_NO_LEGEND:
2290 arg_legend = false;
2291 break;
2292
1f849790
LP
2293 case ARG_USER:
2294 arg_user = true;
2295 break;
2296
2297 case ARG_SYSTEM:
2298 arg_user = false;
2299 break;
2300
2301 case ARG_ADDRESS:
2302 arg_address = optarg;
2303 break;
2304
56e61788
LP
2305 case ARG_SHOW_MACHINE:
2306 arg_show_machine = true;
2307 break;
2308
2309 case ARG_UNIQUE:
2310 arg_unique = true;
1f849790
LP
2311 break;
2312
56e61788
LP
2313 case ARG_ACQUIRED:
2314 arg_acquired = true;
2315 break;
2316
2317 case ARG_ACTIVATABLE:
2318 arg_activatable = true;
a4297f08
LP
2319 break;
2320
1f849790
LP
2321 case ARG_MATCH:
2322 if (strv_extend(&arg_matches, optarg) < 0)
2323 return log_oom();
2324 break;
2325
1f70b087 2326 case ARG_SIZE: {
59f448cf 2327 uint64_t sz;
1f70b087 2328
59f448cf 2329 r = parse_size(optarg, 1024, &sz);
48956c39 2330 if (r < 0)
3d9942de 2331 return log_error_errno(r, "Failed to parse size '%s': %m", optarg);
1f70b087 2332
baaa35ad
ZJS
2333 if ((uint64_t) (size_t) sz != sz)
2334 return log_error_errno(SYNTHETIC_ERRNO(E2BIG),
2335 "Size out of range.");
1f70b087 2336
59f448cf 2337 arg_snaplen = (size_t) sz;
1f70b087
LP
2338 break;
2339 }
2340
d9130355
LP
2341 case ARG_LIST:
2342 arg_list = true;
2343 break;
2344
d75edbd6
LP
2345 case 'H':
2346 arg_transport = BUS_TRANSPORT_REMOTE;
2347 arg_host = optarg;
2348 break;
2349
2350 case 'M':
de33fc62 2351 arg_transport = BUS_TRANSPORT_MACHINE;
d75edbd6
LP
2352 arg_host = optarg;
2353 break;
2354
781fa938
LP
2355 case 'q':
2356 arg_quiet = true;
2357 break;
2358
1fc55609
LP
2359 case ARG_VERBOSE:
2360 arg_verbose = true;
2361 break;
2362
38051578
LP
2363 case ARG_EXPECT_REPLY:
2364 r = parse_boolean(optarg);
48956c39 2365 if (r < 0)
3d9942de 2366 return log_error_errno(r, "Failed to parse --expect-reply= parameter '%s': %m", optarg);
38051578 2367
5d904a6a 2368 arg_expect_reply = r;
38051578
LP
2369 break;
2370
38051578
LP
2371 case ARG_AUTO_START:
2372 r = parse_boolean(optarg);
48956c39 2373 if (r < 0)
3d9942de 2374 return log_error_errno(r, "Failed to parse --auto-start= parameter '%s': %m", optarg);
38051578 2375
5d904a6a 2376 arg_auto_start = r;
38051578
LP
2377 break;
2378
38051578
LP
2379 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
2380 r = parse_boolean(optarg);
48956c39 2381 if (r < 0)
3d9942de 2382 return log_error_errno(r, "Failed to parse --allow-interactive-authorization= parameter '%s': %m", optarg);
38051578 2383
5d904a6a 2384 arg_allow_interactive_authorization = r;
38051578
LP
2385 break;
2386
a44b1081
LP
2387 case ARG_TIMEOUT:
2388 r = parse_sec(optarg, &arg_timeout);
48956c39 2389 if (r < 0)
3d9942de 2390 return log_error_errno(r, "Failed to parse --timeout= parameter '%s': %m", optarg);
a44b1081
LP
2391
2392 break;
2393
40ed1a45
LP
2394 case ARG_AUGMENT_CREDS:
2395 r = parse_boolean(optarg);
48956c39 2396 if (r < 0)
3d9942de 2397 return log_error_errno(r, "Failed to parse --augment-creds= parameter '%s': %m", optarg);
40ed1a45 2398
5d904a6a 2399 arg_augment_creds = r;
40ed1a45
LP
2400 break;
2401
56d820b6
LP
2402 case ARG_WATCH_BIND:
2403 r = parse_boolean(optarg);
48956c39 2404 if (r < 0)
3d9942de 2405 return log_error_errno(r, "Failed to parse --watch-bind= parameter '%s': %m", optarg);
56d820b6 2406
5d904a6a 2407 arg_watch_bind = r;
56d820b6
LP
2408 break;
2409
9cebb234
LP
2410 case 'j':
2411 if (on_tty())
2412 arg_json = JSON_PRETTY;
2413 else
2414 arg_json = JSON_SHORT;
2415 break;
2416
2417 case ARG_JSON:
2418 if (streq(optarg, "short"))
2419 arg_json = JSON_SHORT;
2420 else if (streq(optarg, "pretty"))
2421 arg_json = JSON_PRETTY;
2422 else if (streq(optarg, "help")) {
2423 fputs("short\n"
2424 "pretty\n", stdout);
2425 return 0;
baaa35ad
ZJS
2426 } else
2427 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2428 "Unknown JSON out mode: %s",
2429 optarg);
9cebb234
LP
2430
2431 break;
2432
1f849790
LP
2433 case '?':
2434 return -EINVAL;
2435
2436 default:
eb9da376 2437 assert_not_reached("Unhandled option");
1f849790 2438 }
1f849790
LP
2439
2440 return 1;
2441}
2442
9bb31a0c
YW
2443static int busctl_main(int argc, char *argv[]) {
2444
2445 static const Verb verbs[] = {
2446 { "list", VERB_ANY, 1, VERB_DEFAULT, list_bus_names },
2447 { "status", VERB_ANY, 2, 0, status },
2448 { "monitor", VERB_ANY, VERB_ANY, 0, verb_monitor },
2449 { "capture", VERB_ANY, VERB_ANY, 0, verb_capture },
2450 { "tree", VERB_ANY, VERB_ANY, 0, tree },
2451 { "introspect", 3, 4, 0, introspect },
2452 { "call", 5, VERB_ANY, 0, call },
2453 { "get-property", 5, VERB_ANY, 0, get_property },
2454 { "set-property", 6, VERB_ANY, 0, set_property },
2455 { "help", VERB_ANY, VERB_ANY, 0, verb_help },
2456 {}
2457 };
1f849790 2458
9bb31a0c 2459 return dispatch_verb(argc, argv, verbs, NULL);
1f849790
LP
2460}
2461
360f3dc2 2462static int run(int argc, char *argv[]) {
1f849790
LP
2463 int r;
2464
2465 log_parse_environment();
2466 log_open();
2467
2468 r = parse_argv(argc, argv);
2469 if (r <= 0)
360f3dc2 2470 return r;
de1c301e 2471
360f3dc2 2472 return busctl_main(argc, argv);
de1c301e 2473}
360f3dc2
YW
2474
2475DEFINE_MAIN_FUNCTION(run);