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