]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/stdio-bridge/stdio-bridge.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / stdio-bridge / stdio-bridge.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
798c486f
DM
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <errno.h>
22#include <getopt.h>
23#include <poll.h>
24#include <stddef.h>
25#include <string.h>
26#include <unistd.h>
27
28#include "sd-bus.h"
29#include "sd-daemon.h"
30
31#include "bus-internal.h"
32#include "bus-util.h"
33#include "build.h"
34#include "log.h"
35#include "util.h"
36
37#define DEFAULT_BUS_PATH "unix:path=/run/dbus/system_bus_socket"
38
39const char *arg_bus_path = DEFAULT_BUS_PATH;
40
41static int help(void) {
42
43 printf("%s [OPTIONS...]\n\n"
44 "STDIO or socket-activatable proxy to a given DBus endpoint.\n\n"
45 " -h --help Show this help\n"
46 " --version Show package version\n"
47 " --bus-path=PATH Path to the kernel bus (default: %s)\n",
48 program_invocation_short_name, DEFAULT_BUS_PATH);
49
50 return 0;
51}
52
53static int parse_argv(int argc, char *argv[]) {
54
55 enum {
56 ARG_VERSION = 0x100,
57 };
58
59 static const struct option options[] = {
60 { "help", no_argument, NULL, 'h' },
61 { "bus-path", required_argument, NULL, 'p' },
62 { NULL, 0, NULL, 0 }
63 };
64
65 int c;
66
67 assert(argc >= 0);
68 assert(argv);
69
70 while ((c = getopt_long(argc, argv, "hsup:", options, NULL)) >= 0) {
71
72 switch (c) {
73
74 case 'h':
75 help();
76 return 0;
77
78 case ARG_VERSION:
79 return version();
80
81 case '?':
82 return -EINVAL;
83
84 case 'p':
85 arg_bus_path = optarg;
86 break;
87
88 default:
89 log_error("Unknown option code %c", c);
90 return -EINVAL;
91 }
92 }
93
94 return 1;
95}
96
97int main(int argc, char *argv[]) {
98 _cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL;
99 sd_id128_t server_id;
100 bool is_unix;
101 int r, in_fd, out_fd;
102
103 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
104 log_parse_environment();
105 log_open();
106
107 r = parse_argv(argc, argv);
108 if (r <= 0)
109 goto finish;
110
111 r = sd_listen_fds(0);
112 if (r == 0) {
113 in_fd = STDIN_FILENO;
114 out_fd = STDOUT_FILENO;
115 } else if (r == 1) {
116 in_fd = SD_LISTEN_FDS_START;
117 out_fd = SD_LISTEN_FDS_START;
118 } else {
35bca925 119 log_error("Illegal number of file descriptors passed.");
798c486f
DM
120 goto finish;
121 }
122
123 is_unix =
124 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
125 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
126
127 r = sd_bus_new(&a);
128 if (r < 0) {
129 log_error_errno(r, "Failed to allocate bus: %m");
130 goto finish;
131 }
132
133 r = sd_bus_set_address(a, arg_bus_path);
134 if (r < 0) {
135 log_error_errno(r, "Failed to set address to connect to: %m");
136 goto finish;
137 }
138
139 r = sd_bus_negotiate_fds(a, is_unix);
140 if (r < 0) {
141 log_error_errno(r, "Failed to set FD negotiation: %m");
142 goto finish;
143 }
144
145 r = sd_bus_start(a);
146 if (r < 0) {
147 log_error_errno(r, "Failed to start bus client: %m");
148 goto finish;
149 }
150
151 r = sd_bus_get_bus_id(a, &server_id);
152 if (r < 0) {
153 log_error_errno(r, "Failed to get server ID: %m");
154 goto finish;
155 }
156
157 r = sd_bus_new(&b);
158 if (r < 0) {
159 log_error_errno(r, "Failed to allocate bus: %m");
160 goto finish;
161 }
162
163 r = sd_bus_set_fd(b, in_fd, out_fd);
164 if (r < 0) {
165 log_error_errno(r, "Failed to set fds: %m");
166 goto finish;
167 }
168
169 r = sd_bus_set_server(b, 1, server_id);
170 if (r < 0) {
171 log_error_errno(r, "Failed to set server mode: %m");
172 goto finish;
173 }
174
175 r = sd_bus_negotiate_fds(b, is_unix);
176 if (r < 0) {
177 log_error_errno(r, "Failed to set FD negotiation: %m");
178 goto finish;
179 }
180
181 r = sd_bus_set_anonymous(b, true);
182 if (r < 0) {
183 log_error_errno(r, "Failed to set anonymous authentication: %m");
184 goto finish;
185 }
186
187 r = sd_bus_start(b);
188 if (r < 0) {
189 log_error_errno(r, "Failed to start bus client: %m");
190 goto finish;
191 }
192
193 for (;;) {
35bca925 194 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
798c486f
DM
195 int events_a, events_b, fd;
196 uint64_t timeout_a, timeout_b, t;
197 struct timespec _ts, *ts;
198
199 r = sd_bus_process(a, &m);
200 if (r < 0) {
201 log_error_errno(r, "Failed to process bus a: %m");
202 goto finish;
203 }
204
205 if (m) {
206 r = sd_bus_send(b, m, NULL);
207 if (r < 0) {
208 log_error_errno(r, "Failed to send message: %m");
209 goto finish;
210 }
211 }
212
213 if (r > 0)
214 continue;
215
216 r = sd_bus_process(b, &m);
217 if (r < 0) {
218 /* treat 'connection reset by peer' as clean exit condition */
219 if (r == -ECONNRESET)
220 r = 0;
221
222 goto finish;
223 }
224
225 if (m) {
226 r = sd_bus_send(a, m, NULL);
227 if (r < 0) {
228 log_error_errno(r, "Failed to send message: %m");
229 goto finish;
230 }
231 }
232
233 if (r > 0)
234 continue;
235
236 fd = sd_bus_get_fd(a);
237 if (fd < 0) {
20268e0c 238 r = fd;
798c486f
DM
239 log_error_errno(r, "Failed to get fd: %m");
240 goto finish;
241 }
242
243 events_a = sd_bus_get_events(a);
244 if (events_a < 0) {
20268e0c 245 r = events_a;
798c486f
DM
246 log_error_errno(r, "Failed to get events mask: %m");
247 goto finish;
248 }
249
250 r = sd_bus_get_timeout(a, &timeout_a);
251 if (r < 0) {
252 log_error_errno(r, "Failed to get timeout: %m");
253 goto finish;
254 }
255
256 events_b = sd_bus_get_events(b);
257 if (events_b < 0) {
20268e0c 258 r = events_b;
798c486f
DM
259 log_error_errno(r, "Failed to get events mask: %m");
260 goto finish;
261 }
262
263 r = sd_bus_get_timeout(b, &timeout_b);
264 if (r < 0) {
265 log_error_errno(r, "Failed to get timeout: %m");
266 goto finish;
267 }
268
269 t = timeout_a;
270 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
271 t = timeout_b;
272
273 if (t == (uint64_t) -1)
274 ts = NULL;
275 else {
276 usec_t nw;
277
278 nw = now(CLOCK_MONOTONIC);
279 if (t > nw)
280 t -= nw;
281 else
282 t = 0;
283
284 ts = timespec_store(&_ts, t);
285 }
286
287 {
288 struct pollfd p[3] = {
289 {.fd = fd, .events = events_a, },
290 {.fd = STDIN_FILENO, .events = events_b & POLLIN, },
291 {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }};
292
293 r = ppoll(p, ELEMENTSOF(p), ts, NULL);
294 }
295 if (r < 0) {
25f027c5 296 log_error_errno(errno, "ppoll() failed: %m");
798c486f
DM
297 goto finish;
298 }
299 }
300
798c486f
DM
301finish:
302 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
303}