chiark / gitweb /
bus-proxyd: show address nicely in --help
[elogind.git] / src / bus-proxyd / bus-proxyd.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 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 <sys/socket.h>
23 #include <sys/un.h>
24 #include <sys/types.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/poll.h>
30 #include <stddef.h>
31 #include <getopt.h>
32
33 #include "log.h"
34 #include "util.h"
35 #include "socket-util.h"
36 #include "sd-daemon.h"
37 #include "sd-bus.h"
38 #include "bus-internal.h"
39 #include "bus-message.h"
40 #include "bus-util.h"
41 #include "build.h"
42 #include "strv.h"
43
44 #define UNIX_BUS_PATH "unix:path=/run/dbus/system_bus_socket"
45 #define KERNEL_BUS_PATH "kernel:path=/dev/kdbus/0-system/bus"
46
47 #ifdef ENABLE_KDBUS
48 #  define DEFAULT_BUS_PATH KERNEL_BUS_PATH ";" UNIX_BUS_PATH
49 #else
50 #  define DEFAULT_BUS_PATH UNIX_BUS_PATH
51 #endif
52
53 static const char *arg_address = DEFAULT_BUS_PATH;
54 static char *arg_command_line_buffer = NULL;
55
56 static int help(void) {
57
58         printf("%s [OPTIONS...]\n\n"
59                "Connect STDIO or a socket to a given bus address.\n\n"
60                "  -h --help              Show this help\n"
61                "     --version           Show package version\n"
62                "     --address=ADDRESS   Connect to the bus specified by ADDRESS\n"
63                "                         (default: " DEFAULT_BUS_PATH ")\n",
64                program_invocation_short_name);
65
66         return 0;
67 }
68
69 static int parse_argv(int argc, char *argv[]) {
70
71         enum {
72                 ARG_VERSION = 0x100,
73                 ARG_ADDRESS,
74         };
75
76         static const struct option options[] = {
77                 { "help",       no_argument,       NULL, 'h'            },
78                 { "version",    no_argument,       NULL, ARG_VERSION    },
79                 { "address",    required_argument, NULL, ARG_ADDRESS    },
80                 { NULL,         0,                 NULL, 0              }
81         };
82
83         int c;
84
85         assert(argc >= 0);
86         assert(argv);
87
88         while ((c = getopt_long(argc, argv, "hsup:", options, NULL)) >= 0) {
89
90                 switch (c) {
91
92                 case 'h':
93                         help();
94                         return 0;
95
96                 case ARG_VERSION:
97                         puts(PACKAGE_STRING);
98                         puts(SYSTEMD_FEATURES);
99                         return 0;
100
101                 case ARG_ADDRESS:
102                         arg_address = optarg;
103                         break;
104
105                 case '?':
106                         return -EINVAL;
107
108                 default:
109                         assert_not_reached("Unhandled option");
110                 }
111         }
112
113         /* If the first command line argument is only "x" characters
114          * we'll write who we are talking to into it, so that "ps" is
115          * explanatory */
116         arg_command_line_buffer = argv[optind];
117         if (argc > optind + 1 ||
118             (arg_command_line_buffer && arg_command_line_buffer[strspn(arg_command_line_buffer, "x")] != 0)) {
119                 log_error("Too many arguments");
120                 return -EINVAL;
121         }
122
123         return 1;
124 }
125
126 int main(int argc, char *argv[]) {
127
128         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
129         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
130         sd_id128_t server_id;
131         int r, in_fd, out_fd;
132         char **cmdline;
133         const char *comm;
134         bool is_unix;
135         uid_t uid;
136         pid_t pid;
137
138         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
139         log_parse_environment();
140         log_open();
141
142         r = parse_argv(argc, argv);
143         if (r <= 0)
144                 goto finish;
145
146         r = sd_listen_fds(0);
147         if (r == 0) {
148                 in_fd = STDIN_FILENO;
149                 out_fd = STDOUT_FILENO;
150         } else if (r == 1) {
151                 in_fd = SD_LISTEN_FDS_START;
152                 out_fd = SD_LISTEN_FDS_START;
153         } else {
154                 log_error("Illegal number of file descriptors passed\n");
155                 goto finish;
156         }
157
158         is_unix =
159                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
160                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
161
162         r = sd_bus_new(&a);
163         if (r < 0) {
164                 log_error("Failed to allocate bus: %s", strerror(-r));
165                 goto finish;
166         }
167
168         r = sd_bus_set_address(a, arg_address);
169         if (r < 0) {
170                 log_error("Failed to set address to connect to: %s", strerror(-r));
171                 goto finish;
172         }
173
174         r = sd_bus_negotiate_fds(a, is_unix);
175         if (r < 0) {
176                 log_error("Failed to set FD negotiation: %s", strerror(-r));
177                 goto finish;
178         }
179
180         r = sd_bus_start(a);
181         if (r < 0) {
182                 log_error("Failed to start bus client: %s", strerror(-r));
183                 goto finish;
184         }
185
186         r = sd_bus_get_server_id(a, &server_id);
187         if (r < 0) {
188                 log_error("Failed to get server ID: %s", strerror(-r));
189                 goto finish;
190         }
191
192         r = sd_bus_new(&b);
193         if (r < 0) {
194                 log_error("Failed to allocate bus: %s", strerror(-r));
195                 goto finish;
196         }
197
198         r = sd_bus_set_fd(b, in_fd, out_fd);
199         if (r < 0) {
200                 log_error("Failed to set fds: %s", strerror(-r));
201                 goto finish;
202         }
203
204         r = sd_bus_set_server(b, 1, server_id);
205         if (r < 0) {
206                 log_error("Failed to set server mode: %s", strerror(-r));
207                 goto finish;
208         }
209
210         r = sd_bus_negotiate_fds(b, is_unix);
211         if (r < 0) {
212                 log_error("Failed to set FD negotiation: %s", strerror(-r));
213                 goto finish;
214         }
215
216         r = sd_bus_set_anonymous(b, true);
217         if (r < 0) {
218                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
219                 goto finish;
220         }
221
222         r = sd_bus_start(b);
223         if (r < 0) {
224                 log_error("Failed to start bus client: %s", strerror(-r));
225                 goto finish;
226         }
227
228         if (sd_bus_get_peer_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM, &creds) >= 0 &&
229             sd_bus_creds_get_uid(creds, &uid) >= 0 &&
230             sd_bus_creds_get_pid(creds, &pid) >= 0 &&
231             sd_bus_creds_get_cmdline(creds, &cmdline) >= 0 &&
232             sd_bus_creds_get_comm(creds, &comm) >= 0) {
233                 _cleanup_free_ char *p = NULL, *name = NULL;
234
235                 name = uid_to_name(uid);
236                 if (!name) {
237                         r = log_oom();
238                         goto finish;
239                 }
240
241                 p = strv_join(cmdline, " ");
242                 if (!p) {
243                         r = log_oom();
244                         goto finish;
245                 }
246
247                 /* The status string gets the full command line ... */
248                 sd_notifyf(false,
249                            "STATUS=Processing requests from client PID %lu (%s); UID %lu (%s)",
250                            (unsigned long) pid, p,
251                            (unsigned long) uid, name);
252
253                 /* ... and the argv line only the short comm */
254                 if (arg_command_line_buffer) {
255                         size_t m, w;
256
257                         m = strlen(arg_command_line_buffer);
258                         w = snprintf(arg_command_line_buffer, m,
259                                      "[PID %lu/%s; UID %lu/%s]",
260                                      (unsigned long) pid, comm,
261                                      (unsigned long) uid, name);
262
263                         if (m > w)
264                                 memset(arg_command_line_buffer + w, 0, m - w);
265
266                 }
267         }
268
269         for (;;) {
270                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
271                 int events_a, events_b, fd;
272                 uint64_t timeout_a, timeout_b, t;
273                 struct timespec _ts, *ts;
274                 struct pollfd *pollfd;
275                 int k;
276
277                 r = sd_bus_process(a, &m);
278                 if (r < 0) {
279                         /* treat 'connection reset by peer' as clean exit condition */
280                         if (r == -ECONNRESET)
281                                 r = 0;
282                         else
283                                 log_error("Failed to process bus a: %s", strerror(-r));
284
285                         goto finish;
286                 }
287
288                 if (m) {
289                         /* We officially got EOF, let's quit */
290                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
291                                 r = 0;
292                                 goto finish;
293                         }
294
295                         k = sd_bus_send(b, m, NULL);
296                         if (k < 0) {
297                                 r = k;
298                                 log_error("Failed to send message: %s", strerror(-r));
299                                 goto finish;
300                         }
301                 }
302
303                 if (r > 0)
304                         continue;
305
306                 r = sd_bus_process(b, &m);
307                 if (r < 0) {
308                         /* treat 'connection reset by peer' as clean exit condition */
309                         if (r == -ECONNRESET)
310                                 r = 0;
311                         else
312                                 log_error("Failed to process bus b: %s", strerror(-r));
313
314                         goto finish;
315                 }
316
317                 if (m) {
318                         /* We officially got EOF, let's quit */
319                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
320                                 r = 0;
321                                 goto finish;
322                         }
323
324                         k = sd_bus_send(a, m, NULL);
325                         if (k < 0) {
326                                 r = k;
327                                 log_error("Failed to send message: %s", strerror(-r));
328                                 goto finish;
329                         }
330                 }
331
332                 if (r > 0)
333                         continue;
334
335                 fd = sd_bus_get_fd(a);
336                 if (fd < 0) {
337                         log_error("Failed to get fd: %s", strerror(-r));
338                         goto finish;
339                 }
340
341                 events_a = sd_bus_get_events(a);
342                 if (events_a < 0) {
343                         log_error("Failed to get events mask: %s", strerror(-r));
344                         goto finish;
345                 }
346
347                 r = sd_bus_get_timeout(a, &timeout_a);
348                 if (r < 0) {
349                         log_error("Failed to get timeout: %s", strerror(-r));
350                         goto finish;
351                 }
352
353                 events_b = sd_bus_get_events(b);
354                 if (events_b < 0) {
355                         log_error("Failed to get events mask: %s", strerror(-r));
356                         goto finish;
357                 }
358
359                 r = sd_bus_get_timeout(b, &timeout_b);
360                 if (r < 0) {
361                         log_error("Failed to get timeout: %s", strerror(-r));
362                         goto finish;
363                 }
364
365                 t = timeout_a;
366                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
367                         t = timeout_b;
368
369                 if (t == (uint64_t) -1)
370                         ts = NULL;
371                 else {
372                         usec_t nw;
373
374                         nw = now(CLOCK_MONOTONIC);
375                         if (t > nw)
376                                 t -= nw;
377                         else
378                                 t = 0;
379
380                         ts = timespec_store(&_ts, t);
381                 }
382
383                 pollfd = (struct pollfd[3]) {
384                         {.fd = fd,     .events = events_a,           },
385                         {.fd = in_fd,  .events = events_b & POLLIN,  },
386                         {.fd = out_fd, .events = events_b & POLLOUT, }
387                 };
388
389                 r = ppoll(pollfd, 3, ts, NULL);
390                 if (r < 0) {
391                         log_error("ppoll() failed: %m");
392                         goto finish;
393                 }
394         }
395
396         r = 0;
397
398 finish:
399         sd_bus_flush(a);
400         sd_bus_flush(b);
401
402         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
403 }