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