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