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_no_unique = false;
39 static bool arg_no_machine = false;
40 static char **arg_matches = NULL;
41 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
42 static char *arg_host = NULL;
43 static bool arg_user = false;
45 static void pager_open_if_enabled(void) {
47 /* Cache result before we open the pager */
54 static int list_bus_names(sd_bus *bus, char **argv) {
55 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
56 _cleanup_free_ char **merged = NULL;
57 _cleanup_hashmap_free_ Hashmap *names = NULL;
68 r = sd_bus_list_names(bus, &acquired, &activatable);
70 log_error("Failed to list names: %s", strerror(-r));
74 pager_open_if_enabled();
76 names = hashmap_new(string_hash_func, string_compare_func);
80 STRV_FOREACH(i, acquired) {
81 max_i = MAX(max_i, strlen(*i));
83 r = hashmap_put(names, *i, INT_TO_PTR(1));
85 log_error("Failed to add to hashmap: %s", strerror(-r));
90 STRV_FOREACH(i, activatable) {
91 max_i = MAX(max_i, strlen(*i));
93 r = hashmap_put(names, *i, INT_TO_PTR(2));
94 if (r < 0 && r != -EEXIST) {
95 log_error("Failed to add to hashmap: %s", strerror(-r));
100 merged = new(char*, hashmap_size(names) + 1);
101 HASHMAP_FOREACH_KEY(v, k, names, iterator)
107 printf("%-*s %*s %-*s %-*s %-*s",
108 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 20, "CONNECTION");
115 STRV_FOREACH(i, merged) {
116 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
119 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
122 printf("%-*s", (int) max_i, *i);
123 printf(" - - - (activation) ");
132 if (arg_no_unique && (*i)[0] == ':')
135 printf("%-*s", (int) max_i, *i);
137 r = sd_bus_get_owner(bus, *i, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_UNIQUE_NAME, &creds);
143 r = sd_bus_creds_get_pid(creds, &pid);
145 const char *comm = NULL;
147 sd_bus_creds_get_comm(creds, &comm);
149 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
151 fputs(" - - ", stdout);
153 r = sd_bus_creds_get_uid(creds, &uid);
155 _cleanup_free_ char *u = NULL;
157 u = uid_to_name(uid);
166 fputs(" - ", stdout);
168 r = sd_bus_creds_get_unique_name(creds, &unique);
170 printf(" %-20s", unique);
172 fputs(" - ", stdout);
180 r = sd_bus_get_owner_machine_id(bus, *i, &mid);
182 char m[SD_ID128_STRING_MAX];
183 printf(" %s\n", sd_id128_to_string(mid, m));
192 static int monitor(sd_bus *bus, char *argv[]) {
193 bool added_something = false;
197 STRV_FOREACH(i, argv+1) {
198 _cleanup_free_ char *m = NULL;
200 if (!service_name_is_valid(*i)) {
201 log_error("Invalid service name '%s'", *i);
205 m = strjoin("sender='", *i, "'", NULL);
209 r = sd_bus_add_match(bus, m, NULL, NULL);
211 log_error("Failed to add match: %s", strerror(-r));
215 added_something = true;
218 STRV_FOREACH(i, arg_matches) {
219 r = sd_bus_add_match(bus, *i, NULL, NULL);
221 log_error("Failed to add match: %s", strerror(-r));
225 added_something = true;
228 if (!added_something) {
229 r = sd_bus_add_match(bus, "", NULL, NULL);
231 log_error("Failed to add match: %s", strerror(-r));
237 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
239 r = sd_bus_process(bus, &m);
241 log_error("Failed to process bus: %s", strerror(-r));
246 bus_message_dump(m, stdout, true);
253 r = sd_bus_wait(bus, (uint64_t) -1);
255 log_error("Failed to wait for bus: %s", strerror(-r));
261 static int status(sd_bus *bus, char *argv[]) {
262 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
268 if (strv_length(argv) != 2) {
269 log_error("Expects one argument.");
273 r = parse_pid(argv[1], &pid);
275 r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
277 r = sd_bus_creds_new_from_pid(pid, _SD_BUS_CREDS_ALL, &creds);
280 log_error("Failed to get credentials: %s", strerror(-r));
284 bus_creds_dump(creds, NULL);
288 static int help(void) {
290 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
291 "Introspect the bus.\n\n"
292 " -h --help Show this help\n"
293 " --version Show package version\n"
294 " --no-pager Do not pipe output into a pager\n"
295 " --system Connect to system bus\n"
296 " --user Connect to user bus\n"
297 " -H --host=[USER@]HOST Operate on remote host\n"
298 " -M --machine=CONTAINER Operate on local container\n"
299 " --address=ADDRESS Connect to bus specified by address\n"
300 " --no-unique Only show well-known names\n"
301 " --no-machine Don't show machine ID column in list\n"
302 " --match=MATCH Only show matching messages\n\n"
304 " list List bus names\n"
305 " monitor [SERVICE...] Show bus traffic\n"
306 " status ENDPOINT Show endpoint status\n"
307 " help Show this help\n",
308 program_invocation_short_name);
313 static int parse_argv(int argc, char *argv[]) {
326 static const struct option options[] = {
327 { "help", no_argument, NULL, 'h' },
328 { "version", no_argument, NULL, ARG_VERSION },
329 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
330 { "system", no_argument, NULL, ARG_SYSTEM },
331 { "user", no_argument, NULL, ARG_USER },
332 { "address", required_argument, NULL, ARG_ADDRESS },
333 { "no-unique", no_argument, NULL, ARG_NO_UNIQUE },
334 { "no-machine", no_argument, NULL, ARG_NO_MACHINE },
335 { "match", required_argument, NULL, ARG_MATCH },
336 { "host", required_argument, NULL, 'H' },
337 { "machine", required_argument, NULL, 'M' },
346 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
354 puts(PACKAGE_STRING);
355 puts(SYSTEMD_FEATURES);
371 arg_address = optarg;
375 arg_no_unique = true;
379 arg_no_machine = true;
383 if (strv_extend(&arg_matches, optarg) < 0)
388 arg_transport = BUS_TRANSPORT_REMOTE;
393 arg_transport = BUS_TRANSPORT_CONTAINER;
401 assert_not_reached("Unhandled option");
408 static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
411 if (optind >= argc ||
412 streq(argv[optind], "list"))
413 return list_bus_names(bus, argv + optind);
415 if (streq(argv[optind], "monitor"))
416 return monitor(bus, argv + optind);
418 if (streq(argv[optind], "status"))
419 return status(bus, argv + optind);
421 if (streq(argv[optind], "help"))
424 log_error("Unknown command '%s'", argv[optind]);
428 int main(int argc, char *argv[]) {
429 _cleanup_bus_unref_ sd_bus *bus = NULL;
432 log_parse_environment();
435 r = parse_argv(argc, argv);
440 r = sd_bus_new(&bus);
442 log_error("Failed to allocate bus: %s", strerror(-r));
446 r = sd_bus_set_address(bus, arg_address);
448 log_error("Failed to set address: %s", strerror(-r));
452 r = sd_bus_set_bus_client(bus, true);
454 log_error("Failed to set bus client: %s", strerror(-r));
458 r = sd_bus_start(bus);
460 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
463 log_error("Failed to connect to bus: %s", strerror(-r));
467 r = busctl_main(bus, argc, argv);
472 strv_free(arg_matches);
474 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;