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