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