chiark / gitweb /
busctl: include connection name in busctl output
[elogind.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 %-*s",
110                (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "CONNECTION-NAME");
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,
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);
146                 if (r >= 0) {
147                         const char *unique, *session, *unit, *cn;
148                         pid_t pid;
149                         uid_t uid;
150
151                         r = sd_bus_creds_get_pid(creds, &pid);
152                         if (r >= 0) {
153                                 const char *comm = NULL;
154
155                                 sd_bus_creds_get_comm(creds, &comm);
156
157                                 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
158                         } else
159                                 fputs("          - -              ", stdout);
160
161                         r = sd_bus_creds_get_uid(creds, &uid);
162                         if (r >= 0) {
163                                 _cleanup_free_ char *u = NULL;
164
165                                 u = uid_to_name(uid);
166                                 if (!u)
167                                         return log_oom();
168
169                                 if (strlen(u) > 16)
170                                         u[16] = 0;
171
172                                 printf(" %-16s", u);
173                         } else
174                                 fputs(" -               ", stdout);
175
176                         r = sd_bus_creds_get_unique_name(creds, &unique);
177                         if (r >= 0)
178                                 printf(" %-13s", unique);
179                         else
180                                 fputs(" -            ", stdout);
181
182                         r = sd_bus_creds_get_unit(creds, &unit);
183                         if (r >= 0) {
184                                 _cleanup_free_ char *e;
185
186                                 e = ellipsize(unit, 25, 100);
187                                 if (!e)
188                                         return log_oom();
189
190                                 printf(" %-25s", e);
191                         } else
192                                 fputs(" -                        ", stdout);
193
194                         r = sd_bus_creds_get_session(creds, &session);
195                         if (r >= 0)
196                                 printf(" %-10s", session);
197                         else
198                                 fputs(" -         ", stdout);
199
200                         r = sd_bus_creds_get_connection_name(creds, &cn);
201                         if (r >= 0)
202                                 printf(" %-19s", cn);
203                         else
204                                 fputs(" -                  ", stdout);
205
206                 } else
207                         printf("          - -               -                -             -                         -          -                  ");
208
209                 if (arg_show_machine) {
210                         r = sd_bus_get_owner_machine_id(bus, *i, &mid);
211                         if (r >= 0) {
212                                 char m[SD_ID128_STRING_MAX];
213                                 printf(" %s\n", sd_id128_to_string(mid, m));
214                         } else
215                                 puts(" -");
216                 } else
217                         putchar('\n');
218         }
219
220         return 0;
221 }
222
223 static int monitor(sd_bus *bus, char *argv[]) {
224         bool added_something = false;
225         char **i;
226         int r;
227
228         STRV_FOREACH(i, argv+1) {
229                 _cleanup_free_ char *m = NULL;
230
231                 if (!service_name_is_valid(*i)) {
232                         log_error("Invalid service name '%s'", *i);
233                         return -EINVAL;
234                 }
235
236                 m = strjoin("sender='", *i, "'", NULL);
237                 if (!m)
238                         return log_oom();
239
240                 r = sd_bus_add_match(bus, m, NULL, NULL);
241                 if (r < 0) {
242                         log_error("Failed to add match: %s", strerror(-r));
243                         return r;
244                 }
245
246                 added_something = true;
247         }
248
249         STRV_FOREACH(i, arg_matches) {
250                 r = sd_bus_add_match(bus, *i, NULL, NULL);
251                 if (r < 0) {
252                         log_error("Failed to add match: %s", strerror(-r));
253                         return r;
254                 }
255
256                 added_something = true;
257         }
258
259         if (!added_something) {
260                 r = sd_bus_add_match(bus, "", NULL, NULL);
261                 if (r < 0) {
262                         log_error("Failed to add match: %s", strerror(-r));
263                         return r;
264                 }
265         }
266
267         for (;;) {
268                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
269
270                 r = sd_bus_process(bus, &m);
271                 if (r < 0) {
272                         log_error("Failed to process bus: %s", strerror(-r));
273                         return r;
274                 }
275
276                 if (m) {
277                         bus_message_dump(m, stdout, true);
278                         continue;
279                 }
280
281                 if (r > 0)
282                         continue;
283
284                 r = sd_bus_wait(bus, (uint64_t) -1);
285                 if (r < 0) {
286                         log_error("Failed to wait for bus: %s", strerror(-r));
287                         return r;
288                 }
289         }
290 }
291
292 static int status(sd_bus *bus, char *argv[]) {
293         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
294         pid_t pid;
295         int r;
296
297         assert(bus);
298
299         if (strv_length(argv) != 2) {
300                 log_error("Expects one argument.");
301                 return -EINVAL;
302         }
303
304         r = parse_pid(argv[1], &pid);
305         if (r < 0)
306                 r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
307         else
308                 r = sd_bus_creds_new_from_pid(pid, _SD_BUS_CREDS_ALL, &creds);
309
310         if (r < 0) {
311                 log_error("Failed to get credentials: %s", strerror(-r));
312                 return r;
313         }
314
315         bus_creds_dump(creds, NULL);
316         return 0;
317 }
318
319 static int help(void) {
320
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"
336                "Commands:\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);
342
343         return 0;
344 }
345
346 static int parse_argv(int argc, char *argv[]) {
347
348         enum {
349                 ARG_VERSION = 0x100,
350                 ARG_NO_PAGER,
351                 ARG_SYSTEM,
352                 ARG_USER,
353                 ARG_ADDRESS,
354                 ARG_MATCH,
355                 ARG_SHOW_MACHINE,
356                 ARG_UNIQUE,
357                 ARG_ACQUIRED,
358                 ARG_ACTIVATABLE
359         };
360
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'              },
375                 {},
376         };
377
378         int c;
379
380         assert(argc >= 0);
381         assert(argv);
382
383         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
384
385                 switch (c) {
386
387                 case 'h':
388                         return help();
389
390                 case ARG_VERSION:
391                         puts(PACKAGE_STRING);
392                         puts(SYSTEMD_FEATURES);
393                         return 0;
394
395                 case ARG_NO_PAGER:
396                         arg_no_pager = true;
397                         break;
398
399                 case ARG_USER:
400                         arg_user = true;
401                         break;
402
403                 case ARG_SYSTEM:
404                         arg_user = false;
405                         break;
406
407                 case ARG_ADDRESS:
408                         arg_address = optarg;
409                         break;
410
411                 case ARG_SHOW_MACHINE:
412                         arg_show_machine = true;
413                         break;
414
415                 case ARG_UNIQUE:
416                         arg_unique = true;
417                         break;
418
419                 case ARG_ACQUIRED:
420                         arg_acquired = true;
421                         break;
422
423                 case ARG_ACTIVATABLE:
424                         arg_activatable = true;
425                         break;
426
427                 case ARG_MATCH:
428                         if (strv_extend(&arg_matches, optarg) < 0)
429                                 return log_oom();
430                         break;
431
432                 case 'H':
433                         arg_transport = BUS_TRANSPORT_REMOTE;
434                         arg_host = optarg;
435                         break;
436
437                 case 'M':
438                         arg_transport = BUS_TRANSPORT_CONTAINER;
439                         arg_host = optarg;
440                         break;
441
442                 case '?':
443                         return -EINVAL;
444
445                 default:
446                         assert_not_reached("Unhandled option");
447                 }
448         }
449
450         if (!arg_unique && !arg_acquired && !arg_activatable)
451                 arg_unique = arg_acquired = arg_activatable = true;
452
453         return 1;
454 }
455
456 static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
457         assert(bus);
458
459         if (optind >= argc ||
460             streq(argv[optind], "list"))
461                 return list_bus_names(bus, argv + optind);
462
463         if (streq(argv[optind], "monitor"))
464                 return monitor(bus, argv + optind);
465
466         if (streq(argv[optind], "status"))
467                 return status(bus, argv + optind);
468
469         if (streq(argv[optind], "help"))
470                 return help();
471
472         log_error("Unknown command '%s'", argv[optind]);
473         return -EINVAL;
474 }
475
476 int main(int argc, char *argv[]) {
477         _cleanup_bus_unref_ sd_bus *bus = NULL;
478         int r;
479
480         log_parse_environment();
481         log_open();
482
483         r = parse_argv(argc, argv);
484         if (r <= 0)
485                 goto finish;
486
487         if (arg_address) {
488                 r = sd_bus_new(&bus);
489                 if (r < 0) {
490                         log_error("Failed to allocate bus: %s", strerror(-r));
491                         goto finish;
492                 }
493
494                 r = sd_bus_set_address(bus, arg_address);
495                 if (r < 0) {
496                         log_error("Failed to set address: %s", strerror(-r));
497                         goto finish;
498                 }
499
500                 r = sd_bus_set_bus_client(bus, true);
501                 if (r < 0) {
502                         log_error("Failed to set bus client: %s", strerror(-r));
503                         goto finish;
504                 }
505
506                 r = sd_bus_start(bus);
507         } else
508                 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
509
510         if (r < 0) {
511                 log_error("Failed to connect to bus: %s", strerror(-r));
512                 goto finish;
513         }
514
515         r = busctl_main(bus, argc, argv);
516
517 finish:
518         pager_close();
519
520         strv_free(arg_matches);
521
522         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
523 }