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