]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
sd-bus: add sd_bus_message_is_empty() for checking whether a message carries any...
[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"
de1c301e 37
1f849790 38static bool arg_no_pager = false;
17d47d8d 39static bool arg_legend = true;
1f849790 40static char *arg_address = NULL;
56e61788
LP
41static bool arg_unique = false;
42static bool arg_acquired = false;
43static bool arg_activatable = false;
44static bool arg_show_machine = false;
1f849790 45static char **arg_matches = NULL;
d75edbd6
LP
46static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
47static char *arg_host = NULL;
48static bool arg_user = false;
1f70b087 49static size_t arg_snaplen = 4096;
d9130355 50static bool arg_list = false;
1f849790
LP
51
52static void pager_open_if_enabled(void) {
53
54 /* Cache result before we open the pager */
55 if (arg_no_pager)
56 return;
57
58 pager_open(false);
59}
60
61static int list_bus_names(sd_bus *bus, char **argv) {
71f2ab46 62 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
63 _cleanup_free_ char **merged = NULL;
64 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
65 char **i;
66 int r;
89ffcd2a 67 size_t max_i = 0;
5e2f14e6
LP
68 unsigned n = 0;
69 void *v;
70 char *k;
71 Iterator iterator;
de1c301e 72
1f849790 73 assert(bus);
de1c301e 74
d9130355
LP
75 if (!arg_unique && !arg_acquired && !arg_activatable)
76 arg_unique = arg_acquired = arg_activatable = true;
77
56e61788 78 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
de1c301e
LP
79 if (r < 0) {
80 log_error("Failed to list names: %s", strerror(-r));
1f849790 81 return r;
de1c301e
LP
82 }
83
1f849790
LP
84 pager_open_if_enabled();
85
d5099efc 86 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
87 if (!names)
88 return log_oom();
89ffcd2a 89
5e2f14e6 90 STRV_FOREACH(i, acquired) {
71f2ab46
LP
91 max_i = MAX(max_i, strlen(*i));
92
5e2f14e6
LP
93 r = hashmap_put(names, *i, INT_TO_PTR(1));
94 if (r < 0) {
95 log_error("Failed to add to hashmap: %s", strerror(-r));
96 return r;
97 }
98 }
99
100 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
101 max_i = MAX(max_i, strlen(*i));
102
5e2f14e6
LP
103 r = hashmap_put(names, *i, INT_TO_PTR(2));
104 if (r < 0 && r != -EEXIST) {
105 log_error("Failed to add to hashmap: %s", strerror(-r));
106 return r;
107 }
108 }
109
110 merged = new(char*, hashmap_size(names) + 1);
111 HASHMAP_FOREACH_KEY(v, k, names, iterator)
112 merged[n++] = k;
113
114 merged[n] = NULL;
115 strv_sort(merged);
116
17d47d8d
TA
117 if (arg_legend) {
118 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
455971c1 119 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
89ffcd2a 120
17d47d8d
TA
121 if (arg_show_machine)
122 puts(" MACHINE");
123 else
124 putchar('\n');
125 }
a4297f08 126
5e2f14e6
LP
127 STRV_FOREACH(i, merged) {
128 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
129 sd_id128_t mid;
71f2ab46 130
5e2f14e6
LP
131 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
132 /* Activatable */
71f2ab46 133
5e2f14e6 134 printf("%-*s", (int) max_i, *i);
56e61788
LP
135 printf(" - - - (activatable) - - ");
136 if (arg_show_machine)
5e2f14e6 137 puts(" -");
56e61788
LP
138 else
139 putchar('\n');
71f2ab46
LP
140 continue;
141
5e2f14e6 142 }
de1c301e 143
56e61788
LP
144 if (!arg_unique && (*i)[0] == ':')
145 continue;
146
147 if (!arg_acquired && (*i)[0] != ':')
1f849790 148 continue;
89ffcd2a
LP
149
150 printf("%-*s", (int) max_i, *i);
de1c301e 151
056f95d0 152 r = sd_bus_get_name_creds(bus, *i,
14008e4e
LP
153 SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
154 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
455971c1 155 SD_BUS_CREDS_DESCRIPTION, &creds);
89ffcd2a 156 if (r >= 0) {
14008e4e 157 const char *unique, *session, *unit, *cn;
5b12334d
LP
158 pid_t pid;
159 uid_t uid;
de1c301e 160
5b12334d
LP
161 r = sd_bus_creds_get_pid(creds, &pid);
162 if (r >= 0) {
163 const char *comm = NULL;
de1c301e 164
5b12334d 165 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 166
5b12334d
LP
167 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
168 } else
a4297f08 169 fputs(" - - ", stdout);
89ffcd2a 170
5b12334d
LP
171 r = sd_bus_creds_get_uid(creds, &uid);
172 if (r >= 0) {
173 _cleanup_free_ char *u = NULL;
89ffcd2a 174
5b12334d
LP
175 u = uid_to_name(uid);
176 if (!u)
177 return log_oom();
89ffcd2a 178
5b12334d
LP
179 if (strlen(u) > 16)
180 u[16] = 0;
181
182 printf(" %-16s", u);
183 } else
a4297f08 184 fputs(" - ", stdout);
89ffcd2a 185
49b832c5
LP
186 r = sd_bus_creds_get_unique_name(creds, &unique);
187 if (r >= 0)
56e61788
LP
188 printf(" %-13s", unique);
189 else
190 fputs(" - ", stdout);
191
192 r = sd_bus_creds_get_unit(creds, &unit);
193 if (r >= 0) {
194 _cleanup_free_ char *e;
195
196 e = ellipsize(unit, 25, 100);
197 if (!e)
198 return log_oom();
199
200 printf(" %-25s", e);
201 } else
202 fputs(" - ", stdout);
203
204 r = sd_bus_creds_get_session(creds, &session);
205 if (r >= 0)
206 printf(" %-10s", session);
a4297f08 207 else
56e61788 208 fputs(" - ", stdout);
7b0b392f 209
455971c1 210 r = sd_bus_creds_get_description(creds, &cn);
14008e4e
LP
211 if (r >= 0)
212 printf(" %-19s", cn);
213 else
214 fputs(" - ", stdout);
215
7b0b392f 216 } else
14008e4e 217 printf(" - - - - - - - ");
a4297f08 218
56e61788 219 if (arg_show_machine) {
056f95d0 220 r = sd_bus_get_name_machine_id(bus, *i, &mid);
a4297f08
LP
221 if (r >= 0) {
222 char m[SD_ID128_STRING_MAX];
223 printf(" %s\n", sd_id128_to_string(mid, m));
224 } else
225 puts(" -");
56e61788
LP
226 } else
227 putchar('\n');
de1c301e
LP
228 }
229
1f849790
LP
230 return 0;
231}
232
d9130355
LP
233static void print_subtree(const char *prefix, const char *path, char **l) {
234 const char *vertical, *space;
235 char **n;
236
237 /* We assume the list is sorted. Let's first skip over the
238 * entry we are looking at. */
239 for (;;) {
240 if (!*l)
241 return;
242
243 if (!streq(*l, path))
244 break;
245
246 l++;
247 }
248
249 vertical = strappenda(prefix, draw_special_char(DRAW_TREE_VERTICAL));
250 space = strappenda(prefix, draw_special_char(DRAW_TREE_SPACE));
251
252 for (;;) {
253 bool has_more = false;
254
255 if (!*l || !path_startswith(*l, path))
256 break;
257
258 n = l + 1;
259 for (;;) {
260 if (!*n || !path_startswith(*n, path))
261 break;
262
263 if (!path_startswith(*n, *l)) {
264 has_more = true;
265 break;
266 }
267
268 n++;
269 }
270
271 printf("%s%s%s\n", prefix, draw_special_char(has_more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), *l);
272
273 print_subtree(has_more ? vertical : space, *l, l);
274 l = n;
275 }
276}
277
278static void print_tree(const char *prefix, char **l) {
279
280 pager_open_if_enabled();
281
282 prefix = strempty(prefix);
283
284 if (arg_list) {
285 char **i;
286
287 STRV_FOREACH(i, l)
288 printf("%s%s\n", prefix, *i);
289 return;
290 }
291
292 if (!strv_isempty(l))
293 printf("%s/\n", prefix);
294
295 print_subtree(prefix, "/", l);
296}
297
298static int parse_xml_annotation(
299 const char **p,
300 void **xml_state) {
301
302
303 enum {
304 STATE_ANNOTATION,
305 STATE_NAME,
306 STATE_VALUE
307 } state = STATE_ANNOTATION;
308
309 for (;;) {
310 _cleanup_free_ char *name = NULL;
311
312 int t;
313
314 t = xml_tokenize(p, &name, xml_state, NULL);
315 if (t < 0) {
316 log_error("XML parse error.");
317 return t;
318 }
319
320 if (t == XML_END) {
321 log_error("Premature end of XML data.");
322 return -EBADMSG;
323 }
324
325 switch (state) {
326
327 case STATE_ANNOTATION:
328
329 if (t == XML_ATTRIBUTE_NAME) {
330
331 if (streq_ptr(name, "name"))
332 state = STATE_NAME;
333
334 else if (streq_ptr(name, "value"))
335 state = STATE_VALUE;
336
337 else {
338 log_error("Unexpected <annotation> attribute %s.", name);
339 return -EBADMSG;
340 }
341
342 } else if (t == XML_TAG_CLOSE_EMPTY ||
343 (t == XML_TAG_CLOSE && streq_ptr(name, "annotation")))
344
345 return 0;
346
347 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
348 log_error("Unexpected token in <annotation>. (1)");
349 return -EINVAL;
350 }
351
352 break;
353
354 case STATE_NAME:
355
356 if (t == XML_ATTRIBUTE_VALUE)
357 state = STATE_ANNOTATION;
358 else {
359 log_error("Unexpected token in <annotation>. (2)");
360 return -EINVAL;
361 }
362
363 break;
364
365 case STATE_VALUE:
366
367 if (t == XML_ATTRIBUTE_VALUE)
368 state = STATE_ANNOTATION;
369 else {
370 log_error("Unexpected token in <annotation>. (3)");
371 return -EINVAL;
372 }
373
374 break;
375
376 default:
377 assert_not_reached("Bad state");
378 }
379 }
380}
381
382static int parse_xml_node(
383 const char *prefix,
384 Set *paths,
385 const char **p,
386 void **xml_state) {
387
388 enum {
389 STATE_NODE,
390 STATE_NODE_NAME,
391 STATE_INTERFACE,
392 STATE_INTERFACE_NAME,
393 STATE_METHOD,
394 STATE_METHOD_NAME,
395 STATE_METHOD_ARG,
396 STATE_METHOD_ARG_NAME,
397 STATE_METHOD_ARG_TYPE,
398 STATE_METHOD_ARG_DIRECTION,
399 STATE_SIGNAL,
400 STATE_SIGNAL_NAME,
401 STATE_SIGNAL_ARG,
402 STATE_SIGNAL_ARG_NAME,
403 STATE_SIGNAL_ARG_TYPE,
404 STATE_PROPERTY,
405 STATE_PROPERTY_NAME,
406 STATE_PROPERTY_TYPE,
407 STATE_PROPERTY_ACCESS,
408 } state = STATE_NODE;
409
410 _cleanup_free_ char *node_path = NULL;
411 const char *np = prefix;
412 int r;
413
414 for (;;) {
415 _cleanup_free_ char *name = NULL;
416 int t;
417
418 t = xml_tokenize(p, &name, xml_state, NULL);
419 if (t < 0) {
420 log_error("XML parse error.");
421 return t;
422 }
423
424 if (t == XML_END) {
425 log_error("Premature end of XML data.");
426 return -EBADMSG;
427 }
428
429 switch (state) {
430
431 case STATE_NODE:
432 if (t == XML_ATTRIBUTE_NAME) {
433
434 if (streq_ptr(name, "name"))
435 state = STATE_NODE_NAME;
436 else {
437 log_error("Unexpected <node> attribute %s.", name);
438 return -EBADMSG;
439 }
440
441 } else if (t == XML_TAG_OPEN) {
442
443 if (streq_ptr(name, "interface"))
444 state = STATE_INTERFACE;
445
446 else if (streq_ptr(name, "node")) {
447
448 r = parse_xml_node(np, paths, p, xml_state);
449 if (r < 0)
450 return r;
451 } else {
452 log_error("Unexpected <node> tag %s.", name);
453 return -EBADMSG;
454 }
455 } else if (t == XML_TAG_CLOSE_EMPTY ||
456 (t == XML_TAG_CLOSE && streq_ptr(name, "node"))) {
457
458 if (paths) {
459 if (!node_path) {
460 node_path = strdup(np);
461 if (!node_path)
462 return log_oom();
463 }
464
465 r = set_put(paths, node_path);
466 if (r < 0)
467 return log_oom();
468 else if (r > 0)
469 node_path = NULL;
470 }
471
472 return 0;
473
474 } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
475 log_error("Unexpected token in <node>. (1)");
476 return -EINVAL;
477 }
478
479 break;
480
481 case STATE_NODE_NAME:
482
483 if (t == XML_ATTRIBUTE_VALUE) {
484
485 free(node_path);
486
487 if (name[0] == '/') {
488 node_path = name;
489 name = NULL;
490 } else {
491
492 if (endswith(prefix, "/"))
493 node_path = strappend(prefix, name);
494 else
495 node_path = strjoin(prefix, "/", name, NULL);
496 if (!node_path)
497 return log_oom();
498 }
499
500 np = node_path;
501 state = STATE_NODE;
502 } else {
503 log_error("Unexpected token in <node>. (2)");
504 return -EINVAL;
505 }
506
507 break;
508
509 case STATE_INTERFACE:
510
511 if (t == XML_ATTRIBUTE_NAME) {
512 if (streq_ptr(name, "name"))
513 state = STATE_INTERFACE_NAME;
514 else {
515 log_error("Unexpected <interface> attribute %s.", name);
516 return -EBADMSG;
517 }
518
519 } else if (t == XML_TAG_OPEN) {
520 if (streq_ptr(name, "method"))
521 state = STATE_METHOD;
522 else if (streq_ptr(name, "signal"))
523 state = STATE_SIGNAL;
524 else if (streq_ptr(name, "property"))
525 state = STATE_PROPERTY;
526 else if (streq_ptr(name, "annotation")) {
527 r = parse_xml_annotation(p, xml_state);
528 if (r < 0)
529 return r;
530 } else {
531 log_error("Unexpected <interface> tag %s.", name);
532 return -EINVAL;
533 }
534 } else if (t == XML_TAG_CLOSE_EMPTY ||
535 (t == XML_TAG_CLOSE && streq_ptr(name, "interface")))
536
537 state = STATE_NODE;
538
539 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
540 log_error("Unexpected token in <interface>. (1)");
541 return -EINVAL;
542 }
543
544 break;
545
546 case STATE_INTERFACE_NAME:
547
548 if (t == XML_ATTRIBUTE_VALUE)
549 state = STATE_INTERFACE;
550 else {
551 log_error("Unexpected token in <interface>. (2)");
552 return -EINVAL;
553 }
554
555 break;
556
557 case STATE_METHOD:
558
559 if (t == XML_ATTRIBUTE_NAME) {
560 if (streq_ptr(name, "name"))
561 state = STATE_METHOD_NAME;
562 else {
563 log_error("Unexpected <method> attribute %s", name);
564 return -EBADMSG;
565 }
566 } else if (t == XML_TAG_OPEN) {
567 if (streq_ptr(name, "arg"))
568 state = STATE_METHOD_ARG;
569 else if (streq_ptr(name, "annotation")) {
570 r = parse_xml_annotation(p, xml_state);
571 if (r < 0)
572 return r;
573 } else {
574 log_error("Unexpected <method> tag %s.", name);
575 return -EINVAL;
576 }
577 } else if (t == XML_TAG_CLOSE_EMPTY ||
578 (t == XML_TAG_CLOSE && streq_ptr(name, "method")))
579
580 state = STATE_INTERFACE;
581
582 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
583 log_error("Unexpected token in <method> (1).");
584 return -EINVAL;
585 }
586
587 break;
588
589 case STATE_METHOD_NAME:
590
591 if (t == XML_ATTRIBUTE_VALUE)
592 state = STATE_METHOD;
593 else {
594 log_error("Unexpected token in <method> (2).");
595 return -EINVAL;
596 }
597
598 break;
599
600 case STATE_METHOD_ARG:
601
602 if (t == XML_ATTRIBUTE_NAME) {
603 if (streq_ptr(name, "name"))
604 state = STATE_METHOD_ARG_NAME;
605 else if (streq_ptr(name, "type"))
606 state = STATE_METHOD_ARG_TYPE;
607 else if (streq_ptr(name, "direction"))
608 state = STATE_METHOD_ARG_DIRECTION;
609 else {
610 log_error("Unexpected method <arg> attribute %s.", name);
611 return -EBADMSG;
612 }
613 } else if (t == XML_TAG_OPEN) {
614 if (streq_ptr(name, "annotation")) {
615 r = parse_xml_annotation(p, xml_state);
616 if (r < 0)
617 return r;
618 } else {
619 log_error("Unexpected method <arg> tag %s.", name);
620 return -EINVAL;
621 }
622 } else if (t == XML_TAG_CLOSE_EMPTY ||
623 (t == XML_TAG_CLOSE && streq_ptr(name, "arg")))
624
625 state = STATE_METHOD;
626
627 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
628 log_error("Unexpected token in method <arg>. (1)");
629 return -EINVAL;
630 }
631
632 break;
633
634 case STATE_METHOD_ARG_NAME:
635
636 if (t == XML_ATTRIBUTE_VALUE)
637 state = STATE_METHOD_ARG;
638 else {
639 log_error("Unexpected token in method <arg>. (2)");
640 return -EINVAL;
641 }
642
643 break;
644
645 case STATE_METHOD_ARG_TYPE:
646
647 if (t == XML_ATTRIBUTE_VALUE)
648 state = STATE_METHOD_ARG;
649 else {
650 log_error("Unexpected token in method <arg>. (3)");
651 return -EINVAL;
652 }
653
654 break;
655
656 case STATE_METHOD_ARG_DIRECTION:
657
658 if (t == XML_ATTRIBUTE_VALUE)
659 state = STATE_METHOD_ARG;
660 else {
661 log_error("Unexpected token in method <arg>. (4)");
662 return -EINVAL;
663 }
664
665 break;
666
667 case STATE_SIGNAL:
668
669 if (t == XML_ATTRIBUTE_NAME) {
670 if (streq_ptr(name, "name"))
671 state = STATE_SIGNAL_NAME;
672 else {
673 log_error("Unexpected <signal> attribute %s.", name);
674 return -EBADMSG;
675 }
676 } else if (t == XML_TAG_OPEN) {
677 if (streq_ptr(name, "arg"))
678 state = STATE_SIGNAL_ARG;
679 else if (streq_ptr(name, "annotation")) {
680 r = parse_xml_annotation(p, xml_state);
681 if (r < 0)
682 return r;
683 } else {
684 log_error("Unexpected <signal> tag %s.", name);
685 return -EINVAL;
686 }
687 } else if (t == XML_TAG_CLOSE_EMPTY ||
688 (t == XML_TAG_CLOSE && streq_ptr(name, "signal")))
689
690 state = STATE_INTERFACE;
691
692 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
693 log_error("Unexpected token in <signal>. (1)");
694 return -EINVAL;
695 }
696
697 break;
698
699 case STATE_SIGNAL_NAME:
700
701 if (t == XML_ATTRIBUTE_VALUE)
702 state = STATE_SIGNAL;
703 else {
704 log_error("Unexpected token in <signal>. (2)");
705 return -EINVAL;
706 }
707
708 break;
709
710
711 case STATE_SIGNAL_ARG:
712
713 if (t == XML_ATTRIBUTE_NAME) {
714 if (streq_ptr(name, "name"))
715 state = STATE_SIGNAL_ARG_NAME;
716 else if (streq_ptr(name, "type"))
717 state = STATE_SIGNAL_ARG_TYPE;
718 else {
719 log_error("Unexpected signal <arg> attribute %s.", name);
720 return -EBADMSG;
721 }
722 } else if (t == XML_TAG_OPEN) {
723 if (streq_ptr(name, "annotation")) {
724 r = parse_xml_annotation(p, xml_state);
725 if (r < 0)
726 return r;
727 } else {
728 log_error("Unexpected signal <arg> tag %s.", name);
729 return -EINVAL;
730 }
731 } else if (t == XML_TAG_CLOSE_EMPTY ||
732 (t == XML_TAG_CLOSE && streq_ptr(name, "arg")))
733
734 state = STATE_SIGNAL;
735
736 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
737 log_error("Unexpected token in signal <arg> (1).");
738 return -EINVAL;
739 }
740
741 break;
742
743 case STATE_SIGNAL_ARG_NAME:
744
745 if (t == XML_ATTRIBUTE_VALUE)
746 state = STATE_SIGNAL_ARG;
747 else {
748 log_error("Unexpected token in signal <arg> (2).");
749 return -EINVAL;
750 }
751
752 break;
753
754 case STATE_SIGNAL_ARG_TYPE:
755
756 if (t == XML_ATTRIBUTE_VALUE)
757 state = STATE_SIGNAL_ARG;
758 else {
759 log_error("Unexpected token in signal <arg> (3).");
760 return -EINVAL;
761 }
762
763 break;
764
765 case STATE_PROPERTY:
766
767 if (t == XML_ATTRIBUTE_NAME) {
768 if (streq_ptr(name, "name"))
769 state = STATE_PROPERTY_NAME;
770 else if (streq_ptr(name, "type"))
771 state = STATE_PROPERTY_TYPE;
772 else if (streq_ptr(name, "access"))
773 state = STATE_PROPERTY_ACCESS;
774 else {
775 log_error("Unexpected <property> attribute %s.", name);
776 return -EBADMSG;
777 }
778 } else if (t == XML_TAG_OPEN) {
779
780 if (streq_ptr(name, "annotation")) {
781 r = parse_xml_annotation(p, xml_state);
782 if (r < 0)
783 return r;
784 } else {
785 log_error("Unexpected <property> tag %s.", name);
786 return -EINVAL;
787 }
788
789 } else if (t == XML_TAG_CLOSE_EMPTY ||
790 (t == XML_TAG_CLOSE && streq_ptr(name, "property")))
791
792 state = STATE_INTERFACE;
793
794 else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) {
795 log_error("Unexpected token in <property>. (1)");
796 return -EINVAL;
797 }
798
799 break;
800
801 case STATE_PROPERTY_NAME:
802
803 if (t == XML_ATTRIBUTE_VALUE)
804 state = STATE_PROPERTY;
805 else {
806 log_error("Unexpected token in <property>. (2)");
807 return -EINVAL;
808 }
809
810 break;
811
812 case STATE_PROPERTY_TYPE:
813
814 if (t == XML_ATTRIBUTE_VALUE)
815 state = STATE_PROPERTY;
816 else {
817 log_error("Unexpected token in <property>. (3)");
818 return -EINVAL;
819 }
820
821 break;
822
823 case STATE_PROPERTY_ACCESS:
824
825 if (t == XML_ATTRIBUTE_VALUE)
826 state = STATE_PROPERTY;
827 else {
828 log_error("Unexpected token in <property>. (4)");
829 return -EINVAL;
830 }
831
832 break;
833 }
834 }
835}
836
837static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths) {
838 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
839 _cleanup_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
840 const char *xml, *p;
841 void *xml_state = NULL;
842 int r;
843
844 r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
845 if (r < 0) {
846 log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r));
847 return r;
848 }
849
850 r = sd_bus_message_read(reply, "s", &xml);
851 if (r < 0)
852 return bus_log_parse_error(r);
853
854 /* fputs(xml, stdout); */
855
856 p = xml;
857 for (;;) {
858 _cleanup_free_ char *name = NULL;
859
860 r = xml_tokenize(&p, &name, &xml_state, NULL);
861 if (r < 0) {
862 log_error("XML parse error");
863 return r;
864 }
865
866 if (r == XML_END)
867 break;
868
869 if (r == XML_TAG_OPEN) {
870
871 if (streq(name, "node")) {
872 r = parse_xml_node(path, paths, &p, &xml_state);
873 if (r < 0)
874 return r;
875 } else {
876 log_error("Unexpected tag '%s' in introspection data.", name);
877 return -EBADMSG;
878 }
879 } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) {
880 log_error("Unexpected token.");
881 return -EINVAL;
882 }
883 }
884
885 return 0;
886}
887
888static int tree_one(sd_bus *bus, const char *service, const char *prefix) {
889 _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
890 _cleanup_free_ char **l = NULL;
891 char *m;
892 int r;
893
894 paths = set_new(&string_hash_ops);
895 if (!paths)
896 return log_oom();
897
898 done = set_new(&string_hash_ops);
899 if (!done)
900 return log_oom();
901
902 failed = set_new(&string_hash_ops);
903 if (!failed)
904 return log_oom();
905
906 m = strdup("/");
907 if (!m)
908 return log_oom();
909
910 r = set_put(paths, m);
911 if (r < 0) {
912 free(m);
913 return log_oom();
914 }
915
916 for (;;) {
917 _cleanup_free_ char *p = NULL;
918 int q;
919
920 p = set_steal_first(paths);
921 if (!p)
922 break;
923
924 if (set_contains(done, p) ||
925 set_contains(failed, p))
926 continue;
927
928 q = find_nodes(bus, service, p, paths);
929 if (q < 0) {
930 if (r >= 0)
931 r = q;
932
933 q = set_put(failed, p);
934 } else
935 q = set_put(done, p);
936
937 if (q < 0)
938 return log_oom();
939
940 assert(q != 0);
941 p = NULL;
942 }
943
944 l = set_get_strv(done);
945 if (!l)
946 return log_oom();
947
948 strv_sort(l);
949 print_tree(prefix, l);
950
951 fflush(stdout);
952
953 return r;
954}
955
956static int tree(sd_bus *bus, char **argv) {
957 char **i;
958 int r = 0;
959
960 if (!arg_unique && !arg_acquired)
961 arg_acquired = true;
962
963 if (strv_length(argv) <= 1) {
964 _cleanup_strv_free_ char **names = NULL;
965 bool not_first = true;
966
967 r = sd_bus_list_names(bus, &names, NULL);
968 if (r < 0) {
969 log_error("Failed to get name list: %s", strerror(-r));
970 return r;
971 }
972
973 pager_open_if_enabled();
974
975 STRV_FOREACH(i, names) {
976 int q;
977
978 if (!arg_unique && (*i)[0] == ':')
979 continue;
980
981 if (!arg_acquired && (*i)[0] == ':')
982 continue;
983
984 if (not_first)
985 printf("\n");
986
987 printf("Service %s:\n", *i);
988
989 q = tree_one(bus, *i, "\t");
990 if (q < 0 && r >= 0)
991 r = q;
992
993 not_first = true;
994 }
995 } else {
996 pager_open_if_enabled();
997
998 STRV_FOREACH(i, argv+1) {
999 int q;
1000
1001 if (i > argv+1)
1002 printf("\n");
1003
1004 if (argv[2])
1005 printf("Service %s:\n", *i);
1006
1007 q = tree_one(bus, *i, NULL);
1008 if (q < 0 && r >= 0)
1009 r = q;
1010 }
1011 }
1012
1013 return r;
1014}
1015
1f70b087
LP
1016static int message_dump(sd_bus_message *m, FILE *f) {
1017 return bus_message_dump(m, f, true);
1018}
1019
1020static int message_pcap(sd_bus_message *m, FILE *f) {
1021 return bus_message_pcap_frame(m, arg_snaplen, f);
1022}
1023
1024static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
b51f299a 1025 bool added_something = false;
1f849790
LP
1026 char **i;
1027 int r;
1028
1029 STRV_FOREACH(i, argv+1) {
1030 _cleanup_free_ char *m = NULL;
1031
1032 if (!service_name_is_valid(*i)) {
1033 log_error("Invalid service name '%s'", *i);
1034 return -EINVAL;
1035 }
1036
1037 m = strjoin("sender='", *i, "'", NULL);
1038 if (!m)
1039 return log_oom();
1040
19befb2d 1041 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
1f849790
LP
1042 if (r < 0) {
1043 log_error("Failed to add match: %s", strerror(-r));
1044 return r;
1045 }
b51f299a
LP
1046
1047 added_something = true;
1f849790
LP
1048 }
1049
1050 STRV_FOREACH(i, arg_matches) {
19befb2d 1051 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
1f849790
LP
1052 if (r < 0) {
1053 log_error("Failed to add match: %s", strerror(-r));
1054 return r;
1055 }
b51f299a
LP
1056
1057 added_something = true;
1058 }
1059
1060 if (!added_something) {
19befb2d 1061 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
b51f299a
LP
1062 if (r < 0) {
1063 log_error("Failed to add match: %s", strerror(-r));
1064 return r;
1065 }
1f849790
LP
1066 }
1067
1068 for (;;) {
1069 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1070
1071 r = sd_bus_process(bus, &m);
1072 if (r < 0) {
1073 log_error("Failed to process bus: %s", strerror(-r));
1074 return r;
1075 }
1076
1077 if (m) {
1f70b087 1078 dump(m, stdout);
1f849790
LP
1079 continue;
1080 }
1081
1082 if (r > 0)
1083 continue;
1084
1085 r = sd_bus_wait(bus, (uint64_t) -1);
1086 if (r < 0) {
1087 log_error("Failed to wait for bus: %s", strerror(-r));
1088 return r;
1089 }
1090 }
95c4fe82 1091}
1f849790 1092
1f70b087
LP
1093static int capture(sd_bus *bus, char *argv[]) {
1094 int r;
1095
1096 if (isatty(fileno(stdout)) > 0) {
1097 log_error("Refusing to write message data to console, please redirect output to a file.");
1098 return -EINVAL;
1099 }
1100
1101 bus_pcap_header(arg_snaplen, stdout);
1102
1103 r = monitor(bus, argv, message_pcap);
1104 if (r < 0)
1105 return r;
1106
1107 if (ferror(stdout)) {
1108 log_error("Couldn't write capture file.");
1109 return -EIO;
1110 }
1111
1112 return r;
1113}
1114
95c4fe82
LP
1115static int status(sd_bus *bus, char *argv[]) {
1116 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
1117 pid_t pid;
1118 int r;
1119
1120 assert(bus);
1121
1122 if (strv_length(argv) != 2) {
1123 log_error("Expects one argument.");
1124 return -EINVAL;
1125 }
1126
1127 r = parse_pid(argv[1], &pid);
1128 if (r < 0)
056f95d0 1129 r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
95c4fe82 1130 else
151b9b96 1131 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
95c4fe82
LP
1132
1133 if (r < 0) {
1134 log_error("Failed to get credentials: %s", strerror(-r));
1135 return r;
1136 }
1137
1138 bus_creds_dump(creds, NULL);
1139 return 0;
1f849790
LP
1140}
1141
1142static int help(void) {
1f849790
LP
1143 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1144 "Introspect the bus.\n\n"
d75edbd6
LP
1145 " -h --help Show this help\n"
1146 " --version Show package version\n"
a86a47ce 1147 " --no-pager Do not pipe output into a pager\n"
17d47d8d 1148 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
1149 " --system Connect to system bus\n"
1150 " --user Connect to user bus\n"
1151 " -H --host=[USER@]HOST Operate on remote host\n"
1152 " -M --machine=CONTAINER Operate on local container\n"
1153 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
1154 " --show-machine Show machine ID column in list\n"
1155 " --unique Only show unique names\n"
1156 " --acquired Only show acquired names\n"
1157 " --activatable Only show activatable names\n"
d9130355
LP
1158 " --match=MATCH Only show matching messages\n"
1159 " --list Don't show tree, but simple object path list\n\n"
1f849790 1160 "Commands:\n"
d75edbd6 1161 " list List bus names\n"
d9130355 1162 " tree [SERVICE...] Show object tree of service\n"
d94fe1f1 1163 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1164 " capture [SERVICE...] Capture bus traffic as pcap\n"
56e61788 1165 " status NAME Show name status\n"
601185b4
ZJS
1166 " help Show this help\n"
1167 , program_invocation_short_name);
1f849790
LP
1168
1169 return 0;
1170}
1171
1172static int parse_argv(int argc, char *argv[]) {
1173
1174 enum {
1175 ARG_VERSION = 0x100,
1176 ARG_NO_PAGER,
17d47d8d 1177 ARG_NO_LEGEND,
1f849790
LP
1178 ARG_SYSTEM,
1179 ARG_USER,
1180 ARG_ADDRESS,
1181 ARG_MATCH,
56e61788
LP
1182 ARG_SHOW_MACHINE,
1183 ARG_UNIQUE,
1184 ARG_ACQUIRED,
1f70b087
LP
1185 ARG_ACTIVATABLE,
1186 ARG_SIZE,
d9130355 1187 ARG_LIST,
1f849790
LP
1188 };
1189
1190 static const struct option options[] = {
56e61788
LP
1191 { "help", no_argument, NULL, 'h' },
1192 { "version", no_argument, NULL, ARG_VERSION },
1193 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1194 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1195 { "system", no_argument, NULL, ARG_SYSTEM },
1196 { "user", no_argument, NULL, ARG_USER },
1197 { "address", required_argument, NULL, ARG_ADDRESS },
1198 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1199 { "unique", no_argument, NULL, ARG_UNIQUE },
1200 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1201 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1202 { "match", required_argument, NULL, ARG_MATCH },
1203 { "host", required_argument, NULL, 'H' },
1204 { "machine", required_argument, NULL, 'M' },
1f70b087 1205 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1206 { "list", no_argument, NULL, ARG_LIST },
eb9da376 1207 {},
1f849790
LP
1208 };
1209
1f70b087 1210 int c, r;
1f849790
LP
1211
1212 assert(argc >= 0);
1213 assert(argv);
1214
601185b4 1215 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1f849790
LP
1216
1217 switch (c) {
1218
1219 case 'h':
1220 return help();
1221
1222 case ARG_VERSION:
1223 puts(PACKAGE_STRING);
1224 puts(SYSTEMD_FEATURES);
1225 return 0;
1226
1227 case ARG_NO_PAGER:
1228 arg_no_pager = true;
1229 break;
1230
17d47d8d
TA
1231 case ARG_NO_LEGEND:
1232 arg_legend = false;
1233 break;
1234
1f849790
LP
1235 case ARG_USER:
1236 arg_user = true;
1237 break;
1238
1239 case ARG_SYSTEM:
1240 arg_user = false;
1241 break;
1242
1243 case ARG_ADDRESS:
1244 arg_address = optarg;
1245 break;
1246
56e61788
LP
1247 case ARG_SHOW_MACHINE:
1248 arg_show_machine = true;
1249 break;
1250
1251 case ARG_UNIQUE:
1252 arg_unique = true;
1f849790
LP
1253 break;
1254
56e61788
LP
1255 case ARG_ACQUIRED:
1256 arg_acquired = true;
1257 break;
1258
1259 case ARG_ACTIVATABLE:
1260 arg_activatable = true;
a4297f08
LP
1261 break;
1262
1f849790
LP
1263 case ARG_MATCH:
1264 if (strv_extend(&arg_matches, optarg) < 0)
1265 return log_oom();
1266 break;
1267
1f70b087
LP
1268 case ARG_SIZE: {
1269 off_t o;
1270
1271 r = parse_size(optarg, 0, &o);
1272 if (r < 0) {
1273 log_error("Failed to parse size: %s", optarg);
1274 return r;
1275 }
1276
1277 if ((off_t) (size_t) o != o) {
1278 log_error("Size out of range.");
1279 return -E2BIG;
1280 }
1281
1282 arg_snaplen = (size_t) o;
1283 break;
1284 }
1285
d9130355
LP
1286 case ARG_LIST:
1287 arg_list = true;
1288 break;
1289
d75edbd6
LP
1290 case 'H':
1291 arg_transport = BUS_TRANSPORT_REMOTE;
1292 arg_host = optarg;
1293 break;
1294
1295 case 'M':
1296 arg_transport = BUS_TRANSPORT_CONTAINER;
1297 arg_host = optarg;
1298 break;
1299
1f849790
LP
1300 case '?':
1301 return -EINVAL;
1302
1303 default:
eb9da376 1304 assert_not_reached("Unhandled option");
1f849790 1305 }
1f849790
LP
1306
1307 return 1;
1308}
1309
1310static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
1311 assert(bus);
1312
1313 if (optind >= argc ||
1314 streq(argv[optind], "list"))
1315 return list_bus_names(bus, argv + optind);
1316
1317 if (streq(argv[optind], "monitor"))
1f70b087
LP
1318 return monitor(bus, argv + optind, message_dump);
1319
1320 if (streq(argv[optind], "capture"))
1321 return capture(bus, argv + optind);
1f849790 1322
95c4fe82
LP
1323 if (streq(argv[optind], "status"))
1324 return status(bus, argv + optind);
d9130355
LP
1325
1326 if (streq(argv[optind], "tree"))
1327 return tree(bus, argv + optind);
95c4fe82 1328
1f849790
LP
1329 if (streq(argv[optind], "help"))
1330 return help();
1331
1332 log_error("Unknown command '%s'", argv[optind]);
1333 return -EINVAL;
1334}
1335
1336int main(int argc, char *argv[]) {
24996861 1337 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1f849790
LP
1338 int r;
1339
1340 log_parse_environment();
1341 log_open();
1342
1343 r = parse_argv(argc, argv);
1344 if (r <= 0)
1345 goto finish;
1346
09365592
LP
1347 r = sd_bus_new(&bus);
1348 if (r < 0) {
1349 log_error("Failed to allocate bus: %s", strerror(-r));
1350 goto finish;
1351 }
1352
1f70b087
LP
1353 if (streq_ptr(argv[optind], "monitor") ||
1354 streq_ptr(argv[optind], "capture")) {
09365592
LP
1355
1356 r = sd_bus_set_monitor(bus, true);
1f849790 1357 if (r < 0) {
09365592 1358 log_error("Failed to set monitor mode: %s", strerror(-r));
1f849790
LP
1359 goto finish;
1360 }
d0ce7734
LP
1361
1362 r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL);
1363 if (r < 0) {
1364 log_error("Failed to enable credentials: %s", strerror(-r));
1365 goto finish;
1366 }
1367
1368 r = sd_bus_negotiate_timestamp(bus, true);
1369 if (r < 0) {
1370 log_error("Failed to enable timestamps: %s", strerror(-r));
1371 goto finish;
1372 }
1373
1374 r = sd_bus_negotiate_fds(bus, true);
1375 if (r < 0) {
1376 log_error("Failed to enable fds: %s", strerror(-r));
1377 goto finish;
1378 }
09365592 1379 }
1f849790 1380
09365592 1381 if (arg_address)
1f849790 1382 r = sd_bus_set_address(bus, arg_address);
09365592
LP
1383 else {
1384 switch (arg_transport) {
1f849790 1385
09365592
LP
1386 case BUS_TRANSPORT_LOCAL:
1387 if (arg_user)
1388 r = bus_set_address_user(bus);
1389 else
1390 r = bus_set_address_system(bus);
1391 break;
1392
1393 case BUS_TRANSPORT_REMOTE:
1394 r = bus_set_address_system_remote(bus, arg_host);
1395 break;
1396
1397 case BUS_TRANSPORT_CONTAINER:
1398 r = bus_set_address_system_container(bus, arg_host);
1399 break;
1400
1401 default:
1402 assert_not_reached("Hmm, unknown transport type.");
1f849790 1403 }
09365592
LP
1404 }
1405 if (r < 0) {
1406 log_error("Failed to set address: %s", strerror(-r));
1407 goto finish;
1408 }
1f849790 1409
09365592
LP
1410 r = sd_bus_set_bus_client(bus, true);
1411 if (r < 0) {
1412 log_error("Failed to set bus client: %s", strerror(-r));
1413 goto finish;
1414 }
1f849790 1415
09365592 1416 r = sd_bus_start(bus);
1f849790
LP
1417 if (r < 0) {
1418 log_error("Failed to connect to bus: %s", strerror(-r));
1419 goto finish;
1420 }
1421
1422 r = busctl_main(bus, argc, argv);
1423
1424finish:
1425 pager_close();
d75edbd6 1426
1f849790 1427 strv_free(arg_matches);
de1c301e 1428
de1c301e
LP
1429 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1430}