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