1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010-2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/epoll.h>
29 #include <sys/ioctl.h>
36 #include "alloc-util.h"
41 #include "time-util.h"
48 PTYForwardFlags flags
;
50 sd_event_source
*stdin_event_source
;
51 sd_event_source
*stdout_event_source
;
52 sd_event_source
*master_event_source
;
54 sd_event_source
*sigwinch_event_source
;
56 struct termios saved_stdin_attr
;
57 struct termios saved_stdout_attr
;
62 bool stdin_readable
:1;
64 bool stdout_writable
:1;
66 bool master_readable
:1;
67 bool master_writable
:1;
70 bool read_from_master
:1;
78 char in_buffer
[LINE_MAX
], out_buffer
[LINE_MAX
];
79 size_t in_buffer_full
, out_buffer_full
;
81 usec_t escape_timestamp
;
82 unsigned escape_counter
;
84 PTYForwardHandler handler
;
88 #define ESCAPE_USEC (1*USEC_PER_SEC)
90 static void pty_forward_disconnect(PTYForward
*f
) {
93 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
94 f
->stdout_event_source
= sd_event_source_unref(f
->stdout_event_source
);
96 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
97 f
->sigwinch_event_source
= sd_event_source_unref(f
->sigwinch_event_source
);
98 f
->event
= sd_event_unref(f
->event
);
101 tcsetattr(STDOUT_FILENO
, TCSANOW
, &f
->saved_stdout_attr
);
103 tcsetattr(STDIN_FILENO
, TCSANOW
, &f
->saved_stdin_attr
);
105 f
->saved_stdout
= f
->saved_stdin
= false;
108 /* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */
109 fd_nonblock(STDIN_FILENO
, false);
110 fd_nonblock(STDOUT_FILENO
, false);
113 static int pty_forward_done(PTYForward
*f
, int rcode
) {
114 _cleanup_(sd_event_unrefp
) sd_event
*e
= NULL
;
120 e
= sd_event_ref(f
->event
);
123 pty_forward_disconnect(f
);
126 return f
->handler(f
, rcode
, f
->userdata
);
128 return sd_event_exit(e
, rcode
< 0 ? EXIT_FAILURE
: rcode
);
131 static bool look_for_escape(PTYForward
*f
, const char *buffer
, size_t n
) {
138 for (p
= buffer
; p
< buffer
+ n
; p
++) {
142 usec_t nw
= now(CLOCK_MONOTONIC
);
144 if (f
->escape_counter
== 0 || nw
> f
->escape_timestamp
+ ESCAPE_USEC
) {
145 f
->escape_timestamp
= nw
;
146 f
->escape_counter
= 1;
148 (f
->escape_counter
)++;
150 if (f
->escape_counter
>= 3)
154 f
->escape_timestamp
= 0;
155 f
->escape_counter
= 0;
162 static bool ignore_vhangup(PTYForward
*f
) {
165 if (f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
)
168 if ((f
->flags
& PTY_FORWARD_IGNORE_INITIAL_VHANGUP
) && !f
->read_from_master
)
174 static int shovel(PTYForward
*f
) {
179 while ((f
->stdin_readable
&& f
->in_buffer_full
<= 0) ||
180 (f
->master_writable
&& f
->in_buffer_full
> 0) ||
181 (f
->master_readable
&& f
->out_buffer_full
<= 0) ||
182 (f
->stdout_writable
&& f
->out_buffer_full
> 0)) {
184 if (f
->stdin_readable
&& f
->in_buffer_full
< LINE_MAX
) {
186 k
= read(STDIN_FILENO
, f
->in_buffer
+ f
->in_buffer_full
, LINE_MAX
- f
->in_buffer_full
);
190 f
->stdin_readable
= false;
191 else if (IN_SET(errno
, EIO
, EPIPE
, ECONNRESET
)) {
192 f
->stdin_readable
= false;
193 f
->stdin_hangup
= true;
195 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
197 log_error_errno(errno
, "read(): %m");
198 return pty_forward_done(f
, -errno
);
202 f
->stdin_readable
= false;
203 f
->stdin_hangup
= true;
205 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
207 /* Check if ^] has been pressed three times within one second. If we get this we quite
209 if (look_for_escape(f
, f
->in_buffer
+ f
->in_buffer_full
, k
))
210 return pty_forward_done(f
, -ECANCELED
);
212 f
->in_buffer_full
+= (size_t) k
;
216 if (f
->master_writable
&& f
->in_buffer_full
> 0) {
218 k
= write(f
->master
, f
->in_buffer
, f
->in_buffer_full
);
221 if (IN_SET(errno
, EAGAIN
, EIO
))
222 f
->master_writable
= false;
223 else if (IN_SET(errno
, EPIPE
, ECONNRESET
)) {
224 f
->master_writable
= f
->master_readable
= false;
225 f
->master_hangup
= true;
227 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
229 log_error_errno(errno
, "write(): %m");
230 return pty_forward_done(f
, -errno
);
233 assert(f
->in_buffer_full
>= (size_t) k
);
234 memmove(f
->in_buffer
, f
->in_buffer
+ k
, f
->in_buffer_full
- k
);
235 f
->in_buffer_full
-= k
;
239 if (f
->master_readable
&& f
->out_buffer_full
< LINE_MAX
) {
241 k
= read(f
->master
, f
->out_buffer
+ f
->out_buffer_full
, LINE_MAX
- f
->out_buffer_full
);
244 /* Note that EIO on the master device
245 * might be caused by vhangup() or
246 * temporary closing of everything on
247 * the other side, we treat it like
248 * EAGAIN here and try again, unless
249 * ignore_vhangup is off. */
251 if (errno
== EAGAIN
|| (errno
== EIO
&& ignore_vhangup(f
)))
252 f
->master_readable
= false;
253 else if (IN_SET(errno
, EPIPE
, ECONNRESET
, EIO
)) {
254 f
->master_readable
= f
->master_writable
= false;
255 f
->master_hangup
= true;
257 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
259 log_error_errno(errno
, "read(): %m");
260 return pty_forward_done(f
, -errno
);
263 f
->read_from_master
= true;
264 f
->out_buffer_full
+= (size_t) k
;
268 if (f
->stdout_writable
&& f
->out_buffer_full
> 0) {
270 k
= write(STDOUT_FILENO
, f
->out_buffer
, f
->out_buffer_full
);
274 f
->stdout_writable
= false;
275 else if (IN_SET(errno
, EIO
, EPIPE
, ECONNRESET
)) {
276 f
->stdout_writable
= false;
277 f
->stdout_hangup
= true;
278 f
->stdout_event_source
= sd_event_source_unref(f
->stdout_event_source
);
280 log_error_errno(errno
, "write(): %m");
281 return pty_forward_done(f
, -errno
);
287 f
->last_char
= f
->out_buffer
[k
-1];
288 f
->last_char_set
= true;
291 assert(f
->out_buffer_full
>= (size_t) k
);
292 memmove(f
->out_buffer
, f
->out_buffer
+ k
, f
->out_buffer_full
- k
);
293 f
->out_buffer_full
-= k
;
298 if (f
->stdin_hangup
|| f
->stdout_hangup
|| f
->master_hangup
) {
299 /* Exit the loop if any side hung up and if there's
300 * nothing more to write or nothing we could write. */
302 if ((f
->out_buffer_full
<= 0 || f
->stdout_hangup
) &&
303 (f
->in_buffer_full
<= 0 || f
->master_hangup
))
304 return pty_forward_done(f
, 0);
307 /* If we were asked to drain, and there's nothing more to handle from the master, then call the callback
309 if (f
->drain
&& f
->out_buffer_full
== 0 && !f
->master_readable
)
310 return pty_forward_done(f
, 0);
315 static int on_master_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
316 PTYForward
*f
= userdata
;
320 assert(e
== f
->master_event_source
);
322 assert(fd
== f
->master
);
324 if (revents
& (EPOLLIN
|EPOLLHUP
))
325 f
->master_readable
= true;
327 if (revents
& (EPOLLOUT
|EPOLLHUP
))
328 f
->master_writable
= true;
333 static int on_stdin_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
334 PTYForward
*f
= userdata
;
338 assert(e
== f
->stdin_event_source
);
340 assert(fd
== STDIN_FILENO
);
342 if (revents
& (EPOLLIN
|EPOLLHUP
))
343 f
->stdin_readable
= true;
348 static int on_stdout_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
349 PTYForward
*f
= userdata
;
353 assert(e
== f
->stdout_event_source
);
355 assert(fd
== STDOUT_FILENO
);
357 if (revents
& (EPOLLOUT
|EPOLLHUP
))
358 f
->stdout_writable
= true;
363 static int on_sigwinch_event(sd_event_source
*e
, const struct signalfd_siginfo
*si
, void *userdata
) {
364 PTYForward
*f
= userdata
;
369 assert(e
== f
->sigwinch_event_source
);
371 /* The window size changed, let's forward that. */
372 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) >= 0)
373 (void) ioctl(f
->master
, TIOCSWINSZ
, &ws
);
381 PTYForwardFlags flags
,
384 _cleanup_(pty_forward_freep
) PTYForward
*f
= NULL
;
388 f
= new0(PTYForward
, 1);
395 f
->event
= sd_event_ref(event
);
397 r
= sd_event_default(&f
->event
);
402 if (!(flags
& PTY_FORWARD_READ_ONLY
)) {
403 r
= fd_nonblock(STDIN_FILENO
, true);
407 r
= fd_nonblock(STDOUT_FILENO
, true);
412 r
= fd_nonblock(master
, true);
418 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) >= 0)
419 (void) ioctl(master
, TIOCSWINSZ
, &ws
);
421 if (!(flags
& PTY_FORWARD_READ_ONLY
)) {
422 if (tcgetattr(STDIN_FILENO
, &f
->saved_stdin_attr
) >= 0) {
423 struct termios raw_stdin_attr
;
425 f
->saved_stdin
= true;
427 raw_stdin_attr
= f
->saved_stdin_attr
;
428 cfmakeraw(&raw_stdin_attr
);
429 raw_stdin_attr
.c_oflag
= f
->saved_stdin_attr
.c_oflag
;
430 tcsetattr(STDIN_FILENO
, TCSANOW
, &raw_stdin_attr
);
433 if (tcgetattr(STDOUT_FILENO
, &f
->saved_stdout_attr
) >= 0) {
434 struct termios raw_stdout_attr
;
436 f
->saved_stdout
= true;
438 raw_stdout_attr
= f
->saved_stdout_attr
;
439 cfmakeraw(&raw_stdout_attr
);
440 raw_stdout_attr
.c_iflag
= f
->saved_stdout_attr
.c_iflag
;
441 raw_stdout_attr
.c_lflag
= f
->saved_stdout_attr
.c_lflag
;
442 tcsetattr(STDOUT_FILENO
, TCSANOW
, &raw_stdout_attr
);
445 r
= sd_event_add_io(f
->event
, &f
->stdin_event_source
, STDIN_FILENO
, EPOLLIN
|EPOLLET
, on_stdin_event
, f
);
446 if (r
< 0 && r
!= -EPERM
)
450 (void) sd_event_source_set_description(f
->stdin_event_source
, "ptyfwd-stdin");
453 r
= sd_event_add_io(f
->event
, &f
->stdout_event_source
, STDOUT_FILENO
, EPOLLOUT
|EPOLLET
, on_stdout_event
, f
);
455 /* stdout without epoll support. Likely redirected to regular file. */
456 f
->stdout_writable
= true;
460 (void) sd_event_source_set_description(f
->stdout_event_source
, "ptyfwd-stdout");
462 r
= sd_event_add_io(f
->event
, &f
->master_event_source
, master
, EPOLLIN
|EPOLLOUT
|EPOLLET
, on_master_event
, f
);
466 (void) sd_event_source_set_description(f
->master_event_source
, "ptyfwd-master");
468 r
= sd_event_add_signal(f
->event
, &f
->sigwinch_event_source
, SIGWINCH
, on_sigwinch_event
, f
);
472 (void) sd_event_source_set_description(f
->sigwinch_event_source
, "ptyfwd-sigwinch");
480 PTYForward
*pty_forward_free(PTYForward
*f
) {
481 pty_forward_disconnect(f
);
485 int pty_forward_get_last_char(PTYForward
*f
, char *ch
) {
489 if (!f
->last_char_set
)
496 int pty_forward_set_ignore_vhangup(PTYForward
*f
, bool b
) {
501 if (!!(f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
) == b
)
504 SET_FLAG(f
->flags
, PTY_FORWARD_IGNORE_VHANGUP
, b
);
506 if (!ignore_vhangup(f
)) {
508 /* We shall now react to vhangup()s? Let's check
509 * immediately if we might be in one */
511 f
->master_readable
= true;
520 bool pty_forward_get_ignore_vhangup(PTYForward
*f
) {
523 return !!(f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
);
526 bool pty_forward_is_done(PTYForward
*f
) {
532 void pty_forward_set_handler(PTYForward
*f
, PTYForwardHandler cb
, void *userdata
) {
536 f
->userdata
= userdata
;
539 bool pty_forward_drain(PTYForward
*f
) {
542 /* Starts draining the forwarder. Specifically:
544 * - Returns true if there are no unprocessed bytes from the pty, false otherwise
546 * - Makes sure the handler function is called the next time the number of unprocessed bytes hits zero
551 return f
->out_buffer_full
== 0 && !f
->master_readable
;