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