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