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