]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/busctl/busctl.c
sd-bus: extend sd_bus_message_read_strv() to paths and signatures
[thirdparty/systemd.git] / src / busctl / busctl.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
de1c301e 2
1f849790
LP
3#include <getopt.h>
4
de1c301e 5#include "sd-bus.h"
3f6fd1ba 6
b5efdb8a 7#include "alloc-util.h"
2b5c5383 8#include "bus-dump.h"
3f6fd1ba 9#include "bus-internal.h"
2de62253 10#include "bus-message.h"
781fa938 11#include "bus-signature.h"
79f34de9 12#include "bus-type.h"
3f6fd1ba 13#include "bus-util.h"
a1ad3767 14#include "busctl-introspect.h"
4f5dd394 15#include "escape.h"
3ffd4af2 16#include "fd-util.h"
6b0f5484 17#include "fileio.h"
81896fa2 18#include "format-table.h"
9cebb234 19#include "json.h"
8752c575 20#include "locale-util.h"
3f6fd1ba 21#include "log.h"
360f3dc2 22#include "main-func.h"
3f6fd1ba 23#include "pager.h"
6bedfcbb 24#include "parse-util.h"
3f6fd1ba 25#include "path-util.h"
294bf0c3 26#include "pretty-print.h"
3f6fd1ba 27#include "set.h"
760877e9 28#include "sort-util.h"
3f6fd1ba 29#include "strv.h"
288a74cc 30#include "terminal-util.h"
b1d4f8e1 31#include "user-util.h"
9bb31a0c 32#include "verbs.h"
de1c301e 33
923d37c6 34static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
0221d68a 35static PagerFlags arg_pager_flags = 0;
17d47d8d 36static bool arg_legend = true;
b683b82f 37static bool arg_full = false;
5e4c5bde 38static const char *arg_address = NULL;
56e61788
LP
39static bool arg_unique = false;
40static bool arg_acquired = false;
41static bool arg_activatable = false;
42static bool arg_show_machine = false;
1f849790 43static char **arg_matches = NULL;
d75edbd6 44static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
02bf91ee 45static const char *arg_host = NULL;
d75edbd6 46static bool arg_user = false;
1f70b087 47static size_t arg_snaplen = 4096;
d9130355 48static bool arg_list = false;
781fa938 49static bool arg_quiet = false;
1fc55609 50static bool arg_verbose = false;
d5c8d823 51static bool arg_xml_interface = false;
38051578
LP
52static bool arg_expect_reply = true;
53static bool arg_auto_start = true;
54static bool arg_allow_interactive_authorization = true;
40ed1a45 55static bool arg_augment_creds = true;
56d820b6 56static bool arg_watch_bind = false;
a44b1081 57static usec_t arg_timeout = 0;
143aea38 58static const char *arg_destination = NULL;
1f849790 59
360f3dc2
YW
60STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep);
61
1d58a1fe
LP
62#define NAME_IS_ACQUIRED INT_TO_PTR(1)
63#define NAME_IS_ACTIVATABLE INT_TO_PTR(2)
64
2de62253 65static int json_transform_message(sd_bus_message *m, JsonVariant **ret);
2de62253 66
9bb31a0c 67static int acquire_bus(bool set_monitor, sd_bus **ret) {
b1a4981a 68 _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
9bb31a0c
YW
69 int r;
70
71 r = sd_bus_new(&bus);
72 if (r < 0)
73 return log_error_errno(r, "Failed to allocate bus: %m");
74
75 if (set_monitor) {
76 r = sd_bus_set_monitor(bus, true);
77 if (r < 0)
78 return log_error_errno(r, "Failed to set monitor mode: %m");
79
80 r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL);
81 if (r < 0)
82 return log_error_errno(r, "Failed to enable credentials: %m");
83
84 r = sd_bus_negotiate_timestamp(bus, true);
85 if (r < 0)
86 return log_error_errno(r, "Failed to enable timestamps: %m");
87
88 r = sd_bus_negotiate_fds(bus, true);
89 if (r < 0)
90 return log_error_errno(r, "Failed to enable fds: %m");
91 }
92
93 r = sd_bus_set_bus_client(bus, true);
94 if (r < 0)
95 return log_error_errno(r, "Failed to set bus client: %m");
96
97 r = sd_bus_set_watch_bind(bus, arg_watch_bind);
98 if (r < 0)
16c347b3
ZJS
99 return log_error_errno(r, "Failed to set watch-bind setting to '%s': %m",
100 yes_no(arg_watch_bind));
9bb31a0c
YW
101
102 if (arg_address)
103 r = sd_bus_set_address(bus, arg_address);
104 else {
105 switch (arg_transport) {
106
107 case BUS_TRANSPORT_LOCAL:
062ac2ea 108 if (arg_user)
9bb31a0c 109 r = bus_set_address_user(bus);
062ac2ea 110 else
9bb31a0c 111 r = bus_set_address_system(bus);
9bb31a0c
YW
112 break;
113
114 case BUS_TRANSPORT_REMOTE:
115 r = bus_set_address_system_remote(bus, arg_host);
116 break;
117
118 case BUS_TRANSPORT_MACHINE:
1b630835 119 r = bus_set_address_machine(bus, arg_user, arg_host);
9bb31a0c
YW
120 break;
121
122 default:
123 assert_not_reached("Hmm, unknown transport type.");
124 }
125 }
126 if (r < 0)
ab4a88eb 127 return bus_log_address_error(r);
9bb31a0c
YW
128
129 r = sd_bus_start(bus);
130 if (r < 0)
ab4a88eb 131 return bus_log_connect_error(r);
9bb31a0c 132
1cc6c93a 133 *ret = TAKE_PTR(bus);
9bb31a0c
YW
134
135 return 0;
136}
137
138static int list_bus_names(int argc, char **argv, void *userdata) {
71f2ab46 139 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
81896fa2 140 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5e2f14e6 141 _cleanup_hashmap_free_ Hashmap *names = NULL;
81896fa2 142 _cleanup_(table_unrefp) Table *table = NULL;
81896fa2
LP
143 char **i, *k;
144 void *v;
145 int r;
146
147 enum {
148 COLUMN_ACTIVATABLE,
149 COLUMN_NAME,
150 COLUMN_PID,
151 COLUMN_PROCESS,
152 COLUMN_USER,
153 COLUMN_CONNECTION,
154 COLUMN_UNIT,
155 COLUMN_SESSION,
156 COLUMN_DESCRIPTION,
157 COLUMN_MACHINE,
158 };
de1c301e 159
d9130355
LP
160 if (!arg_unique && !arg_acquired && !arg_activatable)
161 arg_unique = arg_acquired = arg_activatable = true;
162
9bb31a0c
YW
163 r = acquire_bus(false, &bus);
164 if (r < 0)
165 return r;
166
16c347b3
ZJS
167 r = sd_bus_list_names(bus,
168 (arg_acquired || arg_unique) ? &acquired : NULL,
169 arg_activatable ? &activatable : NULL);
23bbb0de
MS
170 if (r < 0)
171 return log_error_errno(r, "Failed to list names: %m");
de1c301e 172
d5099efc 173 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
174 if (!names)
175 return log_oom();
89ffcd2a 176
5e2f14e6 177 STRV_FOREACH(i, acquired) {
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) {
1d58a1fe 184 r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE);
23bbb0de
MS
185 if (r < 0 && r != -EEXIST)
186 return log_error_errno(r, "Failed to add to hashmap: %m");
5e2f14e6
LP
187 }
188
16c347b3
ZJS
189 table = table_new("activatable",
190 "name",
191 "pid",
192 "process",
193 "user",
194 "connection",
195 "unit",
196 "session",
197 "description",
198 "machine");
81896fa2 199 if (!table)
cb05d2a5
YW
200 return log_oom();
201
b683b82f
YW
202 if (arg_full)
203 table_set_width(table, 0);
204
81896fa2
LP
205 r = table_set_align_percent(table, table_get_cell(table, 0, COLUMN_PID), 100);
206 if (r < 0)
207 return log_error_errno(r, "Failed to set alignment: %m");
5e2f14e6 208
81896fa2
LP
209 r = table_set_empty_string(table, "-");
210 if (r < 0)
211 return log_error_errno(r, "Failed to set empty string: %m");
5e2f14e6 212
ad5555b4 213 r = table_set_sort(table, (size_t) COLUMN_NAME, (size_t) -1);
81896fa2
LP
214 if (r < 0)
215 return log_error_errno(r, "Failed to set sort column: %m");
89ffcd2a 216
81896fa2 217 if (arg_show_machine)
bec31cf5
AP
218 r = table_set_display(table, (size_t) COLUMN_NAME,
219 (size_t) COLUMN_PID,
220 (size_t) COLUMN_PROCESS,
221 (size_t) COLUMN_USER,
222 (size_t) COLUMN_CONNECTION,
223 (size_t) COLUMN_UNIT,
224 (size_t) COLUMN_SESSION,
225 (size_t) COLUMN_DESCRIPTION,
226 (size_t) COLUMN_MACHINE,
227 (size_t) -1);
81896fa2 228 else
bec31cf5
AP
229 r = table_set_display(table, (size_t) COLUMN_NAME,
230 (size_t) COLUMN_PID,
231 (size_t) COLUMN_PROCESS,
232 (size_t) COLUMN_USER,
233 (size_t) COLUMN_CONNECTION,
234 (size_t) COLUMN_UNIT,
235 (size_t) COLUMN_SESSION,
236 (size_t) COLUMN_DESCRIPTION,
237 (size_t) -1);
238
81896fa2
LP
239 if (r < 0)
240 return log_error_errno(r, "Failed to set columns to display: %m");
a4297f08 241
90e74a66 242 HASHMAP_FOREACH_KEY(v, k, names) {
4afd3348 243 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
71f2ab46 244
81896fa2
LP
245 if (v == NAME_IS_ACTIVATABLE) {
246 r = table_add_many(
247 table,
248 TABLE_INT, PTR_TO_INT(v),
249 TABLE_STRING, k,
250 TABLE_EMPTY,
251 TABLE_EMPTY,
252 TABLE_EMPTY,
253 TABLE_STRING, "(activatable)", TABLE_SET_COLOR, ansi_grey(),
254 TABLE_EMPTY,
255 TABLE_EMPTY,
256 TABLE_EMPTY,
257 TABLE_EMPTY);
258 if (r < 0)
bd17fa8c 259 return table_log_add_error(r);
71f2ab46 260
71f2ab46 261 continue;
5e2f14e6 262 }
de1c301e 263
81896fa2
LP
264 assert(v == NAME_IS_ACQUIRED);
265
266 if (!arg_unique && k[0] == ':')
56e61788
LP
267 continue;
268
81896fa2 269 if (!arg_acquired && k[0] != ':')
1f849790 270 continue;
89ffcd2a 271
81896fa2
LP
272 r = table_add_many(table,
273 TABLE_INT, PTR_TO_INT(v),
274 TABLE_STRING, k);
275 if (r < 0)
bd17fa8c 276 return table_log_add_error(r);
de1c301e 277
40ed1a45 278 r = sd_bus_get_name_creds(
81896fa2 279 bus, k,
40ed1a45 280 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) |
05bae4a6 281 SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
40ed1a45
LP
282 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
283 SD_BUS_CREDS_DESCRIPTION, &creds);
81896fa2
LP
284 if (r < 0) {
285 log_debug_errno(r, "Failed to acquire credentials of service %s, ignoring: %m", k);
286
287 r = table_fill_empty(table, COLUMN_MACHINE);
288 } else {
289 const char *unique = NULL, *session = NULL, *unit = NULL, *cn = NULL;
5b12334d
LP
290 pid_t pid;
291 uid_t uid;
de1c301e 292
5b12334d
LP
293 r = sd_bus_creds_get_pid(creds, &pid);
294 if (r >= 0) {
295 const char *comm = NULL;
de1c301e 296
81896fa2 297 (void) sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 298
81896fa2
LP
299 r = table_add_many(table,
300 TABLE_PID, pid,
301 TABLE_STRING, strna(comm));
5b12334d 302 } else
81896fa2
LP
303 r = table_add_many(table, TABLE_EMPTY, TABLE_EMPTY);
304 if (r < 0)
bd17fa8c 305 return table_log_add_error(r);
89ffcd2a 306
05bae4a6 307 r = sd_bus_creds_get_euid(creds, &uid);
5b12334d
LP
308 if (r >= 0) {
309 _cleanup_free_ char *u = NULL;
89ffcd2a 310
5b12334d
LP
311 u = uid_to_name(uid);
312 if (!u)
313 return log_oom();
89ffcd2a 314
81896fa2 315 r = table_add_cell(table, NULL, TABLE_STRING, u);
5b12334d 316 } else
81896fa2
LP
317 r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
318 if (r < 0)
bd17fa8c 319 return table_log_add_error(r);
81896fa2
LP
320
321 (void) sd_bus_creds_get_unique_name(creds, &unique);
322 (void) sd_bus_creds_get_unit(creds, &unit);
323 (void) sd_bus_creds_get_session(creds, &session);
324 (void) sd_bus_creds_get_description(creds, &cn);
325
326 r = table_add_many(
327 table,
328 TABLE_STRING, unique,
329 TABLE_STRING, unit,
330 TABLE_STRING, session,
331 TABLE_STRING, cn);
332 }
333 if (r < 0)
bd17fa8c 334 return table_log_add_error(r);
89ffcd2a 335
81896fa2
LP
336 if (arg_show_machine) {
337 sd_id128_t mid;
56e61788 338
81896fa2
LP
339 r = sd_bus_get_name_machine_id(bus, k, &mid);
340 if (r < 0)
341 log_debug_errno(r, "Failed to acquire credentials of service %s, ignoring: %m", k);
342 else {
343 char m[SD_ID128_STRING_MAX];
56e61788 344
81896fa2
LP
345 r = table_add_cell(table, NULL, TABLE_STRING, sd_id128_to_string(mid, m));
346 if (r < 0)
bd17fa8c 347 return table_log_add_error(r);
56e61788 348
81896fa2
LP
349 continue; /* line fully filled, no need to fill the remainder below */
350 }
351 }
56e61788 352
81896fa2
LP
353 r = table_fill_empty(table, 0);
354 if (r < 0)
355 return log_error_errno(r, "Failed to fill line: %m");
356 }
7b0b392f 357
665ffc7f 358 return table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
1f849790
LP
359}
360
d9130355
LP
361static void print_subtree(const char *prefix, const char *path, char **l) {
362 const char *vertical, *space;
363 char **n;
364
365 /* We assume the list is sorted. Let's first skip over the
366 * entry we are looking at. */
367 for (;;) {
368 if (!*l)
369 return;
370
371 if (!streq(*l, path))
372 break;
373
374 l++;
375 }
376
9a6f746f
LP
377 vertical = strjoina(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
378 space = strjoina(prefix, special_glyph(SPECIAL_GLYPH_TREE_SPACE));
d9130355
LP
379
380 for (;;) {
381 bool has_more = false;
382
383 if (!*l || !path_startswith(*l, path))
384 break;
385
386 n = l + 1;
387 for (;;) {
388 if (!*n || !path_startswith(*n, path))
389 break;
390
391 if (!path_startswith(*n, *l)) {
392 has_more = true;
393 break;
394 }
395
396 n++;
397 }
398
16c347b3
ZJS
399 printf("%s%s%s\n",
400 prefix,
401 special_glyph(has_more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT),
402 *l);
d9130355
LP
403
404 print_subtree(has_more ? vertical : space, *l, l);
405 l = n;
406 }
407}
408
445bd57e
ZJS
409static void print_tree(char **l) {
410 if (arg_list)
411 strv_print(l);
412 else if (strv_isempty(l))
56c8b52d 413 printf("No objects discovered.\n");
445bd57e 414 else if (streq(l[0], "/") && !l[1])
56c8b52d 415 printf("Only root object discovered.\n");
445bd57e
ZJS
416 else
417 print_subtree("", "/", l);
d9130355
LP
418}
419
a1ad3767
LP
420static int on_path(const char *path, void *userdata) {
421 Set *paths = userdata;
d9130355
LP
422 int r;
423
a1ad3767 424 assert(paths);
d9130355 425
be327321 426 r = set_put_strdup(&paths, path);
a1ad3767
LP
427 if (r < 0)
428 return log_oom();
d9130355 429
a1ad3767 430 return 0;
d9130355
LP
431}
432
50f20d1b 433static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths) {
0171da06 434 static const XMLIntrospectOps ops = {
a1ad3767
LP
435 .on_path = on_path,
436 };
437
4afd3348
LP
438 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
439 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a1ad3767 440 const char *xml;
d9130355
LP
441 int r;
442
16c347b3
ZJS
443 r = sd_bus_call_method(bus, service, path,
444 "org.freedesktop.DBus.Introspectable", "Introspect",
445 &error, &reply, "");
d9130355 446 if (r < 0) {
50f20d1b
ZJS
447 printf("%sFailed to introspect object %s of service %s: %s%s\n",
448 ansi_highlight_red(),
449 path, service, bus_error_message(&error, r),
450 ansi_normal());
d9130355
LP
451 return r;
452 }
453
454 r = sd_bus_message_read(reply, "s", &xml);
455 if (r < 0)
456 return bus_log_parse_error(r);
457
a1ad3767 458 return parse_xml_introspect(path, xml, &ops, paths);
d9130355
LP
459}
460
445bd57e 461static int tree_one(sd_bus *bus, const char *service) {
4c163bf1 462 _cleanup_set_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
d9130355 463 _cleanup_free_ char **l = NULL;
d9130355
LP
464 int r;
465
4c163bf1
ZJS
466 r = set_put_strdup(&paths, "/");
467 if (r < 0)
d9130355
LP
468 return log_oom();
469
4c163bf1 470 done = set_new(&string_hash_ops_free);
d9130355
LP
471 if (!done)
472 return log_oom();
473
4c163bf1 474 failed = set_new(&string_hash_ops_free);
d9130355
LP
475 if (!failed)
476 return log_oom();
477
d9130355
LP
478 for (;;) {
479 _cleanup_free_ char *p = NULL;
480 int q;
481
482 p = set_steal_first(paths);
483 if (!p)
484 break;
485
486 if (set_contains(done, p) ||
487 set_contains(failed, p))
488 continue;
489
50f20d1b 490 q = find_nodes(bus, service, p, paths);
4c163bf1
ZJS
491 if (q < 0 && r >= 0)
492 r = q;
d9130355 493
4c163bf1
ZJS
494 q = set_consume(q < 0 ? failed : done, TAKE_PTR(p));
495 assert(q != 0);
d9130355
LP
496 if (q < 0)
497 return log_oom();
d9130355
LP
498 }
499
0221d68a 500 (void) pager_open(arg_pager_flags);
73fc23c0 501
d9130355
LP
502 l = set_get_strv(done);
503 if (!l)
504 return log_oom();
505
506 strv_sort(l);
445bd57e 507 print_tree(l);
d9130355
LP
508
509 fflush(stdout);
510
511 return r;
512}
513
9bb31a0c
YW
514static int tree(int argc, char **argv, void *userdata) {
515 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d9130355 516 char **i;
c53aafb7 517 int r;
d9130355 518
50f20d1b
ZJS
519 /* Do superficial verification of arguments before even opening the bus */
520 STRV_FOREACH(i, strv_skip(argv, 1))
521 if (!sd_bus_service_name_is_valid(*i))
522 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
523 "Invalid bus service name: %s", *i);
524
d9130355
LP
525 if (!arg_unique && !arg_acquired)
526 arg_acquired = true;
527
9bb31a0c
YW
528 r = acquire_bus(false, &bus);
529 if (r < 0)
530 return r;
531
532 if (argc <= 1) {
d9130355 533 _cleanup_strv_free_ char **names = NULL;
56c8b52d 534 bool not_first = false;
d9130355
LP
535
536 r = sd_bus_list_names(bus, &names, NULL);
23bbb0de
MS
537 if (r < 0)
538 return log_error_errno(r, "Failed to get name list: %m");
d9130355 539
0221d68a 540 (void) pager_open(arg_pager_flags);
d9130355
LP
541
542 STRV_FOREACH(i, names) {
543 int q;
544
545 if (!arg_unique && (*i)[0] == ':')
546 continue;
547
548 if (!arg_acquired && (*i)[0] == ':')
549 continue;
550
551 if (not_first)
552 printf("\n");
553
1fc464f6 554 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
d9130355 555
445bd57e 556 q = tree_one(bus, *i);
d9130355
LP
557 if (q < 0 && r >= 0)
558 r = q;
559
560 not_first = true;
561 }
50f20d1b
ZJS
562 } else
563 STRV_FOREACH(i, strv_skip(argv, 1)) {
d9130355
LP
564 int q;
565
566 if (i > argv+1)
567 printf("\n");
568
73fc23c0 569 if (argv[2]) {
0221d68a 570 (void) pager_open(arg_pager_flags);
1fc464f6 571 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
73fc23c0 572 }
d9130355 573
445bd57e 574 q = tree_one(bus, *i);
d9130355
LP
575 if (q < 0 && r >= 0)
576 r = q;
577 }
d9130355
LP
578
579 return r;
580}
581
1fc55609
LP
582static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) {
583 int r;
584
585 for (;;) {
586 const char *contents = NULL;
587 char type;
588 union {
589 uint8_t u8;
590 uint16_t u16;
591 int16_t s16;
592 uint32_t u32;
593 int32_t s32;
594 uint64_t u64;
595 int64_t s64;
596 double d64;
597 const char *string;
598 int i;
599 } basic;
600
601 r = sd_bus_message_peek_type(m, &type, &contents);
317f2fc9 602 if (r < 0)
1fc55609 603 return r;
317f2fc9
LP
604 if (r == 0)
605 return needs_space;
1fc55609
LP
606
607 if (bus_type_is_container(type) > 0) {
608
609 r = sd_bus_message_enter_container(m, type, contents);
610 if (r < 0)
611 return r;
612
613 if (type == SD_BUS_TYPE_ARRAY) {
614 unsigned n = 0;
615
616 /* count array entries */
617 for (;;) {
618
619 r = sd_bus_message_skip(m, contents);
620 if (r < 0)
621 return r;
622 if (r == 0)
623 break;
624
625 n++;
626 }
627
628 r = sd_bus_message_rewind(m, false);
629 if (r < 0)
630 return r;
631
632 if (needs_space)
633 fputc(' ', f);
634
635 fprintf(f, "%u", n);
317f2fc9
LP
636 needs_space = true;
637
1fc55609
LP
638 } else if (type == SD_BUS_TYPE_VARIANT) {
639
640 if (needs_space)
641 fputc(' ', f);
642
643 fprintf(f, "%s", contents);
317f2fc9 644 needs_space = true;
1fc55609
LP
645 }
646
317f2fc9 647 r = format_cmdline(m, f, needs_space);
1fc55609
LP
648 if (r < 0)
649 return r;
650
317f2fc9
LP
651 needs_space = r > 0;
652
1fc55609
LP
653 r = sd_bus_message_exit_container(m);
654 if (r < 0)
655 return r;
656
657 continue;
658 }
659
660 r = sd_bus_message_read_basic(m, type, &basic);
661 if (r < 0)
662 return r;
663
664 if (needs_space)
665 fputc(' ', f);
666
667 switch (type) {
668 case SD_BUS_TYPE_BYTE:
669 fprintf(f, "%u", basic.u8);
670 break;
671
672 case SD_BUS_TYPE_BOOLEAN:
673 fputs(true_false(basic.i), f);
674 break;
675
676 case SD_BUS_TYPE_INT16:
677 fprintf(f, "%i", basic.s16);
678 break;
679
680 case SD_BUS_TYPE_UINT16:
681 fprintf(f, "%u", basic.u16);
682 break;
683
684 case SD_BUS_TYPE_INT32:
685 fprintf(f, "%i", basic.s32);
686 break;
687
688 case SD_BUS_TYPE_UINT32:
689 fprintf(f, "%u", basic.u32);
690 break;
691
692 case SD_BUS_TYPE_INT64:
693 fprintf(f, "%" PRIi64, basic.s64);
694 break;
695
696 case SD_BUS_TYPE_UINT64:
697 fprintf(f, "%" PRIu64, basic.u64);
698 break;
699
700 case SD_BUS_TYPE_DOUBLE:
701 fprintf(f, "%g", basic.d64);
702 break;
703
704 case SD_BUS_TYPE_STRING:
705 case SD_BUS_TYPE_OBJECT_PATH:
706 case SD_BUS_TYPE_SIGNATURE: {
707 _cleanup_free_ char *b = NULL;
708
709 b = cescape(basic.string);
710 if (!b)
711 return -ENOMEM;
712
713 fprintf(f, "\"%s\"", b);
714 break;
715 }
716
717 case SD_BUS_TYPE_UNIX_FD:
718 fprintf(f, "%i", basic.i);
719 break;
720
721 default:
722 assert_not_reached("Unknown basic type.");
723 }
724
1ed24c61 725 needs_space = true;
1fc55609
LP
726 }
727}
728
0171da06
LP
729typedef struct Member {
730 const char *type;
731 char *interface;
732 char *name;
733 char *signature;
734 char *result;
1fc55609 735 char *value;
0171da06
LP
736 bool writable;
737 uint64_t flags;
738} Member;
739
7a08d314 740static void member_hash_func(const Member *m, struct siphash *state) {
1e2527a6 741 uint64_t arity = 1;
0171da06
LP
742
743 assert(m);
744 assert(m->type);
745
b826ab58 746 string_hash_func(m->type, state);
0171da06 747
1e2527a6
TG
748 arity += !!m->name + !!m->interface;
749
750 uint64_hash_func(&arity, state);
751
0171da06 752 if (m->name)
b826ab58 753 string_hash_func(m->name, state);
0171da06
LP
754
755 if (m->interface)
b826ab58 756 string_hash_func(m->interface, state);
0171da06
LP
757}
758
dc5f9c6f 759static int member_compare_func(const Member *x, const Member *y) {
0171da06
LP
760 int d;
761
762 assert(x);
763 assert(y);
764 assert(x->type);
765 assert(y->type);
766
c030a850
NK
767 d = strcmp_ptr(x->interface, y->interface);
768 if (d != 0)
769 return d;
0171da06
LP
770
771 d = strcmp(x->type, y->type);
772 if (d != 0)
773 return d;
774
c030a850 775 return strcmp_ptr(x->name, y->name);
0171da06
LP
776}
777
93bab288
YW
778static int member_compare_funcp(Member * const *a, Member * const *b) {
779 return member_compare_func(*a, *b);
0171da06
LP
780}
781
782static void member_free(Member *m) {
783 if (!m)
784 return;
785
786 free(m->interface);
787 free(m->name);
788 free(m->signature);
789 free(m->result);
1fc55609 790 free(m->value);
0171da06
LP
791 free(m);
792}
793
794DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free);
795
796static void member_set_free(Set *s) {
224b0e7a 797 set_free_with_destructor(s, member_free);
0171da06
LP
798}
799
800DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free);
801
802static int on_interface(const char *interface, uint64_t flags, void *userdata) {
803 _cleanup_(member_freep) Member *m;
804 Set *members = userdata;
805 int r;
806
807 assert(interface);
808 assert(members);
809
9600c27c 810 m = new(Member, 1);
0171da06
LP
811 if (!m)
812 return log_oom();
813
9600c27c
LP
814 *m = (Member) {
815 .type = "interface",
816 .flags = flags,
817 };
0171da06
LP
818
819 r = free_and_strdup(&m->interface, interface);
820 if (r < 0)
821 return log_oom();
822
823 r = set_put(members, m);
f17153a7
LP
824 if (r == -EEXIST)
825 return log_error_errno(r, "Invalid introspection data: duplicate interface '%s'.", interface);
826 if (r < 0)
827 return log_oom();
0171da06
LP
828
829 m = NULL;
830 return 0;
831}
832
833static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) {
834 _cleanup_(member_freep) Member *m;
835 Set *members = userdata;
836 int r;
837
838 assert(interface);
839 assert(name);
840
9600c27c 841 m = new(Member, 1);
0171da06
LP
842 if (!m)
843 return log_oom();
844
9600c27c
LP
845 *m = (Member) {
846 .type = "method",
847 .flags = flags,
848 };
0171da06
LP
849
850 r = free_and_strdup(&m->interface, interface);
851 if (r < 0)
852 return log_oom();
853
854 r = free_and_strdup(&m->name, name);
855 if (r < 0)
856 return log_oom();
857
858 r = free_and_strdup(&m->signature, signature);
859 if (r < 0)
860 return log_oom();
861
862 r = free_and_strdup(&m->result, result);
863 if (r < 0)
864 return log_oom();
865
866 r = set_put(members, m);
f17153a7
LP
867 if (r == -EEXIST)
868 return log_error_errno(r, "Invalid introspection data: duplicate method '%s' on interface '%s'.", name, interface);
869 if (r < 0)
870 return log_oom();
0171da06
LP
871
872 m = NULL;
873 return 0;
874}
875
876static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) {
877 _cleanup_(member_freep) Member *m;
878 Set *members = userdata;
879 int r;
880
881 assert(interface);
882 assert(name);
883
9600c27c 884 m = new(Member, 1);
0171da06
LP
885 if (!m)
886 return log_oom();
887
9600c27c
LP
888 *m = (Member) {
889 .type = "signal",
890 .flags = flags,
891 };
0171da06
LP
892
893 r = free_and_strdup(&m->interface, interface);
894 if (r < 0)
895 return log_oom();
896
897 r = free_and_strdup(&m->name, name);
898 if (r < 0)
899 return log_oom();
900
901 r = free_and_strdup(&m->signature, signature);
902 if (r < 0)
903 return log_oom();
904
905 r = set_put(members, m);
f17153a7
LP
906 if (r == -EEXIST)
907 return log_error_errno(r, "Invalid introspection data: duplicate signal '%s' on interface '%s'.", name, interface);
908 if (r < 0)
909 return log_oom();
0171da06
LP
910
911 m = NULL;
912 return 0;
913}
914
915static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) {
916 _cleanup_(member_freep) Member *m;
917 Set *members = userdata;
918 int r;
919
920 assert(interface);
921 assert(name);
922
9600c27c 923 m = new(Member, 1);
0171da06
LP
924 if (!m)
925 return log_oom();
926
9600c27c
LP
927 *m = (Member) {
928 .type = "property",
929 .flags = flags,
930 .writable = writable,
931 };
0171da06
LP
932
933 r = free_and_strdup(&m->interface, interface);
934 if (r < 0)
935 return log_oom();
936
937 r = free_and_strdup(&m->name, name);
938 if (r < 0)
939 return log_oom();
940
941 r = free_and_strdup(&m->signature, signature);
942 if (r < 0)
943 return log_oom();
944
945 r = set_put(members, m);
f17153a7
LP
946 if (r == -EEXIST)
947 return log_error_errno(r, "Invalid introspection data: duplicate property '%s' on interface '%s'.", name, interface);
948 if (r < 0)
949 return log_oom();
0171da06
LP
950
951 m = NULL;
952 return 0;
953}
954
7a08d314 955DEFINE_PRIVATE_HASH_OPS(member_hash_ops, Member, member_hash_func, member_compare_func);
0171da06 956
7a08d314 957static int introspect(int argc, char **argv, void *userdata) {
0171da06
LP
958 static const XMLIntrospectOps ops = {
959 .on_interface = on_interface,
960 .on_method = on_method,
961 .on_signal = on_signal,
962 .on_property = on_property,
963 };
964
9bb31a0c 965 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
9efebb65 966 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_xml = NULL;
4afd3348 967 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
0171da06 968 _cleanup_(member_set_freep) Set *members = NULL;
9bb31a0c
YW
969 unsigned name_width, type_width, signature_width, result_width, j, k = 0;
970 Member *m, **sorted = NULL;
0171da06
LP
971 const char *xml;
972 int r;
0171da06 973
9bb31a0c
YW
974 r = acquire_bus(false, &bus);
975 if (r < 0)
976 return r;
4f44c03e 977
0171da06
LP
978 members = set_new(&member_hash_ops);
979 if (!members)
980 return log_oom();
981
16c347b3
ZJS
982 r = sd_bus_call_method(bus, argv[1], argv[2],
983 "org.freedesktop.DBus.Introspectable", "Introspect",
984 &error, &reply_xml, "");
e51faad3 985 if (r < 0)
16c347b3
ZJS
986 return log_error_errno(r, "Failed to introspect object %s of service %s: %s",
987 argv[2], argv[1], bus_error_message(&error, r));
0171da06 988
9efebb65 989 r = sd_bus_message_read(reply_xml, "s", &xml);
0171da06
LP
990 if (r < 0)
991 return bus_log_parse_error(r);
992
d5c8d823
ZJS
993 if (arg_xml_interface) {
994 /* Just dump the received XML and finish */
8722b297 995 (void) pager_open(arg_pager_flags);
d5c8d823
ZJS
996 puts(xml);
997 return 0;
998 }
999
1fc55609 1000 /* First, get list of all properties */
0171da06
LP
1001 r = parse_xml_introspect(argv[2], xml, &ops, members);
1002 if (r < 0)
1003 return r;
1004
1fc55609 1005 /* Second, find the current values for them */
90e74a66 1006 SET_FOREACH(m, members) {
9efebb65 1007 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
1008
1009 if (!streq(m->type, "property"))
1010 continue;
1011
1012 if (m->value)
1013 continue;
1014
4f44c03e
LP
1015 if (argv[3] && !streq(argv[3], m->interface))
1016 continue;
1017
16c347b3
ZJS
1018 r = sd_bus_call_method(bus, argv[1], argv[2],
1019 "org.freedesktop.DBus.Properties", "GetAll",
1020 &error, &reply, "s", m->interface);
e51faad3 1021 if (r < 0)
24a4e7ff
ZJS
1022 return log_error_errno(r, "Failed to get all properties on interface %s: %s",
1023 m->interface, bus_error_message(&error, r));
1fc55609
LP
1024
1025 r = sd_bus_message_enter_container(reply, 'a', "{sv}");
1026 if (r < 0)
1027 return bus_log_parse_error(r);
1028
1029 for (;;) {
1030 Member *z;
1031 _cleanup_free_ char *buf = NULL;
1032 _cleanup_fclose_ FILE *mf = NULL;
1033 size_t sz = 0;
1034 const char *name;
1035
1036 r = sd_bus_message_enter_container(reply, 'e', "sv");
1037 if (r < 0)
1038 return bus_log_parse_error(r);
1039
1040 if (r == 0)
1041 break;
1042
1043 r = sd_bus_message_read(reply, "s", &name);
1044 if (r < 0)
1045 return bus_log_parse_error(r);
1046
1047 r = sd_bus_message_enter_container(reply, 'v', NULL);
1048 if (r < 0)
1049 return bus_log_parse_error(r);
1050
2fe21124 1051 mf = open_memstream_unlocked(&buf, &sz);
1fc55609
LP
1052 if (!mf)
1053 return log_oom();
1054
1055 r = format_cmdline(reply, mf, false);
1056 if (r < 0)
1057 return bus_log_parse_error(r);
1058
8d3b9edc 1059 mf = safe_fclose(mf);
1fc55609
LP
1060
1061 z = set_get(members, &((Member) {
1062 .type = "property",
1063 .interface = m->interface,
1064 .name = (char*) name }));
8d3b9edc
LP
1065 if (z)
1066 free_and_replace(z->value, buf);
1fc55609
LP
1067
1068 r = sd_bus_message_exit_container(reply);
1069 if (r < 0)
1070 return bus_log_parse_error(r);
1071
1072 r = sd_bus_message_exit_container(reply);
1073 if (r < 0)
1074 return bus_log_parse_error(r);
1075 }
1076
1077 r = sd_bus_message_exit_container(reply);
1078 if (r < 0)
1079 return bus_log_parse_error(r);
1080 }
1081
8722b297
ZJS
1082 name_width = strlen("NAME");
1083 type_width = strlen("TYPE");
1084 signature_width = strlen("SIGNATURE");
1085 result_width = strlen("RESULT/VALUE");
0171da06
LP
1086
1087 sorted = newa(Member*, set_size(members));
1088
90e74a66 1089 SET_FOREACH(m, members) {
4f44c03e
LP
1090 if (argv[3] && !streq(argv[3], m->interface))
1091 continue;
1092
0171da06
LP
1093 if (m->interface)
1094 name_width = MAX(name_width, strlen(m->interface));
1095 if (m->name)
1096 name_width = MAX(name_width, strlen(m->name) + 1);
1097 if (m->type)
1098 type_width = MAX(type_width, strlen(m->type));
1099 if (m->signature)
1100 signature_width = MAX(signature_width, strlen(m->signature));
1101 if (m->result)
1102 result_width = MAX(result_width, strlen(m->result));
1fc55609
LP
1103 if (m->value)
1104 result_width = MAX(result_width, strlen(m->value));
0171da06
LP
1105
1106 sorted[k++] = m;
1107 }
1108
1fc55609
LP
1109 if (result_width > 40)
1110 result_width = 40;
1111
93bab288 1112 typesafe_qsort(sorted, k, member_compare_funcp);
0171da06 1113
8722b297
ZJS
1114 (void) pager_open(arg_pager_flags);
1115
d7a0f1f4 1116 if (arg_legend)
1fc55609
LP
1117 printf("%-*s %-*s %-*s %-*s %s\n",
1118 (int) name_width, "NAME",
1119 (int) type_width, "TYPE",
1120 (int) signature_width, "SIGNATURE",
1121 (int) result_width, "RESULT/VALUE",
1122 "FLAGS");
0171da06
LP
1123
1124 for (j = 0; j < k; j++) {
1fc55609
LP
1125 _cleanup_free_ char *ellipsized = NULL;
1126 const char *rv;
0171da06
LP
1127 bool is_interface;
1128
1129 m = sorted[j];
1130
4f44c03e
LP
1131 if (argv[3] && !streq(argv[3], m->interface))
1132 continue;
1133
0171da06
LP
1134 is_interface = streq(m->type, "interface");
1135
4f44c03e
LP
1136 if (argv[3] && is_interface)
1137 continue;
1138
1fc55609
LP
1139 if (m->value) {
1140 ellipsized = ellipsize(m->value, result_width, 100);
1141 if (!ellipsized)
1142 return log_oom();
1143
1144 rv = ellipsized;
1145 } else
c5984fe1 1146 rv = empty_to_dash(m->result);
1fc55609 1147
0171da06
LP
1148 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
1149 is_interface ? ansi_highlight() : "",
1150 is_interface ? "" : ".",
16c347b3
ZJS
1151 - !is_interface + (int) name_width,
1152 empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name),
1fc464f6 1153 is_interface ? ansi_normal() : "",
c5984fe1
YW
1154 (int) type_width, empty_to_dash(m->type),
1155 (int) signature_width, empty_to_dash(m->signature),
1fc55609 1156 (int) result_width, rv,
0171da06
LP
1157 (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"),
1158 (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "",
1159 (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "",
1160 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "",
1161 (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "",
1162 m->writable ? " writable" : "");
1163 }
1164
1165 return 0;
1166}
1167
1f70b087 1168static int message_dump(sd_bus_message *m, FILE *f) {
2b4a65b6 1169 return sd_bus_message_dump(m, f, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
1f70b087
LP
1170}
1171
1172static int message_pcap(sd_bus_message *m, FILE *f) {
1173 return bus_message_pcap_frame(m, arg_snaplen, f);
1174}
1175
2de62253
LP
1176static int message_json(sd_bus_message *m, FILE *f) {
1177 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
1178 char e[2];
1179 int r;
6fe2a70b 1180 usec_t ts;
2de62253
LP
1181
1182 r = json_transform_message(m, &v);
1183 if (r < 0)
1184 return r;
1185
1186 e[0] = m->header->endian;
1187 e[1] = 0;
1188
6fe2a70b
MT
1189 ts = m->realtime;
1190 if (ts == 0)
1191 ts = now(CLOCK_REALTIME);
1192
2de62253 1193 r = json_build(&w, JSON_BUILD_OBJECT(
16c347b3
ZJS
1194 JSON_BUILD_PAIR("type", JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))),
1195 JSON_BUILD_PAIR("endian", JSON_BUILD_STRING(e)),
1196 JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(m->header->flags)),
1197 JSON_BUILD_PAIR("version", JSON_BUILD_INTEGER(m->header->version)),
1198 JSON_BUILD_PAIR("cookie", JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))),
1199 JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", JSON_BUILD_INTEGER(m->reply_cookie)),
6fe2a70b 1200 JSON_BUILD_PAIR("timestamp-realtime", JSON_BUILD_UNSIGNED(ts)),
16c347b3
ZJS
1201 JSON_BUILD_PAIR_CONDITION(m->sender, "sender", JSON_BUILD_STRING(m->sender)),
1202 JSON_BUILD_PAIR_CONDITION(m->destination, "destination", JSON_BUILD_STRING(m->destination)),
1203 JSON_BUILD_PAIR_CONDITION(m->path, "path", JSON_BUILD_STRING(m->path)),
1204 JSON_BUILD_PAIR_CONDITION(m->interface, "interface", JSON_BUILD_STRING(m->interface)),
1205 JSON_BUILD_PAIR_CONDITION(m->member, "member", JSON_BUILD_STRING(m->member)),
1206 JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", JSON_BUILD_INTEGER(m->monotonic)),
1207 JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", JSON_BUILD_INTEGER(m->realtime)),
1208 JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", JSON_BUILD_INTEGER(m->seqnum)),
1209 JSON_BUILD_PAIR_CONDITION(m->error.name, "error_name", JSON_BUILD_STRING(m->error.name)),
1210 JSON_BUILD_PAIR("payload", JSON_BUILD_VARIANT(v))));
2de62253
LP
1211 if (r < 0)
1212 return log_error_errno(r, "Failed to build JSON object: %m");
1213
923d37c6 1214 json_variant_dump(w, arg_json_format_flags, f, NULL);
2de62253
LP
1215 return 0;
1216}
1217
9bb31a0c
YW
1218static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f)) {
1219 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
17fd7460
LU
1220 _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
1221 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1f849790 1222 char **i;
17fd7460 1223 uint32_t flags = 0;
f5938e8f
TG
1224 const char *unique_name;
1225 bool is_monitor = false;
1f849790
LP
1226 int r;
1227
9bb31a0c
YW
1228 r = acquire_bus(true, &bus);
1229 if (r < 0)
1230 return r;
1231
17fd7460 1232 /* upgrade connection; it's not used for anything else after this call */
24a4e7ff
ZJS
1233 r = sd_bus_message_new_method_call(bus,
1234 &message,
1235 "org.freedesktop.DBus",
1236 "/org/freedesktop/DBus",
1237 "org.freedesktop.DBus.Monitoring",
1238 "BecomeMonitor");
17fd7460
LU
1239 if (r < 0)
1240 return bus_log_create_error(r);
1241
1242 r = sd_bus_message_open_container(message, 'a', "s");
1243 if (r < 0)
1244 return bus_log_create_error(r);
1245
1f849790
LP
1246 STRV_FOREACH(i, argv+1) {
1247 _cleanup_free_ char *m = NULL;
1248
5453a4b1 1249 if (!sd_bus_service_name_is_valid(*i))
0747cde7 1250 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name '%s'", *i);
1f849790 1251
605405c6 1252 m = strjoin("sender='", *i, "'");
1f849790
LP
1253 if (!m)
1254 return log_oom();
1255
17fd7460 1256 r = sd_bus_message_append_basic(message, 's', m);
23bbb0de 1257 if (r < 0)
17fd7460 1258 return bus_log_create_error(r);
b51f299a 1259
f6d1e6cb 1260 free(m);
605405c6 1261 m = strjoin("destination='", *i, "'");
f6d1e6cb
LU
1262 if (!m)
1263 return log_oom();
1264
17fd7460 1265 r = sd_bus_message_append_basic(message, 's', m);
f6d1e6cb 1266 if (r < 0)
17fd7460 1267 return bus_log_create_error(r);
1f849790
LP
1268 }
1269
1270 STRV_FOREACH(i, arg_matches) {
17fd7460 1271 r = sd_bus_message_append_basic(message, 's', *i);
23bbb0de 1272 if (r < 0)
17fd7460 1273 return bus_log_create_error(r);
b51f299a
LP
1274 }
1275
17fd7460
LU
1276 r = sd_bus_message_close_container(message);
1277 if (r < 0)
1278 return bus_log_create_error(r);
1279
1280 r = sd_bus_message_append_basic(message, 'u', &flags);
1281 if (r < 0)
1282 return bus_log_create_error(r);
1283
1284 r = sd_bus_call(bus, message, arg_timeout, &error, NULL);
48956c39 1285 if (r < 0)
24a4e7ff
ZJS
1286 return log_error_errno(r, "Call to org.freedesktop.DBus.Monitoring.BecomeMonitor failed: %s",
1287 bus_error_message(&error, r));
1f849790 1288
f5938e8f
TG
1289 r = sd_bus_get_unique_name(bus, &unique_name);
1290 if (r < 0)
1291 return log_error_errno(r, "Failed to get unique name: %m");
1292
92d66625
LP
1293 log_info("Monitoring bus message stream.");
1294
1f849790 1295 for (;;) {
4afd3348 1296 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1f849790
LP
1297
1298 r = sd_bus_process(bus, &m);
23bbb0de
MS
1299 if (r < 0)
1300 return log_error_errno(r, "Failed to process bus: %m");
1f849790 1301
f5938e8f
TG
1302 if (!is_monitor) {
1303 const char *name;
1304
1305 /* wait until we lose our unique name */
1306 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameLost") <= 0)
1307 continue;
1308
1309 r = sd_bus_message_read(m, "s", &name);
1310 if (r < 0)
d67b1d18 1311 return bus_log_parse_error(r);
f5938e8f 1312
d27d4637
TG
1313 if (streq(name, unique_name))
1314 is_monitor = true;
f5938e8f 1315
d27d4637 1316 continue;
f5938e8f
TG
1317 }
1318
1f849790 1319 if (m) {
1f70b087 1320 dump(m, stdout);
1d44f758 1321 fflush(stdout);
92d66625
LP
1322
1323 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
1324 log_info("Connection terminated, exiting.");
1325 return 0;
1326 }
1327
1f849790
LP
1328 continue;
1329 }
1330
1331 if (r > 0)
1332 continue;
1333
1334 r = sd_bus_wait(bus, (uint64_t) -1);
23bbb0de
MS
1335 if (r < 0)
1336 return log_error_errno(r, "Failed to wait for bus: %m");
1f849790 1337 }
95c4fe82 1338}
1f849790 1339
9bb31a0c 1340static int verb_monitor(int argc, char **argv, void *userdata) {
923d37c6 1341 return monitor(argc, argv, (arg_json_format_flags & JSON_FORMAT_OFF) ? message_dump : message_json);
9bb31a0c
YW
1342}
1343
1344static int verb_capture(int argc, char **argv, void *userdata) {
1f70b087
LP
1345 int r;
1346
baaa35ad
ZJS
1347 if (isatty(fileno(stdout)) > 0)
1348 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1349 "Refusing to write message data to console, please redirect output to a file.");
1f70b087
LP
1350
1351 bus_pcap_header(arg_snaplen, stdout);
1352
9bb31a0c 1353 r = monitor(argc, argv, message_pcap);
1f70b087
LP
1354 if (r < 0)
1355 return r;
1356
6b0f5484
LP
1357 r = fflush_and_check(stdout);
1358 if (r < 0)
1359 return log_error_errno(r, "Couldn't write capture file: %m");
1f70b087
LP
1360
1361 return r;
1362}
1363
9bb31a0c
YW
1364static int status(int argc, char **argv, void *userdata) {
1365 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 1366 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
95c4fe82
LP
1367 pid_t pid;
1368 int r;
1369
9bb31a0c
YW
1370 r = acquire_bus(false, &bus);
1371 if (r < 0)
1372 return r;
95c4fe82 1373
8722b297
ZJS
1374 (void) pager_open(arg_pager_flags);
1375
9bb31a0c 1376 if (!isempty(argv[1])) {
2e9efd22
LP
1377 r = parse_pid(argv[1], &pid);
1378 if (r < 0)
1379 r = sd_bus_get_name_creds(
1380 bus,
1381 argv[1],
1382 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1383 &creds);
1384 else
1385 r = sd_bus_creds_new_from_pid(
1386 &creds,
1387 pid,
1388 _SD_BUS_CREDS_ALL);
3acc1daf 1389 } else {
5b820358 1390 const char *scope, *address;
5c302692 1391 sd_id128_t bus_id;
3acc1daf 1392
5b820358
LP
1393 r = sd_bus_get_address(bus, &address);
1394 if (r >= 0)
1fc464f6 1395 printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_normal());
5b820358 1396
3acc1daf
LP
1397 r = sd_bus_get_scope(bus, &scope);
1398 if (r >= 0)
1fc464f6 1399 printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_normal());
3acc1daf 1400
5c302692
LP
1401 r = sd_bus_get_bus_id(bus, &bus_id);
1402 if (r >= 0)
16c347b3
ZJS
1403 printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n",
1404 ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
5c302692 1405
2e9efd22 1406 r = sd_bus_get_owner_creds(
40ed1a45 1407 bus,
40ed1a45
LP
1408 (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL,
1409 &creds);
3acc1daf 1410 }
95c4fe82 1411
23bbb0de
MS
1412 if (r < 0)
1413 return log_error_errno(r, "Failed to get credentials: %m");
95c4fe82 1414
d0b2babf 1415 bus_creds_dump(creds, NULL, false);
95c4fe82 1416 return 0;
1f849790
LP
1417}
1418
781fa938
LP
1419static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
1420 char **p;
1421 int r;
1422
1423 assert(m);
1424 assert(signature);
1425 assert(x);
1426
1427 p = *x;
1428
1429 for (;;) {
1430 const char *v;
1431 char t;
1432
1433 t = *signature;
1434 v = *p;
1435
1436 if (t == 0)
1437 break;
baaa35ad
ZJS
1438 if (!v)
1439 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1440 "Too few parameters for signature.");
781fa938
LP
1441
1442 signature++;
1443 p++;
1444
1445 switch (t) {
1446
1447 case SD_BUS_TYPE_BOOLEAN:
1448
1449 r = parse_boolean(v);
48956c39 1450 if (r < 0)
3d9942de 1451 return log_error_errno(r, "Failed to parse '%s' as boolean: %m", v);
781fa938
LP
1452
1453 r = sd_bus_message_append_basic(m, t, &r);
1454 break;
1455
1456 case SD_BUS_TYPE_BYTE: {
1457 uint8_t z;
1458
1459 r = safe_atou8(v, &z);
48956c39 1460 if (r < 0)
3d9942de 1461 return log_error_errno(r, "Failed to parse '%s' as byte (unsigned 8bit integer): %m", v);
781fa938
LP
1462
1463 r = sd_bus_message_append_basic(m, t, &z);
1464 break;
1465 }
1466
1467 case SD_BUS_TYPE_INT16: {
1468 int16_t z;
1469
1470 r = safe_atoi16(v, &z);
48956c39 1471 if (r < 0)
3d9942de 1472 return log_error_errno(r, "Failed to parse '%s' as signed 16bit integer: %m", v);
781fa938
LP
1473
1474 r = sd_bus_message_append_basic(m, t, &z);
1475 break;
1476 }
1477
1478 case SD_BUS_TYPE_UINT16: {
1479 uint16_t z;
1480
1481 r = safe_atou16(v, &z);
48956c39 1482 if (r < 0)
3d9942de 1483 return log_error_errno(r, "Failed to parse '%s' as unsigned 16bit integer: %m", v);
781fa938
LP
1484
1485 r = sd_bus_message_append_basic(m, t, &z);
1486 break;
1487 }
1488
1489 case SD_BUS_TYPE_INT32: {
1490 int32_t z;
1491
1492 r = safe_atoi32(v, &z);
48956c39 1493 if (r < 0)
3d9942de 1494 return log_error_errno(r, "Failed to parse '%s' as signed 32bit integer: %m", v);
781fa938
LP
1495
1496 r = sd_bus_message_append_basic(m, t, &z);
1497 break;
1498 }
1499
1500 case SD_BUS_TYPE_UINT32: {
1501 uint32_t z;
1502
1503 r = safe_atou32(v, &z);
48956c39 1504 if (r < 0)
3d9942de 1505 return log_error_errno(r, "Failed to parse '%s' as unsigned 32bit integer: %m", v);
781fa938
LP
1506
1507 r = sd_bus_message_append_basic(m, t, &z);
1508 break;
1509 }
1510
1511 case SD_BUS_TYPE_INT64: {
1512 int64_t z;
1513
1514 r = safe_atoi64(v, &z);
48956c39 1515 if (r < 0)
3d9942de 1516 return log_error_errno(r, "Failed to parse '%s' as signed 64bit integer: %m", v);
781fa938
LP
1517
1518 r = sd_bus_message_append_basic(m, t, &z);
1519 break;
1520 }
1521
1522 case SD_BUS_TYPE_UINT64: {
1523 uint64_t z;
1524
1525 r = safe_atou64(v, &z);
48956c39 1526 if (r < 0)
3d9942de 1527 return log_error_errno(r, "Failed to parse '%s' as unsigned 64bit integer: %m", v);
781fa938
LP
1528
1529 r = sd_bus_message_append_basic(m, t, &z);
1530 break;
1531 }
1532
781fa938
LP
1533 case SD_BUS_TYPE_DOUBLE: {
1534 double z;
1535
1536 r = safe_atod(v, &z);
48956c39 1537 if (r < 0)
3d9942de 1538 return log_error_errno(r, "Failed to parse '%s' as double precision floating point: %m", v);
781fa938
LP
1539
1540 r = sd_bus_message_append_basic(m, t, &z);
1541 break;
1542 }
1543
1544 case SD_BUS_TYPE_STRING:
1545 case SD_BUS_TYPE_OBJECT_PATH:
1546 case SD_BUS_TYPE_SIGNATURE:
1547
1548 r = sd_bus_message_append_basic(m, t, v);
1549 break;
1550
1551 case SD_BUS_TYPE_ARRAY: {
1552 uint32_t n;
1553 size_t k;
1554
1555 r = safe_atou32(v, &n);
48956c39 1556 if (r < 0)
3d9942de 1557 return log_error_errno(r, "Failed to parse '%s' number of array entries: %m", v);
781fa938
LP
1558
1559 r = signature_element_length(signature, &k);
48956c39 1560 if (r < 0)
3d9942de 1561 return log_error_errno(r, "Invalid array signature: %m");
781fa938
LP
1562
1563 {
781fa938
LP
1564 char s[k + 1];
1565 memcpy(s, signature, k);
1566 s[k] = 0;
1567
1568 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
1569 if (r < 0)
1570 return bus_log_create_error(r);
1571
16c347b3 1572 for (unsigned i = 0; i < n; i++) {
781fa938
LP
1573 r = message_append_cmdline(m, s, &p);
1574 if (r < 0)
1575 return r;
1576 }
1577 }
1578
1579 signature += k;
1580
1581 r = sd_bus_message_close_container(m);
1582 break;
1583 }
1584
1585 case SD_BUS_TYPE_VARIANT:
1586 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v);
1587 if (r < 0)
1588 return bus_log_create_error(r);
1589
1590 r = message_append_cmdline(m, v, &p);
1591 if (r < 0)
1592 return r;
1593
1594 r = sd_bus_message_close_container(m);
1595 break;
1596
1597 case SD_BUS_TYPE_STRUCT_BEGIN:
1598 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
1599 size_t k;
1600
1601 signature--;
1602 p--;
1603
1604 r = signature_element_length(signature, &k);
48956c39 1605 if (r < 0)
3d9942de 1606 return log_error_errno(r, "Invalid struct/dict entry signature: %m");
781fa938
LP
1607
1608 {
1609 char s[k-1];
1610 memcpy(s, signature + 1, k - 2);
1611 s[k - 2] = 0;
1612
16c347b3
ZJS
1613 const char ctype = t == SD_BUS_TYPE_STRUCT_BEGIN ?
1614 SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY;
1615 r = sd_bus_message_open_container(m, ctype, s);
781fa938
LP
1616 if (r < 0)
1617 return bus_log_create_error(r);
1618
1619 r = message_append_cmdline(m, s, &p);
1620 if (r < 0)
1621 return r;
1622 }
1623
1624 signature += k;
1625
1626 r = sd_bus_message_close_container(m);
1627 break;
1628 }
1629
1630 case SD_BUS_TYPE_UNIX_FD:
baaa35ad
ZJS
1631 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1632 "UNIX file descriptor not supported as type.");
781fa938
LP
1633
1634 default:
baaa35ad
ZJS
1635 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1636 "Unknown signature type %c.", t);
781fa938
LP
1637 }
1638
1639 if (r < 0)
1640 return bus_log_create_error(r);
1641 }
1642
1643 *x = p;
1644 return 0;
1645}
1646
9cebb234
LP
1647static int json_transform_one(sd_bus_message *m, JsonVariant **ret);
1648
1649static int json_transform_array_or_struct(sd_bus_message *m, JsonVariant **ret) {
1650 size_t n_elements = 0, n_allocated = 0;
1651 JsonVariant **elements = NULL;
1652 int r;
1653
1654 assert(m);
1655 assert(ret);
1656
1657 for (;;) {
1658 r = sd_bus_message_at_end(m, false);
1659 if (r < 0) {
1660 bus_log_parse_error(r);
1661 goto finish;
1662 }
1663 if (r > 0)
1664 break;
1665
1666 if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 1)) {
1667 r = log_oom();
1668 goto finish;
1669 }
1670
1671 r = json_transform_one(m, elements + n_elements);
1672 if (r < 0)
1673 goto finish;
1674
1675 n_elements++;
1676 }
1677
1678 r = json_variant_new_array(ret, elements, n_elements);
1679
1680finish:
1681 json_variant_unref_many(elements, n_elements);
1682 free(elements);
1683
1684 return r;
1685}
1686
1687static int json_transform_variant(sd_bus_message *m, const char *contents, JsonVariant **ret) {
172e4806 1688 _cleanup_(json_variant_unrefp) JsonVariant *value = NULL;
9cebb234
LP
1689 int r;
1690
1691 assert(m);
1692 assert(contents);
1693 assert(ret);
1694
1695 r = json_transform_one(m, &value);
1696 if (r < 0)
1697 return r;
1698
1699 r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(contents)),
1700 JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(value))));
1701 if (r < 0)
1702 return log_oom();
1703
1704 return r;
1705}
1706
1707static int json_transform_dict_array(sd_bus_message *m, JsonVariant **ret) {
1708 size_t n_elements = 0, n_allocated = 0;
1709 JsonVariant **elements = NULL;
1710 int r;
1711
1712 assert(m);
1713 assert(ret);
1714
1715 for (;;) {
1716 const char *contents;
1717 char type;
1718
1719 r = sd_bus_message_at_end(m, false);
1720 if (r < 0) {
1721 bus_log_parse_error(r);
1722 goto finish;
1723 }
1724 if (r > 0)
1725 break;
1726
1727 r = sd_bus_message_peek_type(m, &type, &contents);
1728 if (r < 0)
1729 return r;
1730
1731 assert(type == 'e');
1732
1733 if (!GREEDY_REALLOC(elements, n_allocated, n_elements + 2)) {
1734 r = log_oom();
1735 goto finish;
1736 }
1737
1738 r = sd_bus_message_enter_container(m, type, contents);
1739 if (r < 0) {
1740 bus_log_parse_error(r);
1741 goto finish;
1742 }
1743
1744 r = json_transform_one(m, elements + n_elements);
1745 if (r < 0)
1746 goto finish;
1747
1748 n_elements++;
1749
1750 r = json_transform_one(m, elements + n_elements);
1751 if (r < 0)
1752 goto finish;
1753
1754 n_elements++;
1755
1756 r = sd_bus_message_exit_container(m);
1757 if (r < 0) {
1758 bus_log_parse_error(r);
1759 goto finish;
1760 }
1761 }
1762
1763 r = json_variant_new_object(ret, elements, n_elements);
1764
1765finish:
1766 json_variant_unref_many(elements, n_elements);
1767 free(elements);
1768
1769 return r;
1770}
1771
1772static int json_transform_one(sd_bus_message *m, JsonVariant **ret) {
1773 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1774 const char *contents;
1775 char type;
1776 int r;
1777
1778 assert(m);
1779 assert(ret);
1780
1781 r = sd_bus_message_peek_type(m, &type, &contents);
1782 if (r < 0)
1783 return bus_log_parse_error(r);
1784
1785 switch (type) {
1786
1787 case SD_BUS_TYPE_BYTE: {
1788 uint8_t b;
1789
1790 r = sd_bus_message_read_basic(m, type, &b);
1791 if (r < 0)
1792 return bus_log_parse_error(r);
1793
1794 r = json_variant_new_unsigned(&v, b);
1795 if (r < 0)
1796 return log_error_errno(r, "Failed to transform byte: %m");
1797
1798 break;
1799 }
1800
1801 case SD_BUS_TYPE_BOOLEAN: {
1802 int b;
1803
1804 r = sd_bus_message_read_basic(m, type, &b);
1805 if (r < 0)
1806 return bus_log_parse_error(r);
1807
1808 r = json_variant_new_boolean(&v, b);
1809 if (r < 0)
1810 return log_error_errno(r, "Failed to transform boolean: %m");
1811
1812 break;
1813 }
1814
1815 case SD_BUS_TYPE_INT16: {
1816 int16_t b;
1817
1818 r = sd_bus_message_read_basic(m, type, &b);
1819 if (r < 0)
1820 return bus_log_parse_error(r);
1821
1822 r = json_variant_new_integer(&v, b);
1823 if (r < 0)
1824 return log_error_errno(r, "Failed to transform int16: %m");
1825
1826 break;
1827 }
1828
1829 case SD_BUS_TYPE_UINT16: {
1830 uint16_t b;
1831
1832 r = sd_bus_message_read_basic(m, type, &b);
1833 if (r < 0)
1834 return bus_log_parse_error(r);
1835
1836 r = json_variant_new_unsigned(&v, b);
1837 if (r < 0)
1838 return log_error_errno(r, "Failed to transform uint16: %m");
1839
1840 break;
1841 }
1842
1843 case SD_BUS_TYPE_INT32: {
1844 int32_t b;
1845
1846 r = sd_bus_message_read_basic(m, type, &b);
1847 if (r < 0)
1848 return bus_log_parse_error(r);
1849
1850 r = json_variant_new_integer(&v, b);
1851 if (r < 0)
1852 return log_error_errno(r, "Failed to transform int32: %m");
1853
1854 break;
1855 }
1856
1857 case SD_BUS_TYPE_UINT32: {
1858 uint32_t b;
1859
1860 r = sd_bus_message_read_basic(m, type, &b);
1861 if (r < 0)
1862 return bus_log_parse_error(r);
1863
1864 r = json_variant_new_unsigned(&v, b);
1865 if (r < 0)
1866 return log_error_errno(r, "Failed to transform uint32: %m");
1867
1868 break;
1869 }
1870
1871 case SD_BUS_TYPE_INT64: {
1872 int64_t b;
1873
1874 r = sd_bus_message_read_basic(m, type, &b);
1875 if (r < 0)
1876 return bus_log_parse_error(r);
1877
1878 r = json_variant_new_integer(&v, b);
1879 if (r < 0)
1880 return log_error_errno(r, "Failed to transform int64: %m");
1881
1882 break;
1883 }
1884
1885 case SD_BUS_TYPE_UINT64: {
1886 uint64_t b;
1887
1888 r = sd_bus_message_read_basic(m, type, &b);
1889 if (r < 0)
1890 return bus_log_parse_error(r);
1891
1892 r = json_variant_new_unsigned(&v, b);
1893 if (r < 0)
1894 return log_error_errno(r, "Failed to transform uint64: %m");
1895
1896 break;
1897 }
1898
1899 case SD_BUS_TYPE_DOUBLE: {
1900 double d;
1901
1902 r = sd_bus_message_read_basic(m, type, &d);
1903 if (r < 0)
1904 return bus_log_parse_error(r);
1905
1906 r = json_variant_new_real(&v, d);
1907 if (r < 0)
1908 return log_error_errno(r, "Failed to transform double: %m");
1909
1910 break;
1911 }
1912
1913 case SD_BUS_TYPE_STRING:
1914 case SD_BUS_TYPE_OBJECT_PATH:
1915 case SD_BUS_TYPE_SIGNATURE: {
1916 const char *s;
1917
1918 r = sd_bus_message_read_basic(m, type, &s);
1919 if (r < 0)
1920 return bus_log_parse_error(r);
1921
1922 r = json_variant_new_string(&v, s);
1923 if (r < 0)
1924 return log_error_errno(r, "Failed to transform double: %m");
1925
1926 break;
1927 }
1928
1929 case SD_BUS_TYPE_UNIX_FD:
1930 r = sd_bus_message_read_basic(m, type, NULL);
1931 if (r < 0)
1932 return bus_log_parse_error(r);
1933
1934 r = json_variant_new_null(&v);
1935 if (r < 0)
1936 return log_error_errno(r, "Failed to transform fd: %m");
1937
1938 break;
1939
1940 case SD_BUS_TYPE_ARRAY:
1941 case SD_BUS_TYPE_VARIANT:
1942 case SD_BUS_TYPE_STRUCT:
1943 r = sd_bus_message_enter_container(m, type, contents);
1944 if (r < 0)
1945 return bus_log_parse_error(r);
1946
1947 if (type == SD_BUS_TYPE_VARIANT)
1948 r = json_transform_variant(m, contents, &v);
1949 else if (type == SD_BUS_TYPE_ARRAY && contents[0] == '{')
1950 r = json_transform_dict_array(m, &v);
1951 else
1952 r = json_transform_array_or_struct(m, &v);
1953 if (r < 0)
1954 return r;
1955
1956 r = sd_bus_message_exit_container(m);
1957 if (r < 0)
1958 return bus_log_parse_error(r);
1959
1960 break;
1961
1962 default:
1963 assert_not_reached("Unexpected element type");
1964 }
1965
1966 *ret = TAKE_PTR(v);
1967 return 0;
1968}
1969
1970static int json_transform_message(sd_bus_message *m, JsonVariant **ret) {
1971 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1972 const char *type;
1973 int r;
1974
1975 assert(m);
1976 assert(ret);
1977
1978 assert_se(type = sd_bus_message_get_signature(m, false));
1979
1980 r = json_transform_array_or_struct(m, &v);
1981 if (r < 0)
1982 return r;
1983
1984 r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("type", JSON_BUILD_STRING(type)),
1985 JSON_BUILD_PAIR("data", JSON_BUILD_VARIANT(v))));
1986 if (r < 0)
1987 return log_oom();
1988
1989 return 0;
1990}
1991
9bb31a0c
YW
1992static int call(int argc, char **argv, void *userdata) {
1993 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348
LP
1994 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1995 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
781fa938
LP
1996 int r;
1997
9bb31a0c
YW
1998 r = acquire_bus(false, &bus);
1999 if (r < 0)
2000 return r;
781fa938
LP
2001
2002 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]);
79f34de9
LP
2003 if (r < 0)
2004 return bus_log_create_error(r);
781fa938 2005
38051578
LP
2006 r = sd_bus_message_set_expect_reply(m, arg_expect_reply);
2007 if (r < 0)
2008 return bus_log_create_error(r);
2009
2010 r = sd_bus_message_set_auto_start(m, arg_auto_start);
2011 if (r < 0)
2012 return bus_log_create_error(r);
2013
2014 r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization);
2015 if (r < 0)
2016 return bus_log_create_error(r);
2017
781fa938
LP
2018 if (!isempty(argv[5])) {
2019 char **p;
2020
2021 p = argv+6;
2022
2023 r = message_append_cmdline(m, argv[5], &p);
2024 if (r < 0)
2025 return r;
2026
0747cde7 2027 if (*p)
16c347b3
ZJS
2028 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2029 "Too many parameters for signature.");
781fa938
LP
2030 }
2031
38051578
LP
2032 if (!arg_expect_reply) {
2033 r = sd_bus_send(bus, m, NULL);
48956c39
LP
2034 if (r < 0)
2035 return log_error_errno(r, "Failed to send message: %m");
38051578
LP
2036
2037 return 0;
2038 }
2039
a44b1081 2040 r = sd_bus_call(bus, m, arg_timeout, &error, &reply);
48956c39 2041 if (r < 0)
24a4e7ff 2042 return log_error_errno(r, "Call failed: %s", bus_error_message(&error, r));
781fa938
LP
2043
2044 r = sd_bus_message_is_empty(reply);
2045 if (r < 0)
2046 return bus_log_parse_error(r);
1fc55609 2047
781fa938 2048 if (r == 0 && !arg_quiet) {
1fc55609 2049
923d37c6 2050 if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
9cebb234
LP
2051 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
2052
923d37c6 2053 if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
0221d68a 2054 (void) pager_open(arg_pager_flags);
9cebb234
LP
2055
2056 r = json_transform_message(reply, &v);
2057 if (r < 0)
2058 return r;
2059
923d37c6 2060 json_variant_dump(v, arg_json_format_flags, NULL, NULL);
9cebb234
LP
2061
2062 } else if (arg_verbose) {
0221d68a 2063 (void) pager_open(arg_pager_flags);
1fc55609 2064
2b4a65b6 2065 r = sd_bus_message_dump(reply, stdout, 0);
1fc55609
LP
2066 if (r < 0)
2067 return r;
2068 } else {
2069
2070 fputs(sd_bus_message_get_signature(reply, true), stdout);
2071 fputc(' ', stdout);
2072
2073 r = format_cmdline(reply, stdout, false);
2074 if (r < 0)
2075 return bus_log_parse_error(r);
2076
2077 fputc('\n', stdout);
2078 }
d55192ad
LP
2079 }
2080
2081 return 0;
2082}
2083
143aea38
YW
2084static int emit_signal(int argc, char **argv, void *userdata) {
2085 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2086 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2087 int r;
2088
2089 r = acquire_bus(false, &bus);
2090 if (r < 0)
2091 return r;
2092
2093 r = sd_bus_message_new_signal(bus, &m, argv[1], argv[2], argv[3]);
2094 if (r < 0)
2095 return bus_log_create_error(r);
2096
2097 if (arg_destination) {
2098 r = sd_bus_message_set_destination(m, arg_destination);
2099 if (r < 0)
2100 return bus_log_create_error(r);
2101 }
2102
2103 r = sd_bus_message_set_auto_start(m, arg_auto_start);
2104 if (r < 0)
2105 return bus_log_create_error(r);
2106
2107 if (!isempty(argv[4])) {
2108 char **p;
2109
2110 p = argv+5;
2111
2112 r = message_append_cmdline(m, argv[4], &p);
2113 if (r < 0)
2114 return r;
2115
2116 if (*p)
16c347b3
ZJS
2117 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2118 "Too many parameters for signature.");
143aea38
YW
2119 }
2120
2121 r = sd_bus_send(bus, m, NULL);
2122 if (r < 0)
2123 return log_error_errno(r, "Failed to send signal: %m");
2124
2125 return 0;
2126}
2127
9bb31a0c
YW
2128static int get_property(int argc, char **argv, void *userdata) {
2129 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348 2130 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609 2131 char **i;
d55192ad
LP
2132 int r;
2133
9bb31a0c
YW
2134 r = acquire_bus(false, &bus);
2135 if (r < 0)
2136 return r;
d55192ad 2137
1fc55609 2138 STRV_FOREACH(i, argv + 4) {
4afd3348 2139 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1fc55609
LP
2140 const char *contents = NULL;
2141 char type;
d55192ad 2142
16c347b3
ZJS
2143 r = sd_bus_call_method(bus, argv[1], argv[2],
2144 "org.freedesktop.DBus.Properties", "Get",
2145 &error, &reply, "ss", argv[3], *i);
48956c39 2146 if (r < 0)
24a4e7ff
ZJS
2147 return log_error_errno(r, "Failed to get property %s on interface %s: %s",
2148 *i, argv[3],
2149 bus_error_message(&error, r));
d55192ad 2150
1fc55609 2151 r = sd_bus_message_peek_type(reply, &type, &contents);
d55192ad
LP
2152 if (r < 0)
2153 return bus_log_parse_error(r);
2154
1fc55609
LP
2155 r = sd_bus_message_enter_container(reply, 'v', contents);
2156 if (r < 0)
2157 return bus_log_parse_error(r);
d55192ad 2158
923d37c6 2159 if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
9cebb234
LP
2160 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
2161
923d37c6 2162 if (arg_json_format_flags & (JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
0221d68a 2163 (void) pager_open(arg_pager_flags);
9cebb234
LP
2164
2165 r = json_transform_variant(reply, contents, &v);
2166 if (r < 0)
2167 return r;
2168
923d37c6 2169 json_variant_dump(v, arg_json_format_flags, NULL, NULL);
9cebb234 2170
2de62253 2171 } else if (arg_verbose) {
0221d68a 2172 (void) pager_open(arg_pager_flags);
d55192ad 2173
2b4a65b6 2174 r = sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY);
d55192ad 2175 if (r < 0)
1fc55609
LP
2176 return r;
2177 } else {
2178 fputs(contents, stdout);
2179 fputc(' ', stdout);
d55192ad 2180
1fc55609 2181 r = format_cmdline(reply, stdout, false);
d55192ad
LP
2182 if (r < 0)
2183 return bus_log_parse_error(r);
2184
1fc55609 2185 fputc('\n', stdout);
d55192ad
LP
2186 }
2187
2188 r = sd_bus_message_exit_container(reply);
2189 if (r < 0)
2190 return bus_log_parse_error(r);
1fc55609 2191 }
d55192ad 2192
1fc55609
LP
2193 return 0;
2194}
d55192ad 2195
9bb31a0c
YW
2196static int set_property(int argc, char **argv, void *userdata) {
2197 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
4afd3348
LP
2198 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2199 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1fc55609
LP
2200 char **p;
2201 int r;
d55192ad 2202
9bb31a0c
YW
2203 r = acquire_bus(false, &bus);
2204 if (r < 0)
2205 return r;
d55192ad 2206
16c347b3
ZJS
2207 r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2],
2208 "org.freedesktop.DBus.Properties", "Set");
1fc55609
LP
2209 if (r < 0)
2210 return bus_log_create_error(r);
d55192ad 2211
1fc55609
LP
2212 r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
2213 if (r < 0)
2214 return bus_log_create_error(r);
d55192ad 2215
1fc55609
LP
2216 r = sd_bus_message_open_container(m, 'v', argv[5]);
2217 if (r < 0)
2218 return bus_log_create_error(r);
2219
9bb31a0c 2220 p = argv + 6;
1fc55609
LP
2221 r = message_append_cmdline(m, argv[5], &p);
2222 if (r < 0)
2223 return r;
2224
2225 r = sd_bus_message_close_container(m);
2226 if (r < 0)
2227 return bus_log_create_error(r);
2228
0747cde7
YW
2229 if (*p)
2230 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature.");
1fc55609 2231
a44b1081 2232 r = sd_bus_call(bus, m, arg_timeout, &error, NULL);
48956c39 2233 if (r < 0)
24a4e7ff
ZJS
2234 return log_error_errno(r, "Failed to set property %s on interface %s: %s",
2235 argv[4], argv[3],
2236 bus_error_message(&error, r));
781fa938
LP
2237
2238 return 0;
2239}
2240
1f849790 2241static int help(void) {
37ec0fdd
LP
2242 _cleanup_free_ char *link = NULL;
2243 int r;
2244
2245 r = terminal_urlify_man("busctl", "1", &link);
2246 if (r < 0)
2247 return log_oom();
2248
bb1a5700
LP
2249 (void) pager_open(arg_pager_flags);
2250
353b2baa
LP
2251 printf("%s [OPTIONS...] COMMAND ...\n\n"
2252 "%sIntrospect the D-Bus IPC bus.%s\n"
2253 "\nCommands:\n"
e1fac8a6
ZJS
2254 " list List bus names\n"
2255 " status [SERVICE] Show bus service, process or bus owner credentials\n"
2256 " monitor [SERVICE...] Show bus traffic\n"
2257 " capture [SERVICE...] Capture bus traffic as pcap\n"
2258 " tree [SERVICE...] Show object tree of service\n"
2259 " introspect SERVICE OBJECT [INTERFACE]\n"
2260 " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n"
2261 " Call a method\n"
2262 " emit OBJECT INTERFACE SIGNAL [SIGNATURE [ARGUMENT...]]\n"
2263 " Emit a signal\n"
2264 " get-property SERVICE OBJECT INTERFACE PROPERTY...\n"
2265 " Get property value\n"
2266 " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
2267 " Set property value\n"
2268 " help Show this help\n"
2269 "\nOptions:\n"
9ecb11f5
YW
2270 " -h --help Show this help\n"
2271 " --version Show package version\n"
2272 " --no-pager Do not pipe output into a pager\n"
2273 " --no-legend Do not show the headers and footers\n"
b683b82f 2274 " -l --full Do not ellipsize output\n"
9ecb11f5
YW
2275 " --system Connect to system bus\n"
2276 " --user Connect to user bus\n"
2277 " -H --host=[USER@]HOST Operate on remote host\n"
2278 " -M --machine=CONTAINER Operate on local container\n"
2279 " --address=ADDRESS Connect to bus specified by address\n"
2280 " --show-machine Show machine ID column in list\n"
2281 " --unique Only show unique names\n"
2282 " --acquired Only show acquired names\n"
2283 " --activatable Only show activatable names\n"
2284 " --match=MATCH Only show matching messages\n"
2285 " --size=SIZE Maximum length of captured packet\n"
2286 " --list Don't show tree, but simple object path list\n"
2287 " -q --quiet Don't show method call reply\n"
2288 " --verbose Show result values in long format\n"
2289 " --json=MODE Output as JSON\n"
2290 " -j Same as --json=pretty on tty, --json=short otherwise\n"
2291 " --expect-reply=BOOL Expect a method call reply\n"
2292 " --auto-start=BOOL Auto-start destination service\n"
38051578 2293 " --allow-interactive-authorization=BOOL\n"
9ecb11f5
YW
2294 " Allow interactive authorization for operation\n"
2295 " --timeout=SECS Maximum time to wait for method call completion\n"
2296 " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n"
2297 " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n"
2298 " system\n"
143aea38 2299 " --destination=SERVICE Destination service of a signal\n"
bc556335
DDM
2300 "\nSee the %s for details.\n",
2301 program_invocation_short_name,
2302 ansi_highlight(),
2303 ansi_normal(),
2304 link);
1f849790
LP
2305
2306 return 0;
2307}
2308
9bb31a0c
YW
2309static int verb_help(int argc, char **argv, void *userdata) {
2310 return help();
2311}
2312
1f849790
LP
2313static int parse_argv(int argc, char *argv[]) {
2314
2315 enum {
2316 ARG_VERSION = 0x100,
2317 ARG_NO_PAGER,
17d47d8d 2318 ARG_NO_LEGEND,
1f849790
LP
2319 ARG_SYSTEM,
2320 ARG_USER,
2321 ARG_ADDRESS,
2322 ARG_MATCH,
56e61788
LP
2323 ARG_SHOW_MACHINE,
2324 ARG_UNIQUE,
2325 ARG_ACQUIRED,
1f70b087
LP
2326 ARG_ACTIVATABLE,
2327 ARG_SIZE,
d9130355 2328 ARG_LIST,
1fc55609 2329 ARG_VERBOSE,
d5c8d823 2330 ARG_XML_INTERFACE,
38051578
LP
2331 ARG_EXPECT_REPLY,
2332 ARG_AUTO_START,
2333 ARG_ALLOW_INTERACTIVE_AUTHORIZATION,
a44b1081 2334 ARG_TIMEOUT,
40ed1a45 2335 ARG_AUGMENT_CREDS,
56d820b6 2336 ARG_WATCH_BIND,
9cebb234 2337 ARG_JSON,
143aea38 2338 ARG_DESTINATION,
1f849790
LP
2339 };
2340
2341 static const struct option options[] = {
9cebb234
LP
2342 { "help", no_argument, NULL, 'h' },
2343 { "version", no_argument, NULL, ARG_VERSION },
2344 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
2345 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
b683b82f 2346 { "full", no_argument, NULL, 'l' },
9cebb234
LP
2347 { "system", no_argument, NULL, ARG_SYSTEM },
2348 { "user", no_argument, NULL, ARG_USER },
2349 { "address", required_argument, NULL, ARG_ADDRESS },
2350 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
2351 { "unique", no_argument, NULL, ARG_UNIQUE },
2352 { "acquired", no_argument, NULL, ARG_ACQUIRED },
2353 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
2354 { "match", required_argument, NULL, ARG_MATCH },
2355 { "host", required_argument, NULL, 'H' },
2356 { "machine", required_argument, NULL, 'M' },
2357 { "size", required_argument, NULL, ARG_SIZE },
2358 { "list", no_argument, NULL, ARG_LIST },
2359 { "quiet", no_argument, NULL, 'q' },
2360 { "verbose", no_argument, NULL, ARG_VERBOSE },
d5c8d823 2361 { "xml-interface", no_argument, NULL, ARG_XML_INTERFACE },
9cebb234
LP
2362 { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY },
2363 { "auto-start", required_argument, NULL, ARG_AUTO_START },
38051578 2364 { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION },
9cebb234
LP
2365 { "timeout", required_argument, NULL, ARG_TIMEOUT },
2366 { "augment-creds", required_argument, NULL, ARG_AUGMENT_CREDS },
2367 { "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
2368 { "json", required_argument, NULL, ARG_JSON },
143aea38 2369 { "destination", required_argument, NULL, ARG_DESTINATION },
eb9da376 2370 {},
1f849790
LP
2371 };
2372
1f70b087 2373 int c, r;
1f849790
LP
2374
2375 assert(argc >= 0);
2376 assert(argv);
2377
150c430f 2378 while ((c = getopt_long(argc, argv, "hH:M:qjl", options, NULL)) >= 0)
1f849790
LP
2379
2380 switch (c) {
2381
2382 case 'h':
2383 return help();
2384
2385 case ARG_VERSION:
3f6fd1ba 2386 return version();
1f849790
LP
2387
2388 case ARG_NO_PAGER:
0221d68a 2389 arg_pager_flags |= PAGER_DISABLE;
1f849790
LP
2390 break;
2391
17d47d8d
TA
2392 case ARG_NO_LEGEND:
2393 arg_legend = false;
2394 break;
2395
b683b82f
YW
2396 case 'l':
2397 arg_full = true;
2398 break;
2399
1f849790
LP
2400 case ARG_USER:
2401 arg_user = true;
2402 break;
2403
2404 case ARG_SYSTEM:
2405 arg_user = false;
2406 break;
2407
2408 case ARG_ADDRESS:
2409 arg_address = optarg;
2410 break;
2411
56e61788
LP
2412 case ARG_SHOW_MACHINE:
2413 arg_show_machine = true;
2414 break;
2415
2416 case ARG_UNIQUE:
2417 arg_unique = true;
1f849790
LP
2418 break;
2419
56e61788
LP
2420 case ARG_ACQUIRED:
2421 arg_acquired = true;
2422 break;
2423
2424 case ARG_ACTIVATABLE:
2425 arg_activatable = true;
a4297f08
LP
2426 break;
2427
1f849790
LP
2428 case ARG_MATCH:
2429 if (strv_extend(&arg_matches, optarg) < 0)
2430 return log_oom();
2431 break;
2432
1f70b087 2433 case ARG_SIZE: {
59f448cf 2434 uint64_t sz;
1f70b087 2435
59f448cf 2436 r = parse_size(optarg, 1024, &sz);
48956c39 2437 if (r < 0)
3d9942de 2438 return log_error_errno(r, "Failed to parse size '%s': %m", optarg);
1f70b087 2439
baaa35ad
ZJS
2440 if ((uint64_t) (size_t) sz != sz)
2441 return log_error_errno(SYNTHETIC_ERRNO(E2BIG),
2442 "Size out of range.");
1f70b087 2443
59f448cf 2444 arg_snaplen = (size_t) sz;
1f70b087
LP
2445 break;
2446 }
2447
d9130355
LP
2448 case ARG_LIST:
2449 arg_list = true;
2450 break;
2451
d75edbd6
LP
2452 case 'H':
2453 arg_transport = BUS_TRANSPORT_REMOTE;
2454 arg_host = optarg;
2455 break;
2456
2457 case 'M':
de33fc62 2458 arg_transport = BUS_TRANSPORT_MACHINE;
d75edbd6
LP
2459 arg_host = optarg;
2460 break;
2461
781fa938
LP
2462 case 'q':
2463 arg_quiet = true;
2464 break;
2465
1fc55609
LP
2466 case ARG_VERBOSE:
2467 arg_verbose = true;
2468 break;
2469
d5c8d823
ZJS
2470 case ARG_XML_INTERFACE:
2471 arg_xml_interface = true;
2472 break;
2473
38051578
LP
2474 case ARG_EXPECT_REPLY:
2475 r = parse_boolean(optarg);
48956c39 2476 if (r < 0)
3d9942de 2477 return log_error_errno(r, "Failed to parse --expect-reply= parameter '%s': %m", optarg);
38051578 2478
5d904a6a 2479 arg_expect_reply = r;
38051578
LP
2480 break;
2481
38051578
LP
2482 case ARG_AUTO_START:
2483 r = parse_boolean(optarg);
48956c39 2484 if (r < 0)
3d9942de 2485 return log_error_errno(r, "Failed to parse --auto-start= parameter '%s': %m", optarg);
38051578 2486
5d904a6a 2487 arg_auto_start = r;
38051578
LP
2488 break;
2489
38051578
LP
2490 case ARG_ALLOW_INTERACTIVE_AUTHORIZATION:
2491 r = parse_boolean(optarg);
48956c39 2492 if (r < 0)
3d9942de 2493 return log_error_errno(r, "Failed to parse --allow-interactive-authorization= parameter '%s': %m", optarg);
38051578 2494
5d904a6a 2495 arg_allow_interactive_authorization = r;
38051578
LP
2496 break;
2497
a44b1081
LP
2498 case ARG_TIMEOUT:
2499 r = parse_sec(optarg, &arg_timeout);
48956c39 2500 if (r < 0)
3d9942de 2501 return log_error_errno(r, "Failed to parse --timeout= parameter '%s': %m", optarg);
a44b1081
LP
2502
2503 break;
2504
40ed1a45
LP
2505 case ARG_AUGMENT_CREDS:
2506 r = parse_boolean(optarg);
48956c39 2507 if (r < 0)
3d9942de 2508 return log_error_errno(r, "Failed to parse --augment-creds= parameter '%s': %m", optarg);
40ed1a45 2509
5d904a6a 2510 arg_augment_creds = r;
40ed1a45
LP
2511 break;
2512
56d820b6
LP
2513 case ARG_WATCH_BIND:
2514 r = parse_boolean(optarg);
48956c39 2515 if (r < 0)
3d9942de 2516 return log_error_errno(r, "Failed to parse --watch-bind= parameter '%s': %m", optarg);
56d820b6 2517
5d904a6a 2518 arg_watch_bind = r;
56d820b6
LP
2519 break;
2520
9cebb234 2521 case 'j':
923d37c6 2522 arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
9cebb234
LP
2523 break;
2524
2525 case ARG_JSON:
923d37c6
LP
2526 r = json_parse_cmdline_parameter_and_warn(optarg, &arg_json_format_flags);
2527 if (r <= 0)
2528 return r;
9cebb234
LP
2529
2530 break;
2531
143aea38
YW
2532 case ARG_DESTINATION:
2533 arg_destination = optarg;
2534 break;
2535
1f849790
LP
2536 case '?':
2537 return -EINVAL;
2538
2539 default:
eb9da376 2540 assert_not_reached("Unhandled option");
1f849790 2541 }
1f849790
LP
2542
2543 return 1;
2544}
2545
9bb31a0c 2546static int busctl_main(int argc, char *argv[]) {
9bb31a0c
YW
2547 static const Verb verbs[] = {
2548 { "list", VERB_ANY, 1, VERB_DEFAULT, list_bus_names },
2549 { "status", VERB_ANY, 2, 0, status },
2550 { "monitor", VERB_ANY, VERB_ANY, 0, verb_monitor },
2551 { "capture", VERB_ANY, VERB_ANY, 0, verb_capture },
2552 { "tree", VERB_ANY, VERB_ANY, 0, tree },
2553 { "introspect", 3, 4, 0, introspect },
2554 { "call", 5, VERB_ANY, 0, call },
143aea38 2555 { "emit", 4, VERB_ANY, 0, emit_signal },
9bb31a0c
YW
2556 { "get-property", 5, VERB_ANY, 0, get_property },
2557 { "set-property", 6, VERB_ANY, 0, set_property },
2558 { "help", VERB_ANY, VERB_ANY, 0, verb_help },
2559 {}
2560 };
1f849790 2561
9bb31a0c 2562 return dispatch_verb(argc, argv, verbs, NULL);
1f849790
LP
2563}
2564
360f3dc2 2565static int run(int argc, char *argv[]) {
1f849790
LP
2566 int r;
2567
d2acb93d 2568 log_setup();
1f849790
LP
2569
2570 r = parse_argv(argc, argv);
2571 if (r <= 0)
360f3dc2 2572 return r;
de1c301e 2573
360f3dc2 2574 return busctl_main(argc, argv);
de1c301e 2575}
360f3dc2
YW
2576
2577DEFINE_MAIN_FUNCTION(run);