chiark / gitweb /
2944596b090b5d981d5dd5b3fcf86f4d36a72eb2
[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                 uint64_t t;
272                 struct timespec _ts, *ts;
273                 struct pollfd *pollfd;
274                 int k, i, fd;
275
276                 struct bus_bus {
277                         sd_bus *bus;
278                         const char *name;
279                         int events;
280                         uint64_t timeout;
281                 } busses[2] = {
282                         {a, "a"},
283                         {b, "b"},
284                 };
285
286                 for (i = 0; i < 2; i ++) {
287                         r = sd_bus_process(busses[i].bus, &m);
288                         if (r < 0) {
289                                 /* treat 'connection reset by peer' as clean exit condition */
290                                 if (r == -ECONNRESET)
291                                         r = 0;
292                                 else
293                                         log_error("Failed to process bus %s: %s",
294                                                   busses[i].name, strerror(-r));
295                                 goto finish;
296                         }
297
298                         if (m) {
299                                 /* We officially got EOF, let's quit */
300                                 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
301                                         r = 0;
302                                         goto finish;
303                                 }
304
305                                 k = sd_bus_send(busses[1-i].bus, m, NULL);
306                                 if (k < 0) {
307                                         r = k;
308                                         log_error("Failed to send message to bus %s: %s",
309                                                   busses[1-i].name, strerror(-r));
310                                         goto finish;
311                                 }
312                         }
313
314                         if (r > 0)
315                                 continue;
316                 }
317
318                 fd = sd_bus_get_fd(a);
319                 if (fd < 0) {
320                         log_error("Failed to get fd: %s", strerror(-r));
321                         goto finish;
322                 }
323
324                 for (i = 0; i < 2; i ++) {
325                         busses[i].events = sd_bus_get_events(a);
326                         if (busses[i].events < 0) {
327                                 log_error("Failed to get events mask: %s", strerror(-r));
328                                 goto finish;
329                         }
330
331                         r = sd_bus_get_timeout(a, &busses[i].timeout);
332                         if (r < 0) {
333                                 log_error("Failed to get timeout: %s", strerror(-r));
334                                 goto finish;
335                         }
336                 }
337
338                 t = busses[0].timeout;
339                 if (t == (uint64_t) -1 ||
340                     (busses[1].timeout != (uint64_t) -1 && busses[1].timeout < t))
341                         t = busses[1].timeout;
342
343                 if (t == (uint64_t) -1)
344                         ts = NULL;
345                 else {
346                         usec_t nw;
347
348                         nw = now(CLOCK_MONOTONIC);
349                         if (t > nw)
350                                 t -= nw;
351                         else
352                                 t = 0;
353
354                         ts = timespec_store(&_ts, t);
355                 }
356
357                 pollfd = (struct pollfd[3]) {
358                         {.fd = fd,     .events = busses[0].events           },
359                         {.fd = in_fd,  .events = busses[1].events & POLLIN  },
360                         {.fd = out_fd, .events = busses[1].events & POLLOUT },
361                 };
362
363                 r = ppoll(pollfd, 3, ts, NULL);
364                 if (r < 0) {
365                         log_error("ppoll() failed: %m");
366                         goto finish;
367                 }
368         }
369
370         r = 0;
371
372 finish:
373         sd_bus_flush(a);
374         sd_bus_flush(b);
375
376         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
377 }