]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/busctl.c
api: in constructor function calls, always put the returned object pointer first...
[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 if (arg_address) {
498 r = sd_bus_new(&bus);
499 if (r < 0) {
500 log_error("Failed to allocate bus: %s", strerror(-r));
501 goto finish;
502 }
503
504 r = sd_bus_set_address(bus, arg_address);
505 if (r < 0) {
506 log_error("Failed to set address: %s", strerror(-r));
507 goto finish;
508 }
509
510 r = sd_bus_set_bus_client(bus, true);
511 if (r < 0) {
512 log_error("Failed to set bus client: %s", strerror(-r));
513 goto finish;
514 }
515
516 r = sd_bus_start(bus);
517 } else
518 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
519
520 if (r < 0) {
521 log_error("Failed to connect to bus: %s", strerror(-r));
522 goto finish;
523 }
524
525 r = busctl_main(bus, argc, argv);
526
527 finish:
528 pager_close();
529
530 strv_free(arg_matches);
531
532 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
533 }