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