]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/busctl.c
sd-bus: add proper monitoring API
[thirdparty/systemd.git] / src / libsystemd / sd-bus / busctl.c
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
22 #include <getopt.h>
23
24 #include "strv.h"
25 #include "util.h"
26 #include "log.h"
27 #include "build.h"
28 #include "pager.h"
29
30 #include "sd-bus.h"
31 #include "bus-message.h"
32 #include "bus-internal.h"
33 #include "bus-util.h"
34 #include "bus-dump.h"
35
36 static bool arg_no_pager = false;
37 static bool arg_legend = true;
38 static char *arg_address = NULL;
39 static bool arg_unique = false;
40 static bool arg_acquired = false;
41 static bool arg_activatable = false;
42 static bool arg_show_machine = false;
43 static char **arg_matches = NULL;
44 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
45 static char *arg_host = NULL;
46 static bool arg_user = false;
47
48 static 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
57 static int list_bus_names(sd_bus *bus, char **argv) {
58 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
59 _cleanup_free_ char **merged = NULL;
60 _cleanup_hashmap_free_ Hashmap *names = NULL;
61 char **i;
62 int r;
63 size_t max_i = 0;
64 unsigned n = 0;
65 void *v;
66 char *k;
67 Iterator iterator;
68
69 assert(bus);
70
71 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
72 if (r < 0) {
73 log_error("Failed to list names: %s", strerror(-r));
74 return r;
75 }
76
77 pager_open_if_enabled();
78
79 names = hashmap_new(string_hash_func, string_compare_func);
80 if (!names)
81 return log_oom();
82
83 STRV_FOREACH(i, acquired) {
84 max_i = MAX(max_i, strlen(*i));
85
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) {
94 max_i = MAX(max_i, strlen(*i));
95
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
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");
113
114 if (arg_show_machine)
115 puts(" MACHINE");
116 else
117 putchar('\n');
118 }
119
120 STRV_FOREACH(i, merged) {
121 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
122 sd_id128_t mid;
123
124 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
125 /* Activatable */
126
127 printf("%-*s", (int) max_i, *i);
128 printf(" - - - (activatable) - - ");
129 if (arg_show_machine)
130 puts(" -");
131 else
132 putchar('\n');
133 continue;
134
135 }
136
137 if (!arg_unique && (*i)[0] == ':')
138 continue;
139
140 if (!arg_acquired && (*i)[0] != ':')
141 continue;
142
143 printf("%-*s", (int) max_i, *i);
144
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);
149 if (r >= 0) {
150 const char *unique, *session, *unit, *cn;
151 pid_t pid;
152 uid_t uid;
153
154 r = sd_bus_creds_get_pid(creds, &pid);
155 if (r >= 0) {
156 const char *comm = NULL;
157
158 sd_bus_creds_get_comm(creds, &comm);
159
160 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
161 } else
162 fputs(" - - ", stdout);
163
164 r = sd_bus_creds_get_uid(creds, &uid);
165 if (r >= 0) {
166 _cleanup_free_ char *u = NULL;
167
168 u = uid_to_name(uid);
169 if (!u)
170 return log_oom();
171
172 if (strlen(u) > 16)
173 u[16] = 0;
174
175 printf(" %-16s", u);
176 } else
177 fputs(" - ", stdout);
178
179 r = sd_bus_creds_get_unique_name(creds, &unique);
180 if (r >= 0)
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);
200 else
201 fputs(" - ", stdout);
202
203 r = sd_bus_creds_get_connection_name(creds, &cn);
204 if (r >= 0)
205 printf(" %-19s", cn);
206 else
207 fputs(" - ", stdout);
208
209 } else
210 printf(" - - - - - - - ");
211
212 if (arg_show_machine) {
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(" -");
219 } else
220 putchar('\n');
221 }
222
223 return 0;
224 }
225
226 static int monitor(sd_bus *bus, char *argv[]) {
227 bool added_something = false;
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 }
248
249 added_something = true;
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 }
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 }
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) {
280 bus_message_dump(m, stdout, true);
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 }
293 }
294
295 static 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
311 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
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;
320 }
321
322 static int help(void) {
323
324 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
325 "Introspect the bus.\n\n"
326 " -h --help Show this help\n"
327 " --version Show package version\n"
328 " --no-pager Do not pipe output into a pager\n"
329 " --no-legend Do not show the headers and footers\n"
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"
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"
339 " --match=MATCH Only show matching messages\n\n"
340 "Commands:\n"
341 " list List bus names\n"
342 " monitor [SERVICE...] Show bus traffic\n"
343 " status NAME Show name status\n"
344 " help Show this help\n",
345 program_invocation_short_name);
346
347 return 0;
348 }
349
350 static int parse_argv(int argc, char *argv[]) {
351
352 enum {
353 ARG_VERSION = 0x100,
354 ARG_NO_PAGER,
355 ARG_NO_LEGEND,
356 ARG_SYSTEM,
357 ARG_USER,
358 ARG_ADDRESS,
359 ARG_MATCH,
360 ARG_SHOW_MACHINE,
361 ARG_UNIQUE,
362 ARG_ACQUIRED,
363 ARG_ACTIVATABLE
364 };
365
366 static const struct option options[] = {
367 { "help", no_argument, NULL, 'h' },
368 { "version", no_argument, NULL, ARG_VERSION },
369 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
370 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
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' },
381 {},
382 };
383
384 int c;
385
386 assert(argc >= 0);
387 assert(argv);
388
389 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
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
405 case ARG_NO_LEGEND:
406 arg_legend = false;
407 break;
408
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
421 case ARG_SHOW_MACHINE:
422 arg_show_machine = true;
423 break;
424
425 case ARG_UNIQUE:
426 arg_unique = true;
427 break;
428
429 case ARG_ACQUIRED:
430 arg_acquired = true;
431 break;
432
433 case ARG_ACTIVATABLE:
434 arg_activatable = true;
435 break;
436
437 case ARG_MATCH:
438 if (strv_extend(&arg_matches, optarg) < 0)
439 return log_oom();
440 break;
441
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
452 case '?':
453 return -EINVAL;
454
455 default:
456 assert_not_reached("Unhandled option");
457 }
458 }
459
460 if (!arg_unique && !arg_acquired && !arg_activatable)
461 arg_unique = arg_acquired = arg_activatable = true;
462
463 return 1;
464 }
465
466 static 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
476 if (streq(argv[optind], "status"))
477 return status(bus, argv + optind);
478
479 if (streq(argv[optind], "help"))
480 return help();
481
482 log_error("Unknown command '%s'", argv[optind]);
483 return -EINVAL;
484 }
485
486 int 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
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);
506 if (r < 0) {
507 log_error("Failed to set monitor mode: %s", strerror(-r));
508 goto finish;
509 }
510 }
511
512 if (arg_address)
513 r = sd_bus_set_address(bus, arg_address);
514 else {
515 switch (arg_transport) {
516
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.");
534 }
535 }
536 if (r < 0) {
537 log_error("Failed to set address: %s", strerror(-r));
538 goto finish;
539 }
540
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 }
546
547 r = sd_bus_start(bus);
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
555 finish:
556 pager_close();
557
558 strv_free(arg_matches);
559
560 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
561 }