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