chiark / gitweb /
bus: reduce calls to KDBUS_CMD_MEMFD_SIZE_SET ioctl
[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         _cleanup_free_ char **merged = NULL;
57         _cleanup_hashmap_free_ Hashmap *names = NULL;
58         char **i;
59         int r;
60         size_t max_i = 0;
61         unsigned n = 0;
62         void *v;
63         char *k;
64         Iterator iterator;
65
66         assert(bus);
67
68         r = sd_bus_list_names(bus, &acquired, &activatable);
69         if (r < 0) {
70                 log_error("Failed to list names: %s", strerror(-r));
71                 return r;
72         }
73
74         pager_open_if_enabled();
75
76         names = hashmap_new(string_hash_func, string_compare_func);
77         if (!names)
78                 return log_oom();
79
80         STRV_FOREACH(i, acquired) {
81                 max_i = MAX(max_i, strlen(*i));
82
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) {
91                 max_i = MAX(max_i, strlen(*i));
92
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
107         printf("%-*s %*s %-*s %-*s %-*s",
108                (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 20, "CONNECTION");
109
110         if (!arg_no_machine)
111                 puts(" MACHINE");
112         else
113                 putchar('\n');
114
115         STRV_FOREACH(i, merged) {
116                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
117                 sd_id128_t mid;
118
119                 if (hashmap_get(names, *i) == INT_TO_PTR(2)) {
120                         /* Activatable */
121
122                         printf("%-*s", (int) max_i, *i);
123                         printf("          - -               -                (activation)        ");
124                         if (arg_no_machine)
125                                 putchar('\n');
126                         else
127                                 puts(" -");
128                         continue;
129
130                 }
131
132                 if (arg_no_unique && (*i)[0] == ':')
133                         continue;
134
135                 printf("%-*s", (int) max_i, *i);
136
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);
138                 if (r >= 0) {
139                         const char *unique;
140                         pid_t pid;
141                         uid_t uid;
142
143                         r = sd_bus_creds_get_pid(creds, &pid);
144                         if (r >= 0) {
145                                 const char *comm = NULL;
146
147                                 sd_bus_creds_get_comm(creds, &comm);
148
149                                 printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
150                         } else
151                                 fputs("          - -              ", stdout);
152
153                         r = sd_bus_creds_get_uid(creds, &uid);
154                         if (r >= 0) {
155                                 _cleanup_free_ char *u = NULL;
156
157                                 u = uid_to_name(uid);
158                                 if (!u)
159                                         return log_oom();
160
161                                 if (strlen(u) > 16)
162                                         u[16] = 0;
163
164                                 printf(" %-16s", u);
165                         } else
166                                 fputs(" -               ", stdout);
167
168                         r = sd_bus_creds_get_unique_name(creds, &unique);
169                         if (r >= 0)
170                                 printf(" %-20s", unique);
171                         else
172                                 fputs(" -                   ", stdout);
173
174                 } else
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                 }
187         }
188
189         return 0;
190 }
191
192 static int monitor(sd_bus *bus, char *argv[]) {
193         bool added_something = false;
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                 }
214
215                 added_something = true;
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                 }
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                 }
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) {
246                         bus_message_dump(m, stdout, true);
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         }
259 }
260
261 static 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;
286 }
287
288 static int help(void) {
289
290         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
291                "Introspect the bus.\n\n"
292                "  -h --help               Show this help\n"
293                "     --version            Show package version\n"
294                "     --no-pager           Do not pipe output into a pager\n"
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"
301                "     --no-machine         Don't show machine ID column in list\n"
302                "     --match=MATCH        Only show matching messages\n\n"
303                "Commands:\n"
304                "  list                    List bus names\n"
305                "  monitor [SERVICE...]    Show bus traffic\n"
306                "  status ENDPOINT         Show endpoint status\n"
307                "  help                    Show this help\n",
308                program_invocation_short_name);
309
310         return 0;
311 }
312
313 static 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,
322                 ARG_NO_UNIQUE,
323                 ARG_NO_MACHINE,
324         };
325
326         static const struct option options[] = {
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'            },
338                 {},
339         };
340
341         int c;
342
343         assert(argc >= 0);
344         assert(argv);
345
346         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
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
378                 case ARG_NO_MACHINE:
379                         arg_no_machine = true;
380                         break;
381
382                 case ARG_MATCH:
383                         if (strv_extend(&arg_matches, optarg) < 0)
384                                 return log_oom();
385                         break;
386
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
397                 case '?':
398                         return -EINVAL;
399
400                 default:
401                         assert_not_reached("Unhandled option");
402                 }
403         }
404
405         return 1;
406 }
407
408 static 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
418         if (streq(argv[optind], "status"))
419                 return status(bus, argv + optind);
420
421         if (streq(argv[optind], "help"))
422                 return help();
423
424         log_error("Unknown command '%s'", argv[optind]);
425         return -EINVAL;
426 }
427
428 int 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);
459         } else
460                 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
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
469 finish:
470         pager_close();
471
472         strv_free(arg_matches);
473
474         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
475 }