]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
bus: change creds dumping order to be more close to internal storage order
[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;
a44b1081 59static usec_t arg_timeout = 0;
1f849790
LP
60
61static void pager_open_if_enabled(void) {
62
63 /* Cache result before we open the pager */
64 if (arg_no_pager)
65 return;
66
67 pager_open(false);
68}
69
70static int list_bus_names(sd_bus *bus, char **argv) {
71f2ab46 71 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
72 _cleanup_free_ char **merged = NULL;
73 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
74 char **i;
75 int r;
89ffcd2a 76 size_t max_i = 0;
5e2f14e6
LP
77 unsigned n = 0;
78 void *v;
79 char *k;
80 Iterator iterator;
de1c301e 81
1f849790 82 assert(bus);
de1c301e 83
d9130355
LP
84 if (!arg_unique && !arg_acquired && !arg_activatable)
85 arg_unique = arg_acquired = arg_activatable = true;
86
56e61788 87 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
de1c301e
LP
88 if (r < 0) {
89 log_error("Failed to list names: %s", strerror(-r));
1f849790 90 return r;
de1c301e
LP
91 }
92
1f849790
LP
93 pager_open_if_enabled();
94
d5099efc 95 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
96 if (!names)
97 return log_oom();
89ffcd2a 98
5e2f14e6 99 STRV_FOREACH(i, acquired) {
71f2ab46
LP
100 max_i = MAX(max_i, strlen(*i));
101
5e2f14e6
LP
102 r = hashmap_put(names, *i, INT_TO_PTR(1));
103 if (r < 0) {
104 log_error("Failed to add to hashmap: %s", strerror(-r));
105 return r;
106 }
107 }
108
109 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
110 max_i = MAX(max_i, strlen(*i));
111
5e2f14e6
LP
112 r = hashmap_put(names, *i, INT_TO_PTR(2));
113 if (r < 0 && r != -EEXIST) {
114 log_error("Failed to add to hashmap: %s", strerror(-r));
115 return r;
116 }
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
5e2f14e6
LP
140 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
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
056f95d0 161 r = sd_bus_get_name_creds(bus, *i,
14008e4e
LP
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|
455971c1 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);
435 if (r < 0) {
436 log_error("Failed to get name list: %s", strerror(-r));
437 return r;
438 }
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
56c8b52d 454 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off());
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();
56c8b52d 471 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off());
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
543 r = format_cmdline(m, f, true);
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
619 }
620}
621
0171da06
LP
622typedef struct Member {
623 const char *type;
624 char *interface;
625 char *name;
626 char *signature;
627 char *result;
1fc55609 628 char *value;
0171da06
LP
629 bool writable;
630 uint64_t flags;
631} Member;
632
633static unsigned long member_hash_func(const void *p, const uint8_t hash_key[]) {
634 const Member *m = p;
635 unsigned long ul;
636
637 assert(m);
638 assert(m->type);
639
640 ul = string_hash_func(m->type, hash_key);
641
642 if (m->name)
643 ul ^= string_hash_func(m->name, hash_key);
644
645 if (m->interface)
646 ul ^= string_hash_func(m->interface, hash_key);
647
648 return ul;
649}
650
651static int member_compare_func(const void *a, const void *b) {
652 const Member *x = a, *y = b;
653 int d;
654
655 assert(x);
656 assert(y);
657 assert(x->type);
658 assert(y->type);
659
660 if (!x->interface && y->interface)
661 return -1;
662 if (x->interface && !y->interface)
663 return 1;
664 if (x->interface && y->interface) {
665 d = strcmp(x->interface, y->interface);
666 if (d != 0)
667 return d;
668 }
669
670 d = strcmp(x->type, y->type);
671 if (d != 0)
672 return d;
673
674 if (!x->name && y->name)
675 return -1;
676 if (x->name && !y->name)
677 return 1;
678 if (x->name && y->name)
679 return strcmp(x->name, y->name);
680
681 return 0;
682}
683
684static int member_compare_funcp(const void *a, const void *b) {
685 const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b;
686
687 return member_compare_func(*x, *y);
688}
689
690static void member_free(Member *m) {
691 if (!m)
692 return;
693
694 free(m->interface);
695 free(m->name);
696 free(m->signature);
697 free(m->result);
1fc55609 698 free(m->value);
0171da06
LP
699 free(m);
700}
701
702DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
703
704static void member_set_free(Set *s) {
705 Member *m;
706
707 while ((m = set_steal_first(s)))
708 member_free(m);
709
710 set_free(s);
711}
712
713DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
714
715static int on_interface(const char *interface, uint64_t flags, void *userdata) {
716 _cleanup_(member_freep) Member *m;
717 Set *members = userdata;
718 int r;
719
720 assert(interface);
721 assert(members);
722
723 m = new0(Member, 1);
724 if (!m)
725 return log_oom();
726
727 m->type = "interface";
728 m->flags = flags;
729
730 r = free_and_strdup(&m->interface, interface);
731 if (r < 0)
732 return log_oom();
733
734 r = set_put(members, m);
735 if (r <= 0) {
736 log_error("Duplicate interface");
737 return -EINVAL;
738 }
739
740 m = NULL;
741 return 0;
742}
743
744static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
745 _cleanup_(member_freep) Member *m;
746 Set *members = userdata;
747 int r;
748
749 assert(interface);
750 assert(name);
751
752 m = new0(Member, 1);
753 if (!m)
754 return log_oom();
755
756 m->type = "method";
757 m->flags = flags;
758
759 r = free_and_strdup(&m->interface, interface);
760 if (r < 0)
761 return log_oom();
762
763 r = free_and_strdup(&m->name, name);
764 if (r < 0)
765 return log_oom();
766
767 r = free_and_strdup(&m->signature, signature);
768 if (r < 0)
769 return log_oom();
770
771 r = free_and_strdup(&m->result, result);
772 if (r < 0)
773 return log_oom();
774
775 r = set_put(members, m);
776 if (r <= 0) {
777 log_error("Duplicate method");
778 return -EINVAL;
779 }
780
781 m = NULL;
782 return 0;
783}
784
785static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
786 _cleanup_(member_freep) Member *m;
787 Set *members = userdata;
788 int r;
789
790 assert(interface);
791 assert(name);
792
793 m = new0(Member, 1);
794 if (!m)
795 return log_oom();
796
797 m->type = "signal";
798 m->flags = flags;
799
800 r = free_and_strdup(&m->interface, interface);
801 if (r < 0)
802 return log_oom();
803
804 r = free_and_strdup(&m->name, name);
805 if (r < 0)
806 return log_oom();
807
808 r = free_and_strdup(&m->signature, signature);
809 if (r < 0)
810 return log_oom();
811
812 r = set_put(members, m);
813 if (r <= 0) {
814 log_error("Duplicate signal");
815 return -EINVAL;
816 }
817
818 m = NULL;
819 return 0;
820}
821
822static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
823 _cleanup_(member_freep) Member *m;
824 Set *members = userdata;
825 int r;
826
827 assert(interface);
828 assert(name);
829
830 m = new0(Member, 1);
831 if (!m)
832 return log_oom();
833
834 m->type = "property";
835 m->flags = flags;
836 m->writable = writable;
837
838 r = free_and_strdup(&m->interface, interface);
839 if (r < 0)
840 return log_oom();
841
842 r = free_and_strdup(&m->name, name);
843 if (r < 0)
844 return log_oom();
845
846 r = free_and_strdup(&m->signature, signature);
847 if (r < 0)
848 return log_oom();
849
850 r = set_put(members, m);
851 if (r <= 0) {
852 log_error("Duplicate property");
853 return -EINVAL;
854 }
855
856 m = NULL;
857 return 0;
858}
859
860static const char *strdash(const char *x) {
861 return isempty(x) ? "-" : x;
862}
863
864static int introspect(sd_bus *bus, char **argv) {
865 static const struct hash_ops member_hash_ops = {
866 .hash = member_hash_func,
867 .compare = member_compare_func,
868 };
869
870 static const XMLIntrospectOps ops = {
871 .on_interface = on_interface,
872 .on_method = on_method,
873 .on_signal = on_signal,
874 .on_property = on_property,
875 };
876
877 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
878 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
879 _cleanup_(member_set_freep) Set *members = NULL;
880 Iterator i;
881 Member *m;
882 const char *xml;
883 int r;
884 unsigned name_width, type_width, signature_width, result_width;
885 Member **sorted = NULL;
886 unsigned k = 0, j;
887
888 if (strv_length(argv) != 3) {
889 log_error("Requires service and object path argument.");
890 return -EINVAL;
891 }
892
893 members = set_new(&member_hash_ops);
894 if (!members)
895 return log_oom();
896
897 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
898 if (r < 0) {
899 log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
900 return r;
901 }
902
903 r = sd_bus_message_read(reply, "s", &xml);
904 if (r < 0)
905 return bus_log_parse_error(r);
906
1fc55609 907 /* First, get list of all properties */
0171da06
LP
908 r = parse_xml_introspect(argv[2], xml, &ops, members);
909 if (r < 0)
910 return r;
911
1fc55609
LP
912 /* Second, find the current values for them */
913 SET_FOREACH(m, members, i) {
914
915 if (!streq(m->type, "property"))
916 continue;
917
918 if (m->value)
919 continue;
920
921 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
922 if (r < 0) {
923 log_error("%s", bus_error_message(&error, r));
924 return r;
925 }
926
927 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
928 if (r < 0)
929 return bus_log_parse_error(r);
930
931 for (;;) {
932 Member *z;
933 _cleanup_free_ char *buf = NULL;
934 _cleanup_fclose_ FILE *mf = NULL;
935 size_t sz = 0;
936 const char *name;
937
938 r = sd_bus_message_enter_container(reply, 'e', "sv");
939 if (r < 0)
940 return bus_log_parse_error(r);
941
942 if (r == 0)
943 break;
944
945 r = sd_bus_message_read(reply, "s", &name);
946 if (r < 0)
947 return bus_log_parse_error(r);
948
949 r = sd_bus_message_enter_container(reply, 'v', NULL);
950 if (r < 0)
951 return bus_log_parse_error(r);
952
953 mf = open_memstream(&buf, &sz);
954 if (!mf)
955 return log_oom();
956
957 r = format_cmdline(reply, mf, false);
958 if (r < 0)
959 return bus_log_parse_error(r);
960
961 fclose(mf);
962 mf = NULL;
963
964 z = set_get(members, &((Member) {
965 .type = "property",
966 .interface = m->interface,
967 .name = (char*) name }));
968 if (z) {
969 free(z->value);
970 z->value = buf;
971 buf = NULL;
972 }
973
974 r = sd_bus_message_exit_container(reply);
975 if (r < 0)
976 return bus_log_parse_error(r);
977
978 r = sd_bus_message_exit_container(reply);
979 if (r < 0)
980 return bus_log_parse_error(r);
981 }
982
983 r = sd_bus_message_exit_container(reply);
984 if (r < 0)
985 return bus_log_parse_error(r);
986 }
987
0171da06
LP
988 pager_open_if_enabled();
989
990 name_width = strlen("NAME");
991 type_width = strlen("TYPE");
992 signature_width = strlen("SIGNATURE");
1fc55609 993 result_width = strlen("RESULT/VALUE");
0171da06
LP
994
995 sorted = newa(Member*, set_size(members));
996
997 SET_FOREACH(m, members, i) {
998 if (m->interface)
999 name_width = MAX(name_width, strlen(m->interface));
1000 if (m->name)
1001 name_width = MAX(name_width, strlen(m->name) + 1);
1002 if (m->type)
1003 type_width = MAX(type_width, strlen(m->type));
1004 if (m->signature)
1005 signature_width = MAX(signature_width, strlen(m->signature));
1006 if (m->result)
1007 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1008 if (m->value)
1009 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1010
1011 sorted[k++] = m;
1012 }
1013
1fc55609
LP
1014 if (result_width > 40)
1015 result_width = 40;
1016
0171da06
LP
1017 assert(k == set_size(members));
1018 qsort(sorted, k, sizeof(Member*), member_compare_funcp);
1019
1fc55609
LP
1020 if (arg_legend) {
1021 printf("%-*s %-*s %-*s %-*s %s\n",
1022 (int) name_width, "NAME",
1023 (int) type_width, "TYPE",
1024 (int) signature_width, "SIGNATURE",
1025 (int) result_width, "RESULT/VALUE",
1026 "FLAGS");
1027 }
0171da06
LP
1028
1029 for (j = 0; j < k; j++) {
1fc55609
LP
1030 _cleanup_free_ char *ellipsized = NULL;
1031 const char *rv;
0171da06
LP
1032 bool is_interface;
1033
1034 m = sorted[j];
1035
1036 is_interface = streq(m->type, "interface");
1037
1fc55609
LP
1038 if (m->value) {
1039 ellipsized = ellipsize(m->value, result_width, 100);
1040 if (!ellipsized)
1041 return log_oom();
1042
1043 rv = ellipsized;
1044 } else
1045 rv = strdash(m->result);
1046
0171da06
LP
1047 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1048 is_interface ? ansi_highlight() : "",
1049 is_interface ? "" : ".",
1050 - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1051 is_interface ? ansi_highlight_off() : "",
1052 (int) type_width, strdash(m->type),
1053 (int) signature_width, strdash(m->signature),
1fc55609 1054 (int) result_width, rv,
0171da06
LP
1055 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1056 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1057 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1058 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1059 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1060 m->writable ? " writable" : "");
1061 }
1062
1063 return 0;
1064}
1065
1f70b087 1066static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 1067 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1068}
1069
1070static int message_pcap(sd_bus_message *m, FILE *f) {
1071 return bus_message_pcap_frame(m, arg_snaplen, f);
1072}
1073
1074static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
b51f299a 1075 bool added_something = false;
1f849790
LP
1076 char **i;
1077 int r;
1078
1079 STRV_FOREACH(i, argv+1) {
1080 _cleanup_free_ char *m = NULL;
1081
1082 if (!service_name_is_valid(*i)) {
1083 log_error("Invalid service name '%s'", *i);
1084 return -EINVAL;
1085 }
1086
1087 m = strjoin("sender='", *i, "'", NULL);
1088 if (!m)
1089 return log_oom();
1090
19befb2d 1091 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
1f849790
LP
1092 if (r < 0) {
1093 log_error("Failed to add match: %s", strerror(-r));
1094 return r;
1095 }
b51f299a
LP
1096
1097 added_something = true;
1f849790
LP
1098 }
1099
1100 STRV_FOREACH(i, arg_matches) {
19befb2d 1101 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
1f849790
LP
1102 if (r < 0) {
1103 log_error("Failed to add match: %s", strerror(-r));
1104 return r;
1105 }
b51f299a
LP
1106
1107 added_something = true;
1108 }
1109
1110 if (!added_something) {
19befb2d 1111 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
b51f299a
LP
1112 if (r < 0) {
1113 log_error("Failed to add match: %s", strerror(-r));
1114 return r;
1115 }
1f849790
LP
1116 }
1117
1118 for (;;) {
1119 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1120
1121 r = sd_bus_process(bus, &m);
1122 if (r < 0) {
1123 log_error("Failed to process bus: %s", strerror(-r));
1124 return r;
1125 }
1126
1127 if (m) {
1f70b087 1128 dump(m, stdout);
1f849790
LP
1129 continue;
1130 }
1131
1132 if (r > 0)
1133 continue;
1134
1135 r = sd_bus_wait(bus, (uint64_t) -1);
1136 if (r < 0) {
1137 log_error("Failed to wait for bus: %s", strerror(-r));
1138 return r;
1139 }
1140 }
95c4fe82 1141}
1f849790 1142
1f70b087
LP
1143static int capture(sd_bus *bus, char *argv[]) {
1144 int r;
1145
1146 if (isatty(fileno(stdout)) > 0) {
1147 log_error("Refusing to write message data to console, please redirect output to a file.");
1148 return -EINVAL;
1149 }
1150
1151 bus_pcap_header(arg_snaplen, stdout);
1152
1153 r = monitor(bus, argv, message_pcap);
1154 if (r < 0)
1155 return r;
1156
1157 if (ferror(stdout)) {
1158 log_error("Couldn't write capture file.");
1159 return -EIO;
1160 }
1161
1162 return r;
1163}
1164
95c4fe82
LP
1165static int status(sd_bus *bus, char *argv[]) {
1166 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
1167 pid_t pid;
1168 int r;
1169
1170 assert(bus);
1171
1172 if (strv_length(argv) != 2) {
1173 log_error("Expects one argument.");
1174 return -EINVAL;
1175 }
1176
1177 r = parse_pid(argv[1], &pid);
1178 if (r < 0)
056f95d0 1179 r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
95c4fe82 1180 else
151b9b96 1181 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
95c4fe82
LP
1182
1183 if (r < 0) {
1184 log_error("Failed to get credentials: %s", strerror(-r));
1185 return r;
1186 }
1187
d0b2babf 1188 bus_creds_dump(creds, NULL, false);
95c4fe82 1189 return 0;
1f849790
LP
1190}
1191
781fa938
LP
1192static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1193 char **p;
1194 int r;
1195
1196 assert(m);
1197 assert(signature);
1198 assert(x);
1199
1200 p = *x;
1201
1202 for (;;) {
1203 const char *v;
1204 char t;
1205
1206 t = *signature;
1207 v = *p;
1208
1209 if (t == 0)
1210 break;
1211 if (!v) {
1212 log_error("Too few parameters for signature.");
1213 return -EINVAL;
1214 }
1215
1216 signature++;
1217 p++;
1218
1219 switch (t) {
1220
1221 case SD_BUS_TYPE_BOOLEAN:
1222
1223 r = parse_boolean(v);
1224 if (r < 0) {
1225 log_error("Failed to parse as boolean: %s", v);
1226 return r;
1227 }
1228
1229 r = sd_bus_message_append_basic(m, t, &r);
1230 break;
1231
1232 case SD_BUS_TYPE_BYTE: {
1233 uint8_t z;
1234
1235 r = safe_atou8(v, &z);
1236 if (r < 0) {
1237 log_error("Failed to parse as byte (unsigned 8bit integer): %s", v);
1238 return r;
1239 }
1240
1241 r = sd_bus_message_append_basic(m, t, &z);
1242 break;
1243 }
1244
1245 case SD_BUS_TYPE_INT16: {
1246 int16_t z;
1247
1248 r = safe_atoi16(v, &z);
1249 if (r < 0) {
1250 log_error("Failed to parse as signed 16bit integer: %s", v);
1251 return r;
1252 }
1253
1254 r = sd_bus_message_append_basic(m, t, &z);
1255 break;
1256 }
1257
1258 case SD_BUS_TYPE_UINT16: {
1259 uint16_t z;
1260
1261 r = safe_atou16(v, &z);
1262 if (r < 0) {
1263 log_error("Failed to parse as unsigned 16bit integer: %s", v);
1264 return r;
1265 }
1266
1267 r = sd_bus_message_append_basic(m, t, &z);
1268 break;
1269 }
1270
1271 case SD_BUS_TYPE_INT32: {
1272 int32_t z;
1273
1274 r = safe_atoi32(v, &z);
1275 if (r < 0) {
1276 log_error("Failed to parse as signed 32bit integer: %s", v);
1277 return r;
1278 }
1279
1280 r = sd_bus_message_append_basic(m, t, &z);
1281 break;
1282 }
1283
1284 case SD_BUS_TYPE_UINT32: {
1285 uint32_t z;
1286
1287 r = safe_atou32(v, &z);
1288 if (r < 0) {
1289 log_error("Failed to parse as unsigned 32bit integer: %s", v);
1290 return r;
1291 }
1292
1293 r = sd_bus_message_append_basic(m, t, &z);
1294 break;
1295 }
1296
1297 case SD_BUS_TYPE_INT64: {
1298 int64_t z;
1299
1300 r = safe_atoi64(v, &z);
1301 if (r < 0) {
1302 log_error("Failed to parse as signed 64bit integer: %s", v);
1303 return r;
1304 }
1305
1306 r = sd_bus_message_append_basic(m, t, &z);
1307 break;
1308 }
1309
1310 case SD_BUS_TYPE_UINT64: {
1311 uint64_t z;
1312
1313 r = safe_atou64(v, &z);
1314 if (r < 0) {
1315 log_error("Failed to parse as unsigned 64bit integer: %s", v);
1316 return r;
1317 }
1318
1319 r = sd_bus_message_append_basic(m, t, &z);
1320 break;
1321 }
1322
1323
1324 case SD_BUS_TYPE_DOUBLE: {
1325 double z;
1326
1327 r = safe_atod(v, &z);
1328 if (r < 0) {
1329 log_error("Failed to parse as double precision floating point: %s", v);
1330 return r;
1331 }
1332
1333 r = sd_bus_message_append_basic(m, t, &z);
1334 break;
1335 }
1336
1337 case SD_BUS_TYPE_STRING:
1338 case SD_BUS_TYPE_OBJECT_PATH:
1339 case SD_BUS_TYPE_SIGNATURE:
1340
1341 r = sd_bus_message_append_basic(m, t, v);
1342 break;
1343
1344 case SD_BUS_TYPE_ARRAY: {
1345 uint32_t n;
1346 size_t k;
1347
1348 r = safe_atou32(v, &n);
1349 if (r < 0) {
1350 log_error("Failed to parse number of array entries: %s", v);
1351 return r;
1352 }
1353
1354 r = signature_element_length(signature, &k);
1355 if (r < 0) {
1356 log_error("Invalid array signature.");
1357 return r;
1358 }
1359
1360 {
1361 unsigned i;
1362 char s[k + 1];
1363 memcpy(s, signature, k);
1364 s[k] = 0;
1365
1366 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1367 if (r < 0)
1368 return bus_log_create_error(r);
1369
1370 for (i = 0; i < n; i++) {
1371 r = message_append_cmdline(m, s, &p);
1372 if (r < 0)
1373 return r;
1374 }
1375 }
1376
1377 signature += k;
1378
1379 r = sd_bus_message_close_container(m);
1380 break;
1381 }
1382
1383 case SD_BUS_TYPE_VARIANT:
1384 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1385 if (r < 0)
1386 return bus_log_create_error(r);
1387
1388 r = message_append_cmdline(m, v, &p);
1389 if (r < 0)
1390 return r;
1391
1392 r = sd_bus_message_close_container(m);
1393 break;
1394
1395 case SD_BUS_TYPE_STRUCT_BEGIN:
1396 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1397 size_t k;
1398
1399 signature--;
1400 p--;
1401
1402 r = signature_element_length(signature, &k);
1403 if (r < 0) {
1404 log_error("Invalid struct/dict entry signature.");
1405 return r;
1406 }
1407
1408 {
1409 char s[k-1];
1410 memcpy(s, signature + 1, k - 2);
1411 s[k - 2] = 0;
1412
1413 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1414 if (r < 0)
1415 return bus_log_create_error(r);
1416
1417 r = message_append_cmdline(m, s, &p);
1418 if (r < 0)
1419 return r;
1420 }
1421
1422 signature += k;
1423
1424 r = sd_bus_message_close_container(m);
1425 break;
1426 }
1427
1428 case SD_BUS_TYPE_UNIX_FD:
1429 log_error("UNIX file descriptor not supported as type.");
1430 return -EINVAL;
1431
1432 default:
1433 log_error("Unknown signature type %c.", t);
1434 return -EINVAL;
1435 }
1436
1437 if (r < 0)
1438 return bus_log_create_error(r);
1439 }
1440
1441 *x = p;
1442 return 0;
1443}
1444
1445static int call(sd_bus *bus, char *argv[]) {
1446 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1447 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
1448 int r;
1449
1450 assert(bus);
1451
1452 if (strv_length(argv) < 5) {
1453 log_error("Expects at least four arguments.");
1454 return -EINVAL;
1455 }
1456
1457 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
1458 if (r < 0)
1459 return bus_log_create_error(r);
781fa938 1460
38051578
LP
1461 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
1462 if (r < 0)
1463 return bus_log_create_error(r);
1464
1465 r = sd_bus_message_set_auto_start(m, arg_auto_start);
1466 if (r < 0)
1467 return bus_log_create_error(r);
1468
1469 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
1470 if (r < 0)
1471 return bus_log_create_error(r);
1472
781fa938
LP
1473 if (!isempty(argv[5])) {
1474 char **p;
1475
1476 p = argv+6;
1477
1478 r = message_append_cmdline(m, argv[5], &p);
1479 if (r < 0)
1480 return r;
1481
1482 if (*p) {
1483 log_error("Too many parameters for signature.");
1484 return -EINVAL;
1485 }
1486 }
1487
38051578
LP
1488 if (!arg_expect_reply) {
1489 r = sd_bus_send(bus, m, NULL);
1490 if (r < 0) {
1491 log_error("Failed to send message.");
1492 return r;
1493 }
1494
1495 return 0;
1496 }
1497
a44b1081 1498 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
781fa938
LP
1499 if (r < 0) {
1500 log_error("%s", bus_error_message(&error, r));
1501 return r;
1502 }
1503
1504 r = sd_bus_message_is_empty(reply);
1505 if (r < 0)
1506 return bus_log_parse_error(r);
1fc55609 1507
781fa938 1508 if (r == 0 && !arg_quiet) {
1fc55609
LP
1509
1510 if (arg_verbose) {
1511 pager_open_if_enabled();
1512
1513 r = bus_message_dump(reply, stdout, 0);
1514 if (r < 0)
1515 return r;
1516 } else {
1517
1518 fputs(sd_bus_message_get_signature(reply, true), stdout);
1519 fputc(' ', stdout);
1520
1521 r = format_cmdline(reply, stdout, false);
1522 if (r < 0)
1523 return bus_log_parse_error(r);
1524
1525 fputc('\n', stdout);
1526 }
d55192ad
LP
1527 }
1528
1529 return 0;
1530}
1531
1532static int get_property(sd_bus *bus, char *argv[]) {
1533 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1534 unsigned n;
1fc55609 1535 char **i;
d55192ad
LP
1536 int r;
1537
1538 assert(bus);
1539
1540 n = strv_length(argv);
1fc55609
LP
1541 if (n < 5) {
1542 log_error("Expects at least four arguments.");
d55192ad
LP
1543 return -EINVAL;
1544 }
1545
1fc55609 1546 STRV_FOREACH(i, argv + 4) {
d55192ad 1547 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1fc55609
LP
1548 const char *contents = NULL;
1549 char type;
d55192ad 1550
1fc55609 1551 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
d55192ad
LP
1552 if (r < 0) {
1553 log_error("%s", bus_error_message(&error, r));
1554 return r;
1555 }
1556
1fc55609 1557 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
1558 if (r < 0)
1559 return bus_log_parse_error(r);
1560
1fc55609
LP
1561 r = sd_bus_message_enter_container(reply, 'v', contents);
1562 if (r < 0)
1563 return bus_log_parse_error(r);
d55192ad 1564
1fc55609 1565 if (arg_verbose) {
d55192ad 1566 pager_open_if_enabled();
d55192ad 1567
1fc55609 1568 r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 1569 if (r < 0)
1fc55609
LP
1570 return r;
1571 } else {
1572 fputs(contents, stdout);
1573 fputc(' ', stdout);
d55192ad 1574
1fc55609 1575 r = format_cmdline(reply, stdout, false);
d55192ad
LP
1576 if (r < 0)
1577 return bus_log_parse_error(r);
1578
1fc55609 1579 fputc('\n', stdout);
d55192ad
LP
1580 }
1581
1582 r = sd_bus_message_exit_container(reply);
1583 if (r < 0)
1584 return bus_log_parse_error(r);
1fc55609 1585 }
d55192ad 1586
1fc55609
LP
1587 return 0;
1588}
d55192ad 1589
1fc55609
LP
1590static int set_property(sd_bus *bus, char *argv[]) {
1591 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1592 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1593 unsigned n;
1594 char **p;
1595 int r;
d55192ad 1596
1fc55609 1597 assert(bus);
d55192ad 1598
1fc55609
LP
1599 n = strv_length(argv);
1600 if (n < 6) {
1601 log_error("Expects at least five arguments.");
1602 return -EINVAL;
1603 }
d55192ad 1604
1fc55609
LP
1605 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
1606 if (r < 0)
1607 return bus_log_create_error(r);
d55192ad 1608
1fc55609
LP
1609 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
1610 if (r < 0)
1611 return bus_log_create_error(r);
d55192ad 1612
1fc55609
LP
1613 r = sd_bus_message_open_container(m, 'v', argv[5]);
1614 if (r < 0)
1615 return bus_log_create_error(r);
1616
1617 p = argv+6;
1618 r = message_append_cmdline(m, argv[5], &p);
1619 if (r < 0)
1620 return r;
1621
1622 r = sd_bus_message_close_container(m);
1623 if (r < 0)
1624 return bus_log_create_error(r);
1625
1626 if (*p) {
1627 log_error("Too many parameters for signature.");
1628 return -EINVAL;
1629 }
1630
a44b1081 1631 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
1fc55609
LP
1632 if (r < 0) {
1633 log_error("%s", bus_error_message(&error, r));
1634 return r;
781fa938
LP
1635 }
1636
1637 return 0;
1638}
1639
1f849790 1640static int help(void) {
1f849790
LP
1641 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1642 "Introspect the bus.\n\n"
d75edbd6
LP
1643 " -h --help Show this help\n"
1644 " --version Show package version\n"
a86a47ce 1645 " --no-pager Do not pipe output into a pager\n"
17d47d8d 1646 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
1647 " --system Connect to system bus\n"
1648 " --user Connect to user bus\n"
1649 " -H --host=[USER@]HOST Operate on remote host\n"
1650 " -M --machine=CONTAINER Operate on local container\n"
1651 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
1652 " --show-machine Show machine ID column in list\n"
1653 " --unique Only show unique names\n"
1654 " --acquired Only show acquired names\n"
1655 " --activatable Only show activatable names\n"
d9130355 1656 " --match=MATCH Only show matching messages\n"
781fa938 1657 " --list Don't show tree, but simple object path list\n"
1fc55609 1658 " --quiet Don't show method call reply\n"
38051578
LP
1659 " --verbose Show result values in long format\n"
1660 " --expect-reply=BOOL Expect a method call reply\n"
1661 " --auto-start=BOOL Auto-start destination service\n"
1662 " --allow-interactive-authorization=BOOL\n"
a44b1081
LP
1663 " Allow interactive authorization for operation\n"
1664 " --timeout=SECS Maximum time to wait for method call completion\n\n"
1f849790 1665 "Commands:\n"
d75edbd6 1666 " list List bus names\n"
0171da06 1667 " status SERVICE Show service name status\n"
d94fe1f1 1668 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1669 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 1670 " tree [SERVICE...] Show object tree of service\n"
1fc55609 1671 " introspect SERVICE OBJECT\n"
0171da06 1672 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 1673 " Call a method\n"
1fc55609 1674 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 1675 " Get property value\n"
1fc55609
LP
1676 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
1677 " Set property value\n"
601185b4
ZJS
1678 " help Show this help\n"
1679 , program_invocation_short_name);
1f849790
LP
1680
1681 return 0;
1682}
1683
1684static int parse_argv(int argc, char *argv[]) {
1685
1686 enum {
1687 ARG_VERSION = 0x100,
1688 ARG_NO_PAGER,
17d47d8d 1689 ARG_NO_LEGEND,
1f849790
LP
1690 ARG_SYSTEM,
1691 ARG_USER,
1692 ARG_ADDRESS,
1693 ARG_MATCH,
56e61788
LP
1694 ARG_SHOW_MACHINE,
1695 ARG_UNIQUE,
1696 ARG_ACQUIRED,
1f70b087
LP
1697 ARG_ACTIVATABLE,
1698 ARG_SIZE,
d9130355 1699 ARG_LIST,
1fc55609 1700 ARG_VERBOSE,
38051578
LP
1701 ARG_EXPECT_REPLY,
1702 ARG_AUTO_START,
1703 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 1704 ARG_TIMEOUT,
1f849790
LP
1705 };
1706
1707 static const struct option options[] = {
56e61788
LP
1708 { "help", no_argument, NULL, 'h' },
1709 { "version", no_argument, NULL, ARG_VERSION },
1710 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1711 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1712 { "system", no_argument, NULL, ARG_SYSTEM },
1713 { "user", no_argument, NULL, ARG_USER },
1714 { "address", required_argument, NULL, ARG_ADDRESS },
1715 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1716 { "unique", no_argument, NULL, ARG_UNIQUE },
1717 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1718 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1719 { "match", required_argument, NULL, ARG_MATCH },
1720 { "host", required_argument, NULL, 'H' },
1721 { "machine", required_argument, NULL, 'M' },
1f70b087 1722 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1723 { "list", no_argument, NULL, ARG_LIST },
781fa938 1724 { "quiet", no_argument, NULL, 'q' },
1fc55609 1725 { "verbose", no_argument, NULL, ARG_VERBOSE },
38051578
LP
1726 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
1727 { "auto-start", required_argument, NULL, ARG_AUTO_START },
1728 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
a44b1081 1729 { "timeout", required_argument, NULL, ARG_TIMEOUT },
eb9da376 1730 {},
1f849790
LP
1731 };
1732
1f70b087 1733 int c, r;
1f849790
LP
1734
1735 assert(argc >= 0);
1736 assert(argv);
1737
781fa938 1738 while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
1f849790
LP
1739
1740 switch (c) {
1741
1742 case 'h':
1743 return help();
1744
1745 case ARG_VERSION:
1746 puts(PACKAGE_STRING);
1747 puts(SYSTEMD_FEATURES);
1748 return 0;
1749
1750 case ARG_NO_PAGER:
1751 arg_no_pager = true;
1752 break;
1753
17d47d8d
TA
1754 case ARG_NO_LEGEND:
1755 arg_legend = false;
1756 break;
1757
1f849790
LP
1758 case ARG_USER:
1759 arg_user = true;
1760 break;
1761
1762 case ARG_SYSTEM:
1763 arg_user = false;
1764 break;
1765
1766 case ARG_ADDRESS:
1767 arg_address = optarg;
1768 break;
1769
56e61788
LP
1770 case ARG_SHOW_MACHINE:
1771 arg_show_machine = true;
1772 break;
1773
1774 case ARG_UNIQUE:
1775 arg_unique = true;
1f849790
LP
1776 break;
1777
56e61788
LP
1778 case ARG_ACQUIRED:
1779 arg_acquired = true;
1780 break;
1781
1782 case ARG_ACTIVATABLE:
1783 arg_activatable = true;
a4297f08
LP
1784 break;
1785
1f849790
LP
1786 case ARG_MATCH:
1787 if (strv_extend(&arg_matches, optarg) < 0)
1788 return log_oom();
1789 break;
1790
1f70b087
LP
1791 case ARG_SIZE: {
1792 off_t o;
1793
1794 r = parse_size(optarg, 0, &o);
1795 if (r < 0) {
1796 log_error("Failed to parse size: %s", optarg);
1797 return r;
1798 }
1799
1800 if ((off_t) (size_t) o != o) {
1801 log_error("Size out of range.");
1802 return -E2BIG;
1803 }
1804
1805 arg_snaplen = (size_t) o;
1806 break;
1807 }
1808
d9130355
LP
1809 case ARG_LIST:
1810 arg_list = true;
1811 break;
1812
d75edbd6
LP
1813 case 'H':
1814 arg_transport = BUS_TRANSPORT_REMOTE;
1815 arg_host = optarg;
1816 break;
1817
1818 case 'M':
1819 arg_transport = BUS_TRANSPORT_CONTAINER;
1820 arg_host = optarg;
1821 break;
1822
781fa938
LP
1823 case 'q':
1824 arg_quiet = true;
1825 break;
1826
1fc55609
LP
1827 case ARG_VERBOSE:
1828 arg_verbose = true;
1829 break;
1830
38051578
LP
1831 case ARG_EXPECT_REPLY:
1832 r = parse_boolean(optarg);
1833 if (r < 0) {
1834 log_error("Failed to parse --expect-reply= parameter.");
1835 return r;
1836 }
1837
1838 arg_expect_reply = !!r;
1839 break;
1840
1841
1842 case ARG_AUTO_START:
1843 r = parse_boolean(optarg);
1844 if (r < 0) {
1845 log_error("Failed to parse --auto-start= parameter.");
1846 return r;
1847 }
1848
1849 arg_auto_start = !!r;
1850 break;
1851
1852
1853 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
1854 r = parse_boolean(optarg);
1855 if (r < 0) {
1856 log_error("Failed to parse --allow-interactive-authorization= parameter.");
1857 return r;
1858 }
1859
1860 arg_allow_interactive_authorization = !!r;
1861 break;
1862
a44b1081
LP
1863 case ARG_TIMEOUT:
1864 r = parse_sec(optarg, &arg_timeout);
1865 if (r < 0) {
1866 log_error("Failed to parse --timeout= parameter.");
1867 return r;
1868 }
1869
1870 break;
1871
1f849790
LP
1872 case '?':
1873 return -EINVAL;
1874
1875 default:
eb9da376 1876 assert_not_reached("Unhandled option");
1f849790 1877 }
1f849790
LP
1878
1879 return 1;
1880}
1881
1882static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
1883 assert(bus);
1884
1885 if (optind >= argc ||
1886 streq(argv[optind], "list"))
1887 return list_bus_names(bus, argv + optind);
1888
1889 if (streq(argv[optind], "monitor"))
1f70b087
LP
1890 return monitor(bus, argv + optind, message_dump);
1891
1892 if (streq(argv[optind], "capture"))
1893 return capture(bus, argv + optind);
1f849790 1894
95c4fe82
LP
1895 if (streq(argv[optind], "status"))
1896 return status(bus, argv + optind);
d9130355
LP
1897
1898 if (streq(argv[optind], "tree"))
1899 return tree(bus, argv + optind);
781fa938 1900
0171da06
LP
1901 if (streq(argv[optind], "introspect"))
1902 return introspect(bus, argv + optind);
1903
781fa938
LP
1904 if (streq(argv[optind], "call"))
1905 return call(bus, argv + optind);
d55192ad
LP
1906
1907 if (streq(argv[optind], "get-property"))
1908 return get_property(bus, argv + optind);
95c4fe82 1909
1fc55609
LP
1910 if (streq(argv[optind], "set-property"))
1911 return set_property(bus, argv + optind);
1912
1f849790
LP
1913 if (streq(argv[optind], "help"))
1914 return help();
1915
1916 log_error("Unknown command '%s'", argv[optind]);
1917 return -EINVAL;
1918}
1919
1920int main(int argc, char *argv[]) {
24996861 1921 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1f849790
LP
1922 int r;
1923
1924 log_parse_environment();
1925 log_open();
1926
1927 r = parse_argv(argc, argv);
1928 if (r <= 0)
1929 goto finish;
1930
09365592
LP
1931 r = sd_bus_new(&bus);
1932 if (r < 0) {
1933 log_error("Failed to allocate bus: %s", strerror(-r));
1934 goto finish;
1935 }
1936
1f70b087
LP
1937 if (streq_ptr(argv[optind], "monitor") ||
1938 streq_ptr(argv[optind], "capture")) {
09365592
LP
1939
1940 r = sd_bus_set_monitor(bus, true);
1f849790 1941 if (r < 0) {
09365592 1942 log_error("Failed to set monitor mode: %s", strerror(-r));
1f849790
LP
1943 goto finish;
1944 }
d0ce7734
LP
1945
1946 r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL);
1947 if (r < 0) {
1948 log_error("Failed to enable credentials: %s", strerror(-r));
1949 goto finish;
1950 }
1951
1952 r = sd_bus_negotiate_timestamp(bus, true);
1953 if (r < 0) {
1954 log_error("Failed to enable timestamps: %s", strerror(-r));
1955 goto finish;
1956 }
1957
1958 r = sd_bus_negotiate_fds(bus, true);
1959 if (r < 0) {
1960 log_error("Failed to enable fds: %s", strerror(-r));
1961 goto finish;
1962 }
09365592 1963 }
1f849790 1964
09365592 1965 if (arg_address)
1f849790 1966 r = sd_bus_set_address(bus, arg_address);
09365592 1967 else {
b18ec7e2
LP
1968 r = sd_bus_set_bus_client(bus, true);
1969 if (r < 0) {
1970 log_error("Failed to set bus client: %s", strerror(-r));
1971 goto finish;
1972 }
1973
09365592 1974 switch (arg_transport) {
1f849790 1975
09365592
LP
1976 case BUS_TRANSPORT_LOCAL:
1977 if (arg_user)
1978 r = bus_set_address_user(bus);
1979 else
1980 r = bus_set_address_system(bus);
1981 break;
1982
1983 case BUS_TRANSPORT_REMOTE:
1984 r = bus_set_address_system_remote(bus, arg_host);
1985 break;
1986
1987 case BUS_TRANSPORT_CONTAINER:
1988 r = bus_set_address_system_container(bus, arg_host);
1989 break;
1990
1991 default:
1992 assert_not_reached("Hmm, unknown transport type.");
1f849790 1993 }
09365592
LP
1994 }
1995 if (r < 0) {
1996 log_error("Failed to set address: %s", strerror(-r));
1997 goto finish;
1998 }
1f849790 1999
09365592 2000 r = sd_bus_start(bus);
1f849790
LP
2001 if (r < 0) {
2002 log_error("Failed to connect to bus: %s", strerror(-r));
2003 goto finish;
2004 }
2005
2006 r = busctl_main(bus, argc, argv);
2007
2008finish:
2009 pager_close();
d75edbd6 2010
1f849790 2011 strv_free(arg_matches);
de1c301e 2012
de1c301e
LP
2013 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2014}