chiark / gitweb /
015f40a65ce0ab5d5f14068e976d9c03159714f7
[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
43 #ifdef ENABLE_KDBUS
44 const char *arg_bus_path = "kernel:path=/dev/kdbus/0-system/bus;unix:path=/run/dbus/system_bus_socket";
45 #else
46 const char *arg_bus_path = "unix:path=/run/dbus/system_bus_socket";
47 #endif
48
49 static int help(void) {
50
51         printf("%s [OPTIONS...]\n\n"
52                "Connection STDIO or a socket to a given bus address.\n\n"
53                "  -h --help              Show this help\n"
54                "     --version           Show package version\n"
55                "  -p --bus-path=PATH     Bus address to forward to (default: %s)\n",
56                program_invocation_short_name, arg_bus_path);
57
58         return 0;
59 }
60
61 static int parse_argv(int argc, char *argv[]) {
62
63         enum {
64                 ARG_VERSION = 0x100,
65         };
66
67         static const struct option options[] = {
68                 { "help",            no_argument,       NULL, 'h'     },
69                 { "bus-path",        required_argument, NULL, 'p'     },
70                 { NULL,              0,                 NULL, 0       }
71         };
72
73         int c;
74
75         assert(argc >= 0);
76         assert(argv);
77
78         while ((c = getopt_long(argc, argv, "hsup:", options, NULL)) >= 0) {
79
80                 switch (c) {
81
82                 case 'h':
83                         help();
84                         return 0;
85
86                 case ARG_VERSION:
87                         puts(PACKAGE_STRING);
88                         puts(SYSTEMD_FEATURES);
89                         return 0;
90
91                 case '?':
92                         return -EINVAL;
93
94                 case 'p':
95                         arg_bus_path = optarg;
96                         break;
97
98                 default:
99                         log_error("Unknown option code %c", c);
100                         return -EINVAL;
101                 }
102         }
103
104         return 1;
105 }
106
107 int main(int argc, char *argv[]) {
108         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
109         sd_id128_t server_id;
110         bool is_unix;
111         int r, in_fd, out_fd;
112
113         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
114         log_parse_environment();
115         log_open();
116
117         r = parse_argv(argc, argv);
118         if (r <= 0)
119                 goto finish;
120
121         r = sd_listen_fds(0);
122         if (r == 0) {
123                 in_fd = STDIN_FILENO;
124                 out_fd = STDOUT_FILENO;
125         } else if (r == 1) {
126                 in_fd = SD_LISTEN_FDS_START;
127                 out_fd = SD_LISTEN_FDS_START;
128         } else {
129                 log_error("Illegal number of file descriptors passed\n");
130                 goto finish;
131         }
132
133         is_unix =
134                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
135                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
136
137         r = sd_bus_new(&a);
138         if (r < 0) {
139                 log_error("Failed to allocate bus: %s", strerror(-r));
140                 goto finish;
141         }
142
143         r = sd_bus_set_address(a, arg_bus_path);
144         if (r < 0) {
145                 log_error("Failed to set address to connect to: %s", strerror(-r));
146                 goto finish;
147         }
148
149         r = sd_bus_negotiate_fds(a, is_unix);
150         if (r < 0) {
151                 log_error("Failed to set FD negotiation: %s", strerror(-r));
152                 goto finish;
153         }
154
155         r = sd_bus_start(a);
156         if (r < 0) {
157                 log_error("Failed to start bus client: %s", strerror(-r));
158                 goto finish;
159         }
160
161         r = sd_bus_get_server_id(a, &server_id);
162         if (r < 0) {
163                 log_error("Failed to get server ID: %s", strerror(-r));
164                 goto finish;
165         }
166
167         r = sd_bus_new(&b);
168         if (r < 0) {
169                 log_error("Failed to allocate bus: %s", strerror(-r));
170                 goto finish;
171         }
172
173         r = sd_bus_set_fd(b, in_fd, out_fd);
174         if (r < 0) {
175                 log_error("Failed to set fds: %s", strerror(-r));
176                 goto finish;
177         }
178
179         r = sd_bus_set_server(b, 1, server_id);
180         if (r < 0) {
181                 log_error("Failed to set server mode: %s", strerror(-r));
182                 goto finish;
183         }
184
185         r = sd_bus_negotiate_fds(b, is_unix);
186         if (r < 0) {
187                 log_error("Failed to set FD negotiation: %s", strerror(-r));
188                 goto finish;
189         }
190
191         r = sd_bus_set_anonymous(b, true);
192         if (r < 0) {
193                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
194                 goto finish;
195         }
196
197         r = sd_bus_start(b);
198         if (r < 0) {
199                 log_error("Failed to start bus client: %s", strerror(-r));
200                 goto finish;
201         }
202
203         for (;;) {
204                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
205                 int events_a, events_b, fd;
206                 uint64_t timeout_a, timeout_b, t;
207                 struct timespec _ts, *ts;
208
209                 r = sd_bus_process(a, &m);
210                 if (r < 0) {
211                         log_error("Failed to process bus a: %s", strerror(-r));
212                         goto finish;
213                 }
214
215                 if (m) {
216                         r = sd_bus_send(b, m, NULL);
217                         if (r < 0) {
218                                 log_error("Failed to send message: %s", strerror(-r));
219                                 goto finish;
220                         }
221                 }
222
223                 if (r > 0)
224                         continue;
225
226                 r = sd_bus_process(b, &m);
227                 if (r < 0) {
228                         /* treat 'connection reset by peer' as clean exit condition */
229                         if (r == -ECONNRESET)
230                                 r = 0;
231
232                         goto finish;
233                 }
234
235                 if (m) {
236                         r = sd_bus_send(a, m, NULL);
237                         if (r < 0) {
238                                 log_error("Failed to send message: %s", strerror(-r));
239                                 goto finish;
240                         }
241                 }
242
243                 if (r > 0)
244                         continue;
245
246                 fd = sd_bus_get_fd(a);
247                 if (fd < 0) {
248                         log_error("Failed to get fd: %s", strerror(-r));
249                         goto finish;
250                 }
251
252                 events_a = sd_bus_get_events(a);
253                 if (events_a < 0) {
254                         log_error("Failed to get events mask: %s", strerror(-r));
255                         goto finish;
256                 }
257
258                 r = sd_bus_get_timeout(a, &timeout_a);
259                 if (r < 0) {
260                         log_error("Failed to get timeout: %s", strerror(-r));
261                         goto finish;
262                 }
263
264                 events_b = sd_bus_get_events(b);
265                 if (events_b < 0) {
266                         log_error("Failed to get events mask: %s", strerror(-r));
267                         goto finish;
268                 }
269
270                 r = sd_bus_get_timeout(b, &timeout_b);
271                 if (r < 0) {
272                         log_error("Failed to get timeout: %s", strerror(-r));
273                         goto finish;
274                 }
275
276                 t = timeout_a;
277                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
278                         t = timeout_b;
279
280                 if (t == (uint64_t) -1)
281                         ts = NULL;
282                 else {
283                         usec_t nw;
284
285                         nw = now(CLOCK_MONOTONIC);
286                         if (t > nw)
287                                 t -= nw;
288                         else
289                                 t = 0;
290
291                         ts = timespec_store(&_ts, t);
292                 }
293
294                 {
295                         struct pollfd p[3] = {
296                                 {.fd = fd,            .events = events_a, },
297                                 {.fd = STDIN_FILENO,  .events = events_b & POLLIN, },
298                                 {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }};
299
300                         r = ppoll(p, ELEMENTSOF(p), ts, NULL);
301                 }
302                 if (r < 0) {
303                         log_error("ppoll() failed: %m");
304                         goto finish;
305                 }
306         }
307
308         r = 0;
309
310 finish:
311         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
312 }