]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/busctl.c
util: fix handling of trailing whitespace in split_quoted()
[thirdparty/systemd.git] / src / libsystemd-bus / busctl.c
CommitLineData
de1c301e
LP
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
1f849790
LP
22#include <getopt.h>
23
89ffcd2a
LP
24#include "strv.h"
25#include "util.h"
26#include "log.h"
1f849790
LP
27#include "build.h"
28#include "pager.h"
89ffcd2a 29
de1c301e 30#include "sd-bus.h"
89ffcd2a
LP
31#include "bus-message.h"
32#include "bus-internal.h"
40ca29a1 33#include "bus-util.h"
2b5c5383 34#include "bus-dump.h"
de1c301e 35
1f849790
LP
36static bool arg_no_pager = false;
37static char *arg_address = NULL;
1f849790
LP
38static bool arg_no_unique = false;
39static char **arg_matches = NULL;
d75edbd6
LP
40static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
41static char *arg_host = NULL;
42static bool arg_user = false;
1f849790
LP
43
44static 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
53static int list_bus_names(sd_bus *bus, char **argv) {
de1c301e
LP
54 _cleanup_strv_free_ char **l = NULL;
55 char **i;
56 int r;
89ffcd2a 57 size_t max_i = 0;
de1c301e 58
1f849790 59 assert(bus);
de1c301e
LP
60
61 r = sd_bus_list_names(bus, &l);
62 if (r < 0) {
63 log_error("Failed to list names: %s", strerror(-r));
1f849790 64 return r;
de1c301e
LP
65 }
66
1f849790
LP
67 pager_open_if_enabled();
68
89ffcd2a
LP
69 strv_sort(l);
70
71 STRV_FOREACH(i, l)
72 max_i = MAX(max_i, strlen(*i));
73
7b0b392f
LP
74 printf("%-*s %*s %-*s %-*s %-*s MACHINE\n",
75 (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 20, "CONNECTION");
89ffcd2a 76
de1c301e
LP
77 STRV_FOREACH(i, l) {
78 _cleanup_free_ char *owner = NULL;
7b0b392f 79 sd_id128_t mid;
89ffcd2a 80 pid_t pid;
de1c301e 81 uid_t uid;
de1c301e 82
1f849790
LP
83 if (arg_no_unique && (*i)[0] == ':')
84 continue;
89ffcd2a
LP
85
86 printf("%-*s", (int) max_i, *i);
de1c301e 87
89ffcd2a
LP
88 r = sd_bus_get_owner_pid(bus, *i, &pid);
89 if (r >= 0) {
90 _cleanup_free_ char *comm = NULL;
de1c301e 91
89ffcd2a 92 printf(" %10lu", (unsigned long) pid);
de1c301e 93
89ffcd2a
LP
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);
1f849790
LP
104 if (!u)
105 return log_oom();
89ffcd2a
LP
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)
7b0b392f 116 printf(" %-20s", owner);
89ffcd2a 117 else
7b0b392f
LP
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
89ffcd2a 125 printf(" -\n");
de1c301e
LP
126 }
127
1f849790
LP
128 return 0;
129}
130
131static int monitor(sd_bus *bus, char *argv[]) {
b51f299a 132 bool added_something = false;
1f849790
LP
133 char **i;
134 int r;
135
136 STRV_FOREACH(i, argv+1) {
137 _cleanup_free_ char *m = NULL;
138
139 if (!service_name_is_valid(*i)) {
140 log_error("Invalid service name '%s'", *i);
141 return -EINVAL;
142 }
143
144 m = strjoin("sender='", *i, "'", NULL);
145 if (!m)
146 return log_oom();
147
148 r = sd_bus_add_match(bus, m, NULL, NULL);
149 if (r < 0) {
150 log_error("Failed to add match: %s", strerror(-r));
151 return r;
152 }
b51f299a
LP
153
154 added_something = true;
1f849790
LP
155 }
156
157 STRV_FOREACH(i, arg_matches) {
158 r = sd_bus_add_match(bus, *i, NULL, NULL);
159 if (r < 0) {
160 log_error("Failed to add match: %s", strerror(-r));
161 return r;
162 }
b51f299a
LP
163
164 added_something = true;
165 }
166
167 if (!added_something) {
168 r = sd_bus_add_match(bus, "", NULL, NULL);
169 if (r < 0) {
170 log_error("Failed to add match: %s", strerror(-r));
171 return r;
172 }
1f849790
LP
173 }
174
175 for (;;) {
176 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
177
178 r = sd_bus_process(bus, &m);
179 if (r < 0) {
180 log_error("Failed to process bus: %s", strerror(-r));
181 return r;
182 }
183
184 if (m) {
c430fee6 185 bus_message_dump(m, stdout, true);
1f849790
LP
186 continue;
187 }
188
189 if (r > 0)
190 continue;
191
192 r = sd_bus_wait(bus, (uint64_t) -1);
193 if (r < 0) {
194 log_error("Failed to wait for bus: %s", strerror(-r));
195 return r;
196 }
197 }
198
199 return -EINVAL;
200}
201
202static int help(void) {
203
204 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
205 "Introspect the bus.\n\n"
d75edbd6
LP
206 " -h --help Show this help\n"
207 " --version Show package version\n"
a86a47ce 208 " --no-pager Do not pipe output into a pager\n"
d75edbd6
LP
209 " --system Connect to system bus\n"
210 " --user Connect to user bus\n"
211 " -H --host=[USER@]HOST Operate on remote host\n"
212 " -M --machine=CONTAINER Operate on local container\n"
213 " --address=ADDRESS Connect to bus specified by address\n"
214 " --no-unique Only show well-known names\n"
a86a47ce 215 " --match=MATCH Only show matching messages\n\n"
1f849790 216 "Commands:\n"
d75edbd6
LP
217 " list List bus names\n"
218 " monitor [SERVICE...] Show bus traffic\n",
1f849790
LP
219 program_invocation_short_name);
220
221 return 0;
222}
223
224static int parse_argv(int argc, char *argv[]) {
225
226 enum {
227 ARG_VERSION = 0x100,
228 ARG_NO_PAGER,
229 ARG_SYSTEM,
230 ARG_USER,
231 ARG_ADDRESS,
232 ARG_MATCH,
233 ARG_NO_UNIQUE
234 };
235
236 static const struct option options[] = {
237 { "help", no_argument, NULL, 'h' },
238 { "version", no_argument, NULL, ARG_VERSION },
239 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
240 { "system", no_argument, NULL, ARG_SYSTEM },
241 { "user", no_argument, NULL, ARG_USER },
242 { "address", required_argument, NULL, ARG_ADDRESS },
243 { "no-unique", no_argument, NULL, ARG_NO_UNIQUE },
244 { "match", required_argument, NULL, ARG_MATCH },
8fe12d88
LP
245 { "host", required_argument, NULL, 'H' },
246 { "machine", required_argument, NULL, 'M' },
eb9da376 247 {},
1f849790
LP
248 };
249
250 int c;
251
252 assert(argc >= 0);
253 assert(argv);
254
d75edbd6 255 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
1f849790
LP
256
257 switch (c) {
258
259 case 'h':
260 return help();
261
262 case ARG_VERSION:
263 puts(PACKAGE_STRING);
264 puts(SYSTEMD_FEATURES);
265 return 0;
266
267 case ARG_NO_PAGER:
268 arg_no_pager = true;
269 break;
270
271 case ARG_USER:
272 arg_user = true;
273 break;
274
275 case ARG_SYSTEM:
276 arg_user = false;
277 break;
278
279 case ARG_ADDRESS:
280 arg_address = optarg;
281 break;
282
283 case ARG_NO_UNIQUE:
284 arg_no_unique = true;
285 break;
286
287 case ARG_MATCH:
288 if (strv_extend(&arg_matches, optarg) < 0)
289 return log_oom();
290 break;
291
d75edbd6
LP
292 case 'H':
293 arg_transport = BUS_TRANSPORT_REMOTE;
294 arg_host = optarg;
295 break;
296
297 case 'M':
298 arg_transport = BUS_TRANSPORT_CONTAINER;
299 arg_host = optarg;
300 break;
301
1f849790
LP
302 case '?':
303 return -EINVAL;
304
305 default:
eb9da376 306 assert_not_reached("Unhandled option");
1f849790
LP
307 }
308 }
309
310 return 1;
311}
312
313static int busctl_main(sd_bus *bus, int argc, char *argv[]) {
314 assert(bus);
315
316 if (optind >= argc ||
317 streq(argv[optind], "list"))
318 return list_bus_names(bus, argv + optind);
319
320 if (streq(argv[optind], "monitor"))
321 return monitor(bus, argv + optind);
322
323 if (streq(argv[optind], "help"))
324 return help();
325
326 log_error("Unknown command '%s'", argv[optind]);
327 return -EINVAL;
328}
329
330int main(int argc, char *argv[]) {
331 _cleanup_bus_unref_ sd_bus *bus = NULL;
332 int r;
333
334 log_parse_environment();
335 log_open();
336
337 r = parse_argv(argc, argv);
338 if (r <= 0)
339 goto finish;
340
341 if (arg_address) {
342 r = sd_bus_new(&bus);
343 if (r < 0) {
344 log_error("Failed to allocate bus: %s", strerror(-r));
345 goto finish;
346 }
347
348 r = sd_bus_set_address(bus, arg_address);
349 if (r < 0) {
350 log_error("Failed to set address: %s", strerror(-r));
351 goto finish;
352 }
353
354 r = sd_bus_set_bus_client(bus, true);
355 if (r < 0) {
356 log_error("Failed to set bus client: %s", strerror(-r));
357 goto finish;
358 }
359
360 r = sd_bus_start(bus);
d75edbd6
LP
361 } else
362 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
1f849790
LP
363
364 if (r < 0) {
365 log_error("Failed to connect to bus: %s", strerror(-r));
366 goto finish;
367 }
368
369 r = busctl_main(bus, argc, argv);
370
371finish:
372 pager_close();
d75edbd6 373
1f849790 374 strv_free(arg_matches);
de1c301e 375
de1c301e
LP
376 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
377}