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