]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/busctl.c
bus: beef up driverd
[thirdparty/systemd.git] / src / libsystemd-bus / busctl.c
CommitLineData
de1c301e
LP
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
1f849790
LP
22#include <getopt.h>
23
89ffcd2a
LP
24#include "strv.h"
25#include "util.h"
26#include "log.h"
1f849790
LP
27#include "build.h"
28#include "pager.h"
89ffcd2a 29
de1c301e 30#include "sd-bus.h"
89ffcd2a
LP
31#include "bus-message.h"
32#include "bus-internal.h"
40ca29a1 33#include "bus-util.h"
2b5c5383 34#include "bus-dump.h"
de1c301e 35
1f849790
LP
36static bool arg_no_pager = false;
37static char *arg_address = NULL;
1f849790 38static bool arg_no_unique = false;
a4297f08 39static bool arg_no_machine = false;
1f849790 40static char **arg_matches = NULL;
d75edbd6
LP
41static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
42static char *arg_host = NULL;
43static bool arg_user = false;
1f849790
LP
44
45static 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
54static int list_bus_names(sd_bus *bus, char **argv) {
71f2ab46 55 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
de1c301e
LP
56 char **i;
57 int r;
89ffcd2a 58 size_t max_i = 0;
de1c301e 59
1f849790 60 assert(bus);
de1c301e 61
71f2ab46 62 r = sd_bus_list_names(bus, &acquired, &activatable);
de1c301e
LP
63 if (r < 0) {
64 log_error("Failed to list names: %s", strerror(-r));
1f849790 65 return r;
de1c301e
LP
66 }
67
1f849790
LP
68 pager_open_if_enabled();
69
71f2ab46
LP
70 strv_sort(acquired);
71 strv_sort(activatable);
89ffcd2a 72
71f2ab46
LP
73 STRV_FOREACH(i, acquired)
74 max_i = MAX(max_i, strlen(*i));
75
76 STRV_FOREACH(i, activatable)
89ffcd2a
LP
77 max_i = MAX(max_i, strlen(*i));
78
a4297f08 79 printf("%-*s %*s %-*s %-*s %-*s",
7b0b392f 80 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 20, "CONNECTION");
89ffcd2a 81
a4297f08
LP
82 if (!arg_no_machine)
83 puts(" MACHINE");
84 else
85 putchar('\n');
86
71f2ab46
LP
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) {
5b12334d 105 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
7b0b392f 106 sd_id128_t mid;
de1c301e 107
1f849790
LP
108 if (arg_no_unique && (*i)[0] == ':')
109 continue;
89ffcd2a
LP
110
111 printf("%-*s", (int) max_i, *i);
de1c301e 112
49b832c5 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);
89ffcd2a 114 if (r >= 0) {
49b832c5 115 const char *unique;
5b12334d
LP
116 pid_t pid;
117 uid_t uid;
de1c301e 118
5b12334d
LP
119 r = sd_bus_creds_get_pid(creds, &pid);
120 if (r >= 0) {
121 const char *comm = NULL;
de1c301e 122
5b12334d 123 sd_bus_creds_get_comm(creds, &comm);
89ffcd2a 124
5b12334d
LP
125 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
126 } else
a4297f08 127 fputs(" - - ", stdout);
89ffcd2a 128
5b12334d
LP
129 r = sd_bus_creds_get_uid(creds, &uid);
130 if (r >= 0) {
131 _cleanup_free_ char *u = NULL;
89ffcd2a 132
5b12334d
LP
133 u = uid_to_name(uid);
134 if (!u)
135 return log_oom();
89ffcd2a 136
5b12334d
LP
137 if (strlen(u) > 16)
138 u[16] = 0;
139
140 printf(" %-16s", u);
141 } else
a4297f08 142 fputs(" - ", stdout);
89ffcd2a 143
49b832c5
LP
144 r = sd_bus_creds_get_unique_name(creds, &unique);
145 if (r >= 0)
146 printf(" %-20s", unique);
a4297f08
LP
147 else
148 fputs(" - ", stdout);
7b0b392f 149
7b0b392f 150 } else
a4297f08
LP
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 }
de1c301e
LP
163 }
164
1f849790
LP
165 return 0;
166}
167
168static int monitor(sd_bus *bus, char *argv[]) {
b51f299a 169 bool added_something = false;
1f849790
LP
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 }
b51f299a
LP
190
191 added_something = true;
1f849790
LP
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 }
b51f299a
LP
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 }
1f849790
LP
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) {
c430fee6 222 bus_message_dump(m, stdout, true);
1f849790
LP
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 }
95c4fe82 235}
1f849790 236
95c4fe82
LP
237static 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;
1f849790
LP
262}
263
264static int help(void) {
265
266 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
267 "Introspect the bus.\n\n"
d75edbd6
LP
268 " -h --help Show this help\n"
269 " --version Show package version\n"
a86a47ce 270 " --no-pager Do not pipe output into a pager\n"
d75edbd6
LP
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"
b5dda4d8
LP
277 " --no-machine Don't show machine ID column in list\n"
278 " --match=MATCH Only show matching messages\n\n"
1f849790 279 "Commands:\n"
d75edbd6 280 " list List bus names\n"
d94fe1f1
KS
281 " monitor [SERVICE...] Show bus traffic\n"
282 " status ENDPOINT Show endpoint status\n"
86cb0691 283 " help Show this help\n",
1f849790
LP
284 program_invocation_short_name);
285
286 return 0;
287}
288
289static 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,
a4297f08
LP
298 ARG_NO_UNIQUE,
299 ARG_NO_MACHINE,
1f849790
LP
300 };
301
302 static const struct option options[] = {
a4297f08
LP
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' },
eb9da376 314 {},
1f849790
LP
315 };
316
317 int c;
318
319 assert(argc >= 0);
320 assert(argv);
321
d75edbd6 322 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
1f849790
LP
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
a4297f08
LP
354 case ARG_NO_MACHINE:
355 arg_no_machine = true;
356 break;
357
1f849790
LP
358 case ARG_MATCH:
359 if (strv_extend(&arg_matches, optarg) < 0)
360 return log_oom();
361 break;
362
d75edbd6
LP
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
1f849790
LP
373 case '?':
374 return -EINVAL;
375
376 default:
eb9da376 377 assert_not_reached("Unhandled option");
1f849790
LP
378 }
379 }
380
381 return 1;
382}
383
384static 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
95c4fe82
LP
394 if (streq(argv[optind], "status"))
395 return status(bus, argv + optind);
396
1f849790
LP
397 if (streq(argv[optind], "help"))
398 return help();
399
400 log_error("Unknown command '%s'", argv[optind]);
401 return -EINVAL;
402}
403
404int 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);
d75edbd6
LP
435 } else
436 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
1f849790
LP
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
445finish:
446 pager_close();
d75edbd6 447
1f849790 448 strv_free(arg_matches);
de1c301e 449
de1c301e
LP
450 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
451}