]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/busctl/busctl.c
locale-util: prefix special glyph enum values with SPECIAL_GLYPH_
[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
9a6f746f
LP
332 vertical = strjoina(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
333 space = strjoina(prefix, special_glyph(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
9a6f746f 354 printf("%s%s%s\n", prefix, special_glyph(has_more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_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
7a08d314 716static void member_hash_func(const Member *m, struct siphash *state) {
1e2527a6 717 uint64_t arity = 1;
0171da06
LP
718
719 assert(m);
720 assert(m->type);
721
b826ab58 722 string_hash_func(m->type, state);
0171da06 723
1e2527a6
TG
724 arity += !!m->name + !!m->interface;
725
726 uint64_hash_func(&arity, state);
727
0171da06 728 if (m->name)
b826ab58 729 string_hash_func(m->name, state);
0171da06
LP
730
731 if (m->interface)
b826ab58 732 string_hash_func(m->interface, state);
0171da06
LP
733}
734
dc5f9c6f 735static int member_compare_func(const Member *x, const Member *y) {
0171da06
LP
736 int d;
737
738 assert(x);
739 assert(y);
740 assert(x->type);
741 assert(y->type);
742
c030a850
NK
743 d = strcmp_ptr(x->interface, y->interface);
744 if (d != 0)
745 return d;
0171da06
LP
746
747 d = strcmp(x->type, y->type);
748 if (d != 0)
749 return d;
750
c030a850 751 return strcmp_ptr(x->name, y->name);
0171da06
LP
752}
753
93bab288
YW
754static int member_compare_funcp(Member * const *a, Member * const *b) {
755 return member_compare_func(*a, *b);
0171da06
LP
756}
757
758static void member_free(Member *m) {
759 if (!m)
760 return;
761
762 free(m->interface);
763 free(m->name);
764 free(m->signature);
765 free(m->result);
1fc55609 766 free(m->value);
0171da06
LP
767 free(m);
768}
769
770DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
771
772static void member_set_free(Set *s) {
224b0e7a 773 set_free_with_destructor(s, member_free);
0171da06
LP
774}
775
776DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
777
778static int on_interface(const char *interface, uint64_t flags, void *userdata) {
779 _cleanup_(member_freep) Member *m;
780 Set *members = userdata;
781 int r;
782
783 assert(interface);
784 assert(members);
785
786 m = new0(Member, 1);
787 if (!m)
788 return log_oom();
789
790 m->type = "interface";
791 m->flags = flags;
792
793 r = free_and_strdup(&m->interface, interface);
794 if (r < 0)
795 return log_oom();
796
797 r = set_put(members, m);
798 if (r <= 0) {
799 log_error("Duplicate interface");
800 return -EINVAL;
801 }
802
803 m = NULL;
804 return 0;
805}
806
807static int on_method(const char *interface, const char *name, const char *signature, const char *result, 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 = "method";
820 m->flags = flags;
821
822 r = free_and_strdup(&m->interface, interface);
823 if (r < 0)
824 return log_oom();
825
826 r = free_and_strdup(&m->name, name);
827 if (r < 0)
828 return log_oom();
829
830 r = free_and_strdup(&m->signature, signature);
831 if (r < 0)
832 return log_oom();
833
834 r = free_and_strdup(&m->result, result);
835 if (r < 0)
836 return log_oom();
837
838 r = set_put(members, m);
839 if (r <= 0) {
840 log_error("Duplicate method");
841 return -EINVAL;
842 }
843
844 m = NULL;
845 return 0;
846}
847
848static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
849 _cleanup_(member_freep) Member *m;
850 Set *members = userdata;
851 int r;
852
853 assert(interface);
854 assert(name);
855
856 m = new0(Member, 1);
857 if (!m)
858 return log_oom();
859
860 m->type = "signal";
861 m->flags = flags;
862
863 r = free_and_strdup(&m->interface, interface);
864 if (r < 0)
865 return log_oom();
866
867 r = free_and_strdup(&m->name, name);
868 if (r < 0)
869 return log_oom();
870
871 r = free_and_strdup(&m->signature, signature);
872 if (r < 0)
873 return log_oom();
874
875 r = set_put(members, m);
876 if (r <= 0) {
877 log_error("Duplicate signal");
878 return -EINVAL;
879 }
880
881 m = NULL;
882 return 0;
883}
884
885static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
886 _cleanup_(member_freep) Member *m;
887 Set *members = userdata;
888 int r;
889
890 assert(interface);
891 assert(name);
892
893 m = new0(Member, 1);
894 if (!m)
895 return log_oom();
896
897 m->type = "property";
898 m->flags = flags;
899 m->writable = writable;
900
901 r = free_and_strdup(&m->interface, interface);
902 if (r < 0)
903 return log_oom();
904
905 r = free_and_strdup(&m->name, name);
906 if (r < 0)
907 return log_oom();
908
909 r = free_and_strdup(&m->signature, signature);
910 if (r < 0)
911 return log_oom();
912
913 r = set_put(members, m);
914 if (r <= 0) {
915 log_error("Duplicate property");
916 return -EINVAL;
917 }
918
919 m = NULL;
920 return 0;
921}
922
7a08d314 923DEFINE_PRIVATE_HASH_OPS(member_hash_ops, Member, member_hash_func, member_compare_func);
0171da06 924
7a08d314 925static int introspect(int argc, char **argv, void *userdata) {
0171da06
LP
926 static const XMLIntrospectOps ops = {
927 .on_interface = on_interface,
928 .on_method = on_method,
929 .on_signal = on_signal,
930 .on_property = on_property,
931 };
932
9bb31a0c 933 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
9efebb65 934 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_xml = NULL;
4afd3348 935 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
0171da06 936 _cleanup_(member_set_freep) Set *members = NULL;
9bb31a0c
YW
937 unsigned name_width, type_width, signature_width, result_width, j, k = 0;
938 Member *m, **sorted = NULL;
0171da06 939 Iterator i;
0171da06
LP
940 const char *xml;
941 int r;
0171da06 942
9bb31a0c
YW
943 r = acquire_bus(false, &bus);
944 if (r < 0)
945 return r;
4f44c03e 946
0171da06
LP
947 members = set_new(&member_hash_ops);
948 if (!members)
949 return log_oom();
950
9efebb65 951 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply_xml, "");
e51faad3
LP
952 if (r < 0)
953 return log_error_errno(r, "Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
0171da06 954
9efebb65 955 r = sd_bus_message_read(reply_xml, "s", &xml);
0171da06
LP
956 if (r < 0)
957 return bus_log_parse_error(r);
958
1fc55609 959 /* First, get list of all properties */
0171da06
LP
960 r = parse_xml_introspect(argv[2], xml, &ops, members);
961 if (r < 0)
962 return r;
963
1fc55609
LP
964 /* Second, find the current values for them */
965 SET_FOREACH(m, members, i) {
9efebb65 966 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
967
968 if (!streq(m->type, "property"))
969 continue;
970
971 if (m->value)
972 continue;
973
4f44c03e
LP
974 if (argv[3] && !streq(argv[3], m->interface))
975 continue;
976
1fc55609 977 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
e51faad3
LP
978 if (r < 0)
979 return log_error_errno(r, "%s", bus_error_message(&error, r));
1fc55609
LP
980
981 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
982 if (r < 0)
983 return bus_log_parse_error(r);
984
985 for (;;) {
986 Member *z;
987 _cleanup_free_ char *buf = NULL;
988 _cleanup_fclose_ FILE *mf = NULL;
989 size_t sz = 0;
990 const char *name;
991
992 r = sd_bus_message_enter_container(reply, 'e', "sv");
993 if (r < 0)
994 return bus_log_parse_error(r);
995
996 if (r == 0)
997 break;
998
999 r = sd_bus_message_read(reply, "s", &name);
1000 if (r < 0)
1001 return bus_log_parse_error(r);
1002
1003 r = sd_bus_message_enter_container(reply, 'v', NULL);
1004 if (r < 0)
1005 return bus_log_parse_error(r);
1006
1007 mf = open_memstream(&buf, &sz);
1008 if (!mf)
1009 return log_oom();
1010
0d536673
LP
1011 (void) __fsetlocking(mf, FSETLOCKING_BYCALLER);
1012
1fc55609
LP
1013 r = format_cmdline(reply, mf, false);
1014 if (r < 0)
1015 return bus_log_parse_error(r);
1016
8d3b9edc 1017 mf = safe_fclose(mf);
1fc55609
LP
1018
1019 z = set_get(members, &((Member) {
1020 .type = "property",
1021 .interface = m->interface,
1022 .name = (char*) name }));
8d3b9edc
LP
1023 if (z)
1024 free_and_replace(z->value, buf);
1fc55609
LP
1025
1026 r = sd_bus_message_exit_container(reply);
1027 if (r < 0)
1028 return bus_log_parse_error(r);
1029
1030 r = sd_bus_message_exit_container(reply);
1031 if (r < 0)
1032 return bus_log_parse_error(r);
1033 }
1034
1035 r = sd_bus_message_exit_container(reply);
1036 if (r < 0)
1037 return bus_log_parse_error(r);
1038 }
1039
0221d68a 1040 (void) pager_open(arg_pager_flags);
0171da06 1041
fbd0b64f
LP
1042 name_width = STRLEN("NAME");
1043 type_width = STRLEN("TYPE");
1044 signature_width = STRLEN("SIGNATURE");
1045 result_width = STRLEN("RESULT/VALUE");
0171da06
LP
1046
1047 sorted = newa(Member*, set_size(members));
1048
1049 SET_FOREACH(m, members, i) {
4f44c03e
LP
1050
1051 if (argv[3] && !streq(argv[3], m->interface))
1052 continue;
1053
0171da06
LP
1054 if (m->interface)
1055 name_width = MAX(name_width, strlen(m->interface));
1056 if (m->name)
1057 name_width = MAX(name_width, strlen(m->name) + 1);
1058 if (m->type)
1059 type_width = MAX(type_width, strlen(m->type));
1060 if (m->signature)
1061 signature_width = MAX(signature_width, strlen(m->signature));
1062 if (m->result)
1063 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1064 if (m->value)
1065 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1066
1067 sorted[k++] = m;
1068 }
1069
1fc55609
LP
1070 if (result_width > 40)
1071 result_width = 40;
1072
93bab288 1073 typesafe_qsort(sorted, k, member_compare_funcp);
0171da06 1074
1fc55609
LP
1075 if (arg_legend) {
1076 printf("%-*s %-*s %-*s %-*s %s\n",
1077 (int) name_width, "NAME",
1078 (int) type_width, "TYPE",
1079 (int) signature_width, "SIGNATURE",
1080 (int) result_width, "RESULT/VALUE",
1081 "FLAGS");
1082 }
0171da06
LP
1083
1084 for (j = 0; j < k; j++) {
1fc55609
LP
1085 _cleanup_free_ char *ellipsized = NULL;
1086 const char *rv;
0171da06
LP
1087 bool is_interface;
1088
1089 m = sorted[j];
1090
4f44c03e
LP
1091 if (argv[3] && !streq(argv[3], m->interface))
1092 continue;
1093
0171da06
LP
1094 is_interface = streq(m->type, "interface");
1095
4f44c03e
LP
1096 if (argv[3] && is_interface)
1097 continue;
1098
1fc55609
LP
1099 if (m->value) {
1100 ellipsized = ellipsize(m->value, result_width, 100);
1101 if (!ellipsized)
1102 return log_oom();
1103
1104 rv = ellipsized;
1105 } else
c5984fe1 1106 rv = empty_to_dash(m->result);
1fc55609 1107
0171da06
LP
1108 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1109 is_interface ? ansi_highlight() : "",
1110 is_interface ? "" : ".",
c5984fe1 1111 - !is_interface + (int) name_width, empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1fc464f6 1112 is_interface ? ansi_normal() : "",
c5984fe1
YW
1113 (int) type_width, empty_to_dash(m->type),
1114 (int) signature_width, empty_to_dash(m->signature),
1fc55609 1115 (int) result_width, rv,
0171da06
LP
1116 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1117 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1118 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1119 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1120 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1121 m->writable ? " writable" : "");
1122 }
1123
1124 return 0;
1125}
1126
1f70b087 1127static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 1128 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1129}
1130
1131static int message_pcap(sd_bus_message *m, FILE *f) {
1132 return bus_message_pcap_frame(m, arg_snaplen, f);
1133}
1134
2de62253
LP
1135static int message_json(sd_bus_message *m, FILE *f) {
1136 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
1137 char e[2];
1138 int r;
1139
1140 r = json_transform_message(m, &v);
1141 if (r < 0)
1142 return r;
1143
1144 e[0] = m->header->endian;
1145 e[1] = 0;
1146
1147 r = json_build(&w, JSON_BUILD_OBJECT(
1148 JSON_BUILD_PAIR("type", JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))),
1149 JSON_BUILD_PAIR("endian", JSON_BUILD_STRING(e)),
1150 JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(m->header->flags)),
1151 JSON_BUILD_PAIR("version", JSON_BUILD_INTEGER(m->header->version)),
3919bc24 1152 JSON_BUILD_PAIR_CONDITION(m->priority != 0, "priority", JSON_BUILD_INTEGER(m->priority)),
2de62253 1153 JSON_BUILD_PAIR("cookie", JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))),
3919bc24
LP
1154 JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", JSON_BUILD_INTEGER(m->reply_cookie)),
1155 JSON_BUILD_PAIR_CONDITION(m->sender, "sender", JSON_BUILD_STRING(m->sender)),
1156 JSON_BUILD_PAIR_CONDITION(m->destination, "destination", JSON_BUILD_STRING(m->destination)),
1157 JSON_BUILD_PAIR_CONDITION(m->path, "path", JSON_BUILD_STRING(m->path)),
1158 JSON_BUILD_PAIR_CONDITION(m->interface, "interface", JSON_BUILD_STRING(m->interface)),
1159 JSON_BUILD_PAIR_CONDITION(m->member, "member", JSON_BUILD_STRING(m->member)),
1160 JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", JSON_BUILD_INTEGER(m->monotonic)),
1161 JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", JSON_BUILD_INTEGER(m->realtime)),
1162 JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", JSON_BUILD_INTEGER(m->seqnum)),
1163 JSON_BUILD_PAIR_CONDITION(m->error.name, "error_name", JSON_BUILD_STRING(m->error.name)),
2de62253
LP
1164 JSON_BUILD_PAIR("payload", JSON_BUILD_VARIANT(v))));
1165 if (r < 0)
1166 return log_error_errno(r, "Failed to build JSON object: %m");
1167
1168 json_dump_with_flags(w, f);
1169 return 0;
1170}
1171
9bb31a0c
YW
1172static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f)) {
1173 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
17fd7460
LU
1174 _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
1175 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1f849790 1176 char **i;
17fd7460 1177 uint32_t flags = 0;
f5938e8f
TG
1178 const char *unique_name;
1179 bool is_monitor = false;
1f849790
LP
1180 int r;
1181
9bb31a0c
YW
1182 r = acquire_bus(true, &bus);
1183 if (r < 0)
1184 return r;
1185
17fd7460
LU
1186 /* upgrade connection; it's not used for anything else after this call */
1187 r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor");
1188 if (r < 0)
1189 return bus_log_create_error(r);
1190
1191 r = sd_bus_message_open_container(message, 'a', "s");
1192 if (r < 0)
1193 return bus_log_create_error(r);
1194
1f849790
LP
1195 STRV_FOREACH(i, argv+1) {
1196 _cleanup_free_ char *m = NULL;
1197
1198 if (!service_name_is_valid(*i)) {
1199 log_error("Invalid service name '%s'", *i);
1200 return -EINVAL;
1201 }
1202
605405c6 1203 m = strjoin("sender='", *i, "'");
1f849790
LP
1204 if (!m)
1205 return log_oom();
1206
17fd7460 1207 r = sd_bus_message_append_basic(message, 's', m);
23bbb0de 1208 if (r < 0)
17fd7460 1209 return bus_log_create_error(r);
b51f299a 1210
f6d1e6cb 1211 free(m);
605405c6 1212 m = strjoin("destination='", *i, "'");
f6d1e6cb
LU
1213 if (!m)
1214 return log_oom();
1215
17fd7460 1216 r = sd_bus_message_append_basic(message, 's', m);
f6d1e6cb 1217 if (r < 0)
17fd7460 1218 return bus_log_create_error(r);
1f849790
LP
1219 }
1220
1221 STRV_FOREACH(i, arg_matches) {
17fd7460 1222 r = sd_bus_message_append_basic(message, 's', *i);
23bbb0de 1223 if (r < 0)
17fd7460 1224 return bus_log_create_error(r);
b51f299a
LP
1225 }
1226
17fd7460
LU
1227 r = sd_bus_message_close_container(message);
1228 if (r < 0)
1229 return bus_log_create_error(r);
1230
1231 r = sd_bus_message_append_basic(message, 'u', &flags);
1232 if (r < 0)
1233 return bus_log_create_error(r);
1234
1235 r = sd_bus_call(bus, message, arg_timeout, &error, NULL);
48956c39
LP
1236 if (r < 0)
1237 return log_error_errno(r, "%s", bus_error_message(&error, r));
1f849790 1238
f5938e8f
TG
1239 r = sd_bus_get_unique_name(bus, &unique_name);
1240 if (r < 0)
1241 return log_error_errno(r, "Failed to get unique name: %m");
1242
92d66625
LP
1243 log_info("Monitoring bus message stream.");
1244
1f849790 1245 for (;;) {
4afd3348 1246 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1f849790
LP
1247
1248 r = sd_bus_process(bus, &m);
23bbb0de
MS
1249 if (r < 0)
1250 return log_error_errno(r, "Failed to process bus: %m");
1f849790 1251
f5938e8f
TG
1252 if (!is_monitor) {
1253 const char *name;
1254
1255 /* wait until we lose our unique name */
1256 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameLost") <= 0)
1257 continue;
1258
1259 r = sd_bus_message_read(m, "s", &name);
1260 if (r < 0)
1261 return log_error_errno(r, "Failed to read lost name: %m");
1262
d27d4637
TG
1263 if (streq(name, unique_name))
1264 is_monitor = true;
f5938e8f 1265
d27d4637 1266 continue;
f5938e8f
TG
1267 }
1268
1f849790 1269 if (m) {
1f70b087 1270 dump(m, stdout);
1d44f758 1271 fflush(stdout);
92d66625
LP
1272
1273 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
1274 log_info("Connection terminated, exiting.");
1275 return 0;
1276 }
1277
1f849790
LP
1278 continue;
1279 }
1280
1281 if (r > 0)
1282 continue;
1283
1284 r = sd_bus_wait(bus, (uint64_t) -1);
23bbb0de
MS
1285 if (r < 0)
1286 return log_error_errno(r, "Failed to wait for bus: %m");
1f849790 1287 }
95c4fe82 1288}
1f849790 1289
9bb31a0c 1290static int verb_monitor(int argc, char **argv, void *userdata) {
2de62253 1291 return monitor(argc, argv, arg_json != JSON_OFF ? message_json : message_dump);
9bb31a0c
YW
1292}
1293
1294static int verb_capture(int argc, char **argv, void *userdata) {
1f70b087
LP
1295 int r;
1296
baaa35ad
ZJS
1297 if (isatty(fileno(stdout)) > 0)
1298 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1299 "Refusing to write message data to console, please redirect output to a file.");
1f70b087
LP
1300
1301 bus_pcap_header(arg_snaplen, stdout);
1302
9bb31a0c 1303 r = monitor(argc, argv, message_pcap);
1f70b087
LP
1304 if (r < 0)
1305 return r;
1306
6b0f5484
LP
1307 r = fflush_and_check(stdout);
1308 if (r < 0)
1309 return log_error_errno(r, "Couldn't write capture file: %m");
1f70b087
LP
1310
1311 return r;
1312}
1313
9bb31a0c
YW
1314static int status(int argc, char **argv, void *userdata) {
1315 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 1316 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
95c4fe82
LP
1317 pid_t pid;
1318 int r;
1319
9bb31a0c
YW
1320 r = acquire_bus(false, &bus);
1321 if (r < 0)
1322 return r;
95c4fe82 1323
9bb31a0c 1324 if (!isempty(argv[1])) {
2e9efd22
LP
1325 r = parse_pid(argv[1], &pid);
1326 if (r < 0)
1327 r = sd_bus_get_name_creds(
1328 bus,
1329 argv[1],
1330 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1331 &creds);
1332 else
1333 r = sd_bus_creds_new_from_pid(
1334 &creds,
1335 pid,
1336 _SD_BUS_CREDS_ALL);
3acc1daf 1337 } else {
5b820358 1338 const char *scope, *address;
5c302692 1339 sd_id128_t bus_id;
3acc1daf 1340
5b820358
LP
1341 r = sd_bus_get_address(bus, &address);
1342 if (r >= 0)
1fc464f6 1343 printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal());
5b820358 1344
3acc1daf
LP
1345 r = sd_bus_get_scope(bus, &scope);
1346 if (r >= 0)
1fc464f6 1347 printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal());
3acc1daf 1348
5c302692
LP
1349 r = sd_bus_get_bus_id(bus, &bus_id);
1350 if (r >= 0)
1fc464f6 1351 printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
5c302692 1352
2e9efd22 1353 r = sd_bus_get_owner_creds(
40ed1a45 1354 bus,
40ed1a45
LP
1355 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1356 &creds);
3acc1daf 1357 }
95c4fe82 1358
23bbb0de
MS
1359 if (r < 0)
1360 return log_error_errno(r, "Failed to get credentials: %m");
95c4fe82 1361
d0b2babf 1362 bus_creds_dump(creds, NULL, false);
95c4fe82 1363 return 0;
1f849790
LP
1364}
1365
781fa938
LP
1366static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1367 char **p;
1368 int r;
1369
1370 assert(m);
1371 assert(signature);
1372 assert(x);
1373
1374 p = *x;
1375
1376 for (;;) {
1377 const char *v;
1378 char t;
1379
1380 t = *signature;
1381 v = *p;
1382
1383 if (t == 0)
1384 break;
baaa35ad
ZJS
1385 if (!v)
1386 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1387 "Too few parameters for signature.");
781fa938
LP
1388
1389 signature++;
1390 p++;
1391
1392 switch (t) {
1393
1394 case SD_BUS_TYPE_BOOLEAN:
1395
1396 r = parse_boolean(v);
48956c39 1397 if (r < 0)
3d9942de 1398 return log_error_errno(r, "Failed to parse '%s' as boolean: %m", v);
781fa938
LP
1399
1400 r = sd_bus_message_append_basic(m, t, &r);
1401 break;
1402
1403 case SD_BUS_TYPE_BYTE: {
1404 uint8_t z;
1405
1406 r = safe_atou8(v, &z);
48956c39 1407 if (r < 0)
3d9942de 1408 return log_error_errno(r, "Failed to parse '%s' as byte (unsigned 8bit integer): %m", v);
781fa938
LP
1409
1410 r = sd_bus_message_append_basic(m, t, &z);
1411 break;
1412 }
1413
1414 case SD_BUS_TYPE_INT16: {
1415 int16_t z;
1416
1417 r = safe_atoi16(v, &z);
48956c39 1418 if (r < 0)
3d9942de 1419 return log_error_errno(r, "Failed to parse '%s' as signed 16bit integer: %m", v);
781fa938
LP
1420
1421 r = sd_bus_message_append_basic(m, t, &z);
1422 break;
1423 }
1424
1425 case SD_BUS_TYPE_UINT16: {
1426 uint16_t z;
1427
1428 r = safe_atou16(v, &z);
48956c39 1429 if (r < 0)
3d9942de 1430 return log_error_errno(r, "Failed to parse '%s' as unsigned 16bit integer: %m", v);
781fa938
LP
1431
1432 r = sd_bus_message_append_basic(m, t, &z);
1433 break;
1434 }
1435
1436 case SD_BUS_TYPE_INT32: {
1437 int32_t z;
1438
1439 r = safe_atoi32(v, &z);
48956c39 1440 if (r < 0)
3d9942de 1441 return log_error_errno(r, "Failed to parse '%s' as signed 32bit integer: %m", v);
781fa938
LP
1442
1443 r = sd_bus_message_append_basic(m, t, &z);
1444 break;
1445 }
1446
1447 case SD_BUS_TYPE_UINT32: {
1448 uint32_t z;
1449
1450 r = safe_atou32(v, &z);
48956c39 1451 if (r < 0)
3d9942de 1452 return log_error_errno(r, "Failed to parse '%s' as unsigned 32bit integer: %m", v);
781fa938
LP
1453
1454 r = sd_bus_message_append_basic(m, t, &z);
1455 break;
1456 }
1457
1458 case SD_BUS_TYPE_INT64: {
1459 int64_t z;
1460
1461 r = safe_atoi64(v, &z);
48956c39 1462 if (r < 0)
3d9942de 1463 return log_error_errno(r, "Failed to parse '%s' as signed 64bit integer: %m", v);
781fa938
LP
1464
1465 r = sd_bus_message_append_basic(m, t, &z);
1466 break;
1467 }
1468
1469 case SD_BUS_TYPE_UINT64: {
1470 uint64_t z;
1471
1472 r = safe_atou64(v, &z);
48956c39 1473 if (r < 0)
3d9942de 1474 return log_error_errno(r, "Failed to parse '%s' as unsigned 64bit integer: %m", v);
781fa938
LP
1475
1476 r = sd_bus_message_append_basic(m, t, &z);
1477 break;
1478 }
1479
781fa938
LP
1480 case SD_BUS_TYPE_DOUBLE: {
1481 double z;
1482
1483 r = safe_atod(v, &z);
48956c39 1484 if (r < 0)
3d9942de 1485 return log_error_errno(r, "Failed to parse '%s' as double precision floating point: %m", v);
781fa938
LP
1486
1487 r = sd_bus_message_append_basic(m, t, &z);
1488 break;
1489 }
1490
1491 case SD_BUS_TYPE_STRING:
1492 case SD_BUS_TYPE_OBJECT_PATH:
1493 case SD_BUS_TYPE_SIGNATURE:
1494
1495 r = sd_bus_message_append_basic(m, t, v);
1496 break;
1497
1498 case SD_BUS_TYPE_ARRAY: {
1499 uint32_t n;
1500 size_t k;
1501
1502 r = safe_atou32(v, &n);
48956c39 1503 if (r < 0)
3d9942de 1504 return log_error_errno(r, "Failed to parse '%s' number of array entries: %m", v);
781fa938
LP
1505
1506 r = signature_element_length(signature, &k);
48956c39 1507 if (r < 0)
3d9942de 1508 return log_error_errno(r, "Invalid array signature: %m");
781fa938
LP
1509
1510 {
1511 unsigned i;
1512 char s[k + 1];
1513 memcpy(s, signature, k);
1514 s[k] = 0;
1515
1516 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1517 if (r < 0)
1518 return bus_log_create_error(r);
1519
1520 for (i = 0; i < n; i++) {
1521 r = message_append_cmdline(m, s, &p);
1522 if (r < 0)
1523 return r;
1524 }
1525 }
1526
1527 signature += k;
1528
1529 r = sd_bus_message_close_container(m);
1530 break;
1531 }
1532
1533 case SD_BUS_TYPE_VARIANT:
1534 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1535 if (r < 0)
1536 return bus_log_create_error(r);
1537
1538 r = message_append_cmdline(m, v, &p);
1539 if (r < 0)
1540 return r;
1541
1542 r = sd_bus_message_close_container(m);
1543 break;
1544
1545 case SD_BUS_TYPE_STRUCT_BEGIN:
1546 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1547 size_t k;
1548
1549 signature--;
1550 p--;
1551
1552 r = signature_element_length(signature, &k);
48956c39 1553 if (r < 0)
3d9942de 1554 return log_error_errno(r, "Invalid struct/dict entry signature: %m");
781fa938
LP
1555
1556 {
1557 char s[k-1];
1558 memcpy(s, signature + 1, k - 2);
1559 s[k - 2] = 0;
1560
1561 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1562 if (r < 0)
1563 return bus_log_create_error(r);
1564
1565 r = message_append_cmdline(m, s, &p);
1566 if (r < 0)
1567 return r;
1568 }
1569
1570 signature += k;
1571
1572 r = sd_bus_message_close_container(m);
1573 break;
1574 }
1575
1576 case SD_BUS_TYPE_UNIX_FD:
baaa35ad
ZJS
1577 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1578 "UNIX file descriptor not supported as type.");
781fa938
LP
1579
1580 default:
baaa35ad
ZJS
1581 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1582 "Unknown signature type %c.", t);
781fa938
LP
1583 }
1584
1585 if (r < 0)
1586 return bus_log_create_error(r);
1587 }
1588
1589 *x = p;
1590 return 0;
1591}
1592
9cebb234
LP
1593static int json_transform_one(sd_bus_message *m, JsonVariant **ret);
1594
1595static int json_transform_array_or_struct(sd_bus_message *m, JsonVariant **ret) {
1596 size_t n_elements = 0, n_allocated = 0;
1597 JsonVariant **elements = NULL;
1598 int r;
1599
1600 assert(m);
1601 assert(ret);
1602
1603 for (;;) {
1604 r = sd_bus_message_at_end(m, false);
1605 if (r < 0) {
1606 bus_log_parse_error(r);
1607 goto finish;
1608 }
1609 if (r > 0)
1610 break;
1611
1612 if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 1)) {
1613 r = log_oom();
1614 goto finish;
1615 }
1616
1617 r = json_transform_one(m, elements + n_elements);
1618 if (r < 0)
1619 goto finish;
1620
1621 n_elements++;
1622 }
1623
1624 r = json_variant_new_array(ret, elements, n_elements);
1625
1626finish:
1627 json_variant_unref_many(elements, n_elements);
1628 free(elements);
1629
1630 return r;
1631}
1632
1633static int json_transform_variant(sd_bus_message *m, const char *contents, JsonVariant **ret) {
172e4806 1634 _cleanup_(json_variant_unrefp) JsonVariant *value = NULL;
9cebb234
LP
1635 int r;
1636
1637 assert(m);
1638 assert(contents);
1639 assert(ret);
1640
1641 r = json_transform_one(m, &value);
1642 if (r < 0)
1643 return r;
1644
1645 r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(contents)),
1646 JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(value))));
1647 if (r < 0)
1648 return log_oom();
1649
1650 return r;
1651}
1652
1653static int json_transform_dict_array(sd_bus_message *m, JsonVariant **ret) {
1654 size_t n_elements = 0, n_allocated = 0;
1655 JsonVariant **elements = NULL;
1656 int r;
1657
1658 assert(m);
1659 assert(ret);
1660
1661 for (;;) {
1662 const char *contents;
1663 char type;
1664
1665 r = sd_bus_message_at_end(m, false);
1666 if (r < 0) {
1667 bus_log_parse_error(r);
1668 goto finish;
1669 }
1670 if (r > 0)
1671 break;
1672
1673 r = sd_bus_message_peek_type(m, &type, &contents);
1674 if (r < 0)
1675 return r;
1676
1677 assert(type == 'e');
1678
1679 if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 2)) {
1680 r = log_oom();
1681 goto finish;
1682 }
1683
1684 r = sd_bus_message_enter_container(m, type, contents);
1685 if (r < 0) {
1686 bus_log_parse_error(r);
1687 goto finish;
1688 }
1689
1690 r = json_transform_one(m, elements + n_elements);
1691 if (r < 0)
1692 goto finish;
1693
1694 n_elements++;
1695
1696 r = json_transform_one(m, elements + n_elements);
1697 if (r < 0)
1698 goto finish;
1699
1700 n_elements++;
1701
1702 r = sd_bus_message_exit_container(m);
1703 if (r < 0) {
1704 bus_log_parse_error(r);
1705 goto finish;
1706 }
1707 }
1708
1709 r = json_variant_new_object(ret, elements, n_elements);
1710
1711finish:
1712 json_variant_unref_many(elements, n_elements);
1713 free(elements);
1714
1715 return r;
1716}
1717
1718static int json_transform_one(sd_bus_message *m, JsonVariant **ret) {
1719 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1720 const char *contents;
1721 char type;
1722 int r;
1723
1724 assert(m);
1725 assert(ret);
1726
1727 r = sd_bus_message_peek_type(m, &type, &contents);
1728 if (r < 0)
1729 return bus_log_parse_error(r);
1730
1731 switch (type) {
1732
1733 case SD_BUS_TYPE_BYTE: {
1734 uint8_t b;
1735
1736 r = sd_bus_message_read_basic(m, type, &b);
1737 if (r < 0)
1738 return bus_log_parse_error(r);
1739
1740 r = json_variant_new_unsigned(&v, b);
1741 if (r < 0)
1742 return log_error_errno(r, "Failed to transform byte: %m");
1743
1744 break;
1745 }
1746
1747 case SD_BUS_TYPE_BOOLEAN: {
1748 int b;
1749
1750 r = sd_bus_message_read_basic(m, type, &b);
1751 if (r < 0)
1752 return bus_log_parse_error(r);
1753
1754 r = json_variant_new_boolean(&v, b);
1755 if (r < 0)
1756 return log_error_errno(r, "Failed to transform boolean: %m");
1757
1758 break;
1759 }
1760
1761 case SD_BUS_TYPE_INT16: {
1762 int16_t b;
1763
1764 r = sd_bus_message_read_basic(m, type, &b);
1765 if (r < 0)
1766 return bus_log_parse_error(r);
1767
1768 r = json_variant_new_integer(&v, b);
1769 if (r < 0)
1770 return log_error_errno(r, "Failed to transform int16: %m");
1771
1772 break;
1773 }
1774
1775 case SD_BUS_TYPE_UINT16: {
1776 uint16_t b;
1777
1778 r = sd_bus_message_read_basic(m, type, &b);
1779 if (r < 0)
1780 return bus_log_parse_error(r);
1781
1782 r = json_variant_new_unsigned(&v, b);
1783 if (r < 0)
1784 return log_error_errno(r, "Failed to transform uint16: %m");
1785
1786 break;
1787 }
1788
1789 case SD_BUS_TYPE_INT32: {
1790 int32_t b;
1791
1792 r = sd_bus_message_read_basic(m, type, &b);
1793 if (r < 0)
1794 return bus_log_parse_error(r);
1795
1796 r = json_variant_new_integer(&v, b);
1797 if (r < 0)
1798 return log_error_errno(r, "Failed to transform int32: %m");
1799
1800 break;
1801 }
1802
1803 case SD_BUS_TYPE_UINT32: {
1804 uint32_t b;
1805
1806 r = sd_bus_message_read_basic(m, type, &b);
1807 if (r < 0)
1808 return bus_log_parse_error(r);
1809
1810 r = json_variant_new_unsigned(&v, b);
1811 if (r < 0)
1812 return log_error_errno(r, "Failed to transform uint32: %m");
1813
1814 break;
1815 }
1816
1817 case SD_BUS_TYPE_INT64: {
1818 int64_t b;
1819
1820 r = sd_bus_message_read_basic(m, type, &b);
1821 if (r < 0)
1822 return bus_log_parse_error(r);
1823
1824 r = json_variant_new_integer(&v, b);
1825 if (r < 0)
1826 return log_error_errno(r, "Failed to transform int64: %m");
1827
1828 break;
1829 }
1830
1831 case SD_BUS_TYPE_UINT64: {
1832 uint64_t b;
1833
1834 r = sd_bus_message_read_basic(m, type, &b);
1835 if (r < 0)
1836 return bus_log_parse_error(r);
1837
1838 r = json_variant_new_unsigned(&v, b);
1839 if (r < 0)
1840 return log_error_errno(r, "Failed to transform uint64: %m");
1841
1842 break;
1843 }
1844
1845 case SD_BUS_TYPE_DOUBLE: {
1846 double d;
1847
1848 r = sd_bus_message_read_basic(m, type, &d);
1849 if (r < 0)
1850 return bus_log_parse_error(r);
1851
1852 r = json_variant_new_real(&v, d);
1853 if (r < 0)
1854 return log_error_errno(r, "Failed to transform double: %m");
1855
1856 break;
1857 }
1858
1859 case SD_BUS_TYPE_STRING:
1860 case SD_BUS_TYPE_OBJECT_PATH:
1861 case SD_BUS_TYPE_SIGNATURE: {
1862 const char *s;
1863
1864 r = sd_bus_message_read_basic(m, type, &s);
1865 if (r < 0)
1866 return bus_log_parse_error(r);
1867
1868 r = json_variant_new_string(&v, s);
1869 if (r < 0)
1870 return log_error_errno(r, "Failed to transform double: %m");
1871
1872 break;
1873 }
1874
1875 case SD_BUS_TYPE_UNIX_FD:
1876 r = sd_bus_message_read_basic(m, type, NULL);
1877 if (r < 0)
1878 return bus_log_parse_error(r);
1879
1880 r = json_variant_new_null(&v);
1881 if (r < 0)
1882 return log_error_errno(r, "Failed to transform fd: %m");
1883
1884 break;
1885
1886 case SD_BUS_TYPE_ARRAY:
1887 case SD_BUS_TYPE_VARIANT:
1888 case SD_BUS_TYPE_STRUCT:
1889 r = sd_bus_message_enter_container(m, type, contents);
1890 if (r < 0)
1891 return bus_log_parse_error(r);
1892
1893 if (type == SD_BUS_TYPE_VARIANT)
1894 r = json_transform_variant(m, contents, &v);
1895 else if (type == SD_BUS_TYPE_ARRAY && contents[0] == '{')
1896 r = json_transform_dict_array(m, &v);
1897 else
1898 r = json_transform_array_or_struct(m, &v);
1899 if (r < 0)
1900 return r;
1901
1902 r = sd_bus_message_exit_container(m);
1903 if (r < 0)
1904 return bus_log_parse_error(r);
1905
1906 break;
1907
1908 default:
1909 assert_not_reached("Unexpected element type");
1910 }
1911
1912 *ret = TAKE_PTR(v);
1913 return 0;
1914}
1915
1916static int json_transform_message(sd_bus_message *m, JsonVariant **ret) {
1917 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1918 const char *type;
1919 int r;
1920
1921 assert(m);
1922 assert(ret);
1923
1924 assert_se(type = sd_bus_message_get_signature(m, false));
1925
1926 r = json_transform_array_or_struct(m, &v);
1927 if (r < 0)
1928 return r;
1929
1930 r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(type)),
1931 JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(v))));
1932 if (r < 0)
1933 return log_oom();
1934
1935 return 0;
1936}
1937
1938static void json_dump_with_flags(JsonVariant *v, FILE *f) {
1939
1940 json_variant_dump(v,
1941 (arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) |
ab91733c 1942 JSON_FORMAT_COLOR_AUTO,
9cebb234
LP
1943 f, NULL);
1944}
1945
9bb31a0c
YW
1946static int call(int argc, char **argv, void *userdata) {
1947 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348
LP
1948 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1949 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
781fa938
LP
1950 int r;
1951
9bb31a0c
YW
1952 r = acquire_bus(false, &bus);
1953 if (r < 0)
1954 return r;
781fa938
LP
1955
1956 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
1957 if (r < 0)
1958 return bus_log_create_error(r);
781fa938 1959
38051578
LP
1960 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
1961 if (r < 0)
1962 return bus_log_create_error(r);
1963
1964 r = sd_bus_message_set_auto_start(m, arg_auto_start);
1965 if (r < 0)
1966 return bus_log_create_error(r);
1967
1968 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
1969 if (r < 0)
1970 return bus_log_create_error(r);
1971
781fa938
LP
1972 if (!isempty(argv[5])) {
1973 char **p;
1974
1975 p = argv+6;
1976
1977 r = message_append_cmdline(m, argv[5], &p);
1978 if (r < 0)
1979 return r;
1980
1981 if (*p) {
1982 log_error("Too many parameters for signature.");
1983 return -EINVAL;
1984 }
1985 }
1986
38051578
LP
1987 if (!arg_expect_reply) {
1988 r = sd_bus_send(bus, m, NULL);
48956c39
LP
1989 if (r < 0)
1990 return log_error_errno(r, "Failed to send message: %m");
38051578
LP
1991
1992 return 0;
1993 }
1994
a44b1081 1995 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
48956c39
LP
1996 if (r < 0)
1997 return log_error_errno(r, "%s", bus_error_message(&error, r));
781fa938
LP
1998
1999 r = sd_bus_message_is_empty(reply);
2000 if (r < 0)
2001 return bus_log_parse_error(r);
1fc55609 2002
781fa938 2003 if (r == 0 && !arg_quiet) {
1fc55609 2004
9cebb234
LP
2005 if (arg_json != JSON_OFF) {
2006 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
2007
2008 if (arg_json != JSON_SHORT)
0221d68a 2009 (void) pager_open(arg_pager_flags);
9cebb234
LP
2010
2011 r = json_transform_message(reply, &v);
2012 if (r < 0)
2013 return r;
2014
2015 json_dump_with_flags(v, stdout);
2016
2017 } else if (arg_verbose) {
0221d68a 2018 (void) pager_open(arg_pager_flags);
1fc55609
LP
2019
2020 r = bus_message_dump(reply, stdout, 0);
2021 if (r < 0)
2022 return r;
2023 } else {
2024
2025 fputs(sd_bus_message_get_signature(reply, true), stdout);
2026 fputc(' ', stdout);
2027
2028 r = format_cmdline(reply, stdout, false);
2029 if (r < 0)
2030 return bus_log_parse_error(r);
2031
2032 fputc('\n', stdout);
2033 }
d55192ad
LP
2034 }
2035
2036 return 0;
2037}
2038
9bb31a0c
YW
2039static int get_property(int argc, char **argv, void *userdata) {
2040 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 2041 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609 2042 char **i;
d55192ad
LP
2043 int r;
2044
9bb31a0c
YW
2045 r = acquire_bus(false, &bus);
2046 if (r < 0)
2047 return r;
d55192ad 2048
1fc55609 2049 STRV_FOREACH(i, argv + 4) {
4afd3348 2050 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
2051 const char *contents = NULL;
2052 char type;
d55192ad 2053
1fc55609 2054 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
48956c39
LP
2055 if (r < 0)
2056 return log_error_errno(r, "%s", bus_error_message(&error, r));
d55192ad 2057
1fc55609 2058 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
2059 if (r < 0)
2060 return bus_log_parse_error(r);
2061
1fc55609
LP
2062 r = sd_bus_message_enter_container(reply, 'v', contents);
2063 if (r < 0)
2064 return bus_log_parse_error(r);
d55192ad 2065
9cebb234
LP
2066 if (arg_json != JSON_OFF) {
2067 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
2068
2069 if (arg_json != JSON_SHORT)
0221d68a 2070 (void) pager_open(arg_pager_flags);
9cebb234
LP
2071
2072 r = json_transform_variant(reply, contents, &v);
2073 if (r < 0)
2074 return r;
2075
2076 json_dump_with_flags(v, stdout);
2077
2de62253 2078 } else if (arg_verbose) {
0221d68a 2079 (void) pager_open(arg_pager_flags);
d55192ad 2080
1fc55609 2081 r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 2082 if (r < 0)
1fc55609
LP
2083 return r;
2084 } else {
2085 fputs(contents, stdout);
2086 fputc(' ', stdout);
d55192ad 2087
1fc55609 2088 r = format_cmdline(reply, stdout, false);
d55192ad
LP
2089 if (r < 0)
2090 return bus_log_parse_error(r);
2091
1fc55609 2092 fputc('\n', stdout);
d55192ad
LP
2093 }
2094
2095 r = sd_bus_message_exit_container(reply);
2096 if (r < 0)
2097 return bus_log_parse_error(r);
1fc55609 2098 }
d55192ad 2099
1fc55609
LP
2100 return 0;
2101}
d55192ad 2102
9bb31a0c
YW
2103static int set_property(int argc, char **argv, void *userdata) {
2104 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348
LP
2105 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2106 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609
LP
2107 char **p;
2108 int r;
d55192ad 2109
9bb31a0c
YW
2110 r = acquire_bus(false, &bus);
2111 if (r < 0)
2112 return r;
d55192ad 2113
1fc55609
LP
2114 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
2115 if (r < 0)
2116 return bus_log_create_error(r);
d55192ad 2117
1fc55609
LP
2118 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
2119 if (r < 0)
2120 return bus_log_create_error(r);
d55192ad 2121
1fc55609
LP
2122 r = sd_bus_message_open_container(m, 'v', argv[5]);
2123 if (r < 0)
2124 return bus_log_create_error(r);
2125
9bb31a0c 2126 p = argv + 6;
1fc55609
LP
2127 r = message_append_cmdline(m, argv[5], &p);
2128 if (r < 0)
2129 return r;
2130
2131 r = sd_bus_message_close_container(m);
2132 if (r < 0)
2133 return bus_log_create_error(r);
2134
2135 if (*p) {
2136 log_error("Too many parameters for signature.");
2137 return -EINVAL;
2138 }
2139
a44b1081 2140 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
48956c39
LP
2141 if (r < 0)
2142 return log_error_errno(r, "%s", bus_error_message(&error, r));
781fa938
LP
2143
2144 return 0;
2145}
2146
1f849790 2147static int help(void) {
37ec0fdd
LP
2148 _cleanup_free_ char *link = NULL;
2149 int r;
2150
2151 r = terminal_urlify_man("busctl", "1", &link);
2152 if (r < 0)
2153 return log_oom();
2154
1f849790
LP
2155 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
2156 "Introspect the bus.\n\n"
d75edbd6
LP
2157 " -h --help Show this help\n"
2158 " --version Show package version\n"
a86a47ce 2159 " --no-pager Do not pipe output into a pager\n"
17d47d8d 2160 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
2161 " --system Connect to system bus\n"
2162 " --user Connect to user bus\n"
2163 " -H --host=[USER@]HOST Operate on remote host\n"
2164 " -M --machine=CONTAINER Operate on local container\n"
2165 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
2166 " --show-machine Show machine ID column in list\n"
2167 " --unique Only show unique names\n"
2168 " --acquired Only show acquired names\n"
2169 " --activatable Only show activatable names\n"
d9130355 2170 " --match=MATCH Only show matching messages\n"
d28ebe27 2171 " --size=SIZE Maximum length of captured packet\n"
781fa938 2172 " --list Don't show tree, but simple object path list\n"
e53fa805 2173 " -q --quiet Don't show method call reply\n"
38051578 2174 " --verbose Show result values in long format\n"
9cebb234
LP
2175 " --json=MODE Output as JSON\n"
2176 " -j Same as --json=pretty on tty, --json=short otherwise\n"
38051578
LP
2177 " --expect-reply=BOOL Expect a method call reply\n"
2178 " --auto-start=BOOL Auto-start destination service\n"
2179 " --allow-interactive-authorization=BOOL\n"
a44b1081 2180 " Allow interactive authorization for operation\n"
40ed1a45 2181 " --timeout=SECS Maximum time to wait for method call completion\n"
56d820b6
LP
2182 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n"
2183 " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n"
2184 " system\n\n"
1f849790 2185 "Commands:\n"
d75edbd6 2186 " list List bus names\n"
2e9efd22 2187 " status [SERVICE] Show bus service, process or bus owner credentials\n"
d94fe1f1 2188 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 2189 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 2190 " tree [SERVICE...] Show object tree of service\n"
4f44c03e 2191 " introspect SERVICE OBJECT [INTERFACE]\n"
0171da06 2192 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 2193 " Call a method\n"
1fc55609 2194 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 2195 " Get property value\n"
1fc55609
LP
2196 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
2197 " Set property value\n"
601185b4 2198 " help Show this help\n"
37ec0fdd
LP
2199 "\nSee the %s for details.\n"
2200 , program_invocation_short_name
2201 , link
2202 );
1f849790
LP
2203
2204 return 0;
2205}
2206
9bb31a0c
YW
2207static int verb_help(int argc, char **argv, void *userdata) {
2208 return help();
2209}
2210
1f849790
LP
2211static int parse_argv(int argc, char *argv[]) {
2212
2213 enum {
2214 ARG_VERSION = 0x100,
2215 ARG_NO_PAGER,
17d47d8d 2216 ARG_NO_LEGEND,
1f849790
LP
2217 ARG_SYSTEM,
2218 ARG_USER,
2219 ARG_ADDRESS,
2220 ARG_MATCH,
56e61788
LP
2221 ARG_SHOW_MACHINE,
2222 ARG_UNIQUE,
2223 ARG_ACQUIRED,
1f70b087
LP
2224 ARG_ACTIVATABLE,
2225 ARG_SIZE,
d9130355 2226 ARG_LIST,
1fc55609 2227 ARG_VERBOSE,
38051578
LP
2228 ARG_EXPECT_REPLY,
2229 ARG_AUTO_START,
2230 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 2231 ARG_TIMEOUT,
40ed1a45 2232 ARG_AUGMENT_CREDS,
56d820b6 2233 ARG_WATCH_BIND,
9cebb234 2234 ARG_JSON,
1f849790
LP
2235 };
2236
2237 static const struct option options[] = {
9cebb234
LP
2238 { "help", no_argument, NULL, 'h' },
2239 { "version", no_argument, NULL, ARG_VERSION },
2240 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2241 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
2242 { "system", no_argument, NULL, ARG_SYSTEM },
2243 { "user", no_argument, NULL, ARG_USER },
2244 { "address", required_argument, NULL, ARG_ADDRESS },
2245 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
2246 { "unique", no_argument, NULL, ARG_UNIQUE },
2247 { "acquired", no_argument, NULL, ARG_ACQUIRED },
2248 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
2249 { "match", required_argument, NULL, ARG_MATCH },
2250 { "host", required_argument, NULL, 'H' },
2251 { "machine", required_argument, NULL, 'M' },
2252 { "size", required_argument, NULL, ARG_SIZE },
2253 { "list", no_argument, NULL, ARG_LIST },
2254 { "quiet", no_argument, NULL, 'q' },
2255 { "verbose", no_argument, NULL, ARG_VERBOSE },
2256 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
2257 { "auto-start", required_argument, NULL, ARG_AUTO_START },
38051578 2258 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
9cebb234
LP
2259 { "timeout", required_argument, NULL, ARG_TIMEOUT },
2260 { "augment-creds", required_argument, NULL, ARG_AUGMENT_CREDS },
2261 { "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
2262 { "json", required_argument, NULL, ARG_JSON },
eb9da376 2263 {},
1f849790
LP
2264 };
2265
1f70b087 2266 int c, r;
1f849790
LP
2267
2268 assert(argc >= 0);
2269 assert(argv);
2270
9cebb234 2271 while ((c = getopt_long(argc, argv, "hH:M:qj", options, NULL)) >= 0)
1f849790
LP
2272
2273 switch (c) {
2274
2275 case 'h':
2276 return help();
2277
2278 case ARG_VERSION:
3f6fd1ba 2279 return version();
1f849790
LP
2280
2281 case ARG_NO_PAGER:
0221d68a 2282 arg_pager_flags |= PAGER_DISABLE;
1f849790
LP
2283 break;
2284
17d47d8d
TA
2285 case ARG_NO_LEGEND:
2286 arg_legend = false;
2287 break;
2288
1f849790
LP
2289 case ARG_USER:
2290 arg_user = true;
2291 break;
2292
2293 case ARG_SYSTEM:
2294 arg_user = false;
2295 break;
2296
2297 case ARG_ADDRESS:
2298 arg_address = optarg;
2299 break;
2300
56e61788
LP
2301 case ARG_SHOW_MACHINE:
2302 arg_show_machine = true;
2303 break;
2304
2305 case ARG_UNIQUE:
2306 arg_unique = true;
1f849790
LP
2307 break;
2308
56e61788
LP
2309 case ARG_ACQUIRED:
2310 arg_acquired = true;
2311 break;
2312
2313 case ARG_ACTIVATABLE:
2314 arg_activatable = true;
a4297f08
LP
2315 break;
2316
1f849790
LP
2317 case ARG_MATCH:
2318 if (strv_extend(&arg_matches, optarg) < 0)
2319 return log_oom();
2320 break;
2321
1f70b087 2322 case ARG_SIZE: {
59f448cf 2323 uint64_t sz;
1f70b087 2324
59f448cf 2325 r = parse_size(optarg, 1024, &sz);
48956c39 2326 if (r < 0)
3d9942de 2327 return log_error_errno(r, "Failed to parse size '%s': %m", optarg);
1f70b087 2328
baaa35ad
ZJS
2329 if ((uint64_t) (size_t) sz != sz)
2330 return log_error_errno(SYNTHETIC_ERRNO(E2BIG),
2331 "Size out of range.");
1f70b087 2332
59f448cf 2333 arg_snaplen = (size_t) sz;
1f70b087
LP
2334 break;
2335 }
2336
d9130355
LP
2337 case ARG_LIST:
2338 arg_list = true;
2339 break;
2340
d75edbd6
LP
2341 case 'H':
2342 arg_transport = BUS_TRANSPORT_REMOTE;
2343 arg_host = optarg;
2344 break;
2345
2346 case 'M':
de33fc62 2347 arg_transport = BUS_TRANSPORT_MACHINE;
d75edbd6
LP
2348 arg_host = optarg;
2349 break;
2350
781fa938
LP
2351 case 'q':
2352 arg_quiet = true;
2353 break;
2354
1fc55609
LP
2355 case ARG_VERBOSE:
2356 arg_verbose = true;
2357 break;
2358
38051578
LP
2359 case ARG_EXPECT_REPLY:
2360 r = parse_boolean(optarg);
48956c39 2361 if (r < 0)
3d9942de 2362 return log_error_errno(r, "Failed to parse --expect-reply= parameter '%s': %m", optarg);
38051578 2363
5d904a6a 2364 arg_expect_reply = r;
38051578
LP
2365 break;
2366
38051578
LP
2367 case ARG_AUTO_START:
2368 r = parse_boolean(optarg);
48956c39 2369 if (r < 0)
3d9942de 2370 return log_error_errno(r, "Failed to parse --auto-start= parameter '%s': %m", optarg);
38051578 2371
5d904a6a 2372 arg_auto_start = r;
38051578
LP
2373 break;
2374
38051578
LP
2375 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
2376 r = parse_boolean(optarg);
48956c39 2377 if (r < 0)
3d9942de 2378 return log_error_errno(r, "Failed to parse --allow-interactive-authorization= parameter '%s': %m", optarg);
38051578 2379
5d904a6a 2380 arg_allow_interactive_authorization = r;
38051578
LP
2381 break;
2382
a44b1081
LP
2383 case ARG_TIMEOUT:
2384 r = parse_sec(optarg, &arg_timeout);
48956c39 2385 if (r < 0)
3d9942de 2386 return log_error_errno(r, "Failed to parse --timeout= parameter '%s': %m", optarg);
a44b1081
LP
2387
2388 break;
2389
40ed1a45
LP
2390 case ARG_AUGMENT_CREDS:
2391 r = parse_boolean(optarg);
48956c39 2392 if (r < 0)
3d9942de 2393 return log_error_errno(r, "Failed to parse --augment-creds= parameter '%s': %m", optarg);
40ed1a45 2394
5d904a6a 2395 arg_augment_creds = r;
40ed1a45
LP
2396 break;
2397
56d820b6
LP
2398 case ARG_WATCH_BIND:
2399 r = parse_boolean(optarg);
48956c39 2400 if (r < 0)
3d9942de 2401 return log_error_errno(r, "Failed to parse --watch-bind= parameter '%s': %m", optarg);
56d820b6 2402
5d904a6a 2403 arg_watch_bind = r;
56d820b6
LP
2404 break;
2405
9cebb234
LP
2406 case 'j':
2407 if (on_tty())
2408 arg_json = JSON_PRETTY;
2409 else
2410 arg_json = JSON_SHORT;
2411 break;
2412
2413 case ARG_JSON:
2414 if (streq(optarg, "short"))
2415 arg_json = JSON_SHORT;
2416 else if (streq(optarg, "pretty"))
2417 arg_json = JSON_PRETTY;
2418 else if (streq(optarg, "help")) {
2419 fputs("short\n"
2420 "pretty\n", stdout);
2421 return 0;
baaa35ad
ZJS
2422 } else
2423 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2424 "Unknown JSON out mode: %s",
2425 optarg);
9cebb234
LP
2426
2427 break;
2428
1f849790
LP
2429 case '?':
2430 return -EINVAL;
2431
2432 default:
eb9da376 2433 assert_not_reached("Unhandled option");
1f849790 2434 }
1f849790
LP
2435
2436 return 1;
2437}
2438
9bb31a0c
YW
2439static int busctl_main(int argc, char *argv[]) {
2440
2441 static const Verb verbs[] = {
2442 { "list", VERB_ANY, 1, VERB_DEFAULT, list_bus_names },
2443 { "status", VERB_ANY, 2, 0, status },
2444 { "monitor", VERB_ANY, VERB_ANY, 0, verb_monitor },
2445 { "capture", VERB_ANY, VERB_ANY, 0, verb_capture },
2446 { "tree", VERB_ANY, VERB_ANY, 0, tree },
2447 { "introspect", 3, 4, 0, introspect },
2448 { "call", 5, VERB_ANY, 0, call },
2449 { "get-property", 5, VERB_ANY, 0, get_property },
2450 { "set-property", 6, VERB_ANY, 0, set_property },
2451 { "help", VERB_ANY, VERB_ANY, 0, verb_help },
2452 {}
2453 };
1f849790 2454
9bb31a0c 2455 return dispatch_verb(argc, argv, verbs, NULL);
1f849790
LP
2456}
2457
360f3dc2 2458static int run(int argc, char *argv[]) {
1f849790
LP
2459 int r;
2460
2461 log_parse_environment();
2462 log_open();
2463
2464 r = parse_argv(argc, argv);
2465 if (r <= 0)
360f3dc2 2466 return r;
de1c301e 2467
360f3dc2 2468 return busctl_main(argc, argv);
de1c301e 2469}
360f3dc2
YW
2470
2471DEFINE_MAIN_FUNCTION(run);