chiark / gitweb /
timedatectl: introduce new command line client for timedated
[elogind.git] / src / stdio-bridge / stdio-bridge.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/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
39 static bool initial_nul = false;
40 static bool auth_over = false;
41
42 static 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
61 static 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
99 static 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
136 int 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
150         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
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;
161         strncpy(sa.un.sun_path, "/run/dbus/system_bus_socket", sizeof(sa.un.sun_path));
162
163         if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
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
359 finish:
360         if (fd >= 0)
361                 close_nointr_nofail(fd);
362
363         if (ep >= 0)
364                 close_nointr_nofail(ep);
365
366         return r;
367 }