chiark / gitweb /
journal: fix minor memory leak
[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                "     --no-pager           Do not pipe output into a pager\n"
187                "     --system             Connect to system bus\n"
188                "     --user               Connect to user bus\n"
189                "  -H --host=[USER@]HOST   Operate on remote host\n"
190                "  -M --machine=CONTAINER  Operate on local container\n"
191                "     --address=ADDRESS    Connect to bus specified by address\n"
192                "     --no-unique          Only show well-known names\n"
193                "     --match=MATCH        Only show matching messages\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                 {},
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                         assert_not_reached("Unhandled option");
283                 }
284         }
285
286         return 1;
287 }
288
289 static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
290         assert(bus);
291
292         if (optind >= argc ||
293             streq(argv[optind], "list"))
294                 return list_bus_names(bus, argv + optind);
295
296         if (streq(argv[optind], "monitor"))
297                 return monitor(bus, argv + optind);
298
299         if (streq(argv[optind], "help"))
300                 return help();
301
302         log_error("Unknown command '%s'", argv[optind]);
303         return -EINVAL;
304 }
305
306 int main(int argc, char *argv[]) {
307         _cleanup_bus_unref_ sd_bus *bus = NULL;
308         int r;
309
310         log_parse_environment();
311         log_open();
312
313         r = parse_argv(argc, argv);
314         if (r <= 0)
315                 goto finish;
316
317         if (arg_address) {
318                 r = sd_bus_new(&bus);
319                 if (r < 0) {
320                         log_error("Failed to allocate bus: %s", strerror(-r));
321                         goto finish;
322                 }
323
324                 r = sd_bus_set_address(bus, arg_address);
325                 if (r < 0) {
326                         log_error("Failed to set address: %s", strerror(-r));
327                         goto finish;
328                 }
329
330                 r = sd_bus_set_bus_client(bus, true);
331                 if (r < 0) {
332                         log_error("Failed to set bus client: %s", strerror(-r));
333                         goto finish;
334                 }
335
336                 r = sd_bus_start(bus);
337         } else
338                 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
339
340         if (r < 0) {
341                 log_error("Failed to connect to bus: %s", strerror(-r));
342                 goto finish;
343         }
344
345         r = busctl_main(bus, argc, argv);
346
347 finish:
348         pager_close();
349
350         strv_free(arg_matches);
351
352         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
353 }