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/ioctl.h>
27 #include "alloc-util.h"
37 PTYForwardFlags flags
;
39 sd_event_source
*stdin_event_source
;
40 sd_event_source
*stdout_event_source
;
41 sd_event_source
*master_event_source
;
43 sd_event_source
*sigwinch_event_source
;
45 struct termios saved_stdin_attr
;
46 struct termios saved_stdout_attr
;
51 bool stdin_readable
:1;
53 bool stdout_writable
:1;
55 bool master_readable
:1;
56 bool master_writable
:1;
59 bool read_from_master
:1;
64 char in_buffer
[LINE_MAX
], out_buffer
[LINE_MAX
];
65 size_t in_buffer_full
, out_buffer_full
;
67 usec_t escape_timestamp
;
68 unsigned escape_counter
;
71 #define ESCAPE_USEC (1*USEC_PER_SEC)
73 static bool look_for_escape(PTYForward
*f
, const char *buffer
, size_t n
) {
80 for (p
= buffer
; p
< buffer
+ n
; p
++) {
84 usec_t nw
= now(CLOCK_MONOTONIC
);
86 if (f
->escape_counter
== 0 || nw
> f
->escape_timestamp
+ ESCAPE_USEC
) {
87 f
->escape_timestamp
= nw
;
88 f
->escape_counter
= 1;
90 (f
->escape_counter
)++;
92 if (f
->escape_counter
>= 3)
96 f
->escape_timestamp
= 0;
97 f
->escape_counter
= 0;
104 static bool ignore_vhangup(PTYForward
*f
) {
107 if (f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
)
110 if ((f
->flags
& PTY_FORWARD_IGNORE_INITIAL_VHANGUP
) && !f
->read_from_master
)
116 static int shovel(PTYForward
*f
) {
121 while ((f
->stdin_readable
&& f
->in_buffer_full
<= 0) ||
122 (f
->master_writable
&& f
->in_buffer_full
> 0) ||
123 (f
->master_readable
&& f
->out_buffer_full
<= 0) ||
124 (f
->stdout_writable
&& f
->out_buffer_full
> 0)) {
126 if (f
->stdin_readable
&& f
->in_buffer_full
< LINE_MAX
) {
128 k
= read(STDIN_FILENO
, f
->in_buffer
+ f
->in_buffer_full
, LINE_MAX
- f
->in_buffer_full
);
132 f
->stdin_readable
= false;
133 else if (errno
== EIO
|| errno
== EPIPE
|| errno
== ECONNRESET
) {
134 f
->stdin_readable
= false;
135 f
->stdin_hangup
= true;
137 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
139 log_error_errno(errno
, "read(): %m");
140 return sd_event_exit(f
->event
, EXIT_FAILURE
);
144 f
->stdin_readable
= false;
145 f
->stdin_hangup
= true;
147 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
149 /* Check if ^] has been
150 * pressed three times within
151 * one second. If we get this
152 * we quite immediately. */
153 if (look_for_escape(f
, f
->in_buffer
+ f
->in_buffer_full
, k
))
154 return sd_event_exit(f
->event
, EXIT_FAILURE
);
156 f
->in_buffer_full
+= (size_t) k
;
160 if (f
->master_writable
&& f
->in_buffer_full
> 0) {
162 k
= write(f
->master
, f
->in_buffer
, f
->in_buffer_full
);
165 if (errno
== EAGAIN
|| errno
== EIO
)
166 f
->master_writable
= false;
167 else if (errno
== EPIPE
|| errno
== ECONNRESET
) {
168 f
->master_writable
= f
->master_readable
= false;
169 f
->master_hangup
= true;
171 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
173 log_error_errno(errno
, "write(): %m");
174 return sd_event_exit(f
->event
, EXIT_FAILURE
);
177 assert(f
->in_buffer_full
>= (size_t) k
);
178 memmove(f
->in_buffer
, f
->in_buffer
+ k
, f
->in_buffer_full
- k
);
179 f
->in_buffer_full
-= k
;
183 if (f
->master_readable
&& f
->out_buffer_full
< LINE_MAX
) {
185 k
= read(f
->master
, f
->out_buffer
+ f
->out_buffer_full
, LINE_MAX
- f
->out_buffer_full
);
188 /* Note that EIO on the master device
189 * might be caused by vhangup() or
190 * temporary closing of everything on
191 * the other side, we treat it like
192 * EAGAIN here and try again, unless
193 * ignore_vhangup is off. */
195 if (errno
== EAGAIN
|| (errno
== EIO
&& ignore_vhangup(f
)))
196 f
->master_readable
= false;
197 else if (errno
== EPIPE
|| errno
== ECONNRESET
|| errno
== EIO
) {
198 f
->master_readable
= f
->master_writable
= false;
199 f
->master_hangup
= true;
201 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
203 log_error_errno(errno
, "read(): %m");
204 return sd_event_exit(f
->event
, EXIT_FAILURE
);
207 f
->read_from_master
= true;
208 f
->out_buffer_full
+= (size_t) k
;
212 if (f
->stdout_writable
&& f
->out_buffer_full
> 0) {
214 k
= write(STDOUT_FILENO
, f
->out_buffer
, f
->out_buffer_full
);
218 f
->stdout_writable
= false;
219 else if (errno
== EIO
|| errno
== EPIPE
|| errno
== ECONNRESET
) {
220 f
->stdout_writable
= false;
221 f
->stdout_hangup
= true;
222 f
->stdout_event_source
= sd_event_source_unref(f
->stdout_event_source
);
224 log_error_errno(errno
, "write(): %m");
225 return sd_event_exit(f
->event
, EXIT_FAILURE
);
231 f
->last_char
= f
->out_buffer
[k
-1];
232 f
->last_char_set
= true;
235 assert(f
->out_buffer_full
>= (size_t) k
);
236 memmove(f
->out_buffer
, f
->out_buffer
+ k
, f
->out_buffer_full
- k
);
237 f
->out_buffer_full
-= k
;
242 if (f
->stdin_hangup
|| f
->stdout_hangup
|| f
->master_hangup
) {
243 /* Exit the loop if any side hung up and if there's
244 * nothing more to write or nothing we could write. */
246 if ((f
->out_buffer_full
<= 0 || f
->stdout_hangup
) &&
247 (f
->in_buffer_full
<= 0 || f
->master_hangup
))
248 return sd_event_exit(f
->event
, EXIT_SUCCESS
);
254 static int on_master_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
255 PTYForward
*f
= userdata
;
259 assert(e
== f
->master_event_source
);
261 assert(fd
== f
->master
);
263 if (revents
& (EPOLLIN
|EPOLLHUP
))
264 f
->master_readable
= true;
266 if (revents
& (EPOLLOUT
|EPOLLHUP
))
267 f
->master_writable
= true;
272 static int on_stdin_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
273 PTYForward
*f
= userdata
;
277 assert(e
== f
->stdin_event_source
);
279 assert(fd
== STDIN_FILENO
);
281 if (revents
& (EPOLLIN
|EPOLLHUP
))
282 f
->stdin_readable
= true;
287 static int on_stdout_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
288 PTYForward
*f
= userdata
;
292 assert(e
== f
->stdout_event_source
);
294 assert(fd
== STDOUT_FILENO
);
296 if (revents
& (EPOLLOUT
|EPOLLHUP
))
297 f
->stdout_writable
= true;
302 static int on_sigwinch_event(sd_event_source
*e
, const struct signalfd_siginfo
*si
, void *userdata
) {
303 PTYForward
*f
= userdata
;
308 assert(e
== f
->sigwinch_event_source
);
310 /* The window size changed, let's forward that. */
311 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) >= 0)
312 (void) ioctl(f
->master
, TIOCSWINSZ
, &ws
);
320 PTYForwardFlags flags
,
323 _cleanup_(pty_forward_freep
) PTYForward
*f
= NULL
;
327 f
= new0(PTYForward
, 1);
334 f
->event
= sd_event_ref(event
);
336 r
= sd_event_default(&f
->event
);
341 if (!(flags
& PTY_FORWARD_READ_ONLY
)) {
342 r
= fd_nonblock(STDIN_FILENO
, true);
346 r
= fd_nonblock(STDOUT_FILENO
, true);
351 r
= fd_nonblock(master
, true);
357 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) >= 0)
358 (void) ioctl(master
, TIOCSWINSZ
, &ws
);
360 if (!(flags
& PTY_FORWARD_READ_ONLY
)) {
361 if (tcgetattr(STDIN_FILENO
, &f
->saved_stdin_attr
) >= 0) {
362 struct termios raw_stdin_attr
;
364 f
->saved_stdin
= true;
366 raw_stdin_attr
= f
->saved_stdin_attr
;
367 cfmakeraw(&raw_stdin_attr
);
368 raw_stdin_attr
.c_oflag
= f
->saved_stdin_attr
.c_oflag
;
369 tcsetattr(STDIN_FILENO
, TCSANOW
, &raw_stdin_attr
);
372 if (tcgetattr(STDOUT_FILENO
, &f
->saved_stdout_attr
) >= 0) {
373 struct termios raw_stdout_attr
;
375 f
->saved_stdout
= true;
377 raw_stdout_attr
= f
->saved_stdout_attr
;
378 cfmakeraw(&raw_stdout_attr
);
379 raw_stdout_attr
.c_iflag
= f
->saved_stdout_attr
.c_iflag
;
380 raw_stdout_attr
.c_lflag
= f
->saved_stdout_attr
.c_lflag
;
381 tcsetattr(STDOUT_FILENO
, TCSANOW
, &raw_stdout_attr
);
384 r
= sd_event_add_io(f
->event
, &f
->stdin_event_source
, STDIN_FILENO
, EPOLLIN
|EPOLLET
, on_stdin_event
, f
);
385 if (r
< 0 && r
!= -EPERM
)
389 r
= sd_event_add_io(f
->event
, &f
->stdout_event_source
, STDOUT_FILENO
, EPOLLOUT
|EPOLLET
, on_stdout_event
, f
);
391 /* stdout without epoll support. Likely redirected to regular file. */
392 f
->stdout_writable
= true;
396 r
= sd_event_add_io(f
->event
, &f
->master_event_source
, master
, EPOLLIN
|EPOLLOUT
|EPOLLET
, on_master_event
, f
);
400 r
= sd_event_add_signal(f
->event
, &f
->sigwinch_event_source
, SIGWINCH
, on_sigwinch_event
, f
);
410 PTYForward
*pty_forward_free(PTYForward
*f
) {
413 sd_event_source_unref(f
->stdin_event_source
);
414 sd_event_source_unref(f
->stdout_event_source
);
415 sd_event_source_unref(f
->master_event_source
);
416 sd_event_source_unref(f
->sigwinch_event_source
);
417 sd_event_unref(f
->event
);
420 tcsetattr(STDOUT_FILENO
, TCSANOW
, &f
->saved_stdout_attr
);
422 tcsetattr(STDIN_FILENO
, TCSANOW
, &f
->saved_stdin_attr
);
427 /* STDIN/STDOUT should not be nonblocking normally, so let's
428 * unconditionally reset it */
429 fd_nonblock(STDIN_FILENO
, false);
430 fd_nonblock(STDOUT_FILENO
, false);
435 int pty_forward_get_last_char(PTYForward
*f
, char *ch
) {
439 if (!f
->last_char_set
)
446 int pty_forward_set_ignore_vhangup(PTYForward
*f
, bool b
) {
451 if (!!(f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
) == b
)
455 f
->flags
|= PTY_FORWARD_IGNORE_VHANGUP
;
457 f
->flags
&= ~PTY_FORWARD_IGNORE_VHANGUP
;
459 if (!ignore_vhangup(f
)) {
461 /* We shall now react to vhangup()s? Let's check
462 * immediately if we might be in one */
464 f
->master_readable
= true;
473 int pty_forward_get_ignore_vhangup(PTYForward
*f
) {
476 return !!(f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
);