1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010-2013 Lennart Poettering
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.
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.
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/>.
22 #include <sys/epoll.h>
23 #include <sys/signalfd.h>
24 #include <sys/ioctl.h>
36 sd_event_source *stdin_event_source;
37 sd_event_source *stdout_event_source;
38 sd_event_source *master_event_source;
40 sd_event_source *sigwinch_event_source;
42 struct termios saved_stdin_attr;
43 struct termios saved_stdout_attr;
48 bool stdin_readable:1;
50 bool stdout_writable:1;
52 bool master_readable:1;
53 bool master_writable:1;
55 bool master_suppressed_hangup:1;
62 char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
63 size_t in_buffer_full, out_buffer_full;
65 usec_t escape_timestamp;
66 unsigned escape_counter;
69 #define ESCAPE_USEC (1*USEC_PER_SEC)
71 static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
78 for (p = buffer; p < buffer + n; p++) {
82 usec_t nw = now(CLOCK_MONOTONIC);
84 if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) {
85 f->escape_timestamp = nw;
86 f->escape_counter = 1;
88 (f->escape_counter)++;
90 if (f->escape_counter >= 3)
94 f->escape_timestamp = 0;
95 f->escape_counter = 0;
102 static int shovel(PTYForward *f) {
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)) {
112 if (f->stdin_readable && f->in_buffer_full < LINE_MAX) {
114 k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full);
118 f->stdin_readable = false;
119 else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
120 f->stdin_readable = false;
121 f->stdin_hangup = true;
123 f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
125 log_error_errno(errno, "read(): %m");
126 return sd_event_exit(f->event, EXIT_FAILURE);
130 f->stdin_readable = false;
131 f->stdin_hangup = true;
133 f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
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);
142 f->in_buffer_full += (size_t) k;
146 if (f->master_writable && f->in_buffer_full > 0) {
148 k = write(f->master, f->in_buffer, f->in_buffer_full);
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;
157 f->master_event_source = sd_event_source_unref(f->master_event_source);
159 log_error_errno(errno, "write(): %m");
160 return sd_event_exit(f->event, EXIT_FAILURE);
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;
169 if (f->master_readable && f->out_buffer_full < LINE_MAX) {
171 k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full);
175 f->master_readable = false;
177 else if (errno == EIO && f->repeat) {
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
186 f->master_readable = false;
187 f->master_suppressed_hangup = true;
189 } else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) {
190 f->master_readable = f->master_writable = false;
191 f->master_hangup = true;
193 f->master_event_source = sd_event_source_unref(f->master_event_source);
195 log_error_errno(errno, "read(): %m");
196 return sd_event_exit(f->event, EXIT_FAILURE);
199 f->out_buffer_full += (size_t) k;
202 if (f->stdout_writable && f->out_buffer_full > 0) {
204 k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full);
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);
214 log_error_errno(errno, "write(): %m");
215 return sd_event_exit(f->event, EXIT_FAILURE);
221 f->last_char = f->out_buffer[k-1];
222 f->last_char_set = true;
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;
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. */
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);
244 static int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
245 PTYForward *f = userdata;
249 assert(e == f->master_event_source);
251 assert(fd == f->master);
253 f->master_suppressed_hangup = false;
255 if (revents & (EPOLLIN|EPOLLHUP))
256 f->master_readable = true;
258 if (revents & (EPOLLOUT|EPOLLHUP))
259 f->master_writable = true;
264 static int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
265 PTYForward *f = userdata;
269 assert(e == f->stdin_event_source);
271 assert(fd == STDIN_FILENO);
273 if (revents & (EPOLLIN|EPOLLHUP))
274 f->stdin_readable = true;
279 static int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
280 PTYForward *f = userdata;
284 assert(e == f->stdout_event_source);
286 assert(fd == STDOUT_FILENO);
288 if (revents & (EPOLLOUT|EPOLLHUP))
289 f->stdout_writable = true;
294 static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *si, void *userdata) {
295 PTYForward *f = userdata;
300 assert(e == f->sigwinch_event_source);
302 /* The window size changed, let's forward that. */
303 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
304 (void)ioctl(f->master, TIOCSWINSZ, &ws);
309 int pty_forward_new(sd_event *event, int master, bool repeat, PTYForward **ret) {
310 _cleanup_(pty_forward_freep) PTYForward *f = NULL;
314 f = new0(PTYForward, 1);
321 f->event = sd_event_ref(event);
323 r = sd_event_default(&f->event);
328 r = fd_nonblock(STDIN_FILENO, true);
332 r = fd_nonblock(STDOUT_FILENO, true);
336 r = fd_nonblock(master, true);
342 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
343 (void)ioctl(master, TIOCSWINSZ, &ws);
345 if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
346 struct termios raw_stdin_attr;
348 f->saved_stdin = true;
350 raw_stdin_attr = f->saved_stdin_attr;
351 cfmakeraw(&raw_stdin_attr);
352 raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
353 tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
356 if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
357 struct termios raw_stdout_attr;
359 f->saved_stdout = true;
361 raw_stdout_attr = f->saved_stdout_attr;
362 cfmakeraw(&raw_stdout_attr);
363 raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
364 raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
365 tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
368 r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
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)
376 r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f);
378 /* stdout without epoll support. Likely redirected to regular file. */
379 f->stdout_writable = true;
383 r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f);
391 PTYForward *pty_forward_free(PTYForward *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);
400 tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
402 tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
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);
415 int pty_forward_get_last_char(PTYForward *f, char *ch) {
419 if (!f->last_char_set)
426 int pty_forward_set_repeat(PTYForward *f, bool repeat) {
431 if (f->repeat == repeat)
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) {
440 /* Let's try to read again from the master fd, and if
441 * it is this will now cause termination of the
444 f->master_readable = true;
453 int pty_forward_get_repeat(PTYForward *f) {