]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/busctl.c
libsystemd: split up into subdirs
[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 char *arg_address = NULL;
38 static bool arg_unique = false;
39 static bool arg_acquired = false;
40 static bool arg_activatable = false;
41 static bool arg_show_machine = false;
42 static char **arg_matches = NULL;
43 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
44 static char *arg_host = NULL;
45 static bool arg_user = false;
46
47 static 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
56 static int list_bus_names(sd_bus *bus, char **argv) {
57 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
58 _cleanup_free_ char **merged = NULL;
59 _cleanup_hashmap_free_ Hashmap *names = NULL;
60 char **i;
61 int r;
62 size_t max_i = 0;
63 unsigned n = 0;
64 void *v;
65 char *k;
66 Iterator iterator;
67
68 assert(bus);
69
70 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
71 if (r < 0) {
72 log_error("Failed to list names: %s", strerror(-r));
73 return r;
74 }
75
76 pager_open_if_enabled();
77
78 names = hashmap_new(string_hash_func, string_compare_func);
79 if (!names)
80 return log_oom();
81
82 STRV_FOREACH(i, acquired) {
83 max_i = MAX(max_i, strlen(*i));
84
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) {
93 max_i = MAX(max_i, strlen(*i));
94
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
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");
111
112 if (arg_show_machine)
113 puts(" MACHINE");
114 else
115 putchar('\n');
116
117 STRV_FOREACH(i, merged) {
118 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
119 sd_id128_t mid;
120
121 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
122 /* Activatable */
123
124 printf("%-*s", (int) max_i, *i);
125 printf(" - - - (activatable) - - ");
126 if (arg_show_machine)
127 puts(" -");
128 else
129 putchar('\n');
130 continue;
131
132 }
133
134 if (!arg_unique && (*i)[0] == ':')
135 continue;
136
137 if (!arg_acquired && (*i)[0] != ':')
138 continue;
139
140 printf("%-*s", (int) max_i, *i);
141
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);
143 if (r >= 0) {
144 const char *unique, *session, *unit;
145 pid_t pid;
146 uid_t uid;
147
148 r = sd_bus_creds_get_pid(creds, &pid);
149 if (r >= 0) {
150 const char *comm = NULL;
151
152 sd_bus_creds_get_comm(creds, &comm);
153
154 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
155 } else
156 fputs(" - - ", stdout);
157
158 r = sd_bus_creds_get_uid(creds, &uid);
159 if (r >= 0) {
160 _cleanup_free_ char *u = NULL;
161
162 u = uid_to_name(uid);
163 if (!u)
164 return log_oom();
165
166 if (strlen(u) > 16)
167 u[16] = 0;
168
169 printf(" %-16s", u);
170 } else
171 fputs(" - ", stdout);
172
173 r = sd_bus_creds_get_unique_name(creds, &unique);
174 if (r >= 0)
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);
194 else
195 fputs(" - ", stdout);
196
197 } else
198 printf(" - - - - - - ");
199
200 if (arg_show_machine) {
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(" -");
207 } else
208 putchar('\n');
209 }
210
211 return 0;
212 }
213
214 static int monitor(sd_bus *bus, char *argv[]) {
215 bool added_something = false;
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 }
236
237 added_something = true;
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 }
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 }
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) {
268 bus_message_dump(m, stdout, true);
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 }
281 }
282
283 static 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;
308 }
309
310 static int help(void) {
311
312 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
313 "Introspect the bus.\n\n"
314 " -h --help Show this help\n"
315 " --version Show package version\n"
316 " --no-pager Do not pipe output into a pager\n"
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"
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"
326 " --match=MATCH Only show matching messages\n\n"
327 "Commands:\n"
328 " list List bus names\n"
329 " monitor [SERVICE...] Show bus traffic\n"
330 " status NAME Show name status\n"
331 " help Show this help\n",
332 program_invocation_short_name);
333
334 return 0;
335 }
336
337 static 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,
346 ARG_SHOW_MACHINE,
347 ARG_UNIQUE,
348 ARG_ACQUIRED,
349 ARG_ACTIVATABLE
350 };
351
352 static const struct option options[] = {
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' },
366 {},
367 };
368
369 int c;
370
371 assert(argc >= 0);
372 assert(argv);
373
374 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
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
402 case ARG_SHOW_MACHINE:
403 arg_show_machine = true;
404 break;
405
406 case ARG_UNIQUE:
407 arg_unique = true;
408 break;
409
410 case ARG_ACQUIRED:
411 arg_acquired = true;
412 break;
413
414 case ARG_ACTIVATABLE:
415 arg_activatable = true;
416 break;
417
418 case ARG_MATCH:
419 if (strv_extend(&arg_matches, optarg) < 0)
420 return log_oom();
421 break;
422
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
433 case '?':
434 return -EINVAL;
435
436 default:
437 assert_not_reached("Unhandled option");
438 }
439 }
440
441 if (!arg_unique && !arg_acquired && !arg_activatable)
442 arg_unique = arg_acquired = arg_activatable = true;
443
444 return 1;
445 }
446
447 static 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
457 if (streq(argv[optind], "status"))
458 return status(bus, argv + optind);
459
460 if (streq(argv[optind], "help"))
461 return help();
462
463 log_error("Unknown command '%s'", argv[optind]);
464 return -EINVAL;
465 }
466
467 int 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);
498 } else
499 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
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
508 finish:
509 pager_close();
510
511 strv_free(arg_matches);
512
513 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
514 }