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