]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/busctl.c
core: expose missing busname properties on the bus
[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;
1f849790
LP
47
48static void pager_open_if_enabled(void) {
49
50 /* Cache result before we open the pager */
51 if (arg_no_pager)
52 return;
53
54 pager_open(false);
55}
56
57static int list_bus_names(sd_bus *bus, char **argv) {
71f2ab46 58 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
5e2f14e6
LP
59 _cleanup_free_ char **merged = NULL;
60 _cleanup_hashmap_free_ Hashmap *names = NULL;
de1c301e
LP
61 char **i;
62 int r;
89ffcd2a 63 size_t max_i = 0;
5e2f14e6
LP
64 unsigned n = 0;
65 void *v;
66 char *k;
67 Iterator iterator;
de1c301e 68
1f849790 69 assert(bus);
de1c301e 70
56e61788 71 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
de1c301e
LP
72 if (r < 0) {
73 log_error("Failed to list names: %s", strerror(-r));
1f849790 74 return r;
de1c301e
LP
75 }
76
1f849790
LP
77 pager_open_if_enabled();
78
5e2f14e6
LP
79 names = hashmap_new(string_hash_func, string_compare_func);
80 if (!names)
81 return log_oom();
89ffcd2a 82
5e2f14e6 83 STRV_FOREACH(i, acquired) {
71f2ab46
LP
84 max_i = MAX(max_i, strlen(*i));
85
5e2f14e6
LP
86 r = hashmap_put(names, *i, INT_TO_PTR(1));
87 if (r < 0) {
88 log_error("Failed to add to hashmap: %s", strerror(-r));
89 return r;
90 }
91 }
92
93 STRV_FOREACH(i, activatable) {
89ffcd2a
LP
94 max_i = MAX(max_i, strlen(*i));
95
5e2f14e6
LP
96 r = hashmap_put(names, *i, INT_TO_PTR(2));
97 if (r < 0 && r != -EEXIST) {
98 log_error("Failed to add to hashmap: %s", strerror(-r));
99 return r;
100 }
101 }
102
103 merged = new(char*, hashmap_size(names) + 1);
104 HASHMAP_FOREACH_KEY(v, k, names, iterator)
105 merged[n++] = k;
106
107 merged[n] = NULL;
108 strv_sort(merged);
109
17d47d8d
TA
110 if (arg_legend) {
111 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
112 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "CONNECTION-NAME");
89ffcd2a 113
17d47d8d
TA
114 if (arg_show_machine)
115 puts(" MACHINE");
116 else
117 putchar('\n');
118 }
a4297f08 119
5e2f14e6
LP
120 STRV_FOREACH(i, merged) {
121 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
122 sd_id128_t mid;
71f2ab46 123
5e2f14e6
LP
124 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
125 /* Activatable */
71f2ab46 126
5e2f14e6 127 printf("%-*s", (int) max_i, *i);
56e61788
LP
128 printf(" - - - (activatable) - - ");
129 if (arg_show_machine)
5e2f14e6 130 puts(" -");
56e61788
LP
131 else
132 putchar('\n');
71f2ab46
LP
133 continue;
134
5e2f14e6 135 }
de1c301e 136
56e61788
LP
137 if (!arg_unique && (*i)[0] == ':')
138 continue;
139
140 if (!arg_acquired && (*i)[0] != ':')
1f849790 141 continue;
89ffcd2a
LP
142
143 printf("%-*s", (int) max_i, *i);
de1c301e 144
14008e4e
LP
145 r = sd_bus_get_owner(bus, *i,
146 SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
147 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
148 SD_BUS_CREDS_CONNECTION_NAME, &creds);
89ffcd2a 149 if (r >= 0) {
14008e4e 150 const char *unique, *session, *unit, *cn;
5b12334d
LP
151 pid_t pid;
152 uid_t uid;
de1c301e 153
5b12334d
LP
154 r = sd_bus_creds_get_pid(creds, &pid);
155 if (r >= 0) {
156 const char *comm = NULL;
de1c301e 157
5b12334d 158 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 159
5b12334d
LP
160 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
161 } else
a4297f08 162 fputs(" - - ", stdout);
89ffcd2a 163
5b12334d
LP
164 r = sd_bus_creds_get_uid(creds, &uid);
165 if (r >= 0) {
166 _cleanup_free_ char *u = NULL;
89ffcd2a 167
5b12334d
LP
168 u = uid_to_name(uid);
169 if (!u)
170 return log_oom();
89ffcd2a 171
5b12334d
LP
172 if (strlen(u) > 16)
173 u[16] = 0;
174
175 printf(" %-16s", u);
176 } else
a4297f08 177 fputs(" - ", stdout);
89ffcd2a 178
49b832c5
LP
179 r = sd_bus_creds_get_unique_name(creds, &unique);
180 if (r >= 0)
56e61788
LP
181 printf(" %-13s", unique);
182 else
183 fputs(" - ", stdout);
184
185 r = sd_bus_creds_get_unit(creds, &unit);
186 if (r >= 0) {
187 _cleanup_free_ char *e;
188
189 e = ellipsize(unit, 25, 100);
190 if (!e)
191 return log_oom();
192
193 printf(" %-25s", e);
194 } else
195 fputs(" - ", stdout);
196
197 r = sd_bus_creds_get_session(creds, &session);
198 if (r >= 0)
199 printf(" %-10s", session);
a4297f08 200 else
56e61788 201 fputs(" - ", stdout);
7b0b392f 202
14008e4e
LP
203 r = sd_bus_creds_get_connection_name(creds, &cn);
204 if (r >= 0)
205 printf(" %-19s", cn);
206 else
207 fputs(" - ", stdout);
208
7b0b392f 209 } else
14008e4e 210 printf(" - - - - - - - ");
a4297f08 211
56e61788 212 if (arg_show_machine) {
a4297f08
LP
213 r = sd_bus_get_owner_machine_id(bus, *i, &mid);
214 if (r >= 0) {
215 char m[SD_ID128_STRING_MAX];
216 printf(" %s\n", sd_id128_to_string(mid, m));
217 } else
218 puts(" -");
56e61788
LP
219 } else
220 putchar('\n');
de1c301e
LP
221 }
222
1f849790
LP
223 return 0;
224}
225
226static int monitor(sd_bus *bus, char *argv[]) {
b51f299a 227 bool added_something = false;
1f849790
LP
228 char **i;
229 int r;
230
231 STRV_FOREACH(i, argv+1) {
232 _cleanup_free_ char *m = NULL;
233
234 if (!service_name_is_valid(*i)) {
235 log_error("Invalid service name '%s'", *i);
236 return -EINVAL;
237 }
238
239 m = strjoin("sender='", *i, "'", NULL);
240 if (!m)
241 return log_oom();
242
243 r = sd_bus_add_match(bus, m, NULL, NULL);
244 if (r < 0) {
245 log_error("Failed to add match: %s", strerror(-r));
246 return r;
247 }
b51f299a
LP
248
249 added_something = true;
1f849790
LP
250 }
251
252 STRV_FOREACH(i, arg_matches) {
253 r = sd_bus_add_match(bus, *i, NULL, NULL);
254 if (r < 0) {
255 log_error("Failed to add match: %s", strerror(-r));
256 return r;
257 }
b51f299a
LP
258
259 added_something = true;
260 }
261
262 if (!added_something) {
263 r = sd_bus_add_match(bus, "", NULL, NULL);
264 if (r < 0) {
265 log_error("Failed to add match: %s", strerror(-r));
266 return r;
267 }
1f849790
LP
268 }
269
270 for (;;) {
271 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
272
273 r = sd_bus_process(bus, &m);
274 if (r < 0) {
275 log_error("Failed to process bus: %s", strerror(-r));
276 return r;
277 }
278
279 if (m) {
c430fee6 280 bus_message_dump(m, stdout, true);
1f849790
LP
281 continue;
282 }
283
284 if (r > 0)
285 continue;
286
287 r = sd_bus_wait(bus, (uint64_t) -1);
288 if (r < 0) {
289 log_error("Failed to wait for bus: %s", strerror(-r));
290 return r;
291 }
292 }
95c4fe82 293}
1f849790 294
95c4fe82
LP
295static int status(sd_bus *bus, char *argv[]) {
296 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
297 pid_t pid;
298 int r;
299
300 assert(bus);
301
302 if (strv_length(argv) != 2) {
303 log_error("Expects one argument.");
304 return -EINVAL;
305 }
306
307 r = parse_pid(argv[1], &pid);
308 if (r < 0)
309 r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
310 else
151b9b96 311 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
95c4fe82
LP
312
313 if (r < 0) {
314 log_error("Failed to get credentials: %s", strerror(-r));
315 return r;
316 }
317
318 bus_creds_dump(creds, NULL);
319 return 0;
1f849790
LP
320}
321
322static int help(void) {
323
324 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
325 "Introspect the bus.\n\n"
d75edbd6
LP
326 " -h --help Show this help\n"
327 " --version Show package version\n"
a86a47ce 328 " --no-pager Do not pipe output into a pager\n"
17d47d8d 329 " --no-legend Do not show the headers and footers\n"
d75edbd6
LP
330 " --system Connect to system bus\n"
331 " --user Connect to user bus\n"
332 " -H --host=[USER@]HOST Operate on remote host\n"
333 " -M --machine=CONTAINER Operate on local container\n"
334 " --address=ADDRESS Connect to bus specified by address\n"
56e61788
LP
335 " --show-machine Show machine ID column in list\n"
336 " --unique Only show unique names\n"
337 " --acquired Only show acquired names\n"
338 " --activatable Only show activatable names\n"
b5dda4d8 339 " --match=MATCH Only show matching messages\n\n"
1f849790 340 "Commands:\n"
d75edbd6 341 " list List bus names\n"
d94fe1f1 342 " monitor [SERVICE...] Show bus traffic\n"
56e61788 343 " status NAME Show name status\n"
86cb0691 344 " help Show this help\n",
1f849790
LP
345 program_invocation_short_name);
346
347 return 0;
348}
349
350static int parse_argv(int argc, char *argv[]) {
351
352 enum {
353 ARG_VERSION = 0x100,
354 ARG_NO_PAGER,
17d47d8d 355 ARG_NO_LEGEND,
1f849790
LP
356 ARG_SYSTEM,
357 ARG_USER,
358 ARG_ADDRESS,
359 ARG_MATCH,
56e61788
LP
360 ARG_SHOW_MACHINE,
361 ARG_UNIQUE,
362 ARG_ACQUIRED,
363 ARG_ACTIVATABLE
1f849790
LP
364 };
365
366 static const struct option options[] = {
56e61788
LP
367 { "help", no_argument, NULL, 'h' },
368 { "version", no_argument, NULL, ARG_VERSION },
369 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
17d47d8d 370 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
56e61788
LP
371 { "system", no_argument, NULL, ARG_SYSTEM },
372 { "user", no_argument, NULL, ARG_USER },
373 { "address", required_argument, NULL, ARG_ADDRESS },
374 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
375 { "unique", no_argument, NULL, ARG_UNIQUE },
376 { "acquired", no_argument, NULL, ARG_ACQUIRED },
377 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
378 { "match", required_argument, NULL, ARG_MATCH },
379 { "host", required_argument, NULL, 'H' },
380 { "machine", required_argument, NULL, 'M' },
eb9da376 381 {},
1f849790
LP
382 };
383
384 int c;
385
386 assert(argc >= 0);
387 assert(argv);
388
d75edbd6 389 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
1f849790
LP
390
391 switch (c) {
392
393 case 'h':
394 return help();
395
396 case ARG_VERSION:
397 puts(PACKAGE_STRING);
398 puts(SYSTEMD_FEATURES);
399 return 0;
400
401 case ARG_NO_PAGER:
402 arg_no_pager = true;
403 break;
404
17d47d8d
TA
405 case ARG_NO_LEGEND:
406 arg_legend = false;
407 break;
408
1f849790
LP
409 case ARG_USER:
410 arg_user = true;
411 break;
412
413 case ARG_SYSTEM:
414 arg_user = false;
415 break;
416
417 case ARG_ADDRESS:
418 arg_address = optarg;
419 break;
420
56e61788
LP
421 case ARG_SHOW_MACHINE:
422 arg_show_machine = true;
423 break;
424
425 case ARG_UNIQUE:
426 arg_unique = true;
1f849790
LP
427 break;
428
56e61788
LP
429 case ARG_ACQUIRED:
430 arg_acquired = true;
431 break;
432
433 case ARG_ACTIVATABLE:
434 arg_activatable = true;
a4297f08
LP
435 break;
436
1f849790
LP
437 case ARG_MATCH:
438 if (strv_extend(&arg_matches, optarg) < 0)
439 return log_oom();
440 break;
441
d75edbd6
LP
442 case 'H':
443 arg_transport = BUS_TRANSPORT_REMOTE;
444 arg_host = optarg;
445 break;
446
447 case 'M':
448 arg_transport = BUS_TRANSPORT_CONTAINER;
449 arg_host = optarg;
450 break;
451
1f849790
LP
452 case '?':
453 return -EINVAL;
454
455 default:
eb9da376 456 assert_not_reached("Unhandled option");
1f849790
LP
457 }
458 }
459
56e61788
LP
460 if (!arg_unique && !arg_acquired && !arg_activatable)
461 arg_unique = arg_acquired = arg_activatable = true;
462
1f849790
LP
463 return 1;
464}
465
466static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
467 assert(bus);
468
469 if (optind >= argc ||
470 streq(argv[optind], "list"))
471 return list_bus_names(bus, argv + optind);
472
473 if (streq(argv[optind], "monitor"))
474 return monitor(bus, argv + optind);
475
95c4fe82
LP
476 if (streq(argv[optind], "status"))
477 return status(bus, argv + optind);
478
1f849790
LP
479 if (streq(argv[optind], "help"))
480 return help();
481
482 log_error("Unknown command '%s'", argv[optind]);
483 return -EINVAL;
484}
485
486int main(int argc, char *argv[]) {
487 _cleanup_bus_unref_ sd_bus *bus = NULL;
488 int r;
489
490 log_parse_environment();
491 log_open();
492
493 r = parse_argv(argc, argv);
494 if (r <= 0)
495 goto finish;
496
09365592
LP
497 r = sd_bus_new(&bus);
498 if (r < 0) {
499 log_error("Failed to allocate bus: %s", strerror(-r));
500 goto finish;
501 }
502
503 if (streq_ptr(argv[optind], "monitor")) {
504
505 r = sd_bus_set_monitor(bus, true);
1f849790 506 if (r < 0) {
09365592 507 log_error("Failed to set monitor mode: %s", strerror(-r));
1f849790
LP
508 goto finish;
509 }
09365592 510 }
1f849790 511
09365592 512 if (arg_address)
1f849790 513 r = sd_bus_set_address(bus, arg_address);
09365592
LP
514 else {
515 switch (arg_transport) {
1f849790 516
09365592
LP
517 case BUS_TRANSPORT_LOCAL:
518 if (arg_user)
519 r = bus_set_address_user(bus);
520 else
521 r = bus_set_address_system(bus);
522 break;
523
524 case BUS_TRANSPORT_REMOTE:
525 r = bus_set_address_system_remote(bus, arg_host);
526 break;
527
528 case BUS_TRANSPORT_CONTAINER:
529 r = bus_set_address_system_container(bus, arg_host);
530 break;
531
532 default:
533 assert_not_reached("Hmm, unknown transport type.");
1f849790 534 }
09365592
LP
535 }
536 if (r < 0) {
537 log_error("Failed to set address: %s", strerror(-r));
538 goto finish;
539 }
1f849790 540
09365592
LP
541 r = sd_bus_set_bus_client(bus, true);
542 if (r < 0) {
543 log_error("Failed to set bus client: %s", strerror(-r));
544 goto finish;
545 }
1f849790 546
09365592 547 r = sd_bus_start(bus);
1f849790
LP
548 if (r < 0) {
549 log_error("Failed to connect to bus: %s", strerror(-r));
550 goto finish;
551 }
552
553 r = busctl_main(bus, argc, argv);
554
555finish:
556 pager_close();
d75edbd6 557
1f849790 558 strv_free(arg_matches);
de1c301e 559
de1c301e
LP
560 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
561}