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