]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/busctl/busctl.c
busctl: modernize code a bit
[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, "");
0171da06
LP
890 if (r < 0) {
891 log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
892 return r;
893 }
894
9efebb65 895 r = sd_bus_message_read(reply_xml, "s", &xml);
0171da06
LP
896 if (r < 0)
897 return bus_log_parse_error(r);
898
1fc55609 899 /* First, get list of all properties */
0171da06
LP
900 r = parse_xml_introspect(argv[2], xml, &ops, members);
901 if (r < 0)
902 return r;
903
1fc55609
LP
904 /* Second, find the current values for them */
905 SET_FOREACH(m, members, i) {
9efebb65 906 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
907
908 if (!streq(m->type, "property"))
909 continue;
910
911 if (m->value)
912 continue;
913
4f44c03e
LP
914 if (argv[3] && !streq(argv[3], m->interface))
915 continue;
916
1fc55609
LP
917 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
918 if (r < 0) {
919 log_error("%s", bus_error_message(&error, r));
920 return r;
921 }
922
923 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
924 if (r < 0)
925 return bus_log_parse_error(r);
926
927 for (;;) {
928 Member *z;
929 _cleanup_free_ char *buf = NULL;
930 _cleanup_fclose_ FILE *mf = NULL;
931 size_t sz = 0;
932 const char *name;
933
934 r = sd_bus_message_enter_container(reply, 'e', "sv");
935 if (r < 0)
936 return bus_log_parse_error(r);
937
938 if (r == 0)
939 break;
940
941 r = sd_bus_message_read(reply, "s", &name);
942 if (r < 0)
943 return bus_log_parse_error(r);
944
945 r = sd_bus_message_enter_container(reply, 'v', NULL);
946 if (r < 0)
947 return bus_log_parse_error(r);
948
949 mf = open_memstream(&buf, &sz);
950 if (!mf)
951 return log_oom();
952
0d536673
LP
953 (void) __fsetlocking(mf, FSETLOCKING_BYCALLER);
954
1fc55609
LP
955 r = format_cmdline(reply, mf, false);
956 if (r < 0)
957 return bus_log_parse_error(r);
958
8d3b9edc 959 mf = safe_fclose(mf);
1fc55609
LP
960
961 z = set_get(members, &((Member) {
962 .type = "property",
963 .interface = m->interface,
964 .name = (char*) name }));
8d3b9edc
LP
965 if (z)
966 free_and_replace(z->value, buf);
1fc55609
LP
967
968 r = sd_bus_message_exit_container(reply);
969 if (r < 0)
970 return bus_log_parse_error(r);
971
972 r = sd_bus_message_exit_container(reply);
973 if (r < 0)
974 return bus_log_parse_error(r);
975 }
976
977 r = sd_bus_message_exit_container(reply);
978 if (r < 0)
979 return bus_log_parse_error(r);
980 }
981
ea4b98e6 982 pager_open(arg_no_pager, false);
0171da06
LP
983
984 name_width = strlen("NAME");
985 type_width = strlen("TYPE");
986 signature_width = strlen("SIGNATURE");
1fc55609 987 result_width = strlen("RESULT/VALUE");
0171da06
LP
988
989 sorted = newa(Member*, set_size(members));
990
991 SET_FOREACH(m, members, i) {
4f44c03e
LP
992
993 if (argv[3] && !streq(argv[3], m->interface))
994 continue;
995
0171da06
LP
996 if (m->interface)
997 name_width = MAX(name_width, strlen(m->interface));
998 if (m->name)
999 name_width = MAX(name_width, strlen(m->name) + 1);
1000 if (m->type)
1001 type_width = MAX(type_width, strlen(m->type));
1002 if (m->signature)
1003 signature_width = MAX(signature_width, strlen(m->signature));
1004 if (m->result)
1005 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1006 if (m->value)
1007 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1008
1009 sorted[k++] = m;
1010 }
1011
1fc55609
LP
1012 if (result_width > 40)
1013 result_width = 40;
1014
0171da06
LP
1015 qsort(sorted, k, sizeof(Member*), member_compare_funcp);
1016
1fc55609
LP
1017 if (arg_legend) {
1018 printf("%-*s %-*s %-*s %-*s %s\n",
1019 (int) name_width, "NAME",
1020 (int) type_width, "TYPE",
1021 (int) signature_width, "SIGNATURE",
1022 (int) result_width, "RESULT/VALUE",
1023 "FLAGS");
1024 }
0171da06
LP
1025
1026 for (j = 0; j < k; j++) {
1fc55609
LP
1027 _cleanup_free_ char *ellipsized = NULL;
1028 const char *rv;
0171da06
LP
1029 bool is_interface;
1030
1031 m = sorted[j];
1032
4f44c03e
LP
1033 if (argv[3] && !streq(argv[3], m->interface))
1034 continue;
1035
0171da06
LP
1036 is_interface = streq(m->type, "interface");
1037
4f44c03e
LP
1038 if (argv[3] && is_interface)
1039 continue;
1040
1fc55609
LP
1041 if (m->value) {
1042 ellipsized = ellipsize(m->value, result_width, 100);
1043 if (!ellipsized)
1044 return log_oom();
1045
1046 rv = ellipsized;
1047 } else
1048 rv = strdash(m->result);
1049
0171da06
LP
1050 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1051 is_interface ? ansi_highlight() : "",
1052 is_interface ? "" : ".",
1053 - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1fc464f6 1054 is_interface ? ansi_normal() : "",
0171da06
LP
1055 (int) type_width, strdash(m->type),
1056 (int) signature_width, strdash(m->signature),
1fc55609 1057 (int) result_width, rv,
0171da06
LP
1058 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1059 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1060 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1061 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1062 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1063 m->writable ? " writable" : "");
1064 }
1065
1066 return 0;
1067}
1068
1f70b087 1069static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 1070 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1071}
1072
1073static int message_pcap(sd_bus_message *m, FILE *f) {
1074 return bus_message_pcap_frame(m, arg_snaplen, f);
1075}
1076
1077static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
17fd7460
LU
1078 _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
1079 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1f849790 1080 char **i;
17fd7460 1081 uint32_t flags = 0;
f5938e8f
TG
1082 const char *unique_name;
1083 bool is_monitor = false;
1f849790
LP
1084 int r;
1085
17fd7460
LU
1086 /* upgrade connection; it's not used for anything else after this call */
1087 r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor");
1088 if (r < 0)
1089 return bus_log_create_error(r);
1090
1091 r = sd_bus_message_open_container(message, 'a', "s");
1092 if (r < 0)
1093 return bus_log_create_error(r);
1094
1f849790
LP
1095 STRV_FOREACH(i, argv+1) {
1096 _cleanup_free_ char *m = NULL;
1097
1098 if (!service_name_is_valid(*i)) {
1099 log_error("Invalid service name '%s'", *i);
1100 return -EINVAL;
1101 }
1102
605405c6 1103 m = strjoin("sender='", *i, "'");
1f849790
LP
1104 if (!m)
1105 return log_oom();
1106
17fd7460 1107 r = sd_bus_message_append_basic(message, 's', m);
23bbb0de 1108 if (r < 0)
17fd7460 1109 return bus_log_create_error(r);
b51f299a 1110
f6d1e6cb 1111 free(m);
605405c6 1112 m = strjoin("destination='", *i, "'");
f6d1e6cb
LU
1113 if (!m)
1114 return log_oom();
1115
17fd7460 1116 r = sd_bus_message_append_basic(message, 's', m);
f6d1e6cb 1117 if (r < 0)
17fd7460 1118 return bus_log_create_error(r);
1f849790
LP
1119 }
1120
1121 STRV_FOREACH(i, arg_matches) {
17fd7460 1122 r = sd_bus_message_append_basic(message, 's', *i);
23bbb0de 1123 if (r < 0)
17fd7460 1124 return bus_log_create_error(r);
b51f299a
LP
1125 }
1126
17fd7460
LU
1127 r = sd_bus_message_close_container(message);
1128 if (r < 0)
1129 return bus_log_create_error(r);
1130
1131 r = sd_bus_message_append_basic(message, 'u', &flags);
1132 if (r < 0)
1133 return bus_log_create_error(r);
1134
1135 r = sd_bus_call(bus, message, arg_timeout, &error, NULL);
1136 if (r < 0) {
1137 log_error("%s", bus_error_message(&error, r));
1138 return r;
1f849790
LP
1139 }
1140
f5938e8f
TG
1141 r = sd_bus_get_unique_name(bus, &unique_name);
1142 if (r < 0)
1143 return log_error_errno(r, "Failed to get unique name: %m");
1144
92d66625
LP
1145 log_info("Monitoring bus message stream.");
1146
1f849790 1147 for (;;) {
4afd3348 1148 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1f849790
LP
1149
1150 r = sd_bus_process(bus, &m);
23bbb0de
MS
1151 if (r < 0)
1152 return log_error_errno(r, "Failed to process bus: %m");
1f849790 1153
f5938e8f
TG
1154 if (!is_monitor) {
1155 const char *name;
1156
1157 /* wait until we lose our unique name */
1158 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameLost") <= 0)
1159 continue;
1160
1161 r = sd_bus_message_read(m, "s", &name);
1162 if (r < 0)
1163 return log_error_errno(r, "Failed to read lost name: %m");
1164
d27d4637
TG
1165 if (streq(name, unique_name))
1166 is_monitor = true;
f5938e8f 1167
d27d4637 1168 continue;
f5938e8f
TG
1169 }
1170
1f849790 1171 if (m) {
1f70b087 1172 dump(m, stdout);
1d44f758 1173 fflush(stdout);
92d66625
LP
1174
1175 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
1176 log_info("Connection terminated, exiting.");
1177 return 0;
1178 }
1179
1f849790
LP
1180 continue;
1181 }
1182
1183 if (r > 0)
1184 continue;
1185
1186 r = sd_bus_wait(bus, (uint64_t) -1);
23bbb0de
MS
1187 if (r < 0)
1188 return log_error_errno(r, "Failed to wait for bus: %m");
1f849790 1189 }
95c4fe82 1190}
1f849790 1191
1f70b087
LP
1192static int capture(sd_bus *bus, char *argv[]) {
1193 int r;
1194
1195 if (isatty(fileno(stdout)) > 0) {
1196 log_error("Refusing to write message data to console, please redirect output to a file.");
1197 return -EINVAL;
1198 }
1199
1200 bus_pcap_header(arg_snaplen, stdout);
1201
1202 r = monitor(bus, argv, message_pcap);
1203 if (r < 0)
1204 return r;
1205
1206 if (ferror(stdout)) {
1207 log_error("Couldn't write capture file.");
1208 return -EIO;
1209 }
1210
1211 return r;
1212}
1213
95c4fe82 1214static int status(sd_bus *bus, char *argv[]) {
4afd3348 1215 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
95c4fe82
LP
1216 pid_t pid;
1217 int r;
1218
1219 assert(bus);
1220
2e9efd22
LP
1221 if (strv_length(argv) > 2) {
1222 log_error("Expects no or one argument.");
95c4fe82
LP
1223 return -EINVAL;
1224 }
1225
2e9efd22
LP
1226 if (argv[1]) {
1227 r = parse_pid(argv[1], &pid);
1228 if (r < 0)
1229 r = sd_bus_get_name_creds(
1230 bus,
1231 argv[1],
1232 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1233 &creds);
1234 else
1235 r = sd_bus_creds_new_from_pid(
1236 &creds,
1237 pid,
1238 _SD_BUS_CREDS_ALL);
3acc1daf 1239 } else {
5b820358 1240 const char *scope, *address;
5c302692 1241 sd_id128_t bus_id;
3acc1daf 1242
5b820358
LP
1243 r = sd_bus_get_address(bus, &address);
1244 if (r >= 0)
1fc464f6 1245 printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal());
5b820358 1246
3acc1daf
LP
1247 r = sd_bus_get_scope(bus, &scope);
1248 if (r >= 0)
1fc464f6 1249 printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal());
3acc1daf 1250
5c302692
LP
1251 r = sd_bus_get_bus_id(bus, &bus_id);
1252 if (r >= 0)
1fc464f6 1253 printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
5c302692 1254
2e9efd22 1255 r = sd_bus_get_owner_creds(
40ed1a45 1256 bus,
40ed1a45
LP
1257 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1258 &creds);
3acc1daf 1259 }
95c4fe82 1260
23bbb0de
MS
1261 if (r < 0)
1262 return log_error_errno(r, "Failed to get credentials: %m");
95c4fe82 1263
d0b2babf 1264 bus_creds_dump(creds, NULL, false);
95c4fe82 1265 return 0;
1f849790
LP
1266}
1267
781fa938
LP
1268static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1269 char **p;
1270 int r;
1271
1272 assert(m);
1273 assert(signature);
1274 assert(x);
1275
1276 p = *x;
1277
1278 for (;;) {
1279 const char *v;
1280 char t;
1281
1282 t = *signature;
1283 v = *p;
1284
1285 if (t == 0)
1286 break;
1287 if (!v) {
1288 log_error("Too few parameters for signature.");
1289 return -EINVAL;
1290 }
1291
1292 signature++;
1293 p++;
1294
1295 switch (t) {
1296
1297 case SD_BUS_TYPE_BOOLEAN:
1298
1299 r = parse_boolean(v);
1300 if (r < 0) {
1301 log_error("Failed to parse as boolean: %s", v);
1302 return r;
1303 }
1304
1305 r = sd_bus_message_append_basic(m, t, &r);
1306 break;
1307
1308 case SD_BUS_TYPE_BYTE: {
1309 uint8_t z;
1310
1311 r = safe_atou8(v, &z);
1312 if (r < 0) {
1313 log_error("Failed to parse as byte (unsigned 8bit integer): %s", v);
1314 return r;
1315 }
1316
1317 r = sd_bus_message_append_basic(m, t, &z);
1318 break;
1319 }
1320
1321 case SD_BUS_TYPE_INT16: {
1322 int16_t z;
1323
1324 r = safe_atoi16(v, &z);
1325 if (r < 0) {
1326 log_error("Failed to parse as signed 16bit integer: %s", v);
1327 return r;
1328 }
1329
1330 r = sd_bus_message_append_basic(m, t, &z);
1331 break;
1332 }
1333
1334 case SD_BUS_TYPE_UINT16: {
1335 uint16_t z;
1336
1337 r = safe_atou16(v, &z);
1338 if (r < 0) {
1339 log_error("Failed to parse as unsigned 16bit integer: %s", v);
1340 return r;
1341 }
1342
1343 r = sd_bus_message_append_basic(m, t, &z);
1344 break;
1345 }
1346
1347 case SD_BUS_TYPE_INT32: {
1348 int32_t z;
1349
1350 r = safe_atoi32(v, &z);
1351 if (r < 0) {
1352 log_error("Failed to parse as signed 32bit integer: %s", v);
1353 return r;
1354 }
1355
1356 r = sd_bus_message_append_basic(m, t, &z);
1357 break;
1358 }
1359
1360 case SD_BUS_TYPE_UINT32: {
1361 uint32_t z;
1362
1363 r = safe_atou32(v, &z);
1364 if (r < 0) {
1365 log_error("Failed to parse as unsigned 32bit integer: %s", v);
1366 return r;
1367 }
1368
1369 r = sd_bus_message_append_basic(m, t, &z);
1370 break;
1371 }
1372
1373 case SD_BUS_TYPE_INT64: {
1374 int64_t z;
1375
1376 r = safe_atoi64(v, &z);
1377 if (r < 0) {
1378 log_error("Failed to parse as signed 64bit integer: %s", v);
1379 return r;
1380 }
1381
1382 r = sd_bus_message_append_basic(m, t, &z);
1383 break;
1384 }
1385
1386 case SD_BUS_TYPE_UINT64: {
1387 uint64_t z;
1388
1389 r = safe_atou64(v, &z);
1390 if (r < 0) {
1391 log_error("Failed to parse as unsigned 64bit integer: %s", v);
1392 return r;
1393 }
1394
1395 r = sd_bus_message_append_basic(m, t, &z);
1396 break;
1397 }
1398
1399
1400 case SD_BUS_TYPE_DOUBLE: {
1401 double z;
1402
1403 r = safe_atod(v, &z);
1404 if (r < 0) {
1405 log_error("Failed to parse as double precision floating point: %s", v);
1406 return r;
1407 }
1408
1409 r = sd_bus_message_append_basic(m, t, &z);
1410 break;
1411 }
1412
1413 case SD_BUS_TYPE_STRING:
1414 case SD_BUS_TYPE_OBJECT_PATH:
1415 case SD_BUS_TYPE_SIGNATURE:
1416
1417 r = sd_bus_message_append_basic(m, t, v);
1418 break;
1419
1420 case SD_BUS_TYPE_ARRAY: {
1421 uint32_t n;
1422 size_t k;
1423
1424 r = safe_atou32(v, &n);
1425 if (r < 0) {
1426 log_error("Failed to parse number of array entries: %s", v);
1427 return r;
1428 }
1429
1430 r = signature_element_length(signature, &k);
1431 if (r < 0) {
1432 log_error("Invalid array signature.");
1433 return r;
1434 }
1435
1436 {
1437 unsigned i;
1438 char s[k + 1];
1439 memcpy(s, signature, k);
1440 s[k] = 0;
1441
1442 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1443 if (r < 0)
1444 return bus_log_create_error(r);
1445
1446 for (i = 0; i < n; i++) {
1447 r = message_append_cmdline(m, s, &p);
1448 if (r < 0)
1449 return r;
1450 }
1451 }
1452
1453 signature += k;
1454
1455 r = sd_bus_message_close_container(m);
1456 break;
1457 }
1458
1459 case SD_BUS_TYPE_VARIANT:
1460 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1461 if (r < 0)
1462 return bus_log_create_error(r);
1463
1464 r = message_append_cmdline(m, v, &p);
1465 if (r < 0)
1466 return r;
1467
1468 r = sd_bus_message_close_container(m);
1469 break;
1470
1471 case SD_BUS_TYPE_STRUCT_BEGIN:
1472 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1473 size_t k;
1474
1475 signature--;
1476 p--;
1477
1478 r = signature_element_length(signature, &k);
1479 if (r < 0) {
1480 log_error("Invalid struct/dict entry signature.");
1481 return r;
1482 }
1483
1484 {
1485 char s[k-1];
1486 memcpy(s, signature + 1, k - 2);
1487 s[k - 2] = 0;
1488
1489 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1490 if (r < 0)
1491 return bus_log_create_error(r);
1492
1493 r = message_append_cmdline(m, s, &p);
1494 if (r < 0)
1495 return r;
1496 }
1497
1498 signature += k;
1499
1500 r = sd_bus_message_close_container(m);
1501 break;
1502 }
1503
1504 case SD_BUS_TYPE_UNIX_FD:
1505 log_error("UNIX file descriptor not supported as type.");
1506 return -EINVAL;
1507
1508 default:
1509 log_error("Unknown signature type %c.", t);
1510 return -EINVAL;
1511 }
1512
1513 if (r < 0)
1514 return bus_log_create_error(r);
1515 }
1516
1517 *x = p;
1518 return 0;
1519}
1520
1521static int call(sd_bus *bus, char *argv[]) {
4afd3348
LP
1522 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1523 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
781fa938
LP
1524 int r;
1525
1526 assert(bus);
1527
1528 if (strv_length(argv) < 5) {
1529 log_error("Expects at least four arguments.");
1530 return -EINVAL;
1531 }
1532
1533 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
1534 if (r < 0)
1535 return bus_log_create_error(r);
781fa938 1536
38051578
LP
1537 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
1538 if (r < 0)
1539 return bus_log_create_error(r);
1540
1541 r = sd_bus_message_set_auto_start(m, arg_auto_start);
1542 if (r < 0)
1543 return bus_log_create_error(r);
1544
1545 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
1546 if (r < 0)
1547 return bus_log_create_error(r);
1548
781fa938
LP
1549 if (!isempty(argv[5])) {
1550 char **p;
1551
1552 p = argv+6;
1553
1554 r = message_append_cmdline(m, argv[5], &p);
1555 if (r < 0)
1556 return r;
1557
1558 if (*p) {
1559 log_error("Too many parameters for signature.");
1560 return -EINVAL;
1561 }
1562 }
1563
38051578
LP
1564 if (!arg_expect_reply) {
1565 r = sd_bus_send(bus, m, NULL);
1566 if (r < 0) {
1567 log_error("Failed to send message.");
1568 return r;
1569 }
1570
1571 return 0;
1572 }
1573
a44b1081 1574 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
781fa938
LP
1575 if (r < 0) {
1576 log_error("%s", bus_error_message(&error, r));
1577 return r;
1578 }
1579
1580 r = sd_bus_message_is_empty(reply);
1581 if (r < 0)
1582 return bus_log_parse_error(r);
1fc55609 1583
781fa938 1584 if (r == 0 && !arg_quiet) {
1fc55609
LP
1585
1586 if (arg_verbose) {
ea4b98e6 1587 pager_open(arg_no_pager, false);
1fc55609
LP
1588
1589 r = bus_message_dump(reply, stdout, 0);
1590 if (r < 0)
1591 return r;
1592 } else {
1593
1594 fputs(sd_bus_message_get_signature(reply, true), stdout);
1595 fputc(' ', stdout);
1596
1597 r = format_cmdline(reply, stdout, false);
1598 if (r < 0)
1599 return bus_log_parse_error(r);
1600
1601 fputc('\n', stdout);
1602 }
d55192ad
LP
1603 }
1604
1605 return 0;
1606}
1607
1608static int get_property(sd_bus *bus, char *argv[]) {
4afd3348 1609 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d55192ad 1610 unsigned n;
1fc55609 1611 char **i;
d55192ad
LP
1612 int r;
1613
1614 assert(bus);
1615
1616 n = strv_length(argv);
1fc55609
LP
1617 if (n < 5) {
1618 log_error("Expects at least four arguments.");
d55192ad
LP
1619 return -EINVAL;
1620 }
1621
1fc55609 1622 STRV_FOREACH(i, argv + 4) {
4afd3348 1623 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
1624 const char *contents = NULL;
1625 char type;
d55192ad 1626
1fc55609 1627 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
d55192ad
LP
1628 if (r < 0) {
1629 log_error("%s", bus_error_message(&error, r));
1630 return r;
1631 }
1632
1fc55609 1633 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
1634 if (r < 0)
1635 return bus_log_parse_error(r);
1636
1fc55609
LP
1637 r = sd_bus_message_enter_container(reply, 'v', contents);
1638 if (r < 0)
1639 return bus_log_parse_error(r);
d55192ad 1640
1fc55609 1641 if (arg_verbose) {
ea4b98e6 1642 pager_open(arg_no_pager, false);
d55192ad 1643
1fc55609 1644 r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 1645 if (r < 0)
1fc55609
LP
1646 return r;
1647 } else {
1648 fputs(contents, stdout);
1649 fputc(' ', stdout);
d55192ad 1650
1fc55609 1651 r = format_cmdline(reply, stdout, false);
d55192ad
LP
1652 if (r < 0)
1653 return bus_log_parse_error(r);
1654
1fc55609 1655 fputc('\n', stdout);
d55192ad
LP
1656 }
1657
1658 r = sd_bus_message_exit_container(reply);
1659 if (r < 0)
1660 return bus_log_parse_error(r);
1fc55609 1661 }
d55192ad 1662
1fc55609
LP
1663 return 0;
1664}
d55192ad 1665
1fc55609 1666static int set_property(sd_bus *bus, char *argv[]) {
4afd3348
LP
1667 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1668 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609
LP
1669 unsigned n;
1670 char **p;
1671 int r;
d55192ad 1672
1fc55609 1673 assert(bus);
d55192ad 1674
1fc55609
LP
1675 n = strv_length(argv);
1676 if (n < 6) {
1677 log_error("Expects at least five arguments.");
1678 return -EINVAL;
1679 }
d55192ad 1680
1fc55609
LP
1681 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
1682 if (r < 0)
1683 return bus_log_create_error(r);
d55192ad 1684
1fc55609
LP
1685 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
1686 if (r < 0)
1687 return bus_log_create_error(r);
d55192ad 1688
1fc55609
LP
1689 r = sd_bus_message_open_container(m, 'v', argv[5]);
1690 if (r < 0)
1691 return bus_log_create_error(r);
1692
1693 p = argv+6;
1694 r = message_append_cmdline(m, argv[5], &p);
1695 if (r < 0)
1696 return r;
1697
1698 r = sd_bus_message_close_container(m);
1699 if (r < 0)
1700 return bus_log_create_error(r);
1701
1702 if (*p) {
1703 log_error("Too many parameters for signature.");
1704 return -EINVAL;
1705 }
1706
a44b1081 1707 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
1fc55609
LP
1708 if (r < 0) {
1709 log_error("%s", bus_error_message(&error, r));
1710 return r;
781fa938
LP
1711 }
1712
1713 return 0;
1714}
1715
1f849790 1716static int help(void) {
1f849790
LP
1717 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1718 "Introspect the bus.\n\n"
d75edbd6
LP
1719 " -h --help Show this help\n"
1720 " --version Show package version\n"
a86a47ce 1721 " --no-pager Do not pipe output into a pager\n"
17d47d8d 1722 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
1723 " --system Connect to system bus\n"
1724 " --user Connect to user bus\n"
1725 " -H --host=[USER@]HOST Operate on remote host\n"
1726 " -M --machine=CONTAINER Operate on local container\n"
1727 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
1728 " --show-machine Show machine ID column in list\n"
1729 " --unique Only show unique names\n"
1730 " --acquired Only show acquired names\n"
1731 " --activatable Only show activatable names\n"
d9130355 1732 " --match=MATCH Only show matching messages\n"
d28ebe27 1733 " --size=SIZE Maximum length of captured packet\n"
781fa938 1734 " --list Don't show tree, but simple object path list\n"
e53fa805 1735 " -q --quiet Don't show method call reply\n"
38051578
LP
1736 " --verbose Show result values in long format\n"
1737 " --expect-reply=BOOL Expect a method call reply\n"
1738 " --auto-start=BOOL Auto-start destination service\n"
1739 " --allow-interactive-authorization=BOOL\n"
a44b1081 1740 " Allow interactive authorization for operation\n"
40ed1a45
LP
1741 " --timeout=SECS Maximum time to wait for method call completion\n"
1742 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n"
1f849790 1743 "Commands:\n"
d75edbd6 1744 " list List bus names\n"
2e9efd22 1745 " status [SERVICE] Show bus service, process or bus owner credentials\n"
d94fe1f1 1746 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1747 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 1748 " tree [SERVICE...] Show object tree of service\n"
4f44c03e 1749 " introspect SERVICE OBJECT [INTERFACE]\n"
0171da06 1750 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 1751 " Call a method\n"
1fc55609 1752 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 1753 " Get property value\n"
1fc55609
LP
1754 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
1755 " Set property value\n"
601185b4
ZJS
1756 " help Show this help\n"
1757 , program_invocation_short_name);
1f849790
LP
1758
1759 return 0;
1760}
1761
1762static int parse_argv(int argc, char *argv[]) {
1763
1764 enum {
1765 ARG_VERSION = 0x100,
1766 ARG_NO_PAGER,
17d47d8d 1767 ARG_NO_LEGEND,
1f849790
LP
1768 ARG_SYSTEM,
1769 ARG_USER,
1770 ARG_ADDRESS,
1771 ARG_MATCH,
56e61788
LP
1772 ARG_SHOW_MACHINE,
1773 ARG_UNIQUE,
1774 ARG_ACQUIRED,
1f70b087
LP
1775 ARG_ACTIVATABLE,
1776 ARG_SIZE,
d9130355 1777 ARG_LIST,
1fc55609 1778 ARG_VERBOSE,
38051578
LP
1779 ARG_EXPECT_REPLY,
1780 ARG_AUTO_START,
1781 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 1782 ARG_TIMEOUT,
40ed1a45 1783 ARG_AUGMENT_CREDS,
1f849790
LP
1784 };
1785
1786 static const struct option options[] = {
56e61788
LP
1787 { "help", no_argument, NULL, 'h' },
1788 { "version", no_argument, NULL, ARG_VERSION },
1789 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1790 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1791 { "system", no_argument, NULL, ARG_SYSTEM },
1792 { "user", no_argument, NULL, ARG_USER },
1793 { "address", required_argument, NULL, ARG_ADDRESS },
1794 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1795 { "unique", no_argument, NULL, ARG_UNIQUE },
1796 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1797 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1798 { "match", required_argument, NULL, ARG_MATCH },
1799 { "host", required_argument, NULL, 'H' },
1800 { "machine", required_argument, NULL, 'M' },
1f70b087 1801 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1802 { "list", no_argument, NULL, ARG_LIST },
781fa938 1803 { "quiet", no_argument, NULL, 'q' },
1fc55609 1804 { "verbose", no_argument, NULL, ARG_VERBOSE },
38051578
LP
1805 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
1806 { "auto-start", required_argument, NULL, ARG_AUTO_START },
1807 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
a44b1081 1808 { "timeout", required_argument, NULL, ARG_TIMEOUT },
40ed1a45 1809 { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS},
eb9da376 1810 {},
1f849790
LP
1811 };
1812
1f70b087 1813 int c, r;
1f849790
LP
1814
1815 assert(argc >= 0);
1816 assert(argv);
1817
781fa938 1818 while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
1f849790
LP
1819
1820 switch (c) {
1821
1822 case 'h':
1823 return help();
1824
1825 case ARG_VERSION:
3f6fd1ba 1826 return version();
1f849790
LP
1827
1828 case ARG_NO_PAGER:
1829 arg_no_pager = true;
1830 break;
1831
17d47d8d
TA
1832 case ARG_NO_LEGEND:
1833 arg_legend = false;
1834 break;
1835
1f849790
LP
1836 case ARG_USER:
1837 arg_user = true;
1838 break;
1839
1840 case ARG_SYSTEM:
1841 arg_user = false;
1842 break;
1843
1844 case ARG_ADDRESS:
1845 arg_address = optarg;
1846 break;
1847
56e61788
LP
1848 case ARG_SHOW_MACHINE:
1849 arg_show_machine = true;
1850 break;
1851
1852 case ARG_UNIQUE:
1853 arg_unique = true;
1f849790
LP
1854 break;
1855
56e61788
LP
1856 case ARG_ACQUIRED:
1857 arg_acquired = true;
1858 break;
1859
1860 case ARG_ACTIVATABLE:
1861 arg_activatable = true;
a4297f08
LP
1862 break;
1863
1f849790
LP
1864 case ARG_MATCH:
1865 if (strv_extend(&arg_matches, optarg) < 0)
1866 return log_oom();
1867 break;
1868
1f70b087 1869 case ARG_SIZE: {
59f448cf 1870 uint64_t sz;
1f70b087 1871
59f448cf 1872 r = parse_size(optarg, 1024, &sz);
1f70b087
LP
1873 if (r < 0) {
1874 log_error("Failed to parse size: %s", optarg);
1875 return r;
1876 }
1877
59f448cf 1878 if ((uint64_t) (size_t) sz != sz) {
1f70b087
LP
1879 log_error("Size out of range.");
1880 return -E2BIG;
1881 }
1882
59f448cf 1883 arg_snaplen = (size_t) sz;
1f70b087
LP
1884 break;
1885 }
1886
d9130355
LP
1887 case ARG_LIST:
1888 arg_list = true;
1889 break;
1890
d75edbd6
LP
1891 case 'H':
1892 arg_transport = BUS_TRANSPORT_REMOTE;
1893 arg_host = optarg;
1894 break;
1895
1896 case 'M':
de33fc62 1897 arg_transport = BUS_TRANSPORT_MACHINE;
d75edbd6
LP
1898 arg_host = optarg;
1899 break;
1900
781fa938
LP
1901 case 'q':
1902 arg_quiet = true;
1903 break;
1904
1fc55609
LP
1905 case ARG_VERBOSE:
1906 arg_verbose = true;
1907 break;
1908
38051578
LP
1909 case ARG_EXPECT_REPLY:
1910 r = parse_boolean(optarg);
1911 if (r < 0) {
1912 log_error("Failed to parse --expect-reply= parameter.");
1913 return r;
1914 }
1915
1916 arg_expect_reply = !!r;
1917 break;
1918
1919
1920 case ARG_AUTO_START:
1921 r = parse_boolean(optarg);
1922 if (r < 0) {
1923 log_error("Failed to parse --auto-start= parameter.");
1924 return r;
1925 }
1926
1927 arg_auto_start = !!r;
1928 break;
1929
1930
1931 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
1932 r = parse_boolean(optarg);
1933 if (r < 0) {
1934 log_error("Failed to parse --allow-interactive-authorization= parameter.");
1935 return r;
1936 }
1937
1938 arg_allow_interactive_authorization = !!r;
1939 break;
1940
a44b1081
LP
1941 case ARG_TIMEOUT:
1942 r = parse_sec(optarg, &arg_timeout);
1943 if (r < 0) {
1944 log_error("Failed to parse --timeout= parameter.");
1945 return r;
1946 }
1947
1948 break;
1949
40ed1a45
LP
1950 case ARG_AUGMENT_CREDS:
1951 r = parse_boolean(optarg);
1952 if (r < 0) {
1953 log_error("Failed to parse --augment-creds= parameter.");
1954 return r;
1955 }
1956
1957 arg_augment_creds = !!r;
1958 break;
1959
1f849790
LP
1960 case '?':
1961 return -EINVAL;
1962
1963 default:
eb9da376 1964 assert_not_reached("Unhandled option");
1f849790 1965 }
1f849790
LP
1966
1967 return 1;
1968}
1969
1970static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
1971 assert(bus);
1972
1973 if (optind >= argc ||
1974 streq(argv[optind], "list"))
1975 return list_bus_names(bus, argv + optind);
1976
1977 if (streq(argv[optind], "monitor"))
1f70b087
LP
1978 return monitor(bus, argv + optind, message_dump);
1979
1980 if (streq(argv[optind], "capture"))
1981 return capture(bus, argv + optind);
1f849790 1982
95c4fe82
LP
1983 if (streq(argv[optind], "status"))
1984 return status(bus, argv + optind);
d9130355
LP
1985
1986 if (streq(argv[optind], "tree"))
1987 return tree(bus, argv + optind);
781fa938 1988
0171da06
LP
1989 if (streq(argv[optind], "introspect"))
1990 return introspect(bus, argv + optind);
1991
781fa938
LP
1992 if (streq(argv[optind], "call"))
1993 return call(bus, argv + optind);
d55192ad
LP
1994
1995 if (streq(argv[optind], "get-property"))
1996 return get_property(bus, argv + optind);
95c4fe82 1997
1fc55609
LP
1998 if (streq(argv[optind], "set-property"))
1999 return set_property(bus, argv + optind);
2000
1f849790
LP
2001 if (streq(argv[optind], "help"))
2002 return help();
2003
2004 log_error("Unknown command '%s'", argv[optind]);
2005 return -EINVAL;
2006}
2007
2008int main(int argc, char *argv[]) {
0b3c84eb 2009 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1f849790
LP
2010 int r;
2011
2012 log_parse_environment();
2013 log_open();
2014
2015 r = parse_argv(argc, argv);
2016 if (r <= 0)
2017 goto finish;
2018
09365592
LP
2019 r = sd_bus_new(&bus);
2020 if (r < 0) {
da927ba9 2021 log_error_errno(r, "Failed to allocate bus: %m");
09365592
LP
2022 goto finish;
2023 }
2024
1cf03a4f 2025 if (STRPTR_IN_SET(argv[optind], "monitor", "capture")) {
09365592
LP
2026
2027 r = sd_bus_set_monitor(bus, true);
1f849790 2028 if (r < 0) {
da927ba9 2029 log_error_errno(r, "Failed to set monitor mode: %m");
1f849790
LP
2030 goto finish;
2031 }
d0ce7734 2032
b5dae4c7 2033 r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL);
d0ce7734 2034 if (r < 0) {
da927ba9 2035 log_error_errno(r, "Failed to enable credentials: %m");
d0ce7734
LP
2036 goto finish;
2037 }
2038
2039 r = sd_bus_negotiate_timestamp(bus, true);
2040 if (r < 0) {
da927ba9 2041 log_error_errno(r, "Failed to enable timestamps: %m");
d0ce7734
LP
2042 goto finish;
2043 }
2044
2045 r = sd_bus_negotiate_fds(bus, true);
2046 if (r < 0) {
da927ba9 2047 log_error_errno(r, "Failed to enable fds: %m");
d0ce7734
LP
2048 goto finish;
2049 }
09365592 2050 }
1f849790 2051
8d87d4a9
DH
2052 r = sd_bus_set_bus_client(bus, true);
2053 if (r < 0) {
2054 log_error_errno(r, "Failed to set bus client: %m");
2055 goto finish;
2056 }
2057
09365592 2058 if (arg_address)
1f849790 2059 r = sd_bus_set_address(bus, arg_address);
09365592
LP
2060 else {
2061 switch (arg_transport) {
1f849790 2062
09365592 2063 case BUS_TRANSPORT_LOCAL:
3acc1daf
LP
2064 if (arg_user) {
2065 bus->is_user = true;
09365592 2066 r = bus_set_address_user(bus);
3acc1daf
LP
2067 } else {
2068 bus->is_system = true;
09365592 2069 r = bus_set_address_system(bus);
3acc1daf 2070 }
09365592
LP
2071 break;
2072
2073 case BUS_TRANSPORT_REMOTE:
2074 r = bus_set_address_system_remote(bus, arg_host);
2075 break;
2076
de33fc62
LP
2077 case BUS_TRANSPORT_MACHINE:
2078 r = bus_set_address_system_machine(bus, arg_host);
09365592
LP
2079 break;
2080
2081 default:
2082 assert_not_reached("Hmm, unknown transport type.");
1f849790 2083 }
09365592
LP
2084 }
2085 if (r < 0) {
da927ba9 2086 log_error_errno(r, "Failed to set address: %m");
09365592
LP
2087 goto finish;
2088 }
1f849790 2089
09365592 2090 r = sd_bus_start(bus);
1f849790 2091 if (r < 0) {
da927ba9 2092 log_error_errno(r, "Failed to connect to bus: %m");
1f849790
LP
2093 goto finish;
2094 }
2095
2096 r = busctl_main(bus, argc, argv);
2097
2098finish:
2099 pager_close();
d75edbd6 2100
1f849790 2101 strv_free(arg_matches);
de1c301e 2102
de1c301e
LP
2103 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2104}