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/>.
29 #include <sys/epoll.h>
30 #include <sys/ioctl.h>
37 #include "alloc-util.h"
42 #include "time-util.h"
49 PTYForwardFlags flags
;
51 sd_event_source
*stdin_event_source
;
52 sd_event_source
*stdout_event_source
;
53 sd_event_source
*master_event_source
;
55 sd_event_source
*sigwinch_event_source
;
57 struct termios saved_stdin_attr
;
58 struct termios saved_stdout_attr
;
63 bool stdin_readable
:1;
65 bool stdout_writable
:1;
67 bool master_readable
:1;
68 bool master_writable
:1;
71 bool read_from_master
:1;
76 char in_buffer
[LINE_MAX
], out_buffer
[LINE_MAX
];
77 size_t in_buffer_full
, out_buffer_full
;
79 usec_t escape_timestamp
;
80 unsigned escape_counter
;
83 #define ESCAPE_USEC (1*USEC_PER_SEC)
85 static bool look_for_escape(PTYForward
*f
, const char *buffer
, size_t n
) {
92 for (p
= buffer
; p
< buffer
+ n
; p
++) {
96 usec_t nw
= now(CLOCK_MONOTONIC
);
98 if (f
->escape_counter
== 0 || nw
> f
->escape_timestamp
+ ESCAPE_USEC
) {
99 f
->escape_timestamp
= nw
;
100 f
->escape_counter
= 1;
102 (f
->escape_counter
)++;
104 if (f
->escape_counter
>= 3)
108 f
->escape_timestamp
= 0;
109 f
->escape_counter
= 0;
116 static bool ignore_vhangup(PTYForward
*f
) {
119 if (f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
)
122 if ((f
->flags
& PTY_FORWARD_IGNORE_INITIAL_VHANGUP
) && !f
->read_from_master
)
128 static int shovel(PTYForward
*f
) {
133 while ((f
->stdin_readable
&& f
->in_buffer_full
<= 0) ||
134 (f
->master_writable
&& f
->in_buffer_full
> 0) ||
135 (f
->master_readable
&& f
->out_buffer_full
<= 0) ||
136 (f
->stdout_writable
&& f
->out_buffer_full
> 0)) {
138 if (f
->stdin_readable
&& f
->in_buffer_full
< LINE_MAX
) {
140 k
= read(STDIN_FILENO
, f
->in_buffer
+ f
->in_buffer_full
, LINE_MAX
- f
->in_buffer_full
);
144 f
->stdin_readable
= false;
145 else if (errno
== EIO
|| errno
== EPIPE
|| errno
== ECONNRESET
) {
146 f
->stdin_readable
= false;
147 f
->stdin_hangup
= true;
149 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
151 log_error_errno(errno
, "read(): %m");
152 return sd_event_exit(f
->event
, EXIT_FAILURE
);
156 f
->stdin_readable
= false;
157 f
->stdin_hangup
= true;
159 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
161 /* Check if ^] has been
162 * pressed three times within
163 * one second. If we get this
164 * we quite immediately. */
165 if (look_for_escape(f
, f
->in_buffer
+ f
->in_buffer_full
, k
))
166 return sd_event_exit(f
->event
, EXIT_FAILURE
);
168 f
->in_buffer_full
+= (size_t) k
;
172 if (f
->master_writable
&& f
->in_buffer_full
> 0) {
174 k
= write(f
->master
, f
->in_buffer
, f
->in_buffer_full
);
177 if (errno
== EAGAIN
|| errno
== EIO
)
178 f
->master_writable
= false;
179 else if (errno
== EPIPE
|| errno
== ECONNRESET
) {
180 f
->master_writable
= f
->master_readable
= false;
181 f
->master_hangup
= true;
183 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
185 log_error_errno(errno
, "write(): %m");
186 return sd_event_exit(f
->event
, EXIT_FAILURE
);
189 assert(f
->in_buffer_full
>= (size_t) k
);
190 memmove(f
->in_buffer
, f
->in_buffer
+ k
, f
->in_buffer_full
- k
);
191 f
->in_buffer_full
-= k
;
195 if (f
->master_readable
&& f
->out_buffer_full
< LINE_MAX
) {
197 k
= read(f
->master
, f
->out_buffer
+ f
->out_buffer_full
, LINE_MAX
- f
->out_buffer_full
);
200 /* Note that EIO on the master device
201 * might be caused by vhangup() or
202 * temporary closing of everything on
203 * the other side, we treat it like
204 * EAGAIN here and try again, unless
205 * ignore_vhangup is off. */
207 if (errno
== EAGAIN
|| (errno
== EIO
&& ignore_vhangup(f
)))
208 f
->master_readable
= false;
209 else if (errno
== EPIPE
|| errno
== ECONNRESET
|| errno
== EIO
) {
210 f
->master_readable
= f
->master_writable
= false;
211 f
->master_hangup
= true;
213 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
215 log_error_errno(errno
, "read(): %m");
216 return sd_event_exit(f
->event
, EXIT_FAILURE
);
219 f
->read_from_master
= true;
220 f
->out_buffer_full
+= (size_t) k
;
224 if (f
->stdout_writable
&& f
->out_buffer_full
> 0) {
226 k
= write(STDOUT_FILENO
, f
->out_buffer
, f
->out_buffer_full
);
230 f
->stdout_writable
= false;
231 else if (errno
== EIO
|| errno
== EPIPE
|| errno
== ECONNRESET
) {
232 f
->stdout_writable
= false;
233 f
->stdout_hangup
= true;
234 f
->stdout_event_source
= sd_event_source_unref(f
->stdout_event_source
);
236 log_error_errno(errno
, "write(): %m");
237 return sd_event_exit(f
->event
, EXIT_FAILURE
);
243 f
->last_char
= f
->out_buffer
[k
-1];
244 f
->last_char_set
= true;
247 assert(f
->out_buffer_full
>= (size_t) k
);
248 memmove(f
->out_buffer
, f
->out_buffer
+ k
, f
->out_buffer_full
- k
);
249 f
->out_buffer_full
-= k
;
254 if (f
->stdin_hangup
|| f
->stdout_hangup
|| f
->master_hangup
) {
255 /* Exit the loop if any side hung up and if there's
256 * nothing more to write or nothing we could write. */
258 if ((f
->out_buffer_full
<= 0 || f
->stdout_hangup
) &&
259 (f
->in_buffer_full
<= 0 || f
->master_hangup
))
260 return sd_event_exit(f
->event
, EXIT_SUCCESS
);
266 static int on_master_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
267 PTYForward
*f
= userdata
;
271 assert(e
== f
->master_event_source
);
273 assert(fd
== f
->master
);
275 if (revents
& (EPOLLIN
|EPOLLHUP
))
276 f
->master_readable
= true;
278 if (revents
& (EPOLLOUT
|EPOLLHUP
))
279 f
->master_writable
= true;
284 static int on_stdin_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
285 PTYForward
*f
= userdata
;
289 assert(e
== f
->stdin_event_source
);
291 assert(fd
== STDIN_FILENO
);
293 if (revents
& (EPOLLIN
|EPOLLHUP
))
294 f
->stdin_readable
= true;
299 static int on_stdout_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
300 PTYForward
*f
= userdata
;
304 assert(e
== f
->stdout_event_source
);
306 assert(fd
== STDOUT_FILENO
);
308 if (revents
& (EPOLLOUT
|EPOLLHUP
))
309 f
->stdout_writable
= true;
314 static int on_sigwinch_event(sd_event_source
*e
, const struct signalfd_siginfo
*si
, void *userdata
) {
315 PTYForward
*f
= userdata
;
320 assert(e
== f
->sigwinch_event_source
);
322 /* The window size changed, let's forward that. */
323 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) >= 0)
324 (void) ioctl(f
->master
, TIOCSWINSZ
, &ws
);
332 PTYForwardFlags flags
,
335 _cleanup_(pty_forward_freep
) PTYForward
*f
= NULL
;
339 f
= new0(PTYForward
, 1);
346 f
->event
= sd_event_ref(event
);
348 r
= sd_event_default(&f
->event
);
353 if (!(flags
& PTY_FORWARD_READ_ONLY
)) {
354 r
= fd_nonblock(STDIN_FILENO
, true);
358 r
= fd_nonblock(STDOUT_FILENO
, true);
363 r
= fd_nonblock(master
, true);
369 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) >= 0)
370 (void) ioctl(master
, TIOCSWINSZ
, &ws
);
372 if (!(flags
& PTY_FORWARD_READ_ONLY
)) {
373 if (tcgetattr(STDIN_FILENO
, &f
->saved_stdin_attr
) >= 0) {
374 struct termios raw_stdin_attr
;
376 f
->saved_stdin
= true;
378 raw_stdin_attr
= f
->saved_stdin_attr
;
379 cfmakeraw(&raw_stdin_attr
);
380 raw_stdin_attr
.c_oflag
= f
->saved_stdin_attr
.c_oflag
;
381 tcsetattr(STDIN_FILENO
, TCSANOW
, &raw_stdin_attr
);
384 if (tcgetattr(STDOUT_FILENO
, &f
->saved_stdout_attr
) >= 0) {
385 struct termios raw_stdout_attr
;
387 f
->saved_stdout
= true;
389 raw_stdout_attr
= f
->saved_stdout_attr
;
390 cfmakeraw(&raw_stdout_attr
);
391 raw_stdout_attr
.c_iflag
= f
->saved_stdout_attr
.c_iflag
;
392 raw_stdout_attr
.c_lflag
= f
->saved_stdout_attr
.c_lflag
;
393 tcsetattr(STDOUT_FILENO
, TCSANOW
, &raw_stdout_attr
);
396 r
= sd_event_add_io(f
->event
, &f
->stdin_event_source
, STDIN_FILENO
, EPOLLIN
|EPOLLET
, on_stdin_event
, f
);
397 if (r
< 0 && r
!= -EPERM
)
401 r
= sd_event_add_io(f
->event
, &f
->stdout_event_source
, STDOUT_FILENO
, EPOLLOUT
|EPOLLET
, on_stdout_event
, f
);
403 /* stdout without epoll support. Likely redirected to regular file. */
404 f
->stdout_writable
= true;
408 r
= sd_event_add_io(f
->event
, &f
->master_event_source
, master
, EPOLLIN
|EPOLLOUT
|EPOLLET
, on_master_event
, f
);
412 r
= sd_event_add_signal(f
->event
, &f
->sigwinch_event_source
, SIGWINCH
, on_sigwinch_event
, f
);
422 PTYForward
*pty_forward_free(PTYForward
*f
) {
425 sd_event_source_unref(f
->stdin_event_source
);
426 sd_event_source_unref(f
->stdout_event_source
);
427 sd_event_source_unref(f
->master_event_source
);
428 sd_event_source_unref(f
->sigwinch_event_source
);
429 sd_event_unref(f
->event
);
432 tcsetattr(STDOUT_FILENO
, TCSANOW
, &f
->saved_stdout_attr
);
434 tcsetattr(STDIN_FILENO
, TCSANOW
, &f
->saved_stdin_attr
);
439 /* STDIN/STDOUT should not be nonblocking normally, so let's
440 * unconditionally reset it */
441 fd_nonblock(STDIN_FILENO
, false);
442 fd_nonblock(STDOUT_FILENO
, false);
447 int pty_forward_get_last_char(PTYForward
*f
, char *ch
) {
451 if (!f
->last_char_set
)
458 int pty_forward_set_ignore_vhangup(PTYForward
*f
, bool b
) {
463 if (!!(f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
) == b
)
467 f
->flags
|= PTY_FORWARD_IGNORE_VHANGUP
;
469 f
->flags
&= ~PTY_FORWARD_IGNORE_VHANGUP
;
471 if (!ignore_vhangup(f
)) {
473 /* We shall now react to vhangup()s? Let's check
474 * immediately if we might be in one */
476 f
->master_readable
= true;
485 int pty_forward_get_ignore_vhangup(PTYForward
*f
) {
488 return !!(f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
);