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