]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
sd-bus: rename "connection name" to "description" for the sd-bus API too
[thirdparty/systemd.git] / src / libsystemd / sd-bus / busctl.c
CommitLineData
de1c301e
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
1f849790
LP
22#include <getopt.h>
23
89ffcd2a
LP
24#include "strv.h"
25#include "util.h"
26#include "log.h"
1f849790
LP
27#include "build.h"
28#include "pager.h"
89ffcd2a 29
de1c301e 30#include "sd-bus.h"
89ffcd2a
LP
31#include "bus-message.h"
32#include "bus-internal.h"
40ca29a1 33#include "bus-util.h"
2b5c5383 34#include "bus-dump.h"
de1c301e 35
1f849790 36static bool arg_no_pager = false;
17d47d8d 37static bool arg_legend = true;
1f849790 38static char *arg_address = NULL;
56e61788
LP
39static bool arg_unique = false;
40static bool arg_acquired = false;
41static bool arg_activatable = false;
42static bool arg_show_machine = false;
1f849790 43static char **arg_matches = NULL;
d75edbd6
LP
44static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
45static char *arg_host = NULL;
46static bool arg_user = false;
1f70b087 47static size_t arg_snaplen = 4096;
1f849790
LP
48
49static void pager_open_if_enabled(void) {
50
51 /* Cache result before we open the pager */
52 if (arg_no_pager)
53 return;
54
55 pager_open(false);
56}
57
58static int list_bus_names(sd_bus *bus, char **argv) {
71f2ab46 59 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
60 _cleanup_free_ char **merged = NULL;
61 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
62 char **i;
63 int r;
89ffcd2a 64 size_t max_i = 0;
5e2f14e6
LP
65 unsigned n = 0;
66 void *v;
67 char *k;
68 Iterator iterator;
de1c301e 69
1f849790 70 assert(bus);
de1c301e 71
56e61788 72 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
de1c301e
LP
73 if (r < 0) {
74 log_error("Failed to list names: %s", strerror(-r));
1f849790 75 return r;
de1c301e
LP
76 }
77
1f849790
LP
78 pager_open_if_enabled();
79
d5099efc 80 names = hashmap_new(&string_hash_ops);
5e2f14e6
LP
81 if (!names)
82 return log_oom();
89ffcd2a 83
5e2f14e6 84 STRV_FOREACH(i, acquired) {
71f2ab46
LP
85 max_i = MAX(max_i, strlen(*i));
86
5e2f14e6
LP
87 r = hashmap_put(names, *i, INT_TO_PTR(1));
88 if (r < 0) {
89 log_error("Failed to add to hashmap: %s", strerror(-r));
90 return r;
91 }
92 }
93
94 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
95 max_i = MAX(max_i, strlen(*i));
96
5e2f14e6
LP
97 r = hashmap_put(names, *i, INT_TO_PTR(2));
98 if (r < 0 && r != -EEXIST) {
99 log_error("Failed to add to hashmap: %s", strerror(-r));
100 return r;
101 }
102 }
103
104 merged = new(char*, hashmap_size(names) + 1);
105 HASHMAP_FOREACH_KEY(v, k, names, iterator)
106 merged[n++] = k;
107
108 merged[n] = NULL;
109 strv_sort(merged);
110
17d47d8d
TA
111 if (arg_legend) {
112 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
455971c1 113 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
89ffcd2a 114
17d47d8d
TA
115 if (arg_show_machine)
116 puts(" MACHINE");
117 else
118 putchar('\n');
119 }
a4297f08 120
5e2f14e6
LP
121 STRV_FOREACH(i, merged) {
122 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
123 sd_id128_t mid;
71f2ab46 124
5e2f14e6
LP
125 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
126 /* Activatable */
71f2ab46 127
5e2f14e6 128 printf("%-*s", (int) max_i, *i);
56e61788
LP
129 printf(" - - - (activatable) - - ");
130 if (arg_show_machine)
5e2f14e6 131 puts(" -");
56e61788
LP
132 else
133 putchar('\n');
71f2ab46
LP
134 continue;
135
5e2f14e6 136 }
de1c301e 137
56e61788
LP
138 if (!arg_unique && (*i)[0] == ':')
139 continue;
140
141 if (!arg_acquired && (*i)[0] != ':')
1f849790 142 continue;
89ffcd2a
LP
143
144 printf("%-*s", (int) max_i, *i);
de1c301e 145
056f95d0 146 r = sd_bus_get_name_creds(bus, *i,
14008e4e
LP
147 SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
148 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
455971c1 149 SD_BUS_CREDS_DESCRIPTION, &creds);
89ffcd2a 150 if (r >= 0) {
14008e4e 151 const char *unique, *session, *unit, *cn;
5b12334d
LP
152 pid_t pid;
153 uid_t uid;
de1c301e 154
5b12334d
LP
155 r = sd_bus_creds_get_pid(creds, &pid);
156 if (r >= 0) {
157 const char *comm = NULL;
de1c301e 158
5b12334d 159 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 160
5b12334d
LP
161 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
162 } else
a4297f08 163 fputs(" - - ", stdout);
89ffcd2a 164
5b12334d
LP
165 r = sd_bus_creds_get_uid(creds, &uid);
166 if (r >= 0) {
167 _cleanup_free_ char *u = NULL;
89ffcd2a 168
5b12334d
LP
169 u = uid_to_name(uid);
170 if (!u)
171 return log_oom();
89ffcd2a 172
5b12334d
LP
173 if (strlen(u) > 16)
174 u[16] = 0;
175
176 printf(" %-16s", u);
177 } else
a4297f08 178 fputs(" - ", stdout);
89ffcd2a 179
49b832c5
LP
180 r = sd_bus_creds_get_unique_name(creds, &unique);
181 if (r >= 0)
56e61788
LP
182 printf(" %-13s", unique);
183 else
184 fputs(" - ", stdout);
185
186 r = sd_bus_creds_get_unit(creds, &unit);
187 if (r >= 0) {
188 _cleanup_free_ char *e;
189
190 e = ellipsize(unit, 25, 100);
191 if (!e)
192 return log_oom();
193
194 printf(" %-25s", e);
195 } else
196 fputs(" - ", stdout);
197
198 r = sd_bus_creds_get_session(creds, &session);
199 if (r >= 0)
200 printf(" %-10s", session);
a4297f08 201 else
56e61788 202 fputs(" - ", stdout);
7b0b392f 203
455971c1 204 r = sd_bus_creds_get_description(creds, &cn);
14008e4e
LP
205 if (r >= 0)
206 printf(" %-19s", cn);
207 else
208 fputs(" - ", stdout);
209
7b0b392f 210 } else
14008e4e 211 printf(" - - - - - - - ");
a4297f08 212
56e61788 213 if (arg_show_machine) {
056f95d0 214 r = sd_bus_get_name_machine_id(bus, *i, &mid);
a4297f08
LP
215 if (r >= 0) {
216 char m[SD_ID128_STRING_MAX];
217 printf(" %s\n", sd_id128_to_string(mid, m));
218 } else
219 puts(" -");
56e61788
LP
220 } else
221 putchar('\n');
de1c301e
LP
222 }
223
1f849790
LP
224 return 0;
225}
226
1f70b087
LP
227static int message_dump(sd_bus_message *m, FILE *f) {
228 return bus_message_dump(m, f, true);
229}
230
231static int message_pcap(sd_bus_message *m, FILE *f) {
232 return bus_message_pcap_frame(m, arg_snaplen, f);
233}
234
235static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
b51f299a 236 bool added_something = false;
1f849790
LP
237 char **i;
238 int r;
239
240 STRV_FOREACH(i, argv+1) {
241 _cleanup_free_ char *m = NULL;
242
243 if (!service_name_is_valid(*i)) {
244 log_error("Invalid service name '%s'", *i);
245 return -EINVAL;
246 }
247
248 m = strjoin("sender='", *i, "'", NULL);
249 if (!m)
250 return log_oom();
251
19befb2d 252 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
1f849790
LP
253 if (r < 0) {
254 log_error("Failed to add match: %s", strerror(-r));
255 return r;
256 }
b51f299a
LP
257
258 added_something = true;
1f849790
LP
259 }
260
261 STRV_FOREACH(i, arg_matches) {
19befb2d 262 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
1f849790
LP
263 if (r < 0) {
264 log_error("Failed to add match: %s", strerror(-r));
265 return r;
266 }
b51f299a
LP
267
268 added_something = true;
269 }
270
271 if (!added_something) {
19befb2d 272 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
b51f299a
LP
273 if (r < 0) {
274 log_error("Failed to add match: %s", strerror(-r));
275 return r;
276 }
1f849790
LP
277 }
278
279 for (;;) {
280 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
281
282 r = sd_bus_process(bus, &m);
283 if (r < 0) {
284 log_error("Failed to process bus: %s", strerror(-r));
285 return r;
286 }
287
288 if (m) {
1f70b087 289 dump(m, stdout);
1f849790
LP
290 continue;
291 }
292
293 if (r > 0)
294 continue;
295
296 r = sd_bus_wait(bus, (uint64_t) -1);
297 if (r < 0) {
298 log_error("Failed to wait for bus: %s", strerror(-r));
299 return r;
300 }
301 }
95c4fe82 302}
1f849790 303
1f70b087
LP
304static int capture(sd_bus *bus, char *argv[]) {
305 int r;
306
307 if (isatty(fileno(stdout)) > 0) {
308 log_error("Refusing to write message data to console, please redirect output to a file.");
309 return -EINVAL;
310 }
311
312 bus_pcap_header(arg_snaplen, stdout);
313
314 r = monitor(bus, argv, message_pcap);
315 if (r < 0)
316 return r;
317
318 if (ferror(stdout)) {
319 log_error("Couldn't write capture file.");
320 return -EIO;
321 }
322
323 return r;
324}
325
95c4fe82
LP
326static int status(sd_bus *bus, char *argv[]) {
327 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
328 pid_t pid;
329 int r;
330
331 assert(bus);
332
333 if (strv_length(argv) != 2) {
334 log_error("Expects one argument.");
335 return -EINVAL;
336 }
337
338 r = parse_pid(argv[1], &pid);
339 if (r < 0)
056f95d0 340 r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
95c4fe82 341 else
151b9b96 342 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
95c4fe82
LP
343
344 if (r < 0) {
345 log_error("Failed to get credentials: %s", strerror(-r));
346 return r;
347 }
348
349 bus_creds_dump(creds, NULL);
350 return 0;
1f849790
LP
351}
352
353static int help(void) {
1f849790
LP
354 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
355 "Introspect the bus.\n\n"
d75edbd6
LP
356 " -h --help Show this help\n"
357 " --version Show package version\n"
a86a47ce 358 " --no-pager Do not pipe output into a pager\n"
17d47d8d 359 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
360 " --system Connect to system bus\n"
361 " --user Connect to user bus\n"
362 " -H --host=[USER@]HOST Operate on remote host\n"
363 " -M --machine=CONTAINER Operate on local container\n"
364 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
365 " --show-machine Show machine ID column in list\n"
366 " --unique Only show unique names\n"
367 " --acquired Only show acquired names\n"
368 " --activatable Only show activatable names\n"
b5dda4d8 369 " --match=MATCH Only show matching messages\n\n"
1f849790 370 "Commands:\n"
d75edbd6 371 " list List bus names\n"
d94fe1f1 372 " monitor [SERVICE...] Show bus traffic\n"
1f70b087 373 " capture [SERVICE...] Capture bus traffic as pcap\n"
56e61788 374 " status NAME Show name status\n"
601185b4
ZJS
375 " help Show this help\n"
376 , program_invocation_short_name);
1f849790
LP
377
378 return 0;
379}
380
381static int parse_argv(int argc, char *argv[]) {
382
383 enum {
384 ARG_VERSION = 0x100,
385 ARG_NO_PAGER,
17d47d8d 386 ARG_NO_LEGEND,
1f849790
LP
387 ARG_SYSTEM,
388 ARG_USER,
389 ARG_ADDRESS,
390 ARG_MATCH,
56e61788
LP
391 ARG_SHOW_MACHINE,
392 ARG_UNIQUE,
393 ARG_ACQUIRED,
1f70b087
LP
394 ARG_ACTIVATABLE,
395 ARG_SIZE,
1f849790
LP
396 };
397
398 static const struct option options[] = {
56e61788
LP
399 { "help", no_argument, NULL, 'h' },
400 { "version", no_argument, NULL, ARG_VERSION },
401 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 402 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
403 { "system", no_argument, NULL, ARG_SYSTEM },
404 { "user", no_argument, NULL, ARG_USER },
405 { "address", required_argument, NULL, ARG_ADDRESS },
406 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
407 { "unique", no_argument, NULL, ARG_UNIQUE },
408 { "acquired", no_argument, NULL, ARG_ACQUIRED },
409 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
410 { "match", required_argument, NULL, ARG_MATCH },
411 { "host", required_argument, NULL, 'H' },
412 { "machine", required_argument, NULL, 'M' },
1f70b087 413 { "size", required_argument, NULL, ARG_SIZE },
eb9da376 414 {},
1f849790
LP
415 };
416
1f70b087 417 int c, r;
1f849790
LP
418
419 assert(argc >= 0);
420 assert(argv);
421
601185b4 422 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1f849790
LP
423
424 switch (c) {
425
426 case 'h':
427 return help();
428
429 case ARG_VERSION:
430 puts(PACKAGE_STRING);
431 puts(SYSTEMD_FEATURES);
432 return 0;
433
434 case ARG_NO_PAGER:
435 arg_no_pager = true;
436 break;
437
17d47d8d
TA
438 case ARG_NO_LEGEND:
439 arg_legend = false;
440 break;
441
1f849790
LP
442 case ARG_USER:
443 arg_user = true;
444 break;
445
446 case ARG_SYSTEM:
447 arg_user = false;
448 break;
449
450 case ARG_ADDRESS:
451 arg_address = optarg;
452 break;
453
56e61788
LP
454 case ARG_SHOW_MACHINE:
455 arg_show_machine = true;
456 break;
457
458 case ARG_UNIQUE:
459 arg_unique = true;
1f849790
LP
460 break;
461
56e61788
LP
462 case ARG_ACQUIRED:
463 arg_acquired = true;
464 break;
465
466 case ARG_ACTIVATABLE:
467 arg_activatable = true;
a4297f08
LP
468 break;
469
1f849790
LP
470 case ARG_MATCH:
471 if (strv_extend(&arg_matches, optarg) < 0)
472 return log_oom();
473 break;
474
1f70b087
LP
475 case ARG_SIZE: {
476 off_t o;
477
478 r = parse_size(optarg, 0, &o);
479 if (r < 0) {
480 log_error("Failed to parse size: %s", optarg);
481 return r;
482 }
483
484 if ((off_t) (size_t) o != o) {
485 log_error("Size out of range.");
486 return -E2BIG;
487 }
488
489 arg_snaplen = (size_t) o;
490 break;
491 }
492
d75edbd6
LP
493 case 'H':
494 arg_transport = BUS_TRANSPORT_REMOTE;
495 arg_host = optarg;
496 break;
497
498 case 'M':
499 arg_transport = BUS_TRANSPORT_CONTAINER;
500 arg_host = optarg;
501 break;
502
1f849790
LP
503 case '?':
504 return -EINVAL;
505
506 default:
eb9da376 507 assert_not_reached("Unhandled option");
1f849790 508 }
1f849790 509
56e61788
LP
510 if (!arg_unique && !arg_acquired && !arg_activatable)
511 arg_unique = arg_acquired = arg_activatable = true;
512
1f849790
LP
513 return 1;
514}
515
516static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
517 assert(bus);
518
519 if (optind >= argc ||
520 streq(argv[optind], "list"))
521 return list_bus_names(bus, argv + optind);
522
523 if (streq(argv[optind], "monitor"))
1f70b087
LP
524 return monitor(bus, argv + optind, message_dump);
525
526 if (streq(argv[optind], "capture"))
527 return capture(bus, argv + optind);
1f849790 528
95c4fe82
LP
529 if (streq(argv[optind], "status"))
530 return status(bus, argv + optind);
531
1f849790
LP
532 if (streq(argv[optind], "help"))
533 return help();
534
535 log_error("Unknown command '%s'", argv[optind]);
536 return -EINVAL;
537}
538
539int main(int argc, char *argv[]) {
24996861 540 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1f849790
LP
541 int r;
542
543 log_parse_environment();
544 log_open();
545
546 r = parse_argv(argc, argv);
547 if (r <= 0)
548 goto finish;
549
09365592
LP
550 r = sd_bus_new(&bus);
551 if (r < 0) {
552 log_error("Failed to allocate bus: %s", strerror(-r));
553 goto finish;
554 }
555
1f70b087
LP
556 if (streq_ptr(argv[optind], "monitor") ||
557 streq_ptr(argv[optind], "capture")) {
09365592
LP
558
559 r = sd_bus_set_monitor(bus, true);
1f849790 560 if (r < 0) {
09365592 561 log_error("Failed to set monitor mode: %s", strerror(-r));
1f849790
LP
562 goto finish;
563 }
d0ce7734
LP
564
565 r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL);
566 if (r < 0) {
567 log_error("Failed to enable credentials: %s", strerror(-r));
568 goto finish;
569 }
570
571 r = sd_bus_negotiate_timestamp(bus, true);
572 if (r < 0) {
573 log_error("Failed to enable timestamps: %s", strerror(-r));
574 goto finish;
575 }
576
577 r = sd_bus_negotiate_fds(bus, true);
578 if (r < 0) {
579 log_error("Failed to enable fds: %s", strerror(-r));
580 goto finish;
581 }
09365592 582 }
1f849790 583
09365592 584 if (arg_address)
1f849790 585 r = sd_bus_set_address(bus, arg_address);
09365592
LP
586 else {
587 switch (arg_transport) {
1f849790 588
09365592
LP
589 case BUS_TRANSPORT_LOCAL:
590 if (arg_user)
591 r = bus_set_address_user(bus);
592 else
593 r = bus_set_address_system(bus);
594 break;
595
596 case BUS_TRANSPORT_REMOTE:
597 r = bus_set_address_system_remote(bus, arg_host);
598 break;
599
600 case BUS_TRANSPORT_CONTAINER:
601 r = bus_set_address_system_container(bus, arg_host);
602 break;
603
604 default:
605 assert_not_reached("Hmm, unknown transport type.");
1f849790 606 }
09365592
LP
607 }
608 if (r < 0) {
609 log_error("Failed to set address: %s", strerror(-r));
610 goto finish;
611 }
1f849790 612
09365592
LP
613 r = sd_bus_set_bus_client(bus, true);
614 if (r < 0) {
615 log_error("Failed to set bus client: %s", strerror(-r));
616 goto finish;
617 }
1f849790 618
09365592 619 r = sd_bus_start(bus);
1f849790
LP
620 if (r < 0) {
621 log_error("Failed to connect to bus: %s", strerror(-r));
622 goto finish;
623 }
624
625 r = busctl_main(bus, argc, argv);
626
627finish:
628 pager_close();
d75edbd6 629
1f849790 630 strv_free(arg_matches);
de1c301e 631
de1c301e
LP
632 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
633}