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