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