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;
61 char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
62 size_t in_buffer_full, out_buffer_full;
64 usec_t escape_timestamp;
65 unsigned escape_counter;
68 #define ESCAPE_USEC (1*USEC_PER_SEC)
70 static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
77 for (p = buffer; p < buffer + n; p++) {
81 usec_t nw = now(CLOCK_MONOTONIC);
83 if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) {
84 f->escape_timestamp = nw;
85 f->escape_counter = 1;
87 (f->escape_counter)++;
89 if (f->escape_counter >= 3)
93 f->escape_timestamp = 0;
94 f->escape_counter = 0;
101 static int shovel(PTYForward *f) {
106 while ((f->stdin_readable && f->in_buffer_full <= 0) ||
107 (f->master_writable && f->in_buffer_full > 0) ||
108 (f->master_readable && f->out_buffer_full <= 0) ||
109 (f->stdout_writable && f->out_buffer_full > 0)) {
111 if (f->stdin_readable && f->in_buffer_full < LINE_MAX) {
113 k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full);
117 f->stdin_readable = false;
118 else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
119 f->stdin_readable = false;
120 f->stdin_hangup = true;
122 f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
124 log_error_errno(errno, "read(): %m");
125 return sd_event_exit(f->event, EXIT_FAILURE);
129 f->stdin_readable = false;
130 f->stdin_hangup = true;
132 f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
134 /* Check if ^] has been
135 * pressed three times within
136 * one second. If we get this
137 * we quite immediately. */
138 if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
139 return sd_event_exit(f->event, EXIT_FAILURE);
141 f->in_buffer_full += (size_t) k;
145 if (f->master_writable && f->in_buffer_full > 0) {
147 k = write(f->master, f->in_buffer, f->in_buffer_full);
150 if (errno == EAGAIN || errno == EIO)
151 f->master_writable = false;
152 else if (errno == EPIPE || errno == ECONNRESET) {
153 f->master_writable = f->master_readable = false;
154 f->master_hangup = true;
156 f->master_event_source = sd_event_source_unref(f->master_event_source);
158 log_error_errno(errno, "write(): %m");
159 return sd_event_exit(f->event, EXIT_FAILURE);
162 assert(f->in_buffer_full >= (size_t) k);
163 memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k);
164 f->in_buffer_full -= k;
168 if (f->master_readable && f->out_buffer_full < LINE_MAX) {
170 k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full);
173 /* Note that EIO on the master device
174 * might be cause by vhangup() or
175 * temporary closing of everything on
176 * the other side, we treat it like
177 * EAGAIN here and try again, unless
180 if (errno == EAGAIN || (errno == EIO && f->repeat))
181 f->master_readable = false;
182 else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) {
183 f->master_readable = f->master_writable = false;
184 f->master_hangup = true;
186 f->master_event_source = sd_event_source_unref(f->master_event_source);
188 log_error_errno(errno, "read(): %m");
189 return sd_event_exit(f->event, EXIT_FAILURE);
192 f->out_buffer_full += (size_t) k;
195 if (f->stdout_writable && f->out_buffer_full > 0) {
197 k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full);
201 f->stdout_writable = false;
202 else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
203 f->stdout_writable = false;
204 f->stdout_hangup = true;
205 f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
207 log_error_errno(errno, "write(): %m");
208 return sd_event_exit(f->event, EXIT_FAILURE);
214 f->last_char = f->out_buffer[k-1];
215 f->last_char_set = true;
218 assert(f->out_buffer_full >= (size_t) k);
219 memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k);
220 f->out_buffer_full -= k;
225 if (f->stdin_hangup || f->stdout_hangup || f->master_hangup) {
226 /* Exit the loop if any side hung up and if there's
227 * nothing more to write or nothing we could write. */
229 if ((f->out_buffer_full <= 0 || f->stdout_hangup) &&
230 (f->in_buffer_full <= 0 || f->master_hangup))
231 return sd_event_exit(f->event, EXIT_SUCCESS);
237 static int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
238 PTYForward *f = userdata;
242 assert(e == f->master_event_source);
244 assert(fd == f->master);
246 if (revents & (EPOLLIN|EPOLLHUP))
247 f->master_readable = true;
249 if (revents & (EPOLLOUT|EPOLLHUP))
250 f->master_writable = true;
255 static int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
256 PTYForward *f = userdata;
260 assert(e == f->stdin_event_source);
262 assert(fd == STDIN_FILENO);
264 if (revents & (EPOLLIN|EPOLLHUP))
265 f->stdin_readable = true;
270 static int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
271 PTYForward *f = userdata;
275 assert(e == f->stdout_event_source);
277 assert(fd == STDOUT_FILENO);
279 if (revents & (EPOLLOUT|EPOLLHUP))
280 f->stdout_writable = true;
285 static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *si, void *userdata) {
286 PTYForward *f = userdata;
291 assert(e == f->sigwinch_event_source);
293 /* The window size changed, let's forward that. */
294 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
295 (void)ioctl(f->master, TIOCSWINSZ, &ws);
300 int pty_forward_new(sd_event *event, int master, bool repeat, PTYForward **ret) {
301 _cleanup_(pty_forward_freep) PTYForward *f = NULL;
305 f = new0(PTYForward, 1);
312 f->event = sd_event_ref(event);
314 r = sd_event_default(&f->event);
319 r = fd_nonblock(STDIN_FILENO, true);
323 r = fd_nonblock(STDOUT_FILENO, true);
327 r = fd_nonblock(master, true);
333 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
334 (void)ioctl(master, TIOCSWINSZ, &ws);
336 if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
337 struct termios raw_stdin_attr;
339 f->saved_stdin = true;
341 raw_stdin_attr = f->saved_stdin_attr;
342 cfmakeraw(&raw_stdin_attr);
343 raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
344 tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
347 if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
348 struct termios raw_stdout_attr;
350 f->saved_stdout = true;
352 raw_stdout_attr = f->saved_stdout_attr;
353 cfmakeraw(&raw_stdout_attr);
354 raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
355 raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
356 tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
359 r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
363 r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
364 if (r < 0 && r != -EPERM)
367 r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f);
369 /* stdout without epoll support. Likely redirected to regular file. */
370 f->stdout_writable = true;
374 r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f);
382 PTYForward *pty_forward_free(PTYForward *f) {
385 sd_event_source_unref(f->stdin_event_source);
386 sd_event_source_unref(f->stdout_event_source);
387 sd_event_source_unref(f->master_event_source);
388 sd_event_unref(f->event);
391 tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
393 tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
398 /* STDIN/STDOUT should not be nonblocking normally, so let's
399 * unconditionally reset it */
400 fd_nonblock(STDIN_FILENO, false);
401 fd_nonblock(STDOUT_FILENO, false);
406 int pty_forward_last_char(PTYForward *f, char *ch) {
410 if (!f->last_char_set)