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