]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/bridge.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / bridge.c
CommitLineData
a8f11321
LP
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
5430f7f2
LP
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
a8f11321
LP
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
5430f7f2 16 Lesser General Public License for more details.
a8f11321 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a8f11321
LP
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/epoll.h>
30#include <stddef.h>
31
32#include "log.h"
33#include "util.h"
34#include "socket-util.h"
35
36#define BUFFER_SIZE (64*1024)
37#define EXTRA_SIZE 16
38
39static bool initial_nul = false;
40static bool auth_over = false;
41
42static void format_uid(char *buf, size_t l) {
43 char text[20 + 1]; /* enough space for a 64bit integer plus NUL */
44 unsigned j;
45
46 assert(l > 0);
47
48 snprintf(text, sizeof(text)-1, "%llu", (unsigned long long) geteuid());
49 text[sizeof(text)-1] = 0;
50
51 memset(buf, 0, l);
52
53 for (j = 0; text[j] && j*2+2 < l; j++) {
54 buf[j*2] = hexchar(text[j] >> 4);
55 buf[j*2+1] = hexchar(text[j] & 0xF);
56 }
57
58 buf[j*2] = 0;
59}
60
61static size_t patch_in_line(char *line, size_t l, size_t left) {
62 size_t r;
63
64 if (line[0] == 0 && !initial_nul) {
65 initial_nul = true;
66 line += 1;
67 l -= 1;
68 r = 1;
69 } else
70 r = 0;
71
72 if (l == 5 && strncmp(line, "BEGIN", 5) == 0) {
73 r += l;
74 auth_over = true;
75
76 } else if (l == 17 && strncmp(line, "NEGOTIATE_UNIX_FD", 17) == 0) {
77 memmove(line + 13, line + 17, left);
78 memcpy(line, "NEGOTIATE_NOP", 13);
79 r += 13;
80
81 } else if (l >= 14 && strncmp(line, "AUTH EXTERNAL ", 14) == 0) {
82 char uid[20*2 + 1];
83 size_t len;
84
85 format_uid(uid, sizeof(uid));
86 len = strlen(uid);
87 assert(len <= EXTRA_SIZE);
88
89 memmove(line + 14 + len, line + l, left);
90 memcpy(line + 14, uid, len);
91
92 r += 14 + len;
93 } else
94 r += l;
95
96 return r;
97}
98
99static size_t patch_in_buffer(char* in_buffer, size_t *in_buffer_full) {
100 size_t i, good = 0;
101
102 if (*in_buffer_full <= 0)
103 return *in_buffer_full;
104
105 /* If authentication is done, we don't touch anything anymore */
106 if (auth_over)
107 return *in_buffer_full;
108
109 if (*in_buffer_full < 2)
110 return 0;
111
112 for (i = 0; i <= *in_buffer_full - 2; i ++) {
113
114 /* Fully lines can be send on */
115 if (in_buffer[i] == '\r' && in_buffer[i+1] == '\n') {
116 if (i > good) {
117 size_t old_length, new_length;
118
119 old_length = i - good;
120 new_length = patch_in_line(in_buffer+good, old_length, *in_buffer_full - i);
121 *in_buffer_full = *in_buffer_full + new_length - old_length;
122
123 good += new_length + 2;
124
125 } else
126 good = i+2;
127 }
128
129 if (auth_over)
130 break;
131 }
132
133 return good;
134}
135
136int main(int argc, char *argv[]) {
137 int r = EXIT_FAILURE, fd = -1, ep = -1;
138 union sockaddr_union sa;
139 char in_buffer[BUFFER_SIZE+EXTRA_SIZE], out_buffer[BUFFER_SIZE+EXTRA_SIZE];
140 size_t in_buffer_full = 0, out_buffer_full = 0;
141 struct epoll_event stdin_ev, stdout_ev, fd_ev;
142 bool stdin_readable = false, stdout_writable = false, fd_readable = false, fd_writable = false;
143 bool stdin_rhup = false, stdout_whup = false, fd_rhup = false, fd_whup = false;
144
145 if (argc > 1) {
146 log_error("This program takes no argument.");
147 return EXIT_FAILURE;
148 }
149
4cfa2c99 150 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
a8f11321
LP
151 log_parse_environment();
152 log_open();
153
154 if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
155 log_error("Failed to create socket: %s", strerror(errno));
156 goto finish;
157 }
158
159 zero(sa);
160 sa.un.sun_family = AF_UNIX;
db019b8d 161 strncpy(sa.un.sun_path, "/run/dbus/system_bus_socket", sizeof(sa.un.sun_path));
a8f11321 162
3338b959 163 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
a8f11321
LP
164 log_error("Failed to connect: %m");
165 goto finish;
166 }
167
168 fd_nonblock(STDIN_FILENO, 1);
169 fd_nonblock(STDOUT_FILENO, 1);
170
171 if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) {
172 log_error("Failed to create epoll: %m");
173 goto finish;
174 }
175
176 zero(stdin_ev);
177 stdin_ev.events = EPOLLIN|EPOLLET;
178 stdin_ev.data.fd = STDIN_FILENO;
179
180 zero(stdout_ev);
181 stdout_ev.events = EPOLLOUT|EPOLLET;
182 stdout_ev.data.fd = STDOUT_FILENO;
183
184 zero(fd_ev);
185 fd_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
186 fd_ev.data.fd = fd;
187
188 if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 ||
189 epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
190 epoll_ctl(ep, EPOLL_CTL_ADD, fd, &fd_ev) < 0) {
191 log_error("Failed to regiser fds in epoll: %m");
192 goto finish;
193 }
194
195 do {
196 struct epoll_event ev[16];
197 ssize_t k;
198 int i, nfds;
199
200 if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) {
201
202 if (errno == EINTR || errno == EAGAIN)
203 continue;
204
205 log_error("epoll_wait(): %m");
206 goto finish;
207 }
208
209 assert(nfds >= 1);
210
211 for (i = 0; i < nfds; i++) {
212 if (ev[i].data.fd == STDIN_FILENO) {
213
214 if (!stdin_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN)))
215 stdin_readable = true;
216
217 } else if (ev[i].data.fd == STDOUT_FILENO) {
218
219 if (ev[i].events & EPOLLHUP) {
220 stdout_writable = false;
221 stdout_whup = true;
222 }
223
224 if (!stdout_whup && (ev[i].events & EPOLLOUT))
225 stdout_writable = true;
226
227 } else if (ev[i].data.fd == fd) {
228
229 if (ev[i].events & EPOLLHUP) {
230 fd_writable = false;
231 fd_whup = true;
232 }
233
234 if (!fd_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN)))
235 fd_readable = true;
236
237 if (!fd_whup && (ev[i].events & EPOLLOUT))
238 fd_writable = true;
239 }
240 }
241
242 while ((stdin_readable && in_buffer_full <= 0) ||
243 (fd_writable && patch_in_buffer(in_buffer, &in_buffer_full) > 0) ||
244 (fd_readable && out_buffer_full <= 0) ||
245 (stdout_writable && out_buffer_full > 0)) {
246
247 size_t in_buffer_good = 0;
248
249 if (stdin_readable && in_buffer_full < BUFFER_SIZE) {
250
251 if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, BUFFER_SIZE - in_buffer_full)) < 0) {
252
253 if (errno == EAGAIN)
254 stdin_readable = false;
255 else if (errno == EPIPE || errno == ECONNRESET)
256 k = 0;
257 else {
258 log_error("read(): %m");
259 goto finish;
260 }
261 } else
262 in_buffer_full += (size_t) k;
263
264 if (k == 0) {
265 stdin_rhup = true;
266 stdin_readable = false;
267 shutdown(STDIN_FILENO, SHUT_RD);
268 close_nointr_nofail(STDIN_FILENO);
269 }
270 }
271
272 in_buffer_good = patch_in_buffer(in_buffer, &in_buffer_full);
273
274 if (fd_writable && in_buffer_good > 0) {
275
276 if ((k = write(fd, in_buffer, in_buffer_good)) < 0) {
277
278 if (errno == EAGAIN)
279 fd_writable = false;
280 else if (errno == EPIPE || errno == ECONNRESET) {
281 fd_whup = true;
282 fd_writable = false;
283 shutdown(fd, SHUT_WR);
284 } else {
285 log_error("write(): %m");
286 goto finish;
287 }
288
289 } else {
290 assert(in_buffer_full >= (size_t) k);
291 memmove(in_buffer, in_buffer + k, in_buffer_full - k);
292 in_buffer_full -= k;
293 }
294 }
295
296 if (fd_readable && out_buffer_full < BUFFER_SIZE) {
297
298 if ((k = read(fd, out_buffer + out_buffer_full, BUFFER_SIZE - out_buffer_full)) < 0) {
299
300 if (errno == EAGAIN)
301 fd_readable = false;
302 else if (errno == EPIPE || errno == ECONNRESET)
303 k = 0;
304 else {
305 log_error("read(): %m");
306 goto finish;
307 }
308 } else
309 out_buffer_full += (size_t) k;
310
311 if (k == 0) {
312 fd_rhup = true;
313 fd_readable = false;
314 shutdown(fd, SHUT_RD);
315 }
316 }
317
318 if (stdout_writable && out_buffer_full > 0) {
319
320 if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) {
321
322 if (errno == EAGAIN)
323 stdout_writable = false;
324 else if (errno == EPIPE || errno == ECONNRESET) {
325 stdout_whup = true;
326 stdout_writable = false;
327 shutdown(STDOUT_FILENO, SHUT_WR);
328 close_nointr(STDOUT_FILENO);
329 } else {
330 log_error("write(): %m");
331 goto finish;
332 }
333
334 } else {
335 assert(out_buffer_full >= (size_t) k);
336 memmove(out_buffer, out_buffer + k, out_buffer_full - k);
337 out_buffer_full -= k;
338 }
339 }
340 }
341
342 if (stdin_rhup && in_buffer_full <= 0 && !fd_whup) {
343 fd_whup = true;
344 fd_writable = false;
345 shutdown(fd, SHUT_WR);
346 }
347
348 if (fd_rhup && out_buffer_full <= 0 && !stdout_whup) {
349 stdout_whup = true;
350 stdout_writable = false;
351 shutdown(STDOUT_FILENO, SHUT_WR);
352 close_nointr(STDOUT_FILENO);
353 }
354
355 } while (!stdout_whup || !fd_whup);
356
357 r = EXIT_SUCCESS;
358
359finish:
360 if (fd >= 0)
361 close_nointr_nofail(fd);
362
363 if (ep >= 0)
364 close_nointr_nofail(ep);
365
366 return r;
367}