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