chiark / gitweb /
a59b8eab4fa9f04ab9b7217ccbaf62dc8efee258
[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 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(pid, _SD_BUS_CREDS_ALL, &creds);
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 }