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