]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/busctl/busctl.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / busctl / busctl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
de1c301e
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
1f849790 21#include <getopt.h>
0d536673 22#include <stdio_ext.h>
1f849790 23
de1c301e 24#include "sd-bus.h"
3f6fd1ba 25
b5efdb8a 26#include "alloc-util.h"
2b5c5383 27#include "bus-dump.h"
3f6fd1ba 28#include "bus-internal.h"
781fa938 29#include "bus-signature.h"
79f34de9 30#include "bus-type.h"
3f6fd1ba 31#include "bus-util.h"
a1ad3767 32#include "busctl-introspect.h"
4f5dd394 33#include "escape.h"
3ffd4af2 34#include "fd-util.h"
8752c575 35#include "locale-util.h"
3f6fd1ba
LP
36#include "log.h"
37#include "pager.h"
6bedfcbb 38#include "parse-util.h"
3f6fd1ba
LP
39#include "path-util.h"
40#include "set.h"
41#include "strv.h"
288a74cc 42#include "terminal-util.h"
b1d4f8e1 43#include "user-util.h"
3f6fd1ba 44#include "util.h"
9bb31a0c 45#include "verbs.h"
de1c301e 46
1f849790 47static bool arg_no_pager = false;
17d47d8d 48static bool arg_legend = true;
1f849790 49static char *arg_address = NULL;
56e61788
LP
50static bool arg_unique = false;
51static bool arg_acquired = false;
52static bool arg_activatable = false;
53static bool arg_show_machine = false;
1f849790 54static char **arg_matches = NULL;
d75edbd6
LP
55static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
56static char *arg_host = NULL;
57static bool arg_user = false;
1f70b087 58static size_t arg_snaplen = 4096;
d9130355 59static bool arg_list = false;
781fa938 60static bool arg_quiet = false;
1fc55609 61static bool arg_verbose = false;
38051578
LP
62static bool arg_expect_reply = true;
63static bool arg_auto_start = true;
64static bool arg_allow_interactive_authorization = true;
40ed1a45 65static bool arg_augment_creds = true;
56d820b6 66static bool arg_watch_bind = false;
a44b1081 67static usec_t arg_timeout = 0;
1f849790 68
1d58a1fe
LP
69#define NAME_IS_ACQUIRED INT_TO_PTR(1)
70#define NAME_IS_ACTIVATABLE INT_TO_PTR(2)
71
9bb31a0c
YW
72static int acquire_bus(bool set_monitor, sd_bus **ret) {
73 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
74 int r;
75
76 r = sd_bus_new(&bus);
77 if (r < 0)
78 return log_error_errno(r, "Failed to allocate bus: %m");
79
80 if (set_monitor) {
81 r = sd_bus_set_monitor(bus, true);
82 if (r < 0)
83 return log_error_errno(r, "Failed to set monitor mode: %m");
84
85 r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL);
86 if (r < 0)
87 return log_error_errno(r, "Failed to enable credentials: %m");
88
89 r = sd_bus_negotiate_timestamp(bus, true);
90 if (r < 0)
91 return log_error_errno(r, "Failed to enable timestamps: %m");
92
93 r = sd_bus_negotiate_fds(bus, true);
94 if (r < 0)
95 return log_error_errno(r, "Failed to enable fds: %m");
96 }
97
98 r = sd_bus_set_bus_client(bus, true);
99 if (r < 0)
100 return log_error_errno(r, "Failed to set bus client: %m");
101
102 r = sd_bus_set_watch_bind(bus, arg_watch_bind);
103 if (r < 0)
104 return log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", yes_no(arg_watch_bind));
105
106 if (arg_address)
107 r = sd_bus_set_address(bus, arg_address);
108 else {
109 switch (arg_transport) {
110
111 case BUS_TRANSPORT_LOCAL:
112 if (arg_user) {
113 bus->is_user = true;
114 r = bus_set_address_user(bus);
115 } else {
116 bus->is_system = true;
117 r = bus_set_address_system(bus);
118 }
119 break;
120
121 case BUS_TRANSPORT_REMOTE:
122 r = bus_set_address_system_remote(bus, arg_host);
123 break;
124
125 case BUS_TRANSPORT_MACHINE:
126 r = bus_set_address_system_machine(bus, arg_host);
127 break;
128
129 default:
130 assert_not_reached("Hmm, unknown transport type.");
131 }
132 }
133 if (r < 0)
134 return log_error_errno(r, "Failed to set address: %m");
135
136 r = sd_bus_start(bus);
137 if (r < 0)
138 return log_error_errno(r, "Failed to connect to bus: %m");
139
1cc6c93a 140 *ret = TAKE_PTR(bus);
9bb31a0c
YW
141
142 return 0;
143}
144
145static int list_bus_names(int argc, char **argv, void *userdata) {
146 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
71f2ab46 147 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
148 _cleanup_free_ char **merged = NULL;
149 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
150 char **i;
151 int r;
89ffcd2a 152 size_t max_i = 0;
5e2f14e6
LP
153 unsigned n = 0;
154 void *v;
155 char *k;
156 Iterator iterator;
de1c301e 157
d9130355
LP
158 if (!arg_unique && !arg_acquired && !arg_activatable)
159 arg_unique = arg_acquired = arg_activatable = true;
160
9bb31a0c
YW
161 r = acquire_bus(false, &bus);
162 if (r < 0)
163 return r;
164
56e61788 165 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
23bbb0de
MS
166 if (r < 0)
167 return log_error_errno(r, "Failed to list names: %m");
de1c301e 168
ee5324aa 169 (void) pager_open(arg_no_pager, false);
1f849790 170
d5099efc 171 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
172 if (!names)
173 return log_oom();
89ffcd2a 174
5e2f14e6 175 STRV_FOREACH(i, acquired) {
71f2ab46
LP
176 max_i = MAX(max_i, strlen(*i));
177
1d58a1fe 178 r = hashmap_put(names, *i, NAME_IS_ACQUIRED);
23bbb0de
MS
179 if (r < 0)
180 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
181 }
182
183 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
184 max_i = MAX(max_i, strlen(*i));
185
1d58a1fe 186 r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE);
23bbb0de
MS
187 if (r < 0 && r != -EEXIST)
188 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
189 }
190
191 merged = new(char*, hashmap_size(names) + 1);
cb05d2a5
YW
192 if (!merged)
193 return log_oom();
194
5e2f14e6
LP
195 HASHMAP_FOREACH_KEY(v, k, names, iterator)
196 merged[n++] = k;
197
198 merged[n] = NULL;
199 strv_sort(merged);
200
17d47d8d
TA
201 if (arg_legend) {
202 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
455971c1 203 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
89ffcd2a 204
17d47d8d
TA
205 if (arg_show_machine)
206 puts(" MACHINE");
207 else
208 putchar('\n');
209 }
a4297f08 210
5e2f14e6 211 STRV_FOREACH(i, merged) {
4afd3348 212 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
5e2f14e6 213 sd_id128_t mid;
71f2ab46 214
1d58a1fe 215 if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) {
5e2f14e6 216 /* Activatable */
71f2ab46 217
5e2f14e6 218 printf("%-*s", (int) max_i, *i);
56e61788
LP
219 printf(" - - - (activatable) - - ");
220 if (arg_show_machine)
5e2f14e6 221 puts(" -");
56e61788
LP
222 else
223 putchar('\n');
71f2ab46
LP
224 continue;
225
5e2f14e6 226 }
de1c301e 227
56e61788
LP
228 if (!arg_unique && (*i)[0] == ':')
229 continue;
230
231 if (!arg_acquired && (*i)[0] != ':')
1f849790 232 continue;
89ffcd2a
LP
233
234 printf("%-*s", (int) max_i, *i);
de1c301e 235
40ed1a45
LP
236 r = sd_bus_get_name_creds(
237 bus, *i,
238 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) |
05bae4a6 239 SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
40ed1a45
LP
240 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
241 SD_BUS_CREDS_DESCRIPTION, &creds);
89ffcd2a 242 if (r >= 0) {
14008e4e 243 const char *unique, *session, *unit, *cn;
5b12334d
LP
244 pid_t pid;
245 uid_t uid;
de1c301e 246
5b12334d
LP
247 r = sd_bus_creds_get_pid(creds, &pid);
248 if (r >= 0) {
249 const char *comm = NULL;
de1c301e 250
5b12334d 251 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 252
5b12334d
LP
253 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
254 } else
a4297f08 255 fputs(" - - ", stdout);
89ffcd2a 256
05bae4a6 257 r = sd_bus_creds_get_euid(creds, &uid);
5b12334d
LP
258 if (r >= 0) {
259 _cleanup_free_ char *u = NULL;
89ffcd2a 260
5b12334d
LP
261 u = uid_to_name(uid);
262 if (!u)
263 return log_oom();
89ffcd2a 264
5b12334d
LP
265 if (strlen(u) > 16)
266 u[16] = 0;
267
268 printf(" %-16s", u);
269 } else
a4297f08 270 fputs(" - ", stdout);
89ffcd2a 271
49b832c5
LP
272 r = sd_bus_creds_get_unique_name(creds, &unique);
273 if (r >= 0)
56e61788
LP
274 printf(" %-13s", unique);
275 else
276 fputs(" - ", stdout);
277
278 r = sd_bus_creds_get_unit(creds, &unit);
279 if (r >= 0) {
280 _cleanup_free_ char *e;
281
282 e = ellipsize(unit, 25, 100);
283 if (!e)
284 return log_oom();
285
286 printf(" %-25s", e);
287 } else
288 fputs(" - ", stdout);
289
290 r = sd_bus_creds_get_session(creds, &session);
291 if (r >= 0)
292 printf(" %-10s", session);
a4297f08 293 else
56e61788 294 fputs(" - ", stdout);
7b0b392f 295
455971c1 296 r = sd_bus_creds_get_description(creds, &cn);
14008e4e
LP
297 if (r >= 0)
298 printf(" %-19s", cn);
299 else
300 fputs(" - ", stdout);
301
7b0b392f 302 } else
14008e4e 303 printf(" - - - - - - - ");
a4297f08 304
56e61788 305 if (arg_show_machine) {
056f95d0 306 r = sd_bus_get_name_machine_id(bus, *i, &mid);
a4297f08
LP
307 if (r >= 0) {
308 char m[SD_ID128_STRING_MAX];
309 printf(" %s\n", sd_id128_to_string(mid, m));
310 } else
311 puts(" -");
56e61788
LP
312 } else
313 putchar('\n');
de1c301e
LP
314 }
315
1f849790
LP
316 return 0;
317}
318
d9130355
LP
319static void print_subtree(const char *prefix, const char *path, char **l) {
320 const char *vertical, *space;
321 char **n;
322
323 /* We assume the list is sorted. Let's first skip over the
324 * entry we are looking at. */
325 for (;;) {
326 if (!*l)
327 return;
328
329 if (!streq(*l, path))
330 break;
331
332 l++;
333 }
334
323b7dc9
ZJS
335 vertical = strjoina(prefix, special_glyph(TREE_VERTICAL));
336 space = strjoina(prefix, special_glyph(TREE_SPACE));
d9130355
LP
337
338 for (;;) {
339 bool has_more = false;
340
341 if (!*l || !path_startswith(*l, path))
342 break;
343
344 n = l + 1;
345 for (;;) {
346 if (!*n || !path_startswith(*n, path))
347 break;
348
349 if (!path_startswith(*n, *l)) {
350 has_more = true;
351 break;
352 }
353
354 n++;
355 }
356
323b7dc9 357 printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l);
d9130355
LP
358
359 print_subtree(has_more ? vertical : space, *l, l);
360 l = n;
361 }
362}
363
364static void print_tree(const char *prefix, char **l) {
365
d9130355
LP
366 prefix = strempty(prefix);
367
368 if (arg_list) {
369 char **i;
370
371 STRV_FOREACH(i, l)
372 printf("%s%s\n", prefix, *i);
373 return;
374 }
375
56c8b52d
LP
376 if (strv_isempty(l)) {
377 printf("No objects discovered.\n");
378 return;
379 }
380
381 if (streq(l[0], "/") && !l[1]) {
382 printf("Only root object discovered.\n");
383 return;
384 }
d9130355
LP
385
386 print_subtree(prefix, "/", l);
387}
388
a1ad3767
LP
389static int on_path(const char *path, void *userdata) {
390 Set *paths = userdata;
d9130355
LP
391 int r;
392
a1ad3767 393 assert(paths);
d9130355 394
a1ad3767
LP
395 r = set_put_strdup(paths, path);
396 if (r < 0)
397 return log_oom();
d9130355 398
a1ad3767 399 return 0;
d9130355
LP
400}
401
73fc23c0 402static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) {
0171da06 403 static const XMLIntrospectOps ops = {
a1ad3767
LP
404 .on_path = on_path,
405 };
406
4afd3348
LP
407 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
408 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a1ad3767 409 const char *xml;
d9130355
LP
410 int r;
411
412 r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
413 if (r < 0) {
73fc23c0
LP
414 if (many)
415 printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r));
416 else
417 log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r));
d9130355
LP
418 return r;
419 }
420
421 r = sd_bus_message_read(reply, "s", &xml);
422 if (r < 0)
423 return bus_log_parse_error(r);
424
a1ad3767 425 return parse_xml_introspect(path, xml, &ops, paths);
d9130355
LP
426}
427
73fc23c0 428static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) {
d9130355
LP
429 _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
430 _cleanup_free_ char **l = NULL;
431 char *m;
432 int r;
433
434 paths = set_new(&string_hash_ops);
435 if (!paths)
436 return log_oom();
437
438 done = set_new(&string_hash_ops);
439 if (!done)
440 return log_oom();
441
442 failed = set_new(&string_hash_ops);
443 if (!failed)
444 return log_oom();
445
446 m = strdup("/");
447 if (!m)
448 return log_oom();
449
450 r = set_put(paths, m);
451 if (r < 0) {
452 free(m);
453 return log_oom();
454 }
455
456 for (;;) {
457 _cleanup_free_ char *p = NULL;
458 int q;
459
460 p = set_steal_first(paths);
461 if (!p)
462 break;
463
464 if (set_contains(done, p) ||
465 set_contains(failed, p))
466 continue;
467
73fc23c0 468 q = find_nodes(bus, service, p, paths, many);
d9130355
LP
469 if (q < 0) {
470 if (r >= 0)
471 r = q;
472
473 q = set_put(failed, p);
474 } else
475 q = set_put(done, p);
476
477 if (q < 0)
478 return log_oom();
479
480 assert(q != 0);
481 p = NULL;
482 }
483
ee5324aa 484 (void) pager_open(arg_no_pager, false);
73fc23c0 485
d9130355
LP
486 l = set_get_strv(done);
487 if (!l)
488 return log_oom();
489
490 strv_sort(l);
491 print_tree(prefix, l);
492
493 fflush(stdout);
494
495 return r;
496}
497
9bb31a0c
YW
498static int tree(int argc, char **argv, void *userdata) {
499 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d9130355
LP
500 char **i;
501 int r = 0;
502
503 if (!arg_unique && !arg_acquired)
504 arg_acquired = true;
505
9bb31a0c
YW
506 r = acquire_bus(false, &bus);
507 if (r < 0)
508 return r;
509
510 if (argc <= 1) {
d9130355 511 _cleanup_strv_free_ char **names = NULL;
56c8b52d 512 bool not_first = false;
d9130355
LP
513
514 r = sd_bus_list_names(bus, &names, NULL);
23bbb0de
MS
515 if (r < 0)
516 return log_error_errno(r, "Failed to get name list: %m");
d9130355 517
ee5324aa 518 (void) pager_open(arg_no_pager, false);
d9130355
LP
519
520 STRV_FOREACH(i, names) {
521 int q;
522
523 if (!arg_unique && (*i)[0] == ':')
524 continue;
525
526 if (!arg_acquired && (*i)[0] == ':')
527 continue;
528
529 if (not_first)
530 printf("\n");
531
1fc464f6 532 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
d9130355 533
73fc23c0 534 q = tree_one(bus, *i, NULL, true);
d9130355
LP
535 if (q < 0 && r >= 0)
536 r = q;
537
538 not_first = true;
539 }
540 } else {
d9130355
LP
541 STRV_FOREACH(i, argv+1) {
542 int q;
543
544 if (i > argv+1)
545 printf("\n");
546
73fc23c0 547 if (argv[2]) {
ee5324aa 548 (void) pager_open(arg_no_pager, false);
1fc464f6 549 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
73fc23c0 550 }
d9130355 551
73fc23c0 552 q = tree_one(bus, *i, NULL, !!argv[2]);
d9130355
LP
553 if (q < 0 && r >= 0)
554 r = q;
555 }
556 }
557
558 return r;
559}
560
1fc55609
LP
561static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) {
562 int r;
563
564 for (;;) {
565 const char *contents = NULL;
566 char type;
567 union {
568 uint8_t u8;
569 uint16_t u16;
570 int16_t s16;
571 uint32_t u32;
572 int32_t s32;
573 uint64_t u64;
574 int64_t s64;
575 double d64;
576 const char *string;
577 int i;
578 } basic;
579
580 r = sd_bus_message_peek_type(m, &type, &contents);
317f2fc9 581 if (r < 0)
1fc55609 582 return r;
317f2fc9
LP
583 if (r == 0)
584 return needs_space;
1fc55609
LP
585
586 if (bus_type_is_container(type) > 0) {
587
588 r = sd_bus_message_enter_container(m, type, contents);
589 if (r < 0)
590 return r;
591
592 if (type == SD_BUS_TYPE_ARRAY) {
593 unsigned n = 0;
594
595 /* count array entries */
596 for (;;) {
597
598 r = sd_bus_message_skip(m, contents);
599 if (r < 0)
600 return r;
601 if (r == 0)
602 break;
603
604 n++;
605 }
606
607 r = sd_bus_message_rewind(m, false);
608 if (r < 0)
609 return r;
610
611 if (needs_space)
612 fputc(' ', f);
613
614 fprintf(f, "%u", n);
317f2fc9
LP
615 needs_space = true;
616
1fc55609
LP
617 } else if (type == SD_BUS_TYPE_VARIANT) {
618
619 if (needs_space)
620 fputc(' ', f);
621
622 fprintf(f, "%s", contents);
317f2fc9 623 needs_space = true;
1fc55609
LP
624 }
625
317f2fc9 626 r = format_cmdline(m, f, needs_space);
1fc55609
LP
627 if (r < 0)
628 return r;
629
317f2fc9
LP
630 needs_space = r > 0;
631
1fc55609
LP
632 r = sd_bus_message_exit_container(m);
633 if (r < 0)
634 return r;
635
636 continue;
637 }
638
639 r = sd_bus_message_read_basic(m, type, &basic);
640 if (r < 0)
641 return r;
642
643 if (needs_space)
644 fputc(' ', f);
645
646 switch (type) {
647 case SD_BUS_TYPE_BYTE:
648 fprintf(f, "%u", basic.u8);
649 break;
650
651 case SD_BUS_TYPE_BOOLEAN:
652 fputs(true_false(basic.i), f);
653 break;
654
655 case SD_BUS_TYPE_INT16:
656 fprintf(f, "%i", basic.s16);
657 break;
658
659 case SD_BUS_TYPE_UINT16:
660 fprintf(f, "%u", basic.u16);
661 break;
662
663 case SD_BUS_TYPE_INT32:
664 fprintf(f, "%i", basic.s32);
665 break;
666
667 case SD_BUS_TYPE_UINT32:
668 fprintf(f, "%u", basic.u32);
669 break;
670
671 case SD_BUS_TYPE_INT64:
672 fprintf(f, "%" PRIi64, basic.s64);
673 break;
674
675 case SD_BUS_TYPE_UINT64:
676 fprintf(f, "%" PRIu64, basic.u64);
677 break;
678
679 case SD_BUS_TYPE_DOUBLE:
680 fprintf(f, "%g", basic.d64);
681 break;
682
683 case SD_BUS_TYPE_STRING:
684 case SD_BUS_TYPE_OBJECT_PATH:
685 case SD_BUS_TYPE_SIGNATURE: {
686 _cleanup_free_ char *b = NULL;
687
688 b = cescape(basic.string);
689 if (!b)
690 return -ENOMEM;
691
692 fprintf(f, "\"%s\"", b);
693 break;
694 }
695
696 case SD_BUS_TYPE_UNIX_FD:
697 fprintf(f, "%i", basic.i);
698 break;
699
700 default:
701 assert_not_reached("Unknown basic type.");
702 }
703
1ed24c61 704 needs_space = true;
1fc55609
LP
705 }
706}
707
0171da06
LP
708typedef struct Member {
709 const char *type;
710 char *interface;
711 char *name;
712 char *signature;
713 char *result;
1fc55609 714 char *value;
0171da06
LP
715 bool writable;
716 uint64_t flags;
717} Member;
718
b826ab58 719static void member_hash_func(const void *p, struct siphash *state) {
0171da06 720 const Member *m = p;
1e2527a6 721 uint64_t arity = 1;
0171da06
LP
722
723 assert(m);
724 assert(m->type);
725
b826ab58 726 string_hash_func(m->type, state);
0171da06 727
1e2527a6
TG
728 arity += !!m->name + !!m->interface;
729
730 uint64_hash_func(&arity, state);
731
0171da06 732 if (m->name)
b826ab58 733 string_hash_func(m->name, state);
0171da06
LP
734
735 if (m->interface)
b826ab58 736 string_hash_func(m->interface, state);
0171da06
LP
737}
738
739static int member_compare_func(const void *a, const void *b) {
740 const Member *x = a, *y = b;
741 int d;
742
743 assert(x);
744 assert(y);
745 assert(x->type);
746 assert(y->type);
747
c030a850
NK
748 d = strcmp_ptr(x->interface, y->interface);
749 if (d != 0)
750 return d;
0171da06
LP
751
752 d = strcmp(x->type, y->type);
753 if (d != 0)
754 return d;
755
c030a850 756 return strcmp_ptr(x->name, y->name);
0171da06
LP
757}
758
759static int member_compare_funcp(const void *a, const void *b) {
760 const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b;
761
762 return member_compare_func(*x, *y);
763}
764
765static void member_free(Member *m) {
766 if (!m)
767 return;
768
769 free(m->interface);
770 free(m->name);
771 free(m->signature);
772 free(m->result);
1fc55609 773 free(m->value);
0171da06
LP
774 free(m);
775}
776
777DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
778
779static void member_set_free(Set *s) {
224b0e7a 780 set_free_with_destructor(s, member_free);
0171da06
LP
781}
782
783DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
784
785static int on_interface(const char *interface, uint64_t flags, void *userdata) {
786 _cleanup_(member_freep) Member *m;
787 Set *members = userdata;
788 int r;
789
790 assert(interface);
791 assert(members);
792
793 m = new0(Member, 1);
794 if (!m)
795 return log_oom();
796
797 m->type = "interface";
798 m->flags = flags;
799
800 r = free_and_strdup(&m->interface, interface);
801 if (r < 0)
802 return log_oom();
803
804 r = set_put(members, m);
805 if (r <= 0) {
806 log_error("Duplicate interface");
807 return -EINVAL;
808 }
809
810 m = NULL;
811 return 0;
812}
813
814static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
815 _cleanup_(member_freep) Member *m;
816 Set *members = userdata;
817 int r;
818
819 assert(interface);
820 assert(name);
821
822 m = new0(Member, 1);
823 if (!m)
824 return log_oom();
825
826 m->type = "method";
827 m->flags = flags;
828
829 r = free_and_strdup(&m->interface, interface);
830 if (r < 0)
831 return log_oom();
832
833 r = free_and_strdup(&m->name, name);
834 if (r < 0)
835 return log_oom();
836
837 r = free_and_strdup(&m->signature, signature);
838 if (r < 0)
839 return log_oom();
840
841 r = free_and_strdup(&m->result, result);
842 if (r < 0)
843 return log_oom();
844
845 r = set_put(members, m);
846 if (r <= 0) {
847 log_error("Duplicate method");
848 return -EINVAL;
849 }
850
851 m = NULL;
852 return 0;
853}
854
855static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
856 _cleanup_(member_freep) Member *m;
857 Set *members = userdata;
858 int r;
859
860 assert(interface);
861 assert(name);
862
863 m = new0(Member, 1);
864 if (!m)
865 return log_oom();
866
867 m->type = "signal";
868 m->flags = flags;
869
870 r = free_and_strdup(&m->interface, interface);
871 if (r < 0)
872 return log_oom();
873
874 r = free_and_strdup(&m->name, name);
875 if (r < 0)
876 return log_oom();
877
878 r = free_and_strdup(&m->signature, signature);
879 if (r < 0)
880 return log_oom();
881
882 r = set_put(members, m);
883 if (r <= 0) {
884 log_error("Duplicate signal");
885 return -EINVAL;
886 }
887
888 m = NULL;
889 return 0;
890}
891
892static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
893 _cleanup_(member_freep) Member *m;
894 Set *members = userdata;
895 int r;
896
897 assert(interface);
898 assert(name);
899
900 m = new0(Member, 1);
901 if (!m)
902 return log_oom();
903
904 m->type = "property";
905 m->flags = flags;
906 m->writable = writable;
907
908 r = free_and_strdup(&m->interface, interface);
909 if (r < 0)
910 return log_oom();
911
912 r = free_and_strdup(&m->name, name);
913 if (r < 0)
914 return log_oom();
915
916 r = free_and_strdup(&m->signature, signature);
917 if (r < 0)
918 return log_oom();
919
920 r = set_put(members, m);
921 if (r <= 0) {
922 log_error("Duplicate property");
923 return -EINVAL;
924 }
925
926 m = NULL;
927 return 0;
928}
929
930static const char *strdash(const char *x) {
931 return isempty(x) ? "-" : x;
932}
933
9bb31a0c 934static int introspect(int argc, char **argv, void *userdata) {
0171da06
LP
935 static const struct hash_ops member_hash_ops = {
936 .hash = member_hash_func,
937 .compare = member_compare_func,
938 };
939
940 static const XMLIntrospectOps ops = {
941 .on_interface = on_interface,
942 .on_method = on_method,
943 .on_signal = on_signal,
944 .on_property = on_property,
945 };
946
9bb31a0c 947 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
9efebb65 948 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_xml = NULL;
4afd3348 949 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
0171da06 950 _cleanup_(member_set_freep) Set *members = NULL;
9bb31a0c
YW
951 unsigned name_width, type_width, signature_width, result_width, j, k = 0;
952 Member *m, **sorted = NULL;
0171da06 953 Iterator i;
0171da06
LP
954 const char *xml;
955 int r;
0171da06 956
9bb31a0c
YW
957 r = acquire_bus(false, &bus);
958 if (r < 0)
959 return r;
4f44c03e 960
0171da06
LP
961 members = set_new(&member_hash_ops);
962 if (!members)
963 return log_oom();
964
9efebb65 965 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply_xml, "");
e51faad3
LP
966 if (r < 0)
967 return log_error_errno(r, "Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
0171da06 968
9efebb65 969 r = sd_bus_message_read(reply_xml, "s", &xml);
0171da06
LP
970 if (r < 0)
971 return bus_log_parse_error(r);
972
1fc55609 973 /* First, get list of all properties */
0171da06
LP
974 r = parse_xml_introspect(argv[2], xml, &ops, members);
975 if (r < 0)
976 return r;
977
1fc55609
LP
978 /* Second, find the current values for them */
979 SET_FOREACH(m, members, i) {
9efebb65 980 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
981
982 if (!streq(m->type, "property"))
983 continue;
984
985 if (m->value)
986 continue;
987
4f44c03e
LP
988 if (argv[3] && !streq(argv[3], m->interface))
989 continue;
990
1fc55609 991 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
e51faad3
LP
992 if (r < 0)
993 return log_error_errno(r, "%s", bus_error_message(&error, r));
1fc55609
LP
994
995 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
996 if (r < 0)
997 return bus_log_parse_error(r);
998
999 for (;;) {
1000 Member *z;
1001 _cleanup_free_ char *buf = NULL;
1002 _cleanup_fclose_ FILE *mf = NULL;
1003 size_t sz = 0;
1004 const char *name;
1005
1006 r = sd_bus_message_enter_container(reply, 'e', "sv");
1007 if (r < 0)
1008 return bus_log_parse_error(r);
1009
1010 if (r == 0)
1011 break;
1012
1013 r = sd_bus_message_read(reply, "s", &name);
1014 if (r < 0)
1015 return bus_log_parse_error(r);
1016
1017 r = sd_bus_message_enter_container(reply, 'v', NULL);
1018 if (r < 0)
1019 return bus_log_parse_error(r);
1020
1021 mf = open_memstream(&buf, &sz);
1022 if (!mf)
1023 return log_oom();
1024
0d536673
LP
1025 (void) __fsetlocking(mf, FSETLOCKING_BYCALLER);
1026
1fc55609
LP
1027 r = format_cmdline(reply, mf, false);
1028 if (r < 0)
1029 return bus_log_parse_error(r);
1030
8d3b9edc 1031 mf = safe_fclose(mf);
1fc55609
LP
1032
1033 z = set_get(members, &((Member) {
1034 .type = "property",
1035 .interface = m->interface,
1036 .name = (char*) name }));
8d3b9edc
LP
1037 if (z)
1038 free_and_replace(z->value, buf);
1fc55609
LP
1039
1040 r = sd_bus_message_exit_container(reply);
1041 if (r < 0)
1042 return bus_log_parse_error(r);
1043
1044 r = sd_bus_message_exit_container(reply);
1045 if (r < 0)
1046 return bus_log_parse_error(r);
1047 }
1048
1049 r = sd_bus_message_exit_container(reply);
1050 if (r < 0)
1051 return bus_log_parse_error(r);
1052 }
1053
ee5324aa 1054 (void) pager_open(arg_no_pager, false);
0171da06 1055
fbd0b64f
LP
1056 name_width = STRLEN("NAME");
1057 type_width = STRLEN("TYPE");
1058 signature_width = STRLEN("SIGNATURE");
1059 result_width = STRLEN("RESULT/VALUE");
0171da06
LP
1060
1061 sorted = newa(Member*, set_size(members));
1062
1063 SET_FOREACH(m, members, i) {
4f44c03e
LP
1064
1065 if (argv[3] && !streq(argv[3], m->interface))
1066 continue;
1067
0171da06
LP
1068 if (m->interface)
1069 name_width = MAX(name_width, strlen(m->interface));
1070 if (m->name)
1071 name_width = MAX(name_width, strlen(m->name) + 1);
1072 if (m->type)
1073 type_width = MAX(type_width, strlen(m->type));
1074 if (m->signature)
1075 signature_width = MAX(signature_width, strlen(m->signature));
1076 if (m->result)
1077 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1078 if (m->value)
1079 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1080
1081 sorted[k++] = m;
1082 }
1083
1fc55609
LP
1084 if (result_width > 40)
1085 result_width = 40;
1086
0171da06
LP
1087 qsort(sorted, k, sizeof(Member*), member_compare_funcp);
1088
1fc55609
LP
1089 if (arg_legend) {
1090 printf("%-*s %-*s %-*s %-*s %s\n",
1091 (int) name_width, "NAME",
1092 (int) type_width, "TYPE",
1093 (int) signature_width, "SIGNATURE",
1094 (int) result_width, "RESULT/VALUE",
1095 "FLAGS");
1096 }
0171da06
LP
1097
1098 for (j = 0; j < k; j++) {
1fc55609
LP
1099 _cleanup_free_ char *ellipsized = NULL;
1100 const char *rv;
0171da06
LP
1101 bool is_interface;
1102
1103 m = sorted[j];
1104
4f44c03e
LP
1105 if (argv[3] && !streq(argv[3], m->interface))
1106 continue;
1107
0171da06
LP
1108 is_interface = streq(m->type, "interface");
1109
4f44c03e
LP
1110 if (argv[3] && is_interface)
1111 continue;
1112
1fc55609
LP
1113 if (m->value) {
1114 ellipsized = ellipsize(m->value, result_width, 100);
1115 if (!ellipsized)
1116 return log_oom();
1117
1118 rv = ellipsized;
1119 } else
1120 rv = strdash(m->result);
1121
0171da06
LP
1122 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1123 is_interface ? ansi_highlight() : "",
1124 is_interface ? "" : ".",
1125 - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1fc464f6 1126 is_interface ? ansi_normal() : "",
0171da06
LP
1127 (int) type_width, strdash(m->type),
1128 (int) signature_width, strdash(m->signature),
1fc55609 1129 (int) result_width, rv,
0171da06
LP
1130 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1131 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1132 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1133 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1134 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1135 m->writable ? " writable" : "");
1136 }
1137
1138 return 0;
1139}
1140
1f70b087 1141static int message_dump(sd_bus_message *m, FILE *f) {
d55192ad 1142 return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1143}
1144
1145static int message_pcap(sd_bus_message *m, FILE *f) {
1146 return bus_message_pcap_frame(m, arg_snaplen, f);
1147}
1148
9bb31a0c
YW
1149static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f)) {
1150 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
17fd7460
LU
1151 _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
1152 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1f849790 1153 char **i;
17fd7460 1154 uint32_t flags = 0;
f5938e8f
TG
1155 const char *unique_name;
1156 bool is_monitor = false;
1f849790
LP
1157 int r;
1158
9bb31a0c
YW
1159 r = acquire_bus(true, &bus);
1160 if (r < 0)
1161 return r;
1162
17fd7460
LU
1163 /* upgrade connection; it's not used for anything else after this call */
1164 r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor");
1165 if (r < 0)
1166 return bus_log_create_error(r);
1167
1168 r = sd_bus_message_open_container(message, 'a', "s");
1169 if (r < 0)
1170 return bus_log_create_error(r);
1171
1f849790
LP
1172 STRV_FOREACH(i, argv+1) {
1173 _cleanup_free_ char *m = NULL;
1174
1175 if (!service_name_is_valid(*i)) {
1176 log_error("Invalid service name '%s'", *i);
1177 return -EINVAL;
1178 }
1179
605405c6 1180 m = strjoin("sender='", *i, "'");
1f849790
LP
1181 if (!m)
1182 return log_oom();
1183
17fd7460 1184 r = sd_bus_message_append_basic(message, 's', m);
23bbb0de 1185 if (r < 0)
17fd7460 1186 return bus_log_create_error(r);
b51f299a 1187
f6d1e6cb 1188 free(m);
605405c6 1189 m = strjoin("destination='", *i, "'");
f6d1e6cb
LU
1190 if (!m)
1191 return log_oom();
1192
17fd7460 1193 r = sd_bus_message_append_basic(message, 's', m);
f6d1e6cb 1194 if (r < 0)
17fd7460 1195 return bus_log_create_error(r);
1f849790
LP
1196 }
1197
1198 STRV_FOREACH(i, arg_matches) {
17fd7460 1199 r = sd_bus_message_append_basic(message, 's', *i);
23bbb0de 1200 if (r < 0)
17fd7460 1201 return bus_log_create_error(r);
b51f299a
LP
1202 }
1203
17fd7460
LU
1204 r = sd_bus_message_close_container(message);
1205 if (r < 0)
1206 return bus_log_create_error(r);
1207
1208 r = sd_bus_message_append_basic(message, 'u', &flags);
1209 if (r < 0)
1210 return bus_log_create_error(r);
1211
1212 r = sd_bus_call(bus, message, arg_timeout, &error, NULL);
1213 if (r < 0) {
1214 log_error("%s", bus_error_message(&error, r));
1215 return r;
1f849790
LP
1216 }
1217
f5938e8f
TG
1218 r = sd_bus_get_unique_name(bus, &unique_name);
1219 if (r < 0)
1220 return log_error_errno(r, "Failed to get unique name: %m");
1221
92d66625
LP
1222 log_info("Monitoring bus message stream.");
1223
1f849790 1224 for (;;) {
4afd3348 1225 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1f849790
LP
1226
1227 r = sd_bus_process(bus, &m);
23bbb0de
MS
1228 if (r < 0)
1229 return log_error_errno(r, "Failed to process bus: %m");
1f849790 1230
f5938e8f
TG
1231 if (!is_monitor) {
1232 const char *name;
1233
1234 /* wait until we lose our unique name */
1235 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameLost") <= 0)
1236 continue;
1237
1238 r = sd_bus_message_read(m, "s", &name);
1239 if (r < 0)
1240 return log_error_errno(r, "Failed to read lost name: %m");
1241
d27d4637
TG
1242 if (streq(name, unique_name))
1243 is_monitor = true;
f5938e8f 1244
d27d4637 1245 continue;
f5938e8f
TG
1246 }
1247
1f849790 1248 if (m) {
1f70b087 1249 dump(m, stdout);
1d44f758 1250 fflush(stdout);
92d66625
LP
1251
1252 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
1253 log_info("Connection terminated, exiting.");
1254 return 0;
1255 }
1256
1f849790
LP
1257 continue;
1258 }
1259
1260 if (r > 0)
1261 continue;
1262
1263 r = sd_bus_wait(bus, (uint64_t) -1);
23bbb0de
MS
1264 if (r < 0)
1265 return log_error_errno(r, "Failed to wait for bus: %m");
1f849790 1266 }
95c4fe82 1267}
1f849790 1268
9bb31a0c
YW
1269static int verb_monitor(int argc, char **argv, void *userdata) {
1270 return monitor(argc, argv, message_dump);
1271}
1272
1273static int verb_capture(int argc, char **argv, void *userdata) {
1f70b087
LP
1274 int r;
1275
1276 if (isatty(fileno(stdout)) > 0) {
1277 log_error("Refusing to write message data to console, please redirect output to a file.");
1278 return -EINVAL;
1279 }
1280
1281 bus_pcap_header(arg_snaplen, stdout);
1282
9bb31a0c 1283 r = monitor(argc, argv, message_pcap);
1f70b087
LP
1284 if (r < 0)
1285 return r;
1286
1287 if (ferror(stdout)) {
1288 log_error("Couldn't write capture file.");
1289 return -EIO;
1290 }
1291
1292 return r;
1293}
1294
9bb31a0c
YW
1295static int status(int argc, char **argv, void *userdata) {
1296 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 1297 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
95c4fe82
LP
1298 pid_t pid;
1299 int r;
1300
9bb31a0c
YW
1301 r = acquire_bus(false, &bus);
1302 if (r < 0)
1303 return r;
95c4fe82 1304
9bb31a0c 1305 if (!isempty(argv[1])) {
2e9efd22
LP
1306 r = parse_pid(argv[1], &pid);
1307 if (r < 0)
1308 r = sd_bus_get_name_creds(
1309 bus,
1310 argv[1],
1311 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1312 &creds);
1313 else
1314 r = sd_bus_creds_new_from_pid(
1315 &creds,
1316 pid,
1317 _SD_BUS_CREDS_ALL);
3acc1daf 1318 } else {
5b820358 1319 const char *scope, *address;
5c302692 1320 sd_id128_t bus_id;
3acc1daf 1321
5b820358
LP
1322 r = sd_bus_get_address(bus, &address);
1323 if (r >= 0)
1fc464f6 1324 printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal());
5b820358 1325
3acc1daf
LP
1326 r = sd_bus_get_scope(bus, &scope);
1327 if (r >= 0)
1fc464f6 1328 printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal());
3acc1daf 1329
5c302692
LP
1330 r = sd_bus_get_bus_id(bus, &bus_id);
1331 if (r >= 0)
1fc464f6 1332 printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
5c302692 1333
2e9efd22 1334 r = sd_bus_get_owner_creds(
40ed1a45 1335 bus,
40ed1a45
LP
1336 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1337 &creds);
3acc1daf 1338 }
95c4fe82 1339
23bbb0de
MS
1340 if (r < 0)
1341 return log_error_errno(r, "Failed to get credentials: %m");
95c4fe82 1342
d0b2babf 1343 bus_creds_dump(creds, NULL, false);
95c4fe82 1344 return 0;
1f849790
LP
1345}
1346
781fa938
LP
1347static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1348 char **p;
1349 int r;
1350
1351 assert(m);
1352 assert(signature);
1353 assert(x);
1354
1355 p = *x;
1356
1357 for (;;) {
1358 const char *v;
1359 char t;
1360
1361 t = *signature;
1362 v = *p;
1363
1364 if (t == 0)
1365 break;
1366 if (!v) {
1367 log_error("Too few parameters for signature.");
1368 return -EINVAL;
1369 }
1370
1371 signature++;
1372 p++;
1373
1374 switch (t) {
1375
1376 case SD_BUS_TYPE_BOOLEAN:
1377
1378 r = parse_boolean(v);
1379 if (r < 0) {
1380 log_error("Failed to parse as boolean: %s", v);
1381 return r;
1382 }
1383
1384 r = sd_bus_message_append_basic(m, t, &r);
1385 break;
1386
1387 case SD_BUS_TYPE_BYTE: {
1388 uint8_t z;
1389
1390 r = safe_atou8(v, &z);
1391 if (r < 0) {
1392 log_error("Failed to parse as byte (unsigned 8bit integer): %s", v);
1393 return r;
1394 }
1395
1396 r = sd_bus_message_append_basic(m, t, &z);
1397 break;
1398 }
1399
1400 case SD_BUS_TYPE_INT16: {
1401 int16_t z;
1402
1403 r = safe_atoi16(v, &z);
1404 if (r < 0) {
1405 log_error("Failed to parse as signed 16bit integer: %s", v);
1406 return r;
1407 }
1408
1409 r = sd_bus_message_append_basic(m, t, &z);
1410 break;
1411 }
1412
1413 case SD_BUS_TYPE_UINT16: {
1414 uint16_t z;
1415
1416 r = safe_atou16(v, &z);
1417 if (r < 0) {
1418 log_error("Failed to parse as unsigned 16bit integer: %s", v);
1419 return r;
1420 }
1421
1422 r = sd_bus_message_append_basic(m, t, &z);
1423 break;
1424 }
1425
1426 case SD_BUS_TYPE_INT32: {
1427 int32_t z;
1428
1429 r = safe_atoi32(v, &z);
1430 if (r < 0) {
1431 log_error("Failed to parse as signed 32bit integer: %s", v);
1432 return r;
1433 }
1434
1435 r = sd_bus_message_append_basic(m, t, &z);
1436 break;
1437 }
1438
1439 case SD_BUS_TYPE_UINT32: {
1440 uint32_t z;
1441
1442 r = safe_atou32(v, &z);
1443 if (r < 0) {
1444 log_error("Failed to parse as unsigned 32bit integer: %s", v);
1445 return r;
1446 }
1447
1448 r = sd_bus_message_append_basic(m, t, &z);
1449 break;
1450 }
1451
1452 case SD_BUS_TYPE_INT64: {
1453 int64_t z;
1454
1455 r = safe_atoi64(v, &z);
1456 if (r < 0) {
1457 log_error("Failed to parse as signed 64bit integer: %s", v);
1458 return r;
1459 }
1460
1461 r = sd_bus_message_append_basic(m, t, &z);
1462 break;
1463 }
1464
1465 case SD_BUS_TYPE_UINT64: {
1466 uint64_t z;
1467
1468 r = safe_atou64(v, &z);
1469 if (r < 0) {
1470 log_error("Failed to parse as unsigned 64bit integer: %s", v);
1471 return r;
1472 }
1473
1474 r = sd_bus_message_append_basic(m, t, &z);
1475 break;
1476 }
1477
1478
1479 case SD_BUS_TYPE_DOUBLE: {
1480 double z;
1481
1482 r = safe_atod(v, &z);
1483 if (r < 0) {
1484 log_error("Failed to parse as double precision floating point: %s", v);
1485 return r;
1486 }
1487
1488 r = sd_bus_message_append_basic(m, t, &z);
1489 break;
1490 }
1491
1492 case SD_BUS_TYPE_STRING:
1493 case SD_BUS_TYPE_OBJECT_PATH:
1494 case SD_BUS_TYPE_SIGNATURE:
1495
1496 r = sd_bus_message_append_basic(m, t, v);
1497 break;
1498
1499 case SD_BUS_TYPE_ARRAY: {
1500 uint32_t n;
1501 size_t k;
1502
1503 r = safe_atou32(v, &n);
1504 if (r < 0) {
1505 log_error("Failed to parse number of array entries: %s", v);
1506 return r;
1507 }
1508
1509 r = signature_element_length(signature, &k);
1510 if (r < 0) {
1511 log_error("Invalid array signature.");
1512 return r;
1513 }
1514
1515 {
1516 unsigned i;
1517 char s[k + 1];
1518 memcpy(s, signature, k);
1519 s[k] = 0;
1520
1521 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1522 if (r < 0)
1523 return bus_log_create_error(r);
1524
1525 for (i = 0; i < n; i++) {
1526 r = message_append_cmdline(m, s, &p);
1527 if (r < 0)
1528 return r;
1529 }
1530 }
1531
1532 signature += k;
1533
1534 r = sd_bus_message_close_container(m);
1535 break;
1536 }
1537
1538 case SD_BUS_TYPE_VARIANT:
1539 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1540 if (r < 0)
1541 return bus_log_create_error(r);
1542
1543 r = message_append_cmdline(m, v, &p);
1544 if (r < 0)
1545 return r;
1546
1547 r = sd_bus_message_close_container(m);
1548 break;
1549
1550 case SD_BUS_TYPE_STRUCT_BEGIN:
1551 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1552 size_t k;
1553
1554 signature--;
1555 p--;
1556
1557 r = signature_element_length(signature, &k);
1558 if (r < 0) {
1559 log_error("Invalid struct/dict entry signature.");
1560 return r;
1561 }
1562
1563 {
1564 char s[k-1];
1565 memcpy(s, signature + 1, k - 2);
1566 s[k - 2] = 0;
1567
1568 r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
1569 if (r < 0)
1570 return bus_log_create_error(r);
1571
1572 r = message_append_cmdline(m, s, &p);
1573 if (r < 0)
1574 return r;
1575 }
1576
1577 signature += k;
1578
1579 r = sd_bus_message_close_container(m);
1580 break;
1581 }
1582
1583 case SD_BUS_TYPE_UNIX_FD:
1584 log_error("UNIX file descriptor not supported as type.");
1585 return -EINVAL;
1586
1587 default:
1588 log_error("Unknown signature type %c.", t);
1589 return -EINVAL;
1590 }
1591
1592 if (r < 0)
1593 return bus_log_create_error(r);
1594 }
1595
1596 *x = p;
1597 return 0;
1598}
1599
9bb31a0c
YW
1600static int call(int argc, char **argv, void *userdata) {
1601 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348
LP
1602 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1603 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
781fa938
LP
1604 int r;
1605
9bb31a0c
YW
1606 r = acquire_bus(false, &bus);
1607 if (r < 0)
1608 return r;
781fa938
LP
1609
1610 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
1611 if (r < 0)
1612 return bus_log_create_error(r);
781fa938 1613
38051578
LP
1614 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
1615 if (r < 0)
1616 return bus_log_create_error(r);
1617
1618 r = sd_bus_message_set_auto_start(m, arg_auto_start);
1619 if (r < 0)
1620 return bus_log_create_error(r);
1621
1622 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
1623 if (r < 0)
1624 return bus_log_create_error(r);
1625
781fa938
LP
1626 if (!isempty(argv[5])) {
1627 char **p;
1628
1629 p = argv+6;
1630
1631 r = message_append_cmdline(m, argv[5], &p);
1632 if (r < 0)
1633 return r;
1634
1635 if (*p) {
1636 log_error("Too many parameters for signature.");
1637 return -EINVAL;
1638 }
1639 }
1640
38051578
LP
1641 if (!arg_expect_reply) {
1642 r = sd_bus_send(bus, m, NULL);
1643 if (r < 0) {
1644 log_error("Failed to send message.");
1645 return r;
1646 }
1647
1648 return 0;
1649 }
1650
a44b1081 1651 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
781fa938
LP
1652 if (r < 0) {
1653 log_error("%s", bus_error_message(&error, r));
1654 return r;
1655 }
1656
1657 r = sd_bus_message_is_empty(reply);
1658 if (r < 0)
1659 return bus_log_parse_error(r);
1fc55609 1660
781fa938 1661 if (r == 0 && !arg_quiet) {
1fc55609
LP
1662
1663 if (arg_verbose) {
ee5324aa 1664 (void) pager_open(arg_no_pager, false);
1fc55609
LP
1665
1666 r = bus_message_dump(reply, stdout, 0);
1667 if (r < 0)
1668 return r;
1669 } else {
1670
1671 fputs(sd_bus_message_get_signature(reply, true), stdout);
1672 fputc(' ', stdout);
1673
1674 r = format_cmdline(reply, stdout, false);
1675 if (r < 0)
1676 return bus_log_parse_error(r);
1677
1678 fputc('\n', stdout);
1679 }
d55192ad
LP
1680 }
1681
1682 return 0;
1683}
1684
9bb31a0c
YW
1685static int get_property(int argc, char **argv, void *userdata) {
1686 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 1687 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609 1688 char **i;
d55192ad
LP
1689 int r;
1690
9bb31a0c
YW
1691 r = acquire_bus(false, &bus);
1692 if (r < 0)
1693 return r;
d55192ad 1694
1fc55609 1695 STRV_FOREACH(i, argv + 4) {
4afd3348 1696 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
1697 const char *contents = NULL;
1698 char type;
d55192ad 1699
1fc55609 1700 r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
d55192ad
LP
1701 if (r < 0) {
1702 log_error("%s", bus_error_message(&error, r));
1703 return r;
1704 }
1705
1fc55609 1706 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
1707 if (r < 0)
1708 return bus_log_parse_error(r);
1709
1fc55609
LP
1710 r = sd_bus_message_enter_container(reply, 'v', contents);
1711 if (r < 0)
1712 return bus_log_parse_error(r);
d55192ad 1713
1fc55609 1714 if (arg_verbose) {
ee5324aa 1715 (void) pager_open(arg_no_pager, false);
d55192ad 1716
1fc55609 1717 r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 1718 if (r < 0)
1fc55609
LP
1719 return r;
1720 } else {
1721 fputs(contents, stdout);
1722 fputc(' ', stdout);
d55192ad 1723
1fc55609 1724 r = format_cmdline(reply, stdout, false);
d55192ad
LP
1725 if (r < 0)
1726 return bus_log_parse_error(r);
1727
1fc55609 1728 fputc('\n', stdout);
d55192ad
LP
1729 }
1730
1731 r = sd_bus_message_exit_container(reply);
1732 if (r < 0)
1733 return bus_log_parse_error(r);
1fc55609 1734 }
d55192ad 1735
1fc55609
LP
1736 return 0;
1737}
d55192ad 1738
9bb31a0c
YW
1739static int set_property(int argc, char **argv, void *userdata) {
1740 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348
LP
1741 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1742 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609
LP
1743 char **p;
1744 int r;
d55192ad 1745
9bb31a0c
YW
1746 r = acquire_bus(false, &bus);
1747 if (r < 0)
1748 return r;
d55192ad 1749
1fc55609
LP
1750 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
1751 if (r < 0)
1752 return bus_log_create_error(r);
d55192ad 1753
1fc55609
LP
1754 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
1755 if (r < 0)
1756 return bus_log_create_error(r);
d55192ad 1757
1fc55609
LP
1758 r = sd_bus_message_open_container(m, 'v', argv[5]);
1759 if (r < 0)
1760 return bus_log_create_error(r);
1761
9bb31a0c 1762 p = argv + 6;
1fc55609
LP
1763 r = message_append_cmdline(m, argv[5], &p);
1764 if (r < 0)
1765 return r;
1766
1767 r = sd_bus_message_close_container(m);
1768 if (r < 0)
1769 return bus_log_create_error(r);
1770
1771 if (*p) {
1772 log_error("Too many parameters for signature.");
1773 return -EINVAL;
1774 }
1775
a44b1081 1776 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
1fc55609
LP
1777 if (r < 0) {
1778 log_error("%s", bus_error_message(&error, r));
1779 return r;
781fa938
LP
1780 }
1781
1782 return 0;
1783}
1784
1f849790 1785static int help(void) {
1f849790
LP
1786 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1787 "Introspect the bus.\n\n"
d75edbd6
LP
1788 " -h --help Show this help\n"
1789 " --version Show package version\n"
a86a47ce 1790 " --no-pager Do not pipe output into a pager\n"
17d47d8d 1791 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
1792 " --system Connect to system bus\n"
1793 " --user Connect to user bus\n"
1794 " -H --host=[USER@]HOST Operate on remote host\n"
1795 " -M --machine=CONTAINER Operate on local container\n"
1796 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
1797 " --show-machine Show machine ID column in list\n"
1798 " --unique Only show unique names\n"
1799 " --acquired Only show acquired names\n"
1800 " --activatable Only show activatable names\n"
d9130355 1801 " --match=MATCH Only show matching messages\n"
d28ebe27 1802 " --size=SIZE Maximum length of captured packet\n"
781fa938 1803 " --list Don't show tree, but simple object path list\n"
e53fa805 1804 " -q --quiet Don't show method call reply\n"
38051578
LP
1805 " --verbose Show result values in long format\n"
1806 " --expect-reply=BOOL Expect a method call reply\n"
1807 " --auto-start=BOOL Auto-start destination service\n"
1808 " --allow-interactive-authorization=BOOL\n"
a44b1081 1809 " Allow interactive authorization for operation\n"
40ed1a45 1810 " --timeout=SECS Maximum time to wait for method call completion\n"
56d820b6
LP
1811 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n"
1812 " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n"
1813 " system\n\n"
1f849790 1814 "Commands:\n"
d75edbd6 1815 " list List bus names\n"
2e9efd22 1816 " status [SERVICE] Show bus service, process or bus owner credentials\n"
d94fe1f1 1817 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 1818 " capture [SERVICE...] Capture bus traffic as pcap\n"
0171da06 1819 " tree [SERVICE...] Show object tree of service\n"
4f44c03e 1820 " introspect SERVICE OBJECT [INTERFACE]\n"
0171da06 1821 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
781fa938 1822 " Call a method\n"
1fc55609 1823 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
d55192ad 1824 " Get property value\n"
1fc55609
LP
1825 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
1826 " Set property value\n"
601185b4
ZJS
1827 " help Show this help\n"
1828 , program_invocation_short_name);
1f849790
LP
1829
1830 return 0;
1831}
1832
9bb31a0c
YW
1833static int verb_help(int argc, char **argv, void *userdata) {
1834 return help();
1835}
1836
1f849790
LP
1837static int parse_argv(int argc, char *argv[]) {
1838
1839 enum {
1840 ARG_VERSION = 0x100,
1841 ARG_NO_PAGER,
17d47d8d 1842 ARG_NO_LEGEND,
1f849790
LP
1843 ARG_SYSTEM,
1844 ARG_USER,
1845 ARG_ADDRESS,
1846 ARG_MATCH,
56e61788
LP
1847 ARG_SHOW_MACHINE,
1848 ARG_UNIQUE,
1849 ARG_ACQUIRED,
1f70b087
LP
1850 ARG_ACTIVATABLE,
1851 ARG_SIZE,
d9130355 1852 ARG_LIST,
1fc55609 1853 ARG_VERBOSE,
38051578
LP
1854 ARG_EXPECT_REPLY,
1855 ARG_AUTO_START,
1856 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 1857 ARG_TIMEOUT,
40ed1a45 1858 ARG_AUGMENT_CREDS,
56d820b6 1859 ARG_WATCH_BIND,
1f849790
LP
1860 };
1861
1862 static const struct option options[] = {
56e61788
LP
1863 { "help", no_argument, NULL, 'h' },
1864 { "version", no_argument, NULL, ARG_VERSION },
1865 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 1866 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
1867 { "system", no_argument, NULL, ARG_SYSTEM },
1868 { "user", no_argument, NULL, ARG_USER },
1869 { "address", required_argument, NULL, ARG_ADDRESS },
1870 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
1871 { "unique", no_argument, NULL, ARG_UNIQUE },
1872 { "acquired", no_argument, NULL, ARG_ACQUIRED },
1873 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
1874 { "match", required_argument, NULL, ARG_MATCH },
1875 { "host", required_argument, NULL, 'H' },
1876 { "machine", required_argument, NULL, 'M' },
1f70b087 1877 { "size", required_argument, NULL, ARG_SIZE },
d9130355 1878 { "list", no_argument, NULL, ARG_LIST },
781fa938 1879 { "quiet", no_argument, NULL, 'q' },
1fc55609 1880 { "verbose", no_argument, NULL, ARG_VERBOSE },
38051578
LP
1881 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
1882 { "auto-start", required_argument, NULL, ARG_AUTO_START },
1883 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
a44b1081 1884 { "timeout", required_argument, NULL, ARG_TIMEOUT },
40ed1a45 1885 { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS},
56d820b6 1886 { "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
eb9da376 1887 {},
1f849790
LP
1888 };
1889
1f70b087 1890 int c, r;
1f849790
LP
1891
1892 assert(argc >= 0);
1893 assert(argv);
1894
781fa938 1895 while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0)
1f849790
LP
1896
1897 switch (c) {
1898
1899 case 'h':
1900 return help();
1901
1902 case ARG_VERSION:
3f6fd1ba 1903 return version();
1f849790
LP
1904
1905 case ARG_NO_PAGER:
1906 arg_no_pager = true;
1907 break;
1908
17d47d8d
TA
1909 case ARG_NO_LEGEND:
1910 arg_legend = false;
1911 break;
1912
1f849790
LP
1913 case ARG_USER:
1914 arg_user = true;
1915 break;
1916
1917 case ARG_SYSTEM:
1918 arg_user = false;
1919 break;
1920
1921 case ARG_ADDRESS:
1922 arg_address = optarg;
1923 break;
1924
56e61788
LP
1925 case ARG_SHOW_MACHINE:
1926 arg_show_machine = true;
1927 break;
1928
1929 case ARG_UNIQUE:
1930 arg_unique = true;
1f849790
LP
1931 break;
1932
56e61788
LP
1933 case ARG_ACQUIRED:
1934 arg_acquired = true;
1935 break;
1936
1937 case ARG_ACTIVATABLE:
1938 arg_activatable = true;
a4297f08
LP
1939 break;
1940
1f849790
LP
1941 case ARG_MATCH:
1942 if (strv_extend(&arg_matches, optarg) < 0)
1943 return log_oom();
1944 break;
1945
1f70b087 1946 case ARG_SIZE: {
59f448cf 1947 uint64_t sz;
1f70b087 1948
59f448cf 1949 r = parse_size(optarg, 1024, &sz);
1f70b087
LP
1950 if (r < 0) {
1951 log_error("Failed to parse size: %s", optarg);
1952 return r;
1953 }
1954
59f448cf 1955 if ((uint64_t) (size_t) sz != sz) {
1f70b087
LP
1956 log_error("Size out of range.");
1957 return -E2BIG;
1958 }
1959
59f448cf 1960 arg_snaplen = (size_t) sz;
1f70b087
LP
1961 break;
1962 }
1963
d9130355
LP
1964 case ARG_LIST:
1965 arg_list = true;
1966 break;
1967
d75edbd6
LP
1968 case 'H':
1969 arg_transport = BUS_TRANSPORT_REMOTE;
1970 arg_host = optarg;
1971 break;
1972
1973 case 'M':
de33fc62 1974 arg_transport = BUS_TRANSPORT_MACHINE;
d75edbd6
LP
1975 arg_host = optarg;
1976 break;
1977
781fa938
LP
1978 case 'q':
1979 arg_quiet = true;
1980 break;
1981
1fc55609
LP
1982 case ARG_VERBOSE:
1983 arg_verbose = true;
1984 break;
1985
38051578
LP
1986 case ARG_EXPECT_REPLY:
1987 r = parse_boolean(optarg);
1988 if (r < 0) {
1989 log_error("Failed to parse --expect-reply= parameter.");
1990 return r;
1991 }
1992
1993 arg_expect_reply = !!r;
1994 break;
1995
1996
1997 case ARG_AUTO_START:
1998 r = parse_boolean(optarg);
1999 if (r < 0) {
2000 log_error("Failed to parse --auto-start= parameter.");
2001 return r;
2002 }
2003
2004 arg_auto_start = !!r;
2005 break;
2006
2007
2008 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
2009 r = parse_boolean(optarg);
2010 if (r < 0) {
2011 log_error("Failed to parse --allow-interactive-authorization= parameter.");
2012 return r;
2013 }
2014
2015 arg_allow_interactive_authorization = !!r;
2016 break;
2017
a44b1081
LP
2018 case ARG_TIMEOUT:
2019 r = parse_sec(optarg, &arg_timeout);
2020 if (r < 0) {
2021 log_error("Failed to parse --timeout= parameter.");
2022 return r;
2023 }
2024
2025 break;
2026
40ed1a45
LP
2027 case ARG_AUGMENT_CREDS:
2028 r = parse_boolean(optarg);
2029 if (r < 0) {
2030 log_error("Failed to parse --augment-creds= parameter.");
2031 return r;
2032 }
2033
2034 arg_augment_creds = !!r;
2035 break;
2036
56d820b6
LP
2037 case ARG_WATCH_BIND:
2038 r = parse_boolean(optarg);
2039 if (r < 0) {
2040 log_error("Failed to parse --watch-bind= parameter.");
2041 return r;
2042 }
2043
2044 arg_watch_bind = !!r;
2045 break;
2046
1f849790
LP
2047 case '?':
2048 return -EINVAL;
2049
2050 default:
eb9da376 2051 assert_not_reached("Unhandled option");
1f849790 2052 }
1f849790
LP
2053
2054 return 1;
2055}
2056
9bb31a0c
YW
2057static int busctl_main(int argc, char *argv[]) {
2058
2059 static const Verb verbs[] = {
2060 { "list", VERB_ANY, 1, VERB_DEFAULT, list_bus_names },
2061 { "status", VERB_ANY, 2, 0, status },
2062 { "monitor", VERB_ANY, VERB_ANY, 0, verb_monitor },
2063 { "capture", VERB_ANY, VERB_ANY, 0, verb_capture },
2064 { "tree", VERB_ANY, VERB_ANY, 0, tree },
2065 { "introspect", 3, 4, 0, introspect },
2066 { "call", 5, VERB_ANY, 0, call },
2067 { "get-property", 5, VERB_ANY, 0, get_property },
2068 { "set-property", 6, VERB_ANY, 0, set_property },
2069 { "help", VERB_ANY, VERB_ANY, 0, verb_help },
2070 {}
2071 };
1f849790 2072
9bb31a0c 2073 return dispatch_verb(argc, argv, verbs, NULL);
1f849790
LP
2074}
2075
2076int main(int argc, char *argv[]) {
1f849790
LP
2077 int r;
2078
2079 log_parse_environment();
2080 log_open();
2081
2082 r = parse_argv(argc, argv);
2083 if (r <= 0)
2084 goto finish;
2085
9bb31a0c 2086 r = busctl_main(argc, argv);
1f849790
LP
2087
2088finish:
0a84daa5
FB
2089 /* make sure we terminate the bus connection first, and then close the
2090 * pager, see issue #3543 for the details. */
1f849790 2091 pager_close();
d75edbd6 2092
9bb31a0c 2093 arg_matches = strv_free(arg_matches);
de1c301e 2094
de1c301e
LP
2095 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2096}