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