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 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;
47 static void pager_open_if_enabled(void) {
49 /* Cache result before we open the pager */
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;
70 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
72 log_error("Failed to list names: %s", strerror(-r));
76 pager_open_if_enabled();
78 names = hashmap_new(string_hash_func, string_compare_func);
82 STRV_FOREACH(i, acquired) {
83 max_i = MAX(max_i, strlen(*i));
85 r = hashmap_put(names, *i, INT_TO_PTR(1));
87 log_error("Failed to add to hashmap: %s", strerror(-r));
92 STRV_FOREACH(i, activatable) {
93 max_i = MAX(max_i, strlen(*i));
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));
102 merged = new(char*, hashmap_size(names) + 1);
103 HASHMAP_FOREACH_KEY(v, k, names, iterator)
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");
112 if (arg_show_machine)
117 STRV_FOREACH(i, merged) {
118 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
121 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
124 printf("%-*s", (int) max_i, *i);
125 printf(" - - - (activatable) - - ");
126 if (arg_show_machine)
134 if (!arg_unique && (*i)[0] == ':')
137 if (!arg_acquired && (*i)[0] != ':')
140 printf("%-*s", (int) max_i, *i);
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);
147 const char *unique, *session, *unit, *cn;
151 r = sd_bus_creds_get_pid(creds, &pid);
153 const char *comm = NULL;
155 sd_bus_creds_get_comm(creds, &comm);
157 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
159 fputs(" - - ", stdout);
161 r = sd_bus_creds_get_uid(creds, &uid);
163 _cleanup_free_ char *u = NULL;
165 u = uid_to_name(uid);
174 fputs(" - ", stdout);
176 r = sd_bus_creds_get_unique_name(creds, &unique);
178 printf(" %-13s", unique);
180 fputs(" - ", stdout);
182 r = sd_bus_creds_get_unit(creds, &unit);
184 _cleanup_free_ char *e;
186 e = ellipsize(unit, 25, 100);
192 fputs(" - ", stdout);
194 r = sd_bus_creds_get_session(creds, &session);
196 printf(" %-10s", session);
198 fputs(" - ", stdout);
200 r = sd_bus_creds_get_connection_name(creds, &cn);
202 printf(" %-19s", cn);
204 fputs(" - ", stdout);
207 printf(" - - - - - - - ");
209 if (arg_show_machine) {
210 r = sd_bus_get_owner_machine_id(bus, *i, &mid);
212 char m[SD_ID128_STRING_MAX];
213 printf(" %s\n", sd_id128_to_string(mid, m));
223 static int monitor(sd_bus *bus, char *argv[]) {
224 bool added_something = false;
228 STRV_FOREACH(i, argv+1) {
229 _cleanup_free_ char *m = NULL;
231 if (!service_name_is_valid(*i)) {
232 log_error("Invalid service name '%s'", *i);
236 m = strjoin("sender='", *i, "'", NULL);
240 r = sd_bus_add_match(bus, m, NULL, NULL);
242 log_error("Failed to add match: %s", strerror(-r));
246 added_something = true;
249 STRV_FOREACH(i, arg_matches) {
250 r = sd_bus_add_match(bus, *i, NULL, NULL);
252 log_error("Failed to add match: %s", strerror(-r));
256 added_something = true;
259 if (!added_something) {
260 r = sd_bus_add_match(bus, "", NULL, NULL);
262 log_error("Failed to add match: %s", strerror(-r));
268 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
270 r = sd_bus_process(bus, &m);
272 log_error("Failed to process bus: %s", strerror(-r));
277 bus_message_dump(m, stdout, true);
284 r = sd_bus_wait(bus, (uint64_t) -1);
286 log_error("Failed to wait for bus: %s", strerror(-r));
292 static int status(sd_bus *bus, char *argv[]) {
293 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
299 if (strv_length(argv) != 2) {
300 log_error("Expects one argument.");
304 r = parse_pid(argv[1], &pid);
306 r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
308 r = sd_bus_creds_new_from_pid(pid, _SD_BUS_CREDS_ALL, &creds);
311 log_error("Failed to get credentials: %s", strerror(-r));
315 bus_creds_dump(creds, NULL);
319 static int help(void) {
321 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
322 "Introspect the bus.\n\n"
323 " -h --help Show this help\n"
324 " --version Show package version\n"
325 " --no-pager Do not pipe output into a pager\n"
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"
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"
335 " --match=MATCH Only show matching messages\n\n"
337 " list List bus names\n"
338 " monitor [SERVICE...] Show bus traffic\n"
339 " status NAME Show name status\n"
340 " help Show this help\n",
341 program_invocation_short_name);
346 static int parse_argv(int argc, char *argv[]) {
361 static const struct option options[] = {
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' },
383 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
391 puts(PACKAGE_STRING);
392 puts(SYSTEMD_FEATURES);
408 arg_address = optarg;
411 case ARG_SHOW_MACHINE:
412 arg_show_machine = true;
423 case ARG_ACTIVATABLE:
424 arg_activatable = true;
428 if (strv_extend(&arg_matches, optarg) < 0)
433 arg_transport = BUS_TRANSPORT_REMOTE;
438 arg_transport = BUS_TRANSPORT_CONTAINER;
446 assert_not_reached("Unhandled option");
450 if (!arg_unique && !arg_acquired && !arg_activatable)
451 arg_unique = arg_acquired = arg_activatable = true;
456 static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
459 if (optind >= argc ||
460 streq(argv[optind], "list"))
461 return list_bus_names(bus, argv + optind);
463 if (streq(argv[optind], "monitor"))
464 return monitor(bus, argv + optind);
466 if (streq(argv[optind], "status"))
467 return status(bus, argv + optind);
469 if (streq(argv[optind], "help"))
472 log_error("Unknown command '%s'", argv[optind]);
476 int main(int argc, char *argv[]) {
477 _cleanup_bus_unref_ sd_bus *bus = NULL;
480 log_parse_environment();
483 r = parse_argv(argc, argv);
488 r = sd_bus_new(&bus);
490 log_error("Failed to allocate bus: %s", strerror(-r));
494 r = sd_bus_set_address(bus, arg_address);
496 log_error("Failed to set address: %s", strerror(-r));
500 r = sd_bus_set_bus_client(bus, true);
502 log_error("Failed to set bus client: %s", strerror(-r));
506 r = sd_bus_start(bus);
508 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
511 log_error("Failed to connect to bus: %s", strerror(-r));
515 r = busctl_main(bus, argc, argv);
520 strv_free(arg_matches);
522 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;