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