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