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