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