]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/busctl.c
nspawn: ignore EEXIST when creating mount point
[thirdparty/systemd.git] / src / libsystemd / sd-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 bool arg_legend = true;
38 static char *arg_address = NULL;
39 static bool arg_unique = false;
40 static bool arg_acquired = false;
41 static bool arg_activatable = false;
42 static bool arg_show_machine = false;
43 static char **arg_matches = NULL;
44 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
45 static char *arg_host = NULL;
46 static bool arg_user = false;
47
48 static void pager_open_if_enabled(void) {
49
50 /* Cache result before we open the pager */
51 if (arg_no_pager)
52 return;
53
54 pager_open(false);
55 }
56
57 static int list_bus_names(sd_bus *bus, char **argv) {
58 _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
59 _cleanup_free_ char **merged = NULL;
60 _cleanup_hashmap_free_ Hashmap *names = NULL;
61 char **i;
62 int r;
63 size_t max_i = 0;
64 unsigned n = 0;
65 void *v;
66 char *k;
67 Iterator iterator;
68
69 assert(bus);
70
71 r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
72 if (r < 0) {
73 log_error("Failed to list names: %s", strerror(-r));
74 return r;
75 }
76
77 pager_open_if_enabled();
78
79 names = hashmap_new(&string_hash_ops);
80 if (!names)
81 return log_oom();
82
83 STRV_FOREACH(i, acquired) {
84 max_i = MAX(max_i, strlen(*i));
85
86 r = hashmap_put(names, *i, INT_TO_PTR(1));
87 if (r < 0) {
88 log_error("Failed to add to hashmap: %s", strerror(-r));
89 return r;
90 }
91 }
92
93 STRV_FOREACH(i, activatable) {
94 max_i = MAX(max_i, strlen(*i));
95
96 r = hashmap_put(names, *i, INT_TO_PTR(2));
97 if (r < 0 && r != -EEXIST) {
98 log_error("Failed to add to hashmap: %s", strerror(-r));
99 return r;
100 }
101 }
102
103 merged = new(char*, hashmap_size(names) + 1);
104 HASHMAP_FOREACH_KEY(v, k, names, iterator)
105 merged[n++] = k;
106
107 merged[n] = NULL;
108 strv_sort(merged);
109
110 if (arg_legend) {
111 printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
112 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "CONNECTION-NAME");
113
114 if (arg_show_machine)
115 puts(" MACHINE");
116 else
117 putchar('\n');
118 }
119
120 STRV_FOREACH(i, merged) {
121 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
122 sd_id128_t mid;
123
124 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
125 /* Activatable */
126
127 printf("%-*s", (int) max_i, *i);
128 printf(" - - - (activatable) - - ");
129 if (arg_show_machine)
130 puts(" -");
131 else
132 putchar('\n');
133 continue;
134
135 }
136
137 if (!arg_unique && (*i)[0] == ':')
138 continue;
139
140 if (!arg_acquired && (*i)[0] != ':')
141 continue;
142
143 printf("%-*s", (int) max_i, *i);
144
145 r = sd_bus_get_name_creds(bus, *i,
146 SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
147 SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
148 SD_BUS_CREDS_CONNECTION_NAME, &creds);
149 if (r >= 0) {
150 const char *unique, *session, *unit, *cn;
151 pid_t pid;
152 uid_t uid;
153
154 r = sd_bus_creds_get_pid(creds, &pid);
155 if (r >= 0) {
156 const char *comm = NULL;
157
158 sd_bus_creds_get_comm(creds, &comm);
159
160 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
161 } else
162 fputs(" - - ", stdout);
163
164 r = sd_bus_creds_get_uid(creds, &uid);
165 if (r >= 0) {
166 _cleanup_free_ char *u = NULL;
167
168 u = uid_to_name(uid);
169 if (!u)
170 return log_oom();
171
172 if (strlen(u) > 16)
173 u[16] = 0;
174
175 printf(" %-16s", u);
176 } else
177 fputs(" - ", stdout);
178
179 r = sd_bus_creds_get_unique_name(creds, &unique);
180 if (r >= 0)
181 printf(" %-13s", unique);
182 else
183 fputs(" - ", stdout);
184
185 r = sd_bus_creds_get_unit(creds, &unit);
186 if (r >= 0) {
187 _cleanup_free_ char *e;
188
189 e = ellipsize(unit, 25, 100);
190 if (!e)
191 return log_oom();
192
193 printf(" %-25s", e);
194 } else
195 fputs(" - ", stdout);
196
197 r = sd_bus_creds_get_session(creds, &session);
198 if (r >= 0)
199 printf(" %-10s", session);
200 else
201 fputs(" - ", stdout);
202
203 r = sd_bus_creds_get_connection_name(creds, &cn);
204 if (r >= 0)
205 printf(" %-19s", cn);
206 else
207 fputs(" - ", stdout);
208
209 } else
210 printf(" - - - - - - - ");
211
212 if (arg_show_machine) {
213 r = sd_bus_get_name_machine_id(bus, *i, &mid);
214 if (r >= 0) {
215 char m[SD_ID128_STRING_MAX];
216 printf(" %s\n", sd_id128_to_string(mid, m));
217 } else
218 puts(" -");
219 } else
220 putchar('\n');
221 }
222
223 return 0;
224 }
225
226 static int monitor(sd_bus *bus, char *argv[]) {
227 bool added_something = false;
228 char **i;
229 int r;
230
231 STRV_FOREACH(i, argv+1) {
232 _cleanup_free_ char *m = NULL;
233
234 if (!service_name_is_valid(*i)) {
235 log_error("Invalid service name '%s'", *i);
236 return -EINVAL;
237 }
238
239 m = strjoin("sender='", *i, "'", NULL);
240 if (!m)
241 return log_oom();
242
243 r = sd_bus_add_match(bus, NULL, m, NULL, NULL);
244 if (r < 0) {
245 log_error("Failed to add match: %s", strerror(-r));
246 return r;
247 }
248
249 added_something = true;
250 }
251
252 STRV_FOREACH(i, arg_matches) {
253 r = sd_bus_add_match(bus, NULL, *i, NULL, NULL);
254 if (r < 0) {
255 log_error("Failed to add match: %s", strerror(-r));
256 return r;
257 }
258
259 added_something = true;
260 }
261
262 if (!added_something) {
263 r = sd_bus_add_match(bus, NULL, "", NULL, NULL);
264 if (r < 0) {
265 log_error("Failed to add match: %s", strerror(-r));
266 return r;
267 }
268 }
269
270 for (;;) {
271 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
272
273 r = sd_bus_process(bus, &m);
274 if (r < 0) {
275 log_error("Failed to process bus: %s", strerror(-r));
276 return r;
277 }
278
279 if (m) {
280 bus_message_dump(m, stdout, true);
281 continue;
282 }
283
284 if (r > 0)
285 continue;
286
287 r = sd_bus_wait(bus, (uint64_t) -1);
288 if (r < 0) {
289 log_error("Failed to wait for bus: %s", strerror(-r));
290 return r;
291 }
292 }
293 }
294
295 static int status(sd_bus *bus, char *argv[]) {
296 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
297 pid_t pid;
298 int r;
299
300 assert(bus);
301
302 if (strv_length(argv) != 2) {
303 log_error("Expects one argument.");
304 return -EINVAL;
305 }
306
307 r = parse_pid(argv[1], &pid);
308 if (r < 0)
309 r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds);
310 else
311 r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL);
312
313 if (r < 0) {
314 log_error("Failed to get credentials: %s", strerror(-r));
315 return r;
316 }
317
318 bus_creds_dump(creds, NULL);
319 return 0;
320 }
321
322 static int help(void) {
323 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
324 "Introspect the bus.\n\n"
325 " -h --help Show this help\n"
326 " --version Show package version\n"
327 " --no-pager Do not pipe output into a pager\n"
328 " --no-legend Do not show the headers and footers\n"
329 " --system Connect to system bus\n"
330 " --user Connect to user bus\n"
331 " -H --host=[USER@]HOST Operate on remote host\n"
332 " -M --machine=CONTAINER Operate on local container\n"
333 " --address=ADDRESS Connect to bus specified by address\n"
334 " --show-machine Show machine ID column in list\n"
335 " --unique Only show unique names\n"
336 " --acquired Only show acquired names\n"
337 " --activatable Only show activatable names\n"
338 " --match=MATCH Only show matching messages\n\n"
339 "Commands:\n"
340 " list List bus names\n"
341 " monitor [SERVICE...] Show bus traffic\n"
342 " status NAME Show name status\n"
343 " help Show this help\n"
344 , program_invocation_short_name);
345
346 return 0;
347 }
348
349 static int parse_argv(int argc, char *argv[]) {
350
351 enum {
352 ARG_VERSION = 0x100,
353 ARG_NO_PAGER,
354 ARG_NO_LEGEND,
355 ARG_SYSTEM,
356 ARG_USER,
357 ARG_ADDRESS,
358 ARG_MATCH,
359 ARG_SHOW_MACHINE,
360 ARG_UNIQUE,
361 ARG_ACQUIRED,
362 ARG_ACTIVATABLE
363 };
364
365 static const struct option options[] = {
366 { "help", no_argument, NULL, 'h' },
367 { "version", no_argument, NULL, ARG_VERSION },
368 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
369 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
370 { "system", no_argument, NULL, ARG_SYSTEM },
371 { "user", no_argument, NULL, ARG_USER },
372 { "address", required_argument, NULL, ARG_ADDRESS },
373 { "show-machine", no_argument, NULL, ARG_SHOW_MACHINE },
374 { "unique", no_argument, NULL, ARG_UNIQUE },
375 { "acquired", no_argument, NULL, ARG_ACQUIRED },
376 { "activatable", no_argument, NULL, ARG_ACTIVATABLE },
377 { "match", required_argument, NULL, ARG_MATCH },
378 { "host", required_argument, NULL, 'H' },
379 { "machine", required_argument, NULL, 'M' },
380 {},
381 };
382
383 int c;
384
385 assert(argc >= 0);
386 assert(argv);
387
388 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
389
390 switch (c) {
391
392 case 'h':
393 return help();
394
395 case ARG_VERSION:
396 puts(PACKAGE_STRING);
397 puts(SYSTEMD_FEATURES);
398 return 0;
399
400 case ARG_NO_PAGER:
401 arg_no_pager = true;
402 break;
403
404 case ARG_NO_LEGEND:
405 arg_legend = false;
406 break;
407
408 case ARG_USER:
409 arg_user = true;
410 break;
411
412 case ARG_SYSTEM:
413 arg_user = false;
414 break;
415
416 case ARG_ADDRESS:
417 arg_address = optarg;
418 break;
419
420 case ARG_SHOW_MACHINE:
421 arg_show_machine = true;
422 break;
423
424 case ARG_UNIQUE:
425 arg_unique = true;
426 break;
427
428 case ARG_ACQUIRED:
429 arg_acquired = true;
430 break;
431
432 case ARG_ACTIVATABLE:
433 arg_activatable = true;
434 break;
435
436 case ARG_MATCH:
437 if (strv_extend(&arg_matches, optarg) < 0)
438 return log_oom();
439 break;
440
441 case 'H':
442 arg_transport = BUS_TRANSPORT_REMOTE;
443 arg_host = optarg;
444 break;
445
446 case 'M':
447 arg_transport = BUS_TRANSPORT_CONTAINER;
448 arg_host = optarg;
449 break;
450
451 case '?':
452 return -EINVAL;
453
454 default:
455 assert_not_reached("Unhandled option");
456 }
457
458 if (!arg_unique && !arg_acquired && !arg_activatable)
459 arg_unique = arg_acquired = arg_activatable = true;
460
461 return 1;
462 }
463
464 static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
465 assert(bus);
466
467 if (optind >= argc ||
468 streq(argv[optind], "list"))
469 return list_bus_names(bus, argv + optind);
470
471 if (streq(argv[optind], "monitor"))
472 return monitor(bus, argv + optind);
473
474 if (streq(argv[optind], "status"))
475 return status(bus, argv + optind);
476
477 if (streq(argv[optind], "help"))
478 return help();
479
480 log_error("Unknown command '%s'", argv[optind]);
481 return -EINVAL;
482 }
483
484 int main(int argc, char *argv[]) {
485 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
486 int r;
487
488 log_parse_environment();
489 log_open();
490
491 r = parse_argv(argc, argv);
492 if (r <= 0)
493 goto finish;
494
495 r = sd_bus_new(&bus);
496 if (r < 0) {
497 log_error("Failed to allocate bus: %s", strerror(-r));
498 goto finish;
499 }
500
501 if (streq_ptr(argv[optind], "monitor")) {
502
503 r = sd_bus_set_monitor(bus, true);
504 if (r < 0) {
505 log_error("Failed to set monitor mode: %s", strerror(-r));
506 goto finish;
507 }
508
509 r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL);
510 if (r < 0) {
511 log_error("Failed to enable credentials: %s", strerror(-r));
512 goto finish;
513 }
514
515 r = sd_bus_negotiate_timestamp(bus, true);
516 if (r < 0) {
517 log_error("Failed to enable timestamps: %s", strerror(-r));
518 goto finish;
519 }
520
521 r = sd_bus_negotiate_fds(bus, true);
522 if (r < 0) {
523 log_error("Failed to enable fds: %s", strerror(-r));
524 goto finish;
525 }
526 }
527
528 if (arg_address)
529 r = sd_bus_set_address(bus, arg_address);
530 else {
531 switch (arg_transport) {
532
533 case BUS_TRANSPORT_LOCAL:
534 if (arg_user)
535 r = bus_set_address_user(bus);
536 else
537 r = bus_set_address_system(bus);
538 break;
539
540 case BUS_TRANSPORT_REMOTE:
541 r = bus_set_address_system_remote(bus, arg_host);
542 break;
543
544 case BUS_TRANSPORT_CONTAINER:
545 r = bus_set_address_system_container(bus, arg_host);
546 break;
547
548 default:
549 assert_not_reached("Hmm, unknown transport type.");
550 }
551 }
552 if (r < 0) {
553 log_error("Failed to set address: %s", strerror(-r));
554 goto finish;
555 }
556
557 r = sd_bus_set_bus_client(bus, true);
558 if (r < 0) {
559 log_error("Failed to set bus client: %s", strerror(-r));
560 goto finish;
561 }
562
563 r = sd_bus_start(bus);
564 if (r < 0) {
565 log_error("Failed to connect to bus: %s", strerror(-r));
566 goto finish;
567 }
568
569 r = busctl_main(bus, argc, argv);
570
571 finish:
572 pager_close();
573
574 strv_free(arg_matches);
575
576 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
577 }