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