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