chiark / gitweb /
b241466e25c528ccb613b293fac0fb0302096b91
[elogind.git] / src / libsystemd / sd-bus / busctl.c
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
22 #include <getopt.h>
23
24 #include "strv.h"
25 #include "util.h"
26 #include "log.h"
27 #include "build.h"
28 #include "pager.h"
29 #include "xml.h"
30 #include "path-util.h"
31
32 #include "sd-bus.h"
33 #include "bus-message.h"
34 #include "bus-internal.h"
35 #include "bus-util.h"
36 #include "bus-dump.h"
37
38 static bool arg_no_pager = false;
39 static bool arg_legend = true;
40 static char *arg_address = NULL;
41 static bool arg_unique = false;
42 static bool arg_acquired = false;
43 static bool arg_activatable = false;
44 static bool arg_show_machine = false;
45 static char **arg_matches = NULL;
46 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
47 static char *arg_host = NULL;
48 static bool arg_user = false;
49 static size_t arg_snaplen = 4096;
50 static bool arg_list = false;
51
52 static 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
61 static int list_bus_names(sd_bus *bus, char **argv) {
62         _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
63         _cleanup_free_ char **merged = NULL;
64         _cleanup_hashmap_free_ Hashmap *names = NULL;
65         char **i;
66         int r;
67         size_t max_i = 0;
68         unsigned n = 0;
69         void *v;
70         char *k;
71         Iterator iterator;
72
73         assert(bus);
74
75         if (!arg_unique && !arg_acquired && !arg_activatable)
76                 arg_unique = arg_acquired = arg_activatable = true;
77
78         r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
79         if (r < 0) {
80                 log_error("Failed to list names: %s", strerror(-r));
81                 return r;
82         }
83
84         pager_open_if_enabled();
85
86         names = hashmap_new(&string_hash_ops);
87         if (!names)
88                 return log_oom();
89
90         STRV_FOREACH(i, acquired) {
91                 max_i = MAX(max_i, strlen(*i));
92
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) {
101                 max_i = MAX(max_i, strlen(*i));
102
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
117         if (arg_legend) {
118                 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
119                        (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
120
121                 if (arg_show_machine)
122                         puts(" MACHINE");
123                 else
124                         putchar('\n');
125         }
126
127         STRV_FOREACH(i, merged) {
128                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
129                 sd_id128_t mid;
130
131                 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
132                         /* Activatable */
133
134                         printf("%-*s", (int) max_i, *i);
135                         printf("          - -               -                (activatable) -                         -         ");
136                         if (arg_show_machine)
137                                 puts(" -");
138                         else
139                                 putchar('\n');
140                         continue;
141
142                 }
143
144                 if (!arg_unique && (*i)[0] == ':')
145                         continue;
146
147                 if (!arg_acquired && (*i)[0] != ':')
148                         continue;
149
150                 printf("%-*s", (int) max_i, *i);
151
152                 r = sd_bus_get_name_creds(bus, *i,
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|
155                                      SD_BUS_CREDS_DESCRIPTION, &creds);
156                 if (r >= 0) {
157                         const char *unique, *session, *unit, *cn;
158                         pid_t pid;
159                         uid_t uid;
160
161                         r = sd_bus_creds_get_pid(creds, &pid);
162                         if (r >= 0) {
163                                 const char *comm = NULL;
164
165                                 sd_bus_creds_get_comm(creds, &comm);
166
167                                 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
168                         } else
169                                 fputs("          - -              ", stdout);
170
171                         r = sd_bus_creds_get_uid(creds, &uid);
172                         if (r >= 0) {
173                                 _cleanup_free_ char *u = NULL;
174
175                                 u = uid_to_name(uid);
176                                 if (!u)
177                                         return log_oom();
178
179                                 if (strlen(u) > 16)
180                                         u[16] = 0;
181
182                                 printf(" %-16s", u);
183                         } else
184                                 fputs(" -               ", stdout);
185
186                         r = sd_bus_creds_get_unique_name(creds, &unique);
187                         if (r >= 0)
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);
207                         else
208                                 fputs(" -         ", stdout);
209
210                         r = sd_bus_creds_get_description(creds, &cn);
211                         if (r >= 0)
212                                 printf(" %-19s", cn);
213                         else
214                                 fputs(" -                  ", stdout);
215
216                 } else
217                         printf("          - -               -                -             -                         -          -                  ");
218
219                 if (arg_show_machine) {
220                         r = sd_bus_get_name_machine_id(bus, *i, &mid);
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(" -");
226                 } else
227                         putchar('\n');
228         }
229
230         return 0;
231 }
232
233 static 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
278 static 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
298 static 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
382 static 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
837 static 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
888 static 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
956 static 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
1016 static int message_dump(sd_bus_message *m, FILE *f) {
1017         return bus_message_dump(m, f, true);
1018 }
1019
1020 static int message_pcap(sd_bus_message *m, FILE *f) {
1021         return bus_message_pcap_frame(m, arg_snaplen, f);
1022 }
1023
1024 static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
1025         bool added_something = false;
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
1041                 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
1042                 if (r < 0) {
1043                         log_error("Failed to add match: %s", strerror(-r));
1044                         return r;
1045                 }
1046
1047                 added_something = true;
1048         }
1049
1050         STRV_FOREACH(i, arg_matches) {
1051                 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
1052                 if (r < 0) {
1053                         log_error("Failed to add match: %s", strerror(-r));
1054                         return r;
1055                 }
1056
1057                 added_something = true;
1058         }
1059
1060         if (!added_something) {
1061                 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
1062                 if (r < 0) {
1063                         log_error("Failed to add match: %s", strerror(-r));
1064                         return r;
1065                 }
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) {
1078                         dump(m, stdout);
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         }
1091 }
1092
1093 static 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
1115 static 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)
1129                 r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
1130         else
1131                 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
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;
1140 }
1141
1142 static int help(void) {
1143         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1144                "Introspect the bus.\n\n"
1145                "  -h --help               Show this help\n"
1146                "     --version            Show package version\n"
1147                "     --no-pager           Do not pipe output into a pager\n"
1148                "     --no-legend          Do not show the headers and footers\n"
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"
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"
1158                "     --match=MATCH        Only show matching messages\n"
1159                "     --list               Don't show tree, but simple object path list\n\n"
1160                "Commands:\n"
1161                "  list                    List bus names\n"
1162                "  tree [SERVICE...]       Show object tree of service\n"
1163                "  monitor [SERVICE...]    Show bus traffic\n"
1164                "  capture [SERVICE...]    Capture bus traffic as pcap\n"
1165                "  status NAME             Show name status\n"
1166                "  help                    Show this help\n"
1167                , program_invocation_short_name);
1168
1169         return 0;
1170 }
1171
1172 static int parse_argv(int argc, char *argv[]) {
1173
1174         enum {
1175                 ARG_VERSION = 0x100,
1176                 ARG_NO_PAGER,
1177                 ARG_NO_LEGEND,
1178                 ARG_SYSTEM,
1179                 ARG_USER,
1180                 ARG_ADDRESS,
1181                 ARG_MATCH,
1182                 ARG_SHOW_MACHINE,
1183                 ARG_UNIQUE,
1184                 ARG_ACQUIRED,
1185                 ARG_ACTIVATABLE,
1186                 ARG_SIZE,
1187                 ARG_LIST,
1188         };
1189
1190         static const struct option options[] = {
1191                 { "help",         no_argument,       NULL, 'h'              },
1192                 { "version",      no_argument,       NULL, ARG_VERSION      },
1193                 { "no-pager",     no_argument,       NULL, ARG_NO_PAGER     },
1194                 { "no-legend",    no_argument,       NULL, ARG_NO_LEGEND    },
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'              },
1205                 { "size",         required_argument, NULL, ARG_SIZE         },
1206                 { "list",         no_argument,       NULL, ARG_LIST         },
1207                 {},
1208         };
1209
1210         int c, r;
1211
1212         assert(argc >= 0);
1213         assert(argv);
1214
1215         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
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
1231                 case ARG_NO_LEGEND:
1232                         arg_legend = false;
1233                         break;
1234
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
1247                 case ARG_SHOW_MACHINE:
1248                         arg_show_machine = true;
1249                         break;
1250
1251                 case ARG_UNIQUE:
1252                         arg_unique = true;
1253                         break;
1254
1255                 case ARG_ACQUIRED:
1256                         arg_acquired = true;
1257                         break;
1258
1259                 case ARG_ACTIVATABLE:
1260                         arg_activatable = true;
1261                         break;
1262
1263                 case ARG_MATCH:
1264                         if (strv_extend(&arg_matches, optarg) < 0)
1265                                 return log_oom();
1266                         break;
1267
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
1286                 case ARG_LIST:
1287                         arg_list = true;
1288                         break;
1289
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
1300                 case '?':
1301                         return -EINVAL;
1302
1303                 default:
1304                         assert_not_reached("Unhandled option");
1305                 }
1306
1307         return 1;
1308 }
1309
1310 static 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"))
1318                 return monitor(bus, argv + optind, message_dump);
1319
1320         if (streq(argv[optind], "capture"))
1321                 return capture(bus, argv + optind);
1322
1323         if (streq(argv[optind], "status"))
1324                 return status(bus, argv + optind);
1325
1326         if (streq(argv[optind], "tree"))
1327                 return tree(bus, argv + optind);
1328
1329         if (streq(argv[optind], "help"))
1330                 return help();
1331
1332         log_error("Unknown command '%s'", argv[optind]);
1333         return -EINVAL;
1334 }
1335
1336 int main(int argc, char *argv[]) {
1337         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
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
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
1353         if (streq_ptr(argv[optind], "monitor") ||
1354             streq_ptr(argv[optind], "capture")) {
1355
1356                 r = sd_bus_set_monitor(bus, true);
1357                 if (r < 0) {
1358                         log_error("Failed to set monitor mode: %s", strerror(-r));
1359                         goto finish;
1360                 }
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                 }
1379         }
1380
1381         if (arg_address)
1382                 r = sd_bus_set_address(bus, arg_address);
1383         else {
1384                 switch (arg_transport) {
1385
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.");
1403                 }
1404         }
1405         if (r < 0) {
1406                 log_error("Failed to set address: %s", strerror(-r));
1407                 goto finish;
1408         }
1409
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         }
1415
1416         r = sd_bus_start(bus);
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
1424 finish:
1425         pager_close();
1426
1427         strv_free(arg_matches);
1428
1429         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1430 }