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