chiark / gitweb /
bus: change bus-proxyd command line parsing to be more similar to other tools
[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                         log_error("Failed to process bus a: %s", strerror(-r));
213                         goto finish;
214                 }
215
216                 if (m) {
217                         r = sd_bus_send(b, m, NULL);
218                         if (r < 0) {
219                                 log_error("Failed to send message: %s", strerror(-r));
220                                 goto finish;
221                         }
222                 }
223
224                 if (r > 0)
225                         continue;
226
227                 r = sd_bus_process(b, &m);
228                 if (r < 0) {
229                         /* treat 'connection reset by peer' as clean exit condition */
230                         if (r == -ECONNRESET)
231                                 r = 0;
232
233                         goto finish;
234                 }
235
236                 if (m) {
237                         r = sd_bus_send(a, m, NULL);
238                         if (r < 0) {
239                                 log_error("Failed to send message: %s", strerror(-r));
240                                 goto finish;
241                         }
242                 }
243
244                 if (r > 0)
245                         continue;
246
247                 fd = sd_bus_get_fd(a);
248                 if (fd < 0) {
249                         log_error("Failed to get fd: %s", strerror(-r));
250                         goto finish;
251                 }
252
253                 events_a = sd_bus_get_events(a);
254                 if (events_a < 0) {
255                         log_error("Failed to get events mask: %s", strerror(-r));
256                         goto finish;
257                 }
258
259                 r = sd_bus_get_timeout(a, &timeout_a);
260                 if (r < 0) {
261                         log_error("Failed to get timeout: %s", strerror(-r));
262                         goto finish;
263                 }
264
265                 events_b = sd_bus_get_events(b);
266                 if (events_b < 0) {
267                         log_error("Failed to get events mask: %s", strerror(-r));
268                         goto finish;
269                 }
270
271                 r = sd_bus_get_timeout(b, &timeout_b);
272                 if (r < 0) {
273                         log_error("Failed to get timeout: %s", strerror(-r));
274                         goto finish;
275                 }
276
277                 t = timeout_a;
278                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
279                         t = timeout_b;
280
281                 if (t == (uint64_t) -1)
282                         ts = NULL;
283                 else {
284                         usec_t nw;
285
286                         nw = now(CLOCK_MONOTONIC);
287                         if (t > nw)
288                                 t -= nw;
289                         else
290                                 t = 0;
291
292                         ts = timespec_store(&_ts, t);
293                 }
294
295                 {
296                         struct pollfd p[3] = {
297                                 {.fd = fd,            .events = events_a, },
298                                 {.fd = STDIN_FILENO,  .events = events_b & POLLIN, },
299                                 {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }};
300
301                         r = ppoll(p, ELEMENTSOF(p), ts, NULL);
302                 }
303                 if (r < 0) {
304                         log_error("ppoll() failed: %m");
305                         goto finish;
306                 }
307         }
308
309         r = 0;
310
311 finish:
312         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
313 }