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