]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
sd-bus: properly handle uninitialized audit creds from kdbus
[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
LP
92 if (r < 0) {
93 log_error("Failed to list names: %s", strerror(-r));
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
LP
107 if (r < 0) {
108 log_error("Failed to add to hashmap: %s", strerror(-r));
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
LP
117 if (r < 0 && r != -EEXIST) {
118 log_error("Failed to add to hashmap: %s", strerror(-r));
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) {
442 log_error("Failed to get name list: %s", strerror(-r));
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
LP
1098 if (r < 0) {
1099 log_error("Failed to add match: %s", strerror(-r));
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
LP
1108 if (r < 0) {
1109 log_error("Failed to add match: %s", strerror(-r));
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
LP
1118 if (r < 0) {
1119 log_error("Failed to add match: %s", strerror(-r));
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) {
1129 log_error("Failed to process bus: %s", strerror(-r));
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) {
1143 log_error("Failed to wait for bus: %s", strerror(-r));
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
1178 if (strv_length(argv) != 2) {
1179 log_error("Expects one argument.");
1180 return -EINVAL;
1181 }
1182
1183 r = parse_pid(argv[1], &pid);
1184 if (r < 0)
40ed1a45
LP
1185 r = sd_bus_get_name_creds(
1186 bus,
1187 argv[1],
1188 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1189 &creds);
95c4fe82 1190 else
151b9b96 1191 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
95c4fe82
LP
1192
1193 if (r < 0) {
1194 log_error("Failed to get credentials: %s", strerror(-r));
1195 return r;
1196 }
1197
d0b2babf 1198 bus_creds_dump(creds, NULL, false);
95c4fe82 1199 return 0;
1f849790
LP
1200}
1201
781fa938
LP
1202static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1203 char **p;
1204 int r;
1205
1206 assert(m);
1207 assert(signature);
1208 assert(x);
1209
1210 p = *x;
1211
1212 for (;;) {
1213 const char *v;
1214 char t;
1215
1216 t = *signature;
1217 v = *p;
1218
1219 if (t == 0)
1220 break;
1221 if (!v) {
1222 log_error("Too few parameters for signature.");
1223 return -EINVAL;
1224 }
1225
1226 signature++;
1227 p++;
1228
1229 switch (t) {
1230
1231 case SD_BUS_TYPE_BOOLEAN:
1232
1233 r = parse_boolean(v);
1234 if (r < 0) {
1235 log_error("Failed to parse as boolean: %s", v);
1236 return r;
1237 }
1238
1239 r = sd_bus_message_append_basic(m, t, &r);
1240 break;
1241
1242 case SD_BUS_TYPE_BYTE: {
1243 uint8_t z;
1244
1245 r = safe_atou8(v, &z);
1246 if (r < 0) {
1247 log_error("Failed to parse as byte (unsigned 8bit integer): %s", v);
1248 return r;
1249 }
1250
1251 r = sd_bus_message_append_basic(m, t, &z);
1252 break;
1253 }
1254
1255 case SD_BUS_TYPE_INT16: {
1256 int16_t z;
1257
1258 r = safe_atoi16(v, &z);
1259 if (r < 0) {
1260 log_error("Failed to parse as signed 16bit integer: %s", v);
1261 return r;
1262 }
1263
1264 r = sd_bus_message_append_basic(m, t, &z);
1265 break;
1266 }
1267
1268 case SD_BUS_TYPE_UINT16: {
1269 uint16_t z;
1270
1271 r = safe_atou16(v, &z);
1272 if (r < 0) {
1273 log_error("Failed to parse as unsigned 16bit integer: %s", v);
1274 return r;
1275 }
1276
1277 r = sd_bus_message_append_basic(m, t, &z);
1278 break;
1279 }
1280
1281 case SD_BUS_TYPE_INT32: {
1282 int32_t z;
1283
1284 r = safe_atoi32(v, &z);
1285 if (r < 0) {
1286 log_error("Failed to parse as signed 32bit integer: %s", v);
1287 return r;
1288 }
1289
1290 r = sd_bus_message_append_basic(m, t, &z);
1291 break;
1292 }
1293
1294 case SD_BUS_TYPE_UINT32: {
1295 uint32_t z;
1296
1297 r = safe_atou32(v, &z);
1298 if (r < 0) {
1299 log_error("Failed to parse as unsigned 32bit integer: %s", v);
1300 return r;
1301 }
1302
1303 r = sd_bus_message_append_basic(m, t, &z);
1304 break;
1305 }
1306
1307 case SD_BUS_TYPE_INT64: {
1308 int64_t z;
1309
1310 r = safe_atoi64(v, &z);
1311 if (r < 0) {
1312 log_error("Failed to parse as signed 64bit integer: %s", v);
1313 return r;
1314 }
1315
1316 r = sd_bus_message_append_basic(m, t, &z);
1317 break;
1318 }
1319
1320 case SD_BUS_TYPE_UINT64: {
1321 uint64_t z;
1322
1323 r = safe_atou64(v, &z);
1324 if (r < 0) {
1325 log_error("Failed to parse as unsigned 64bit integer: %s", v);
1326 return r;
1327 }
1328
1329 r = sd_bus_message_append_basic(m, t, &z);
1330 break;
1331 }
1332
1333
1334 case SD_BUS_TYPE_DOUBLE: {
1335 double z;
1336
1337 r = safe_atod(v, &z);
1338 if (r < 0) {
1339 log_error("Failed to parse as double precision floating point: %s", v);
1340 return r;
1341 }
1342
1343 r = sd_bus_message_append_basic(m, t, &z);
1344 break;
1345 }
1346
1347 case SD_BUS_TYPE_STRING:
1348 case SD_BUS_TYPE_OBJECT_PATH:
1349 case SD_BUS_TYPE_SIGNATURE:
1350
1351 r = sd_bus_message_append_basic(m, t, v);
1352 break;
1353
1354 case SD_BUS_TYPE_ARRAY: {
1355 uint32_t n;
1356 size_t k;
1357
1358 r = safe_atou32(v, &n);
1359 if (r < 0) {
1360 log_error("Failed to parse number of array entries: %s", v);
1361 return r;
1362 }
1363
1364 r = signature_element_length(signature, &k);
1365 if (r < 0) {
1366 log_error("Invalid array signature.");
1367 return r;
1368 }
1369
1370 {
1371 unsigned i;
1372 char s[k + 1];
1373 memcpy(s, signature, k);
1374 s[k] = 0;
1375
1376 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1377 if (r < 0)
1378 return bus_log_create_error(r);
1379
1380 for (i = 0; i < n; i++) {
1381 r = message_append_cmdline(m, s, &p);
1382 if (r < 0)
1383 return r;
1384 }
1385 }
1386
1387 signature += k;
1388
1389 r = sd_bus_message_close_container(m);
1390 break;
1391 }
1392
1393 case SD_BUS_TYPE_VARIANT:
1394 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1395 if (r < 0)
1396 return bus_log_create_error(r);
1397
1398 r = message_append_cmdline(m, v, &p);
1399 if (r < 0)
1400 return r;
1401
1402 r = sd_bus_message_close_container(m);
1403 break;
1404
1405 case SD_BUS_TYPE_STRUCT_BEGIN:
1406 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1407 size_t k;
1408
1409 signature--;
1410 p--;
1411
1412 r = signature_element_length(signature, &k);
1413 if (r < 0) {
1414 log_error("Invalid struct/dict entry signature.");
1415 return r;
1416 }
1417
1418 {
1419 char s[k-1];
1420 memcpy(s, signature + 1, k - 2);
1421 s[k - 2] = 0;
1422
1423 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1424 if (r < 0)
1425 return bus_log_create_error(r);
1426
1427 r = message_append_cmdline(m, s, &p);
1428 if (r < 0)
1429 return r;
1430 }
1431
1432 signature += k;
1433
1434 r = sd_bus_message_close_container(m);
1435 break;
1436 }
1437
1438 case SD_BUS_TYPE_UNIX_FD:
1439 log_error("UNIX file descriptor not supported as type.");
1440 return -EINVAL;
1441
1442 default:
1443 log_error("Unknown signature type %c.", t);
1444 return -EINVAL;
1445 }
1446
1447 if (r < 0)
1448 return bus_log_create_error(r);
1449 }
1450
1451 *x = p;
1452 return 0;
1453}
1454
1455static int call(sd_bus *bus, char *argv[]) {
1456 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1457 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1458 int r;
1459
1460 assert(bus);
1461
1462 if (strv_length(argv) < 5) {
1463 log_error("Expects at least four arguments.");
1464 return -EINVAL;
1465 }
1466
1467 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
1468 if (r < 0)
1469 return bus_log_create_error(r);
781fa938 1470
38051578
LP
1471 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
1472 if (r < 0)
1473 return bus_log_create_error(r);
1474
1475 r = sd_bus_message_set_auto_start(m, arg_auto_start);
1476 if (r < 0)
1477 return bus_log_create_error(r);
1478
1479 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
1480 if (r < 0)
1481 return bus_log_create_error(r);
1482
781fa938
LP
1483 if (!isempty(argv[5])) {
1484 char **p;
1485
1486 p = argv+6;
1487
1488 r = message_append_cmdline(m, argv[5], &p);
1489 if (r < 0)
1490 return r;
1491
1492 if (*p) {
1493 log_error("Too many parameters for signature.");
1494 return -EINVAL;
1495 }
1496 }
1497
38051578
LP
1498 if (!arg_expect_reply) {
1499 r = sd_bus_send(bus, m, NULL);
1500 if (r < 0) {
1501 log_error("Failed to send message.");
1502 return r;
1503 }
1504
1505 return 0;
1506 }
1507
a44b1081 1508 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
781fa938
LP
1509 if (r < 0) {
1510 log_error("%s", bus_error_message(&error, r));
1511 return r;
1512 }
1513
1514 r = sd_bus_message_is_empty(reply);
1515 if (r < 0)
1516 return bus_log_parse_error(r);
1fc55609 1517
781fa938 1518 if (r == 0 && !arg_quiet) {
1fc55609
LP
1519
1520 if (arg_verbose) {
1521 pager_open_if_enabled();
1522
1523 r = bus_message_dump(reply, stdout, 0);
1524 if (r < 0)
1525 return r;
1526 } else {
1527
1528 fputs(sd_bus_message_get_signature(reply, true), stdout);
1529 fputc(' ', stdout);
1530
1531 r = format_cmdline(reply, stdout, false);
1532 if (r < 0)
1533 return bus_log_parse_error(r);
1534
1535 fputc('\n', stdout);
1536 }
d55192ad
LP
1537 }
1538
1539 return 0;
1540}
1541
1542static int get_property(sd_bus *bus, char *argv[]) {
1543 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1544 unsigned n;
1fc55609 1545 char **i;
d55192ad
LP
1546 int r;
1547
1548 assert(bus);
1549
1550 n = strv_length(argv);
1fc55609
LP
1551 if (n < 5) {
1552 log_error("Expects at least four arguments.");
d55192ad
LP
1553 return -EINVAL;
1554 }
1555
1fc55609 1556 STRV_FOREACH(i, argv + 4) {
d55192ad 1557 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1fc55609
LP
1558 const char *contents = NULL;
1559 char type;
d55192ad 1560
1fc55609 1561 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
d55192ad
LP
1562 if (r < 0) {
1563 log_error("%s", bus_error_message(&error, r));
1564 return r;
1565 }
1566
1fc55609 1567 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
1568 if (r < 0)
1569 return bus_log_parse_error(r);
1570
1fc55609
LP
1571 r = sd_bus_message_enter_container(reply, 'v', contents);
1572 if (r < 0)
1573 return bus_log_parse_error(r);
d55192ad 1574
1fc55609 1575 if (arg_verbose) {
d55192ad 1576 pager_open_if_enabled();
d55192ad 1577
1fc55609 1578 r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 1579 if (r < 0)
1fc55609
LP
1580 return r;
1581 } else {
1582 fputs(contents, stdout);
1583 fputc(' ', stdout);
d55192ad 1584
1fc55609 1585 r = format_cmdline(reply, stdout, false);
d55192ad
LP
1586 if (r < 0)
1587 return bus_log_parse_error(r);
1588
1fc55609 1589 fputc('\n', stdout);
d55192ad
LP
1590 }
1591
1592 r = sd_bus_message_exit_container(reply);
1593 if (r < 0)
1594 return bus_log_parse_error(r);
1fc55609 1595 }
d55192ad 1596
1fc55609
LP
1597 return 0;
1598}
d55192ad 1599
1fc55609
LP
1600static int set_property(sd_bus *bus, char *argv[]) {
1601 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1602 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1603 unsigned n;
1604 char **p;
1605 int r;
d55192ad 1606
1fc55609 1607 assert(bus);
d55192ad 1608
1fc55609
LP
1609 n = strv_length(argv);
1610 if (n < 6) {
1611 log_error("Expects at least five arguments.");
1612 return -EINVAL;
1613 }
d55192ad 1614
1fc55609
LP
1615 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
1616 if (r < 0)
1617 return bus_log_create_error(r);
d55192ad 1618
1fc55609
LP
1619 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
1620 if (r < 0)
1621 return bus_log_create_error(r);
d55192ad 1622
1fc55609
LP
1623 r = sd_bus_message_open_container(m, 'v', argv[5]);
1624 if (r < 0)
1625 return bus_log_create_error(r);
1626
1627 p = argv+6;
1628 r = message_append_cmdline(m, argv[5], &p);
1629 if (r < 0)
1630 return r;
1631
1632 r = sd_bus_message_close_container(m);
1633 if (r < 0)
1634 return bus_log_create_error(r);
1635
1636 if (*p) {
1637 log_error("Too many parameters for signature.");
1638 return -EINVAL;
1639 }
1640
a44b1081 1641 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
1fc55609
LP
1642 if (r < 0) {
1643 log_error("%s", bus_error_message(&error, r));
1644 return r;
781fa938
LP
1645 }
1646
1647 return 0;
1648}
1649
1f849790 1650static int help(void) {
1f849790
LP
1651 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1652 "Introspect the bus.\n\n"
d75edbd6
LP
1653 " -h --help Show this help\n"
1654 " --version Show package version\n"
a86a47ce 1655 " --no-pager Do not pipe output into a pager\n"
17d47d8d 1656 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
1657 " --system Connect to system bus\n"
1658 " --user Connect to user bus\n"
1659 " -H --host=[USER@]HOST Operate on remote host\n"
1660 " -M --machine=CONTAINER Operate on local container\n"
1661 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
1662 " --show-machine Show machine ID column in list\n"
1663 " --unique Only show unique names\n"
1664 " --acquired Only show acquired names\n"
1665 " --activatable Only show activatable names\n"
d9130355 1666 " --match=MATCH Only show matching messages\n"
781fa938 1667 " --list Don't show tree, but simple object path list\n"
1fc55609 1668 " --quiet Don't show method call reply\n"
38051578
LP
1669 " --verbose Show result values in long format\n"
1670 " --expect-reply=BOOL Expect a method call reply\n"
1671 " --auto-start=BOOL Auto-start destination service\n"
1672 " --allow-interactive-authorization=BOOL\n"
a44b1081 1673 " Allow interactive authorization for operation\n"
40ed1a45
LP
1674 " --timeout=SECS Maximum time to wait for method call completion\n"
1675 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n"
1f849790 1676 "Commands:\n"
d75edbd6 1677 " list List bus names\n"
0171da06 1678 " status SERVICE Show service name status\n"
d94fe1f1 1679 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1680 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 1681 " tree [SERVICE...] Show object tree of service\n"
1fc55609 1682 " introspect SERVICE OBJECT\n"
0171da06 1683 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 1684 " Call a method\n"
1fc55609 1685 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 1686 " Get property value\n"
1fc55609
LP
1687 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
1688 " Set property value\n"
601185b4
ZJS
1689 " help Show this help\n"
1690 , program_invocation_short_name);
1f849790
LP
1691
1692 return 0;
1693}
1694
1695static int parse_argv(int argc, char *argv[]) {
1696
1697 enum {
1698 ARG_VERSION = 0x100,
1699 ARG_NO_PAGER,
17d47d8d 1700 ARG_NO_LEGEND,
1f849790
LP
1701 ARG_SYSTEM,
1702 ARG_USER,
1703 ARG_ADDRESS,
1704 ARG_MATCH,
56e61788
LP
1705 ARG_SHOW_MACHINE,
1706 ARG_UNIQUE,
1707 ARG_ACQUIRED,
1f70b087
LP
1708 ARG_ACTIVATABLE,
1709 ARG_SIZE,
d9130355 1710 ARG_LIST,
1fc55609 1711 ARG_VERBOSE,
38051578
LP
1712 ARG_EXPECT_REPLY,
1713 ARG_AUTO_START,
1714 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 1715 ARG_TIMEOUT,
40ed1a45 1716 ARG_AUGMENT_CREDS,
1f849790
LP
1717 };
1718
1719 static const struct option options[] = {
56e61788
LP
1720 { "help", no_argument, NULL, 'h' },
1721 { "version", no_argument, NULL, ARG_VERSION },
1722 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1723 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1724 { "system", no_argument, NULL, ARG_SYSTEM },
1725 { "user", no_argument, NULL, ARG_USER },
1726 { "address", required_argument, NULL, ARG_ADDRESS },
1727 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1728 { "unique", no_argument, NULL, ARG_UNIQUE },
1729 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1730 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1731 { "match", required_argument, NULL, ARG_MATCH },
1732 { "host", required_argument, NULL, 'H' },
1733 { "machine", required_argument, NULL, 'M' },
1f70b087 1734 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1735 { "list", no_argument, NULL, ARG_LIST },
781fa938 1736 { "quiet", no_argument, NULL, 'q' },
1fc55609 1737 { "verbose", no_argument, NULL, ARG_VERBOSE },
38051578
LP
1738 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
1739 { "auto-start", required_argument, NULL, ARG_AUTO_START },
1740 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
a44b1081 1741 { "timeout", required_argument, NULL, ARG_TIMEOUT },
40ed1a45 1742 { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS},
eb9da376 1743 {},
1f849790
LP
1744 };
1745
1f70b087 1746 int c, r;
1f849790
LP
1747
1748 assert(argc >= 0);
1749 assert(argv);
1750
781fa938 1751 while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
1f849790
LP
1752
1753 switch (c) {
1754
1755 case 'h':
1756 return help();
1757
1758 case ARG_VERSION:
1759 puts(PACKAGE_STRING);
1760 puts(SYSTEMD_FEATURES);
1761 return 0;
1762
1763 case ARG_NO_PAGER:
1764 arg_no_pager = true;
1765 break;
1766
17d47d8d
TA
1767 case ARG_NO_LEGEND:
1768 arg_legend = false;
1769 break;
1770
1f849790
LP
1771 case ARG_USER:
1772 arg_user = true;
1773 break;
1774
1775 case ARG_SYSTEM:
1776 arg_user = false;
1777 break;
1778
1779 case ARG_ADDRESS:
1780 arg_address = optarg;
1781 break;
1782
56e61788
LP
1783 case ARG_SHOW_MACHINE:
1784 arg_show_machine = true;
1785 break;
1786
1787 case ARG_UNIQUE:
1788 arg_unique = true;
1f849790
LP
1789 break;
1790
56e61788
LP
1791 case ARG_ACQUIRED:
1792 arg_acquired = true;
1793 break;
1794
1795 case ARG_ACTIVATABLE:
1796 arg_activatable = true;
a4297f08
LP
1797 break;
1798
1f849790
LP
1799 case ARG_MATCH:
1800 if (strv_extend(&arg_matches, optarg) < 0)
1801 return log_oom();
1802 break;
1803
1f70b087
LP
1804 case ARG_SIZE: {
1805 off_t o;
1806
1807 r = parse_size(optarg, 0, &o);
1808 if (r < 0) {
1809 log_error("Failed to parse size: %s", optarg);
1810 return r;
1811 }
1812
1813 if ((off_t) (size_t) o != o) {
1814 log_error("Size out of range.");
1815 return -E2BIG;
1816 }
1817
1818 arg_snaplen = (size_t) o;
1819 break;
1820 }
1821
d9130355
LP
1822 case ARG_LIST:
1823 arg_list = true;
1824 break;
1825
d75edbd6
LP
1826 case 'H':
1827 arg_transport = BUS_TRANSPORT_REMOTE;
1828 arg_host = optarg;
1829 break;
1830
1831 case 'M':
1832 arg_transport = BUS_TRANSPORT_CONTAINER;
1833 arg_host = optarg;
1834 break;
1835
781fa938
LP
1836 case 'q':
1837 arg_quiet = true;
1838 break;
1839
1fc55609
LP
1840 case ARG_VERBOSE:
1841 arg_verbose = true;
1842 break;
1843
38051578
LP
1844 case ARG_EXPECT_REPLY:
1845 r = parse_boolean(optarg);
1846 if (r < 0) {
1847 log_error("Failed to parse --expect-reply= parameter.");
1848 return r;
1849 }
1850
1851 arg_expect_reply = !!r;
1852 break;
1853
1854
1855 case ARG_AUTO_START:
1856 r = parse_boolean(optarg);
1857 if (r < 0) {
1858 log_error("Failed to parse --auto-start= parameter.");
1859 return r;
1860 }
1861
1862 arg_auto_start = !!r;
1863 break;
1864
1865
1866 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
1867 r = parse_boolean(optarg);
1868 if (r < 0) {
1869 log_error("Failed to parse --allow-interactive-authorization= parameter.");
1870 return r;
1871 }
1872
1873 arg_allow_interactive_authorization = !!r;
1874 break;
1875
a44b1081
LP
1876 case ARG_TIMEOUT:
1877 r = parse_sec(optarg, &arg_timeout);
1878 if (r < 0) {
1879 log_error("Failed to parse --timeout= parameter.");
1880 return r;
1881 }
1882
1883 break;
1884
40ed1a45
LP
1885 case ARG_AUGMENT_CREDS:
1886 r = parse_boolean(optarg);
1887 if (r < 0) {
1888 log_error("Failed to parse --augment-creds= parameter.");
1889 return r;
1890 }
1891
1892 arg_augment_creds = !!r;
1893 break;
1894
1f849790
LP
1895 case '?':
1896 return -EINVAL;
1897
1898 default:
eb9da376 1899 assert_not_reached("Unhandled option");
1f849790 1900 }
1f849790
LP
1901
1902 return 1;
1903}
1904
1905static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
1906 assert(bus);
1907
1908 if (optind >= argc ||
1909 streq(argv[optind], "list"))
1910 return list_bus_names(bus, argv + optind);
1911
1912 if (streq(argv[optind], "monitor"))
1f70b087
LP
1913 return monitor(bus, argv + optind, message_dump);
1914
1915 if (streq(argv[optind], "capture"))
1916 return capture(bus, argv + optind);
1f849790 1917
95c4fe82
LP
1918 if (streq(argv[optind], "status"))
1919 return status(bus, argv + optind);
d9130355
LP
1920
1921 if (streq(argv[optind], "tree"))
1922 return tree(bus, argv + optind);
781fa938 1923
0171da06
LP
1924 if (streq(argv[optind], "introspect"))
1925 return introspect(bus, argv + optind);
1926
781fa938
LP
1927 if (streq(argv[optind], "call"))
1928 return call(bus, argv + optind);
d55192ad
LP
1929
1930 if (streq(argv[optind], "get-property"))
1931 return get_property(bus, argv + optind);
95c4fe82 1932
1fc55609
LP
1933 if (streq(argv[optind], "set-property"))
1934 return set_property(bus, argv + optind);
1935
1f849790
LP
1936 if (streq(argv[optind], "help"))
1937 return help();
1938
1939 log_error("Unknown command '%s'", argv[optind]);
1940 return -EINVAL;
1941}
1942
1943int main(int argc, char *argv[]) {
24996861 1944 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1f849790
LP
1945 int r;
1946
1947 log_parse_environment();
1948 log_open();
1949
1950 r = parse_argv(argc, argv);
1951 if (r <= 0)
1952 goto finish;
1953
09365592
LP
1954 r = sd_bus_new(&bus);
1955 if (r < 0) {
1956 log_error("Failed to allocate bus: %s", strerror(-r));
1957 goto finish;
1958 }
1959
1f70b087
LP
1960 if (streq_ptr(argv[optind], "monitor") ||
1961 streq_ptr(argv[optind], "capture")) {
09365592
LP
1962
1963 r = sd_bus_set_monitor(bus, true);
1f849790 1964 if (r < 0) {
09365592 1965 log_error("Failed to set monitor mode: %s", strerror(-r));
1f849790
LP
1966 goto finish;
1967 }
d0ce7734
LP
1968
1969 r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL);
1970 if (r < 0) {
1971 log_error("Failed to enable credentials: %s", strerror(-r));
1972 goto finish;
1973 }
1974
1975 r = sd_bus_negotiate_timestamp(bus, true);
1976 if (r < 0) {
1977 log_error("Failed to enable timestamps: %s", strerror(-r));
1978 goto finish;
1979 }
1980
1981 r = sd_bus_negotiate_fds(bus, true);
1982 if (r < 0) {
1983 log_error("Failed to enable fds: %s", strerror(-r));
1984 goto finish;
1985 }
09365592 1986 }
1f849790 1987
09365592 1988 if (arg_address)
1f849790 1989 r = sd_bus_set_address(bus, arg_address);
09365592 1990 else {
b18ec7e2
LP
1991 r = sd_bus_set_bus_client(bus, true);
1992 if (r < 0) {
1993 log_error("Failed to set bus client: %s", strerror(-r));
1994 goto finish;
1995 }
1996
09365592 1997 switch (arg_transport) {
1f849790 1998
09365592
LP
1999 case BUS_TRANSPORT_LOCAL:
2000 if (arg_user)
2001 r = bus_set_address_user(bus);
2002 else
2003 r = bus_set_address_system(bus);
2004 break;
2005
2006 case BUS_TRANSPORT_REMOTE:
2007 r = bus_set_address_system_remote(bus, arg_host);
2008 break;
2009
2010 case BUS_TRANSPORT_CONTAINER:
2011 r = bus_set_address_system_container(bus, arg_host);
2012 break;
2013
2014 default:
2015 assert_not_reached("Hmm, unknown transport type.");
1f849790 2016 }
09365592
LP
2017 }
2018 if (r < 0) {
2019 log_error("Failed to set address: %s", strerror(-r));
2020 goto finish;
2021 }
1f849790 2022
09365592 2023 r = sd_bus_start(bus);
1f849790
LP
2024 if (r < 0) {
2025 log_error("Failed to connect to bus: %s", strerror(-r));
2026 goto finish;
2027 }
2028
2029 r = busctl_main(bus, argc, argv);
2030
2031finish:
2032 pager_close();
d75edbd6 2033
1f849790 2034 strv_free(arg_matches);
de1c301e 2035
de1c301e
LP
2036 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2037}