chiark / gitweb /
4e2e6aff2606e30265f6b24fc7d341bf23f78834
[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_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;
44
45 static void pager_open_if_enabled(void) {
46
47         /* Cache result before we open the pager */
48         if (arg_no_pager)
49                 return;
50
51         pager_open(false);
52 }
53
54 static int list_bus_names(sd_bus *bus, char **argv) {
55         _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
56         char **i;
57         int r;
58         size_t max_i = 0;
59
60         assert(bus);
61
62         r = sd_bus_list_names(bus, &acquired, &activatable);
63         if (r < 0) {
64                 log_error("Failed to list names: %s", strerror(-r));
65                 return r;
66         }
67
68         pager_open_if_enabled();
69
70         strv_sort(acquired);
71         strv_sort(activatable);
72
73         STRV_FOREACH(i, acquired)
74                 max_i = MAX(max_i, strlen(*i));
75
76         STRV_FOREACH(i, activatable)
77                 max_i = MAX(max_i, strlen(*i));
78
79         printf("%-*s %*s %-*s %-*s %-*s",
80                (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 20, "CONNECTION");
81
82         if (!arg_no_machine)
83                 puts(" MACHINE");
84         else
85                 putchar('\n');
86
87         STRV_FOREACH(i, activatable) {
88
89                 /* Skip the bus driver */
90                 if (streq(*i, "org.freedesktop.DBus"))
91                         continue;
92
93                 if (strv_contains(acquired, *i))
94                         continue;
95
96                 printf("%-*s", (int) max_i, *i);
97                 printf("          - -               -                (activation)        ");
98                 if (arg_no_machine)
99                         putchar('\n');
100                 else
101                         puts(" -");
102         }
103
104         STRV_FOREACH(i, acquired) {
105                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
106                 sd_id128_t mid;
107
108                 if (arg_no_unique && (*i)[0] == ':')
109                         continue;
110
111                 printf("%-*s", (int) max_i, *i);
112
113                 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);
114                 if (r >= 0) {
115                         const char *unique;
116                         pid_t pid;
117                         uid_t uid;
118
119                         r = sd_bus_creds_get_pid(creds, &pid);
120                         if (r >= 0) {
121                                 const char *comm = NULL;
122
123                                 sd_bus_creds_get_comm(creds, &comm);
124
125                                 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
126                         } else
127                                 fputs("          - -              ", stdout);
128
129                         r = sd_bus_creds_get_uid(creds, &uid);
130                         if (r >= 0) {
131                                 _cleanup_free_ char *u = NULL;
132
133                                 u = uid_to_name(uid);
134                                 if (!u)
135                                         return log_oom();
136
137                                 if (strlen(u) > 16)
138                                         u[16] = 0;
139
140                                 printf(" %-16s", u);
141                         } else
142                                 fputs(" -               ", stdout);
143
144                         r = sd_bus_creds_get_unique_name(creds, &unique);
145                         if (r >= 0)
146                                 printf(" %-20s", unique);
147                         else
148                                 fputs(" -                   ", stdout);
149
150                 } else
151                         printf("          - -               -                -                   ");
152
153                 if (arg_no_machine)
154                         putchar('\n');
155                 else {
156                         r = sd_bus_get_owner_machine_id(bus, *i, &mid);
157                         if (r >= 0) {
158                                 char m[SD_ID128_STRING_MAX];
159                                 printf(" %s\n", sd_id128_to_string(mid, m));
160                         } else
161                                 puts(" -");
162                 }
163         }
164
165         return 0;
166 }
167
168 static int monitor(sd_bus *bus, char *argv[]) {
169         bool added_something = false;
170         char **i;
171         int r;
172
173         STRV_FOREACH(i, argv+1) {
174                 _cleanup_free_ char *m = NULL;
175
176                 if (!service_name_is_valid(*i)) {
177                         log_error("Invalid service name '%s'", *i);
178                         return -EINVAL;
179                 }
180
181                 m = strjoin("sender='", *i, "'", NULL);
182                 if (!m)
183                         return log_oom();
184
185                 r = sd_bus_add_match(bus, m, NULL, NULL);
186                 if (r < 0) {
187                         log_error("Failed to add match: %s", strerror(-r));
188                         return r;
189                 }
190
191                 added_something = true;
192         }
193
194         STRV_FOREACH(i, arg_matches) {
195                 r = sd_bus_add_match(bus, *i, NULL, NULL);
196                 if (r < 0) {
197                         log_error("Failed to add match: %s", strerror(-r));
198                         return r;
199                 }
200
201                 added_something = true;
202         }
203
204         if (!added_something) {
205                 r = sd_bus_add_match(bus, "", NULL, NULL);
206                 if (r < 0) {
207                         log_error("Failed to add match: %s", strerror(-r));
208                         return r;
209                 }
210         }
211
212         for (;;) {
213                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
214
215                 r = sd_bus_process(bus, &m);
216                 if (r < 0) {
217                         log_error("Failed to process bus: %s", strerror(-r));
218                         return r;
219                 }
220
221                 if (m) {
222                         bus_message_dump(m, stdout, true);
223                         continue;
224                 }
225
226                 if (r > 0)
227                         continue;
228
229                 r = sd_bus_wait(bus, (uint64_t) -1);
230                 if (r < 0) {
231                         log_error("Failed to wait for bus: %s", strerror(-r));
232                         return r;
233                 }
234         }
235 }
236
237 static int status(sd_bus *bus, char *argv[]) {
238         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
239         pid_t pid;
240         int r;
241
242         assert(bus);
243
244         if (strv_length(argv) != 2) {
245                 log_error("Expects one argument.");
246                 return -EINVAL;
247         }
248
249         r = parse_pid(argv[1], &pid);
250         if (r < 0)
251                 r = sd_bus_get_owner(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
252         else
253                 r = sd_bus_creds_new_from_pid(pid, _SD_BUS_CREDS_ALL, &creds);
254
255         if (r < 0) {
256                 log_error("Failed to get credentials: %s", strerror(-r));
257                 return r;
258         }
259
260         bus_creds_dump(creds, NULL);
261         return 0;
262 }
263
264 static int help(void) {
265
266         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
267                "Introspect the bus.\n\n"
268                "  -h --help               Show this help\n"
269                "     --version            Show package version\n"
270                "     --no-pager           Do not pipe output into a pager\n"
271                "     --system             Connect to system bus\n"
272                "     --user               Connect to user bus\n"
273                "  -H --host=[USER@]HOST   Operate on remote host\n"
274                "  -M --machine=CONTAINER  Operate on local container\n"
275                "     --address=ADDRESS    Connect to bus specified by address\n"
276                "     --no-unique          Only show well-known names\n"
277                "     --no-machine         Don't show machine ID column in list\n"
278                "     --match=MATCH        Only show matching messages\n\n"
279                "Commands:\n"
280                "  list                    List bus names\n"
281                "  monitor [SERVICE...]    Show bus traffic\n"
282                "  status ENDPOINT         Show endpoint status\n"
283                "  help                    Show this help\n",
284                program_invocation_short_name);
285
286         return 0;
287 }
288
289 static int parse_argv(int argc, char *argv[]) {
290
291         enum {
292                 ARG_VERSION = 0x100,
293                 ARG_NO_PAGER,
294                 ARG_SYSTEM,
295                 ARG_USER,
296                 ARG_ADDRESS,
297                 ARG_MATCH,
298                 ARG_NO_UNIQUE,
299                 ARG_NO_MACHINE,
300         };
301
302         static const struct option options[] = {
303                 { "help",       no_argument,       NULL, 'h'            },
304                 { "version",    no_argument,       NULL, ARG_VERSION    },
305                 { "no-pager",   no_argument,       NULL, ARG_NO_PAGER   },
306                 { "system",     no_argument,       NULL, ARG_SYSTEM     },
307                 { "user",       no_argument,       NULL, ARG_USER       },
308                 { "address",    required_argument, NULL, ARG_ADDRESS    },
309                 { "no-unique",  no_argument,       NULL, ARG_NO_UNIQUE  },
310                 { "no-machine", no_argument,       NULL, ARG_NO_MACHINE },
311                 { "match",      required_argument, NULL, ARG_MATCH      },
312                 { "host",       required_argument, NULL, 'H'            },
313                 { "machine",    required_argument, NULL, 'M'            },
314                 {},
315         };
316
317         int c;
318
319         assert(argc >= 0);
320         assert(argv);
321
322         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
323
324                 switch (c) {
325
326                 case 'h':
327                         return help();
328
329                 case ARG_VERSION:
330                         puts(PACKAGE_STRING);
331                         puts(SYSTEMD_FEATURES);
332                         return 0;
333
334                 case ARG_NO_PAGER:
335                         arg_no_pager = true;
336                         break;
337
338                 case ARG_USER:
339                         arg_user = true;
340                         break;
341
342                 case ARG_SYSTEM:
343                         arg_user = false;
344                         break;
345
346                 case ARG_ADDRESS:
347                         arg_address = optarg;
348                         break;
349
350                 case ARG_NO_UNIQUE:
351                         arg_no_unique = true;
352                         break;
353
354                 case ARG_NO_MACHINE:
355                         arg_no_machine = true;
356                         break;
357
358                 case ARG_MATCH:
359                         if (strv_extend(&arg_matches, optarg) < 0)
360                                 return log_oom();
361                         break;
362
363                 case 'H':
364                         arg_transport = BUS_TRANSPORT_REMOTE;
365                         arg_host = optarg;
366                         break;
367
368                 case 'M':
369                         arg_transport = BUS_TRANSPORT_CONTAINER;
370                         arg_host = optarg;
371                         break;
372
373                 case '?':
374                         return -EINVAL;
375
376                 default:
377                         assert_not_reached("Unhandled option");
378                 }
379         }
380
381         return 1;
382 }
383
384 static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
385         assert(bus);
386
387         if (optind >= argc ||
388             streq(argv[optind], "list"))
389                 return list_bus_names(bus, argv + optind);
390
391         if (streq(argv[optind], "monitor"))
392                 return monitor(bus, argv + optind);
393
394         if (streq(argv[optind], "status"))
395                 return status(bus, argv + optind);
396
397         if (streq(argv[optind], "help"))
398                 return help();
399
400         log_error("Unknown command '%s'", argv[optind]);
401         return -EINVAL;
402 }
403
404 int main(int argc, char *argv[]) {
405         _cleanup_bus_unref_ sd_bus *bus = NULL;
406         int r;
407
408         log_parse_environment();
409         log_open();
410
411         r = parse_argv(argc, argv);
412         if (r <= 0)
413                 goto finish;
414
415         if (arg_address) {
416                 r = sd_bus_new(&bus);
417                 if (r < 0) {
418                         log_error("Failed to allocate bus: %s", strerror(-r));
419                         goto finish;
420                 }
421
422                 r = sd_bus_set_address(bus, arg_address);
423                 if (r < 0) {
424                         log_error("Failed to set address: %s", strerror(-r));
425                         goto finish;
426                 }
427
428                 r = sd_bus_set_bus_client(bus, true);
429                 if (r < 0) {
430                         log_error("Failed to set bus client: %s", strerror(-r));
431                         goto finish;
432                 }
433
434                 r = sd_bus_start(bus);
435         } else
436                 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
437
438         if (r < 0) {
439                 log_error("Failed to connect to bus: %s", strerror(-r));
440                 goto finish;
441         }
442
443         r = busctl_main(bus, argc, argv);
444
445 finish:
446         pager_close();
447
448         strv_free(arg_matches);
449
450         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
451 }