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