chiark / gitweb /
sd-bus: rename "connection name" to "description" for the sd-bus API too
[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 static size_t arg_snaplen = 4096;
48
49 static void pager_open_if_enabled(void) {
50
51         /* Cache result before we open the pager */
52         if (arg_no_pager)
53                 return;
54
55         pager_open(false);
56 }
57
58 static int list_bus_names(sd_bus *bus, char **argv) {
59         _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
60         _cleanup_free_ char **merged = NULL;
61         _cleanup_hashmap_free_ Hashmap *names = NULL;
62         char **i;
63         int r;
64         size_t max_i = 0;
65         unsigned n = 0;
66         void *v;
67         char *k;
68         Iterator iterator;
69
70         assert(bus);
71
72         r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
73         if (r < 0) {
74                 log_error("Failed to list names: %s", strerror(-r));
75                 return r;
76         }
77
78         pager_open_if_enabled();
79
80         names = hashmap_new(&string_hash_ops);
81         if (!names)
82                 return log_oom();
83
84         STRV_FOREACH(i, acquired) {
85                 max_i = MAX(max_i, strlen(*i));
86
87                 r = hashmap_put(names, *i, INT_TO_PTR(1));
88                 if (r < 0) {
89                         log_error("Failed to add to hashmap: %s", strerror(-r));
90                         return r;
91                 }
92         }
93
94         STRV_FOREACH(i, activatable) {
95                 max_i = MAX(max_i, strlen(*i));
96
97                 r = hashmap_put(names, *i, INT_TO_PTR(2));
98                 if (r < 0 && r != -EEXIST) {
99                         log_error("Failed to add to hashmap: %s", strerror(-r));
100                         return r;
101                 }
102         }
103
104         merged = new(char*, hashmap_size(names) + 1);
105         HASHMAP_FOREACH_KEY(v, k, names, iterator)
106                 merged[n++] = k;
107
108         merged[n] = NULL;
109         strv_sort(merged);
110
111         if (arg_legend) {
112                 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
113                        (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
114
115                 if (arg_show_machine)
116                         puts(" MACHINE");
117                 else
118                         putchar('\n');
119         }
120
121         STRV_FOREACH(i, merged) {
122                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
123                 sd_id128_t mid;
124
125                 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
126                         /* Activatable */
127
128                         printf("%-*s", (int) max_i, *i);
129                         printf("          - -               -                (activatable) -                         -         ");
130                         if (arg_show_machine)
131                                 puts(" -");
132                         else
133                                 putchar('\n');
134                         continue;
135
136                 }
137
138                 if (!arg_unique && (*i)[0] == ':')
139                         continue;
140
141                 if (!arg_acquired && (*i)[0] != ':')
142                         continue;
143
144                 printf("%-*s", (int) max_i, *i);
145
146                 r = sd_bus_get_name_creds(bus, *i,
147                                      SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
148                                      SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
149                                      SD_BUS_CREDS_DESCRIPTION, &creds);
150                 if (r >= 0) {
151                         const char *unique, *session, *unit, *cn;
152                         pid_t pid;
153                         uid_t uid;
154
155                         r = sd_bus_creds_get_pid(creds, &pid);
156                         if (r >= 0) {
157                                 const char *comm = NULL;
158
159                                 sd_bus_creds_get_comm(creds, &comm);
160
161                                 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
162                         } else
163                                 fputs("          - -              ", stdout);
164
165                         r = sd_bus_creds_get_uid(creds, &uid);
166                         if (r >= 0) {
167                                 _cleanup_free_ char *u = NULL;
168
169                                 u = uid_to_name(uid);
170                                 if (!u)
171                                         return log_oom();
172
173                                 if (strlen(u) > 16)
174                                         u[16] = 0;
175
176                                 printf(" %-16s", u);
177                         } else
178                                 fputs(" -               ", stdout);
179
180                         r = sd_bus_creds_get_unique_name(creds, &unique);
181                         if (r >= 0)
182                                 printf(" %-13s", unique);
183                         else
184                                 fputs(" -            ", stdout);
185
186                         r = sd_bus_creds_get_unit(creds, &unit);
187                         if (r >= 0) {
188                                 _cleanup_free_ char *e;
189
190                                 e = ellipsize(unit, 25, 100);
191                                 if (!e)
192                                         return log_oom();
193
194                                 printf(" %-25s", e);
195                         } else
196                                 fputs(" -                        ", stdout);
197
198                         r = sd_bus_creds_get_session(creds, &session);
199                         if (r >= 0)
200                                 printf(" %-10s", session);
201                         else
202                                 fputs(" -         ", stdout);
203
204                         r = sd_bus_creds_get_description(creds, &cn);
205                         if (r >= 0)
206                                 printf(" %-19s", cn);
207                         else
208                                 fputs(" -                  ", stdout);
209
210                 } else
211                         printf("          - -               -                -             -                         -          -                  ");
212
213                 if (arg_show_machine) {
214                         r = sd_bus_get_name_machine_id(bus, *i, &mid);
215                         if (r >= 0) {
216                                 char m[SD_ID128_STRING_MAX];
217                                 printf(" %s\n", sd_id128_to_string(mid, m));
218                         } else
219                                 puts(" -");
220                 } else
221                         putchar('\n');
222         }
223
224         return 0;
225 }
226
227 static int message_dump(sd_bus_message *m, FILE *f) {
228         return bus_message_dump(m, f, true);
229 }
230
231 static int message_pcap(sd_bus_message *m, FILE *f) {
232         return bus_message_pcap_frame(m, arg_snaplen, f);
233 }
234
235 static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) {
236         bool added_something = false;
237         char **i;
238         int r;
239
240         STRV_FOREACH(i, argv+1) {
241                 _cleanup_free_ char *m = NULL;
242
243                 if (!service_name_is_valid(*i)) {
244                         log_error("Invalid service name '%s'", *i);
245                         return -EINVAL;
246                 }
247
248                 m = strjoin("sender='", *i, "'", NULL);
249                 if (!m)
250                         return log_oom();
251
252                 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
253                 if (r < 0) {
254                         log_error("Failed to add match: %s", strerror(-r));
255                         return r;
256                 }
257
258                 added_something = true;
259         }
260
261         STRV_FOREACH(i, arg_matches) {
262                 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
263                 if (r < 0) {
264                         log_error("Failed to add match: %s", strerror(-r));
265                         return r;
266                 }
267
268                 added_something = true;
269         }
270
271         if (!added_something) {
272                 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
273                 if (r < 0) {
274                         log_error("Failed to add match: %s", strerror(-r));
275                         return r;
276                 }
277         }
278
279         for (;;) {
280                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
281
282                 r = sd_bus_process(bus, &m);
283                 if (r < 0) {
284                         log_error("Failed to process bus: %s", strerror(-r));
285                         return r;
286                 }
287
288                 if (m) {
289                         dump(m, stdout);
290                         continue;
291                 }
292
293                 if (r > 0)
294                         continue;
295
296                 r = sd_bus_wait(bus, (uint64_t) -1);
297                 if (r < 0) {
298                         log_error("Failed to wait for bus: %s", strerror(-r));
299                         return r;
300                 }
301         }
302 }
303
304 static int capture(sd_bus *bus, char *argv[]) {
305         int r;
306
307         if (isatty(fileno(stdout)) > 0) {
308                 log_error("Refusing to write message data to console, please redirect output to a file.");
309                 return -EINVAL;
310         }
311
312         bus_pcap_header(arg_snaplen, stdout);
313
314         r = monitor(bus, argv, message_pcap);
315         if (r < 0)
316                 return r;
317
318         if (ferror(stdout)) {
319                 log_error("Couldn't write capture file.");
320                 return -EIO;
321         }
322
323         return r;
324 }
325
326 static int status(sd_bus *bus, char *argv[]) {
327         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
328         pid_t pid;
329         int r;
330
331         assert(bus);
332
333         if (strv_length(argv) != 2) {
334                 log_error("Expects one argument.");
335                 return -EINVAL;
336         }
337
338         r = parse_pid(argv[1], &pid);
339         if (r < 0)
340                 r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
341         else
342                 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
343
344         if (r < 0) {
345                 log_error("Failed to get credentials: %s", strerror(-r));
346                 return r;
347         }
348
349         bus_creds_dump(creds, NULL);
350         return 0;
351 }
352
353 static int help(void) {
354         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
355                "Introspect the bus.\n\n"
356                "  -h --help               Show this help\n"
357                "     --version            Show package version\n"
358                "     --no-pager           Do not pipe output into a pager\n"
359                "     --no-legend          Do not show the headers and footers\n"
360                "     --system             Connect to system bus\n"
361                "     --user               Connect to user bus\n"
362                "  -H --host=[USER@]HOST   Operate on remote host\n"
363                "  -M --machine=CONTAINER  Operate on local container\n"
364                "     --address=ADDRESS    Connect to bus specified by address\n"
365                "     --show-machine       Show machine ID column in list\n"
366                "     --unique             Only show unique names\n"
367                "     --acquired           Only show acquired names\n"
368                "     --activatable        Only show activatable names\n"
369                "     --match=MATCH        Only show matching messages\n\n"
370                "Commands:\n"
371                "  list                    List bus names\n"
372                "  monitor [SERVICE...]    Show bus traffic\n"
373                "  capture [SERVICE...]    Capture bus traffic as pcap\n"
374                "  status NAME             Show name status\n"
375                "  help                    Show this help\n"
376                , program_invocation_short_name);
377
378         return 0;
379 }
380
381 static int parse_argv(int argc, char *argv[]) {
382
383         enum {
384                 ARG_VERSION = 0x100,
385                 ARG_NO_PAGER,
386                 ARG_NO_LEGEND,
387                 ARG_SYSTEM,
388                 ARG_USER,
389                 ARG_ADDRESS,
390                 ARG_MATCH,
391                 ARG_SHOW_MACHINE,
392                 ARG_UNIQUE,
393                 ARG_ACQUIRED,
394                 ARG_ACTIVATABLE,
395                 ARG_SIZE,
396         };
397
398         static const struct option options[] = {
399                 { "help",         no_argument,       NULL, 'h'              },
400                 { "version",      no_argument,       NULL, ARG_VERSION      },
401                 { "no-pager",     no_argument,       NULL, ARG_NO_PAGER     },
402                 { "no-legend",    no_argument,       NULL, ARG_NO_LEGEND    },
403                 { "system",       no_argument,       NULL, ARG_SYSTEM       },
404                 { "user",         no_argument,       NULL, ARG_USER         },
405                 { "address",      required_argument, NULL, ARG_ADDRESS      },
406                 { "show-machine", no_argument,       NULL, ARG_SHOW_MACHINE },
407                 { "unique",       no_argument,       NULL, ARG_UNIQUE       },
408                 { "acquired",     no_argument,       NULL, ARG_ACQUIRED     },
409                 { "activatable",  no_argument,       NULL, ARG_ACTIVATABLE  },
410                 { "match",        required_argument, NULL, ARG_MATCH        },
411                 { "host",         required_argument, NULL, 'H'              },
412                 { "machine",      required_argument, NULL, 'M'              },
413                 { "size",         required_argument, NULL, ARG_SIZE         },
414                 {},
415         };
416
417         int c, r;
418
419         assert(argc >= 0);
420         assert(argv);
421
422         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
423
424                 switch (c) {
425
426                 case 'h':
427                         return help();
428
429                 case ARG_VERSION:
430                         puts(PACKAGE_STRING);
431                         puts(SYSTEMD_FEATURES);
432                         return 0;
433
434                 case ARG_NO_PAGER:
435                         arg_no_pager = true;
436                         break;
437
438                 case ARG_NO_LEGEND:
439                         arg_legend = false;
440                         break;
441
442                 case ARG_USER:
443                         arg_user = true;
444                         break;
445
446                 case ARG_SYSTEM:
447                         arg_user = false;
448                         break;
449
450                 case ARG_ADDRESS:
451                         arg_address = optarg;
452                         break;
453
454                 case ARG_SHOW_MACHINE:
455                         arg_show_machine = true;
456                         break;
457
458                 case ARG_UNIQUE:
459                         arg_unique = true;
460                         break;
461
462                 case ARG_ACQUIRED:
463                         arg_acquired = true;
464                         break;
465
466                 case ARG_ACTIVATABLE:
467                         arg_activatable = true;
468                         break;
469
470                 case ARG_MATCH:
471                         if (strv_extend(&arg_matches, optarg) < 0)
472                                 return log_oom();
473                         break;
474
475                 case ARG_SIZE: {
476                         off_t o;
477
478                         r = parse_size(optarg, 0, &o);
479                         if (r < 0) {
480                                 log_error("Failed to parse size: %s", optarg);
481                                 return r;
482                         }
483
484                         if ((off_t) (size_t) o !=  o) {
485                                 log_error("Size out of range.");
486                                 return -E2BIG;
487                         }
488
489                         arg_snaplen = (size_t) o;
490                         break;
491                 }
492
493                 case 'H':
494                         arg_transport = BUS_TRANSPORT_REMOTE;
495                         arg_host = optarg;
496                         break;
497
498                 case 'M':
499                         arg_transport = BUS_TRANSPORT_CONTAINER;
500                         arg_host = optarg;
501                         break;
502
503                 case '?':
504                         return -EINVAL;
505
506                 default:
507                         assert_not_reached("Unhandled option");
508                 }
509
510         if (!arg_unique && !arg_acquired && !arg_activatable)
511                 arg_unique = arg_acquired = arg_activatable = true;
512
513         return 1;
514 }
515
516 static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
517         assert(bus);
518
519         if (optind >= argc ||
520             streq(argv[optind], "list"))
521                 return list_bus_names(bus, argv + optind);
522
523         if (streq(argv[optind], "monitor"))
524                 return monitor(bus, argv + optind, message_dump);
525
526         if (streq(argv[optind], "capture"))
527                 return capture(bus, argv + optind);
528
529         if (streq(argv[optind], "status"))
530                 return status(bus, argv + optind);
531
532         if (streq(argv[optind], "help"))
533                 return help();
534
535         log_error("Unknown command '%s'", argv[optind]);
536         return -EINVAL;
537 }
538
539 int main(int argc, char *argv[]) {
540         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
541         int r;
542
543         log_parse_environment();
544         log_open();
545
546         r = parse_argv(argc, argv);
547         if (r <= 0)
548                 goto finish;
549
550         r = sd_bus_new(&bus);
551         if (r < 0) {
552                 log_error("Failed to allocate bus: %s", strerror(-r));
553                 goto finish;
554         }
555
556         if (streq_ptr(argv[optind], "monitor") ||
557             streq_ptr(argv[optind], "capture")) {
558
559                 r = sd_bus_set_monitor(bus, true);
560                 if (r < 0) {
561                         log_error("Failed to set monitor mode: %s", strerror(-r));
562                         goto finish;
563                 }
564
565                 r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL);
566                 if (r < 0) {
567                         log_error("Failed to enable credentials: %s", strerror(-r));
568                         goto finish;
569                 }
570
571                 r = sd_bus_negotiate_timestamp(bus, true);
572                 if (r < 0) {
573                         log_error("Failed to enable timestamps: %s", strerror(-r));
574                         goto finish;
575                 }
576
577                 r = sd_bus_negotiate_fds(bus, true);
578                 if (r < 0) {
579                         log_error("Failed to enable fds: %s", strerror(-r));
580                         goto finish;
581                 }
582         }
583
584         if (arg_address)
585                 r = sd_bus_set_address(bus, arg_address);
586         else {
587                 switch (arg_transport) {
588
589                 case BUS_TRANSPORT_LOCAL:
590                         if (arg_user)
591                                 r = bus_set_address_user(bus);
592                         else
593                                 r = bus_set_address_system(bus);
594                         break;
595
596                 case BUS_TRANSPORT_REMOTE:
597                         r = bus_set_address_system_remote(bus, arg_host);
598                         break;
599
600                 case BUS_TRANSPORT_CONTAINER:
601                         r = bus_set_address_system_container(bus, arg_host);
602                         break;
603
604                 default:
605                         assert_not_reached("Hmm, unknown transport type.");
606                 }
607         }
608         if (r < 0) {
609                 log_error("Failed to set address: %s", strerror(-r));
610                 goto finish;
611         }
612
613         r = sd_bus_set_bus_client(bus, true);
614         if (r < 0) {
615                 log_error("Failed to set bus client: %s", strerror(-r));
616                 goto finish;
617         }
618
619         r = sd_bus_start(bus);
620         if (r < 0) {
621                 log_error("Failed to connect to bus: %s", strerror(-r));
622                 goto finish;
623         }
624
625         r = busctl_main(bus, argc, argv);
626
627 finish:
628         pager_close();
629
630         strv_free(arg_matches);
631
632         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
633 }