]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/ptyfwd.c
btrfs-util: rework how we iterate through the results of the TREE_SEARCH results
[thirdparty/systemd.git] / src / shared / ptyfwd.c
CommitLineData
4ba93280
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-2013 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/epoll.h>
23#include <sys/signalfd.h>
24#include <sys/ioctl.h>
25#include <limits.h>
26#include <termios.h>
27
28#include "util.h"
29#include "ptyfwd.h"
30
023fb90b
LP
31struct PTYForward {
32 sd_event *event;
04d39279 33
023fb90b
LP
34 int master;
35
36 sd_event_source *stdin_event_source;
37 sd_event_source *stdout_event_source;
38 sd_event_source *master_event_source;
39
40 sd_event_source *sigwinch_event_source;
41
42 struct termios saved_stdin_attr;
43 struct termios saved_stdout_attr;
44
45 bool saved_stdin:1;
46 bool saved_stdout:1;
47
48 bool stdin_readable:1;
49 bool stdin_hangup:1;
50 bool stdout_writable:1;
51 bool stdout_hangup:1;
52 bool master_readable:1;
53 bool master_writable:1;
54 bool master_hangup:1;
0ec5543c 55 bool master_suppressed_hangup:1;
023fb90b 56
9b15b784
LP
57 bool repeat:1;
58
59 bool last_char_set:1;
60 char last_char;
61
023fb90b
LP
62 char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
63 size_t in_buffer_full, out_buffer_full;
64
65 usec_t escape_timestamp;
66 unsigned escape_counter;
67};
68
69#define ESCAPE_USEC (1*USEC_PER_SEC)
70
71static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
04d39279
LP
72 const char *p;
73
023fb90b 74 assert(f);
04d39279
LP
75 assert(buffer);
76 assert(n > 0);
77
78 for (p = buffer; p < buffer + n; p++) {
79
80 /* Check for ^] */
81 if (*p == 0x1D) {
82 usec_t nw = now(CLOCK_MONOTONIC);
83
023fb90b
LP
84 if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) {
85 f->escape_timestamp = nw;
86 f->escape_counter = 1;
04d39279 87 } else {
023fb90b 88 (f->escape_counter)++;
04d39279 89
023fb90b 90 if (f->escape_counter >= 3)
04d39279
LP
91 return true;
92 }
93 } else {
023fb90b
LP
94 f->escape_timestamp = 0;
95 f->escape_counter = 0;
04d39279
LP
96 }
97 }
98
99 return false;
100}
101
023fb90b
LP
102static int shovel(PTYForward *f) {
103 ssize_t k;
4ba93280 104
023fb90b 105 assert(f);
4ba93280 106
023fb90b
LP
107 while ((f->stdin_readable && f->in_buffer_full <= 0) ||
108 (f->master_writable && f->in_buffer_full > 0) ||
109 (f->master_readable && f->out_buffer_full <= 0) ||
110 (f->stdout_writable && f->out_buffer_full > 0)) {
4ba93280 111
023fb90b 112 if (f->stdin_readable && f->in_buffer_full < LINE_MAX) {
4ba93280 113
023fb90b
LP
114 k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full);
115 if (k < 0) {
4ba93280 116
023fb90b
LP
117 if (errno == EAGAIN)
118 f->stdin_readable = false;
119 else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
120 f->stdin_readable = false;
121 f->stdin_hangup = true;
4ba93280 122
023fb90b
LP
123 f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
124 } else {
56f64d95 125 log_error_errno(errno, "read(): %m");
023fb90b
LP
126 return sd_event_exit(f->event, EXIT_FAILURE);
127 }
128 } else if (k == 0) {
129 /* EOF on stdin */
130 f->stdin_readable = false;
131 f->stdin_hangup = true;
132
133 f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
134 } else {
135 /* Check if ^] has been
136 * pressed three times within
137 * one second. If we get this
138 * we quite immediately. */
139 if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
140 return sd_event_exit(f->event, EXIT_FAILURE);
141
142 f->in_buffer_full += (size_t) k;
143 }
4ba93280
LP
144 }
145
023fb90b 146 if (f->master_writable && f->in_buffer_full > 0) {
4ba93280 147
023fb90b
LP
148 k = write(f->master, f->in_buffer, f->in_buffer_full);
149 if (k < 0) {
4ba93280 150
023fb90b
LP
151 if (errno == EAGAIN || errno == EIO)
152 f->master_writable = false;
153 else if (errno == EPIPE || errno == ECONNRESET) {
154 f->master_writable = f->master_readable = false;
155 f->master_hangup = true;
4ba93280 156
023fb90b
LP
157 f->master_event_source = sd_event_source_unref(f->master_event_source);
158 } else {
56f64d95 159 log_error_errno(errno, "write(): %m");
023fb90b
LP
160 return sd_event_exit(f->event, EXIT_FAILURE);
161 }
162 } else {
163 assert(f->in_buffer_full >= (size_t) k);
164 memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k);
165 f->in_buffer_full -= k;
166 }
4ba93280
LP
167 }
168
023fb90b 169 if (f->master_readable && f->out_buffer_full < LINE_MAX) {
4ba93280 170
023fb90b
LP
171 k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full);
172 if (k < 0) {
4ba93280 173
0ec5543c
LP
174 if (errno == EAGAIN)
175 f->master_readable = false;
176
177 else if (errno == EIO && f->repeat) {
178
179 /* Note that EIO on the master device
180 * might be cause by vhangup() or
181 * temporary closing of everything on
182 * the other side, we treat it like
183 * EAGAIN here and try again, unless
184 * repeat is off. */
4ba93280 185
023fb90b 186 f->master_readable = false;
0ec5543c
LP
187 f->master_suppressed_hangup = true;
188
189 } else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) {
023fb90b
LP
190 f->master_readable = f->master_writable = false;
191 f->master_hangup = true;
4ba93280 192
023fb90b 193 f->master_event_source = sd_event_source_unref(f->master_event_source);
04d39279 194 } else {
56f64d95 195 log_error_errno(errno, "read(): %m");
023fb90b 196 return sd_event_exit(f->event, EXIT_FAILURE);
04d39279 197 }
023fb90b
LP
198 } else
199 f->out_buffer_full += (size_t) k;
200 }
4ba93280 201
023fb90b 202 if (f->stdout_writable && f->out_buffer_full > 0) {
4ba93280 203
023fb90b
LP
204 k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full);
205 if (k < 0) {
4ba93280 206
023fb90b
LP
207 if (errno == EAGAIN)
208 f->stdout_writable = false;
209 else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
210 f->stdout_writable = false;
211 f->stdout_hangup = true;
212 f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
4ba93280 213 } else {
56f64d95 214 log_error_errno(errno, "write(): %m");
023fb90b 215 return sd_event_exit(f->event, EXIT_FAILURE);
4ba93280 216 }
4ba93280 217
023fb90b 218 } else {
9b15b784
LP
219
220 if (k > 0) {
221 f->last_char = f->out_buffer[k-1];
222 f->last_char_set = true;
223 }
224
023fb90b
LP
225 assert(f->out_buffer_full >= (size_t) k);
226 memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k);
227 f->out_buffer_full -= k;
4ba93280 228 }
023fb90b
LP
229 }
230 }
231
232 if (f->stdin_hangup || f->stdout_hangup || f->master_hangup) {
233 /* Exit the loop if any side hung up and if there's
234 * nothing more to write or nothing we could write. */
4ba93280 235
023fb90b
LP
236 if ((f->out_buffer_full <= 0 || f->stdout_hangup) &&
237 (f->in_buffer_full <= 0 || f->master_hangup))
238 return sd_event_exit(f->event, EXIT_SUCCESS);
239 }
4ba93280 240
023fb90b
LP
241 return 0;
242}
4ba93280 243
023fb90b
LP
244static int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
245 PTYForward *f = userdata;
4ba93280 246
023fb90b
LP
247 assert(f);
248 assert(e);
249 assert(e == f->master_event_source);
250 assert(fd >= 0);
251 assert(fd == f->master);
04d39279 252
0ec5543c
LP
253 f->master_suppressed_hangup = false;
254
023fb90b
LP
255 if (revents & (EPOLLIN|EPOLLHUP))
256 f->master_readable = true;
04d39279 257
023fb90b
LP
258 if (revents & (EPOLLOUT|EPOLLHUP))
259 f->master_writable = true;
04d39279 260
023fb90b
LP
261 return shovel(f);
262}
04d39279 263
023fb90b
LP
264static int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
265 PTYForward *f = userdata;
04d39279 266
023fb90b
LP
267 assert(f);
268 assert(e);
269 assert(e == f->stdin_event_source);
270 assert(fd >= 0);
271 assert(fd == STDIN_FILENO);
04d39279 272
023fb90b
LP
273 if (revents & (EPOLLIN|EPOLLHUP))
274 f->stdin_readable = true;
04d39279 275
023fb90b
LP
276 return shovel(f);
277}
04d39279 278
023fb90b
LP
279static int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
280 PTYForward *f = userdata;
04d39279 281
023fb90b
LP
282 assert(f);
283 assert(e);
284 assert(e == f->stdout_event_source);
285 assert(fd >= 0);
286 assert(fd == STDOUT_FILENO);
04d39279 287
023fb90b
LP
288 if (revents & (EPOLLOUT|EPOLLHUP))
289 f->stdout_writable = true;
04d39279 290
023fb90b
LP
291 return shovel(f);
292}
04d39279 293
023fb90b
LP
294static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *si, void *userdata) {
295 PTYForward *f = userdata;
296 struct winsize ws;
04d39279 297
023fb90b
LP
298 assert(f);
299 assert(e);
300 assert(e == f->sigwinch_event_source);
301
302 /* The window size changed, let's forward that. */
303 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
7c63b23f 304 (void)ioctl(f->master, TIOCSWINSZ, &ws);
023fb90b
LP
305
306 return 0;
4ba93280 307}
04d39279 308
9b15b784 309int pty_forward_new(sd_event *event, int master, bool repeat, PTYForward **ret) {
023fb90b 310 _cleanup_(pty_forward_freep) PTYForward *f = NULL;
04d39279
LP
311 struct winsize ws;
312 int r;
313
023fb90b
LP
314 f = new0(PTYForward, 1);
315 if (!f)
316 return -ENOMEM;
317
9b15b784
LP
318 f->repeat = repeat;
319
023fb90b
LP
320 if (event)
321 f->event = sd_event_ref(event);
322 else {
323 r = sd_event_default(&f->event);
324 if (r < 0)
325 return r;
326 }
327
328 r = fd_nonblock(STDIN_FILENO, true);
329 if (r < 0)
330 return r;
331
332 r = fd_nonblock(STDOUT_FILENO, true);
333 if (r < 0)
334 return r;
335
336 r = fd_nonblock(master, true);
337 if (r < 0)
338 return r;
339
340 f->master = master;
341
eaf73b06 342 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
7c63b23f 343 (void)ioctl(master, TIOCSWINSZ, &ws);
04d39279 344
023fb90b
LP
345 if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
346 struct termios raw_stdin_attr;
04d39279 347
023fb90b
LP
348 f->saved_stdin = true;
349
350 raw_stdin_attr = f->saved_stdin_attr;
90d14d20 351 cfmakeraw(&raw_stdin_attr);
023fb90b 352 raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
90d14d20
LS
353 tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
354 }
90d14d20 355
023fb90b
LP
356 if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
357 struct termios raw_stdout_attr;
358
359 f->saved_stdout = true;
360
361 raw_stdout_attr = f->saved_stdout_attr;
90d14d20 362 cfmakeraw(&raw_stdout_attr);
023fb90b
LP
363 raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
364 raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
90d14d20 365 tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
04d39279
LP
366 }
367
023fb90b
LP
368 r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
369 if (r < 0)
370 return r;
04d39279 371
023fb90b
LP
372 r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
373 if (r < 0 && r != -EPERM)
374 return r;
375
376 r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f);
377 if (r == -EPERM)
378 /* stdout without epoll support. Likely redirected to regular file. */
379 f->stdout_writable = true;
380 else if (r < 0)
381 return r;
382
383 r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f);
384
385 *ret = f;
386 f = NULL;
387
388 return 0;
389}
390
391PTYForward *pty_forward_free(PTYForward *f) {
392
393 if (f) {
394 sd_event_source_unref(f->stdin_event_source);
395 sd_event_source_unref(f->stdout_event_source);
396 sd_event_source_unref(f->master_event_source);
397 sd_event_unref(f->event);
398
399 if (f->saved_stdout)
400 tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
401 if (f->saved_stdin)
402 tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
403
404 free(f);
405 }
04d39279 406
d60473c7
LP
407 /* STDIN/STDOUT should not be nonblocking normally, so let's
408 * unconditionally reset it */
409 fd_nonblock(STDIN_FILENO, false);
410 fd_nonblock(STDOUT_FILENO, false);
411
023fb90b 412 return NULL;
04d39279 413}
9b15b784 414
0ec5543c 415int pty_forward_get_last_char(PTYForward *f, char *ch) {
9b15b784
LP
416 assert(f);
417 assert(ch);
418
419 if (!f->last_char_set)
420 return -ENXIO;
421
422 *ch = f->last_char;
423 return 0;
424}
0ec5543c
LP
425
426int pty_forward_set_repeat(PTYForward *f, bool repeat) {
427 int r;
428
429 assert(f);
430
431 if (f->repeat == repeat)
432 return 0;
433
434 f->repeat = repeat;
435
436 /* Are we currently in a suppress hangup phase? If so, let's
437 * immediately terminate things */
438 if (!f->repeat && f->master_suppressed_hangup) {
439
440 /* Let's try to read again from the master fd, and if
441 * it is this will now cause termination of the
442 * session. */
443
444 f->master_readable = true;
445 r = shovel(f);
446 if (r < 0)
447 return r;
448 }
449
450 return 0;
451}
452
453int pty_forward_get_repeat(PTYForward *f) {
454 assert(f);
455
456 return f->repeat;
457}