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