1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
31 #include "bus-message.h"
32 #include "bus-internal.h"
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;
48 static void pager_open_if_enabled(void) {
50 /* Cache result before we open the pager */
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;
71 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
73 log_error("Failed to list names: %s", strerror(-r));
77 pager_open_if_enabled();
79 names = hashmap_new(string_hash_func, string_compare_func);
83 STRV_FOREACH(i, acquired) {
84 max_i = MAX(max_i, strlen(*i));
86 r = hashmap_put(names, *i, INT_TO_PTR(1));
88 log_error("Failed to add to hashmap: %s", strerror(-r));
93 STRV_FOREACH(i, activatable) {
94 max_i = MAX(max_i, strlen(*i));
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));
103 merged = new(char*, hashmap_size(names) + 1);
104 HASHMAP_FOREACH_KEY(v, k, names, iterator)
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");
114 if (arg_show_machine)
120 STRV_FOREACH(i, merged) {
121 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
124 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
127 printf("%-*s", (int) max_i, *i);
128 printf(" - - - (activatable) - - ");
129 if (arg_show_machine)
137 if (!arg_unique && (*i)[0] == ':')
140 if (!arg_acquired && (*i)[0] != ':')
143 printf("%-*s", (int) max_i, *i);
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);
150 const char *unique, *session, *unit, *cn;
154 r = sd_bus_creds_get_pid(creds, &pid);
156 const char *comm = NULL;
158 sd_bus_creds_get_comm(creds, &comm);
160 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
162 fputs(" - - ", stdout);
164 r = sd_bus_creds_get_uid(creds, &uid);
166 _cleanup_free_ char *u = NULL;
168 u = uid_to_name(uid);
177 fputs(" - ", stdout);
179 r = sd_bus_creds_get_unique_name(creds, &unique);
181 printf(" %-13s", unique);
183 fputs(" - ", stdout);
185 r = sd_bus_creds_get_unit(creds, &unit);
187 _cleanup_free_ char *e;
189 e = ellipsize(unit, 25, 100);
195 fputs(" - ", stdout);
197 r = sd_bus_creds_get_session(creds, &session);
199 printf(" %-10s", session);
201 fputs(" - ", stdout);
203 r = sd_bus_creds_get_connection_name(creds, &cn);
205 printf(" %-19s", cn);
207 fputs(" - ", stdout);
210 printf(" - - - - - - - ");
212 if (arg_show_machine) {
213 r = sd_bus_get_owner_machine_id(bus, *i, &mid);
215 char m[SD_ID128_STRING_MAX];
216 printf(" %s\n", sd_id128_to_string(mid, m));
226 static int monitor(sd_bus *bus, char *argv[]) {
227 bool added_something = false;
231 STRV_FOREACH(i, argv+1) {
232 _cleanup_free_ char *m = NULL;
234 if (!service_name_is_valid(*i)) {
235 log_error("Invalid service name '%s'", *i);
239 m = strjoin("sender='", *i, "'", NULL);
243 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
245 log_error("Failed to add match: %s", strerror(-r));
249 added_something = true;
252 STRV_FOREACH(i, arg_matches) {
253 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
255 log_error("Failed to add match: %s", strerror(-r));
259 added_something = true;
262 if (!added_something) {
263 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
265 log_error("Failed to add match: %s", strerror(-r));
271 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
273 r = sd_bus_process(bus, &m);
275 log_error("Failed to process bus: %s", strerror(-r));
280 bus_message_dump(m, stdout, true);
287 r = sd_bus_wait(bus, (uint64_t) -1);
289 log_error("Failed to wait for bus: %s", strerror(-r));
295 static int status(sd_bus *bus, char *argv[]) {
296 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
302 if (strv_length(argv) != 2) {
303 log_error("Expects one argument.");
307 r = parse_pid(argv[1], &pid);
309 r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
311 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
314 log_error("Failed to get credentials: %s", strerror(-r));
318 bus_creds_dump(creds, NULL);
322 static int help(void) {
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"
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);
350 static int parse_argv(int argc, char *argv[]) {
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' },
389 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
397 puts(PACKAGE_STRING);
398 puts(SYSTEMD_FEATURES);
418 arg_address = optarg;
421 case ARG_SHOW_MACHINE:
422 arg_show_machine = true;
433 case ARG_ACTIVATABLE:
434 arg_activatable = true;
438 if (strv_extend(&arg_matches, optarg) < 0)
443 arg_transport = BUS_TRANSPORT_REMOTE;
448 arg_transport = BUS_TRANSPORT_CONTAINER;
456 assert_not_reached("Unhandled option");
460 if (!arg_unique && !arg_acquired && !arg_activatable)
461 arg_unique = arg_acquired = arg_activatable = true;
466 static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
469 if (optind >= argc ||
470 streq(argv[optind], "list"))
471 return list_bus_names(bus, argv + optind);
473 if (streq(argv[optind], "monitor"))
474 return monitor(bus, argv + optind);
476 if (streq(argv[optind], "status"))
477 return status(bus, argv + optind);
479 if (streq(argv[optind], "help"))
482 log_error("Unknown command '%s'", argv[optind]);
486 int main(int argc, char *argv[]) {
487 _cleanup_bus_unref_ sd_bus *bus = NULL;
490 log_parse_environment();
493 r = parse_argv(argc, argv);
497 r = sd_bus_new(&bus);
499 log_error("Failed to allocate bus: %s", strerror(-r));
503 if (streq_ptr(argv[optind], "monitor")) {
505 r = sd_bus_set_monitor(bus, true);
507 log_error("Failed to set monitor mode: %s", strerror(-r));
511 r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL);
513 log_error("Failed to enable credentials: %s", strerror(-r));
517 r = sd_bus_negotiate_timestamp(bus, true);
519 log_error("Failed to enable timestamps: %s", strerror(-r));
523 r = sd_bus_negotiate_fds(bus, true);
525 log_error("Failed to enable fds: %s", strerror(-r));
531 r = sd_bus_set_address(bus, arg_address);
533 switch (arg_transport) {
535 case BUS_TRANSPORT_LOCAL:
537 r = bus_set_address_user(bus);
539 r = bus_set_address_system(bus);
542 case BUS_TRANSPORT_REMOTE:
543 r = bus_set_address_system_remote(bus, arg_host);
546 case BUS_TRANSPORT_CONTAINER:
547 r = bus_set_address_system_container(bus, arg_host);
551 assert_not_reached("Hmm, unknown transport type.");
555 log_error("Failed to set address: %s", strerror(-r));
559 r = sd_bus_set_bus_client(bus, true);
561 log_error("Failed to set bus client: %s", strerror(-r));
565 r = sd_bus_start(bus);
567 log_error("Failed to connect to bus: %s", strerror(-r));
571 r = busctl_main(bus, argc, argv);
576 strv_free(arg_matches);
578 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;