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 bool drained(PTYForward
*f
) {
179 if (f
->out_buffer_full
> 0)
182 if (f
->master_readable
)
185 if (ioctl(f
->master
, TIOCINQ
, &q
) < 0)
186 log_debug_errno(errno
, "TIOCINQ failed on master: %m");
190 if (ioctl(f
->master
, TIOCOUTQ
, &q
) < 0)
191 log_debug_errno(errno
, "TIOCOUTQ failed on master: %m");
198 static int shovel(PTYForward
*f
) {
203 while ((f
->stdin_readable
&& f
->in_buffer_full
<= 0) ||
204 (f
->master_writable
&& f
->in_buffer_full
> 0) ||
205 (f
->master_readable
&& f
->out_buffer_full
<= 0) ||
206 (f
->stdout_writable
&& f
->out_buffer_full
> 0)) {
208 if (f
->stdin_readable
&& f
->in_buffer_full
< LINE_MAX
) {
210 k
= read(STDIN_FILENO
, f
->in_buffer
+ f
->in_buffer_full
, LINE_MAX
- f
->in_buffer_full
);
214 f
->stdin_readable
= false;
215 else if (IN_SET(errno
, EIO
, EPIPE
, ECONNRESET
)) {
216 f
->stdin_readable
= false;
217 f
->stdin_hangup
= true;
219 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
221 log_error_errno(errno
, "read(): %m");
222 return pty_forward_done(f
, -errno
);
226 f
->stdin_readable
= false;
227 f
->stdin_hangup
= true;
229 f
->stdin_event_source
= sd_event_source_unref(f
->stdin_event_source
);
231 /* Check if ^] has been pressed three times within one second. If we get this we quite
233 if (look_for_escape(f
, f
->in_buffer
+ f
->in_buffer_full
, k
))
234 return pty_forward_done(f
, -ECANCELED
);
236 f
->in_buffer_full
+= (size_t) k
;
240 if (f
->master_writable
&& f
->in_buffer_full
> 0) {
242 k
= write(f
->master
, f
->in_buffer
, f
->in_buffer_full
);
245 if (IN_SET(errno
, EAGAIN
, EIO
))
246 f
->master_writable
= false;
247 else if (IN_SET(errno
, EPIPE
, ECONNRESET
)) {
248 f
->master_writable
= f
->master_readable
= false;
249 f
->master_hangup
= true;
251 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
253 log_error_errno(errno
, "write(): %m");
254 return pty_forward_done(f
, -errno
);
257 assert(f
->in_buffer_full
>= (size_t) k
);
258 memmove(f
->in_buffer
, f
->in_buffer
+ k
, f
->in_buffer_full
- k
);
259 f
->in_buffer_full
-= k
;
263 if (f
->master_readable
&& f
->out_buffer_full
< LINE_MAX
) {
265 k
= read(f
->master
, f
->out_buffer
+ f
->out_buffer_full
, LINE_MAX
- f
->out_buffer_full
);
268 /* Note that EIO on the master device
269 * might be caused by vhangup() or
270 * temporary closing of everything on
271 * the other side, we treat it like
272 * EAGAIN here and try again, unless
273 * ignore_vhangup is off. */
275 if (errno
== EAGAIN
|| (errno
== EIO
&& ignore_vhangup(f
)))
276 f
->master_readable
= false;
277 else if (IN_SET(errno
, EPIPE
, ECONNRESET
, EIO
)) {
278 f
->master_readable
= f
->master_writable
= false;
279 f
->master_hangup
= true;
281 f
->master_event_source
= sd_event_source_unref(f
->master_event_source
);
283 log_error_errno(errno
, "read(): %m");
284 return pty_forward_done(f
, -errno
);
287 f
->read_from_master
= true;
288 f
->out_buffer_full
+= (size_t) k
;
292 if (f
->stdout_writable
&& f
->out_buffer_full
> 0) {
294 k
= write(STDOUT_FILENO
, f
->out_buffer
, f
->out_buffer_full
);
298 f
->stdout_writable
= false;
299 else if (IN_SET(errno
, EIO
, EPIPE
, ECONNRESET
)) {
300 f
->stdout_writable
= false;
301 f
->stdout_hangup
= true;
302 f
->stdout_event_source
= sd_event_source_unref(f
->stdout_event_source
);
304 log_error_errno(errno
, "write(): %m");
305 return pty_forward_done(f
, -errno
);
311 f
->last_char
= f
->out_buffer
[k
-1];
312 f
->last_char_set
= true;
315 assert(f
->out_buffer_full
>= (size_t) k
);
316 memmove(f
->out_buffer
, f
->out_buffer
+ k
, f
->out_buffer_full
- k
);
317 f
->out_buffer_full
-= k
;
322 if (f
->stdin_hangup
|| f
->stdout_hangup
|| f
->master_hangup
) {
323 /* Exit the loop if any side hung up and if there's
324 * nothing more to write or nothing we could write. */
326 if ((f
->out_buffer_full
<= 0 || f
->stdout_hangup
) &&
327 (f
->in_buffer_full
<= 0 || f
->master_hangup
))
328 return pty_forward_done(f
, 0);
331 /* If we were asked to drain, and there's nothing more to handle from the master, then call the callback
333 if (f
->drain
&& drained(f
))
334 return pty_forward_done(f
, 0);
339 static int on_master_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
340 PTYForward
*f
= userdata
;
344 assert(e
== f
->master_event_source
);
346 assert(fd
== f
->master
);
348 if (revents
& (EPOLLIN
|EPOLLHUP
))
349 f
->master_readable
= true;
351 if (revents
& (EPOLLOUT
|EPOLLHUP
))
352 f
->master_writable
= true;
357 static int on_stdin_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
358 PTYForward
*f
= userdata
;
362 assert(e
== f
->stdin_event_source
);
364 assert(fd
== STDIN_FILENO
);
366 if (revents
& (EPOLLIN
|EPOLLHUP
))
367 f
->stdin_readable
= true;
372 static int on_stdout_event(sd_event_source
*e
, int fd
, uint32_t revents
, void *userdata
) {
373 PTYForward
*f
= userdata
;
377 assert(e
== f
->stdout_event_source
);
379 assert(fd
== STDOUT_FILENO
);
381 if (revents
& (EPOLLOUT
|EPOLLHUP
))
382 f
->stdout_writable
= true;
387 static int on_sigwinch_event(sd_event_source
*e
, const struct signalfd_siginfo
*si
, void *userdata
) {
388 PTYForward
*f
= userdata
;
393 assert(e
== f
->sigwinch_event_source
);
395 /* The window size changed, let's forward that. */
396 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) >= 0)
397 (void) ioctl(f
->master
, TIOCSWINSZ
, &ws
);
405 PTYForwardFlags flags
,
408 _cleanup_(pty_forward_freep
) PTYForward
*f
= NULL
;
412 f
= new0(PTYForward
, 1);
419 f
->event
= sd_event_ref(event
);
421 r
= sd_event_default(&f
->event
);
426 if (!(flags
& PTY_FORWARD_READ_ONLY
)) {
427 r
= fd_nonblock(STDIN_FILENO
, true);
431 r
= fd_nonblock(STDOUT_FILENO
, true);
436 r
= fd_nonblock(master
, true);
442 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &ws
) >= 0)
443 (void) ioctl(master
, TIOCSWINSZ
, &ws
);
445 if (!(flags
& PTY_FORWARD_READ_ONLY
)) {
446 if (tcgetattr(STDIN_FILENO
, &f
->saved_stdin_attr
) >= 0) {
447 struct termios raw_stdin_attr
;
449 f
->saved_stdin
= true;
451 raw_stdin_attr
= f
->saved_stdin_attr
;
452 cfmakeraw(&raw_stdin_attr
);
453 raw_stdin_attr
.c_oflag
= f
->saved_stdin_attr
.c_oflag
;
454 tcsetattr(STDIN_FILENO
, TCSANOW
, &raw_stdin_attr
);
457 if (tcgetattr(STDOUT_FILENO
, &f
->saved_stdout_attr
) >= 0) {
458 struct termios raw_stdout_attr
;
460 f
->saved_stdout
= true;
462 raw_stdout_attr
= f
->saved_stdout_attr
;
463 cfmakeraw(&raw_stdout_attr
);
464 raw_stdout_attr
.c_iflag
= f
->saved_stdout_attr
.c_iflag
;
465 raw_stdout_attr
.c_lflag
= f
->saved_stdout_attr
.c_lflag
;
466 tcsetattr(STDOUT_FILENO
, TCSANOW
, &raw_stdout_attr
);
469 r
= sd_event_add_io(f
->event
, &f
->stdin_event_source
, STDIN_FILENO
, EPOLLIN
|EPOLLET
, on_stdin_event
, f
);
470 if (r
< 0 && r
!= -EPERM
)
474 (void) sd_event_source_set_description(f
->stdin_event_source
, "ptyfwd-stdin");
477 r
= sd_event_add_io(f
->event
, &f
->stdout_event_source
, STDOUT_FILENO
, EPOLLOUT
|EPOLLET
, on_stdout_event
, f
);
479 /* stdout without epoll support. Likely redirected to regular file. */
480 f
->stdout_writable
= true;
484 (void) sd_event_source_set_description(f
->stdout_event_source
, "ptyfwd-stdout");
486 r
= sd_event_add_io(f
->event
, &f
->master_event_source
, master
, EPOLLIN
|EPOLLOUT
|EPOLLET
, on_master_event
, f
);
490 (void) sd_event_source_set_description(f
->master_event_source
, "ptyfwd-master");
492 r
= sd_event_add_signal(f
->event
, &f
->sigwinch_event_source
, SIGWINCH
, on_sigwinch_event
, f
);
496 (void) sd_event_source_set_description(f
->sigwinch_event_source
, "ptyfwd-sigwinch");
504 PTYForward
*pty_forward_free(PTYForward
*f
) {
505 pty_forward_disconnect(f
);
509 int pty_forward_get_last_char(PTYForward
*f
, char *ch
) {
513 if (!f
->last_char_set
)
520 int pty_forward_set_ignore_vhangup(PTYForward
*f
, bool b
) {
525 if (!!(f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
) == b
)
528 SET_FLAG(f
->flags
, PTY_FORWARD_IGNORE_VHANGUP
, b
);
530 if (!ignore_vhangup(f
)) {
532 /* We shall now react to vhangup()s? Let's check
533 * immediately if we might be in one */
535 f
->master_readable
= true;
544 bool pty_forward_get_ignore_vhangup(PTYForward
*f
) {
547 return !!(f
->flags
& PTY_FORWARD_IGNORE_VHANGUP
);
550 bool pty_forward_is_done(PTYForward
*f
) {
556 void pty_forward_set_handler(PTYForward
*f
, PTYForwardHandler cb
, void *userdata
) {
560 f
->userdata
= userdata
;
563 bool pty_forward_drain(PTYForward
*f
) {
566 /* Starts draining the forwarder. Specifically:
568 * - Returns true if there are no unprocessed bytes from the pty, false otherwise
570 * - Makes sure the handler function is called the next time the number of unprocessed bytes hits zero
577 int pty_forward_set_priority(PTYForward
*f
, int64_t priority
) {
581 r
= sd_event_source_set_priority(f
->stdin_event_source
, priority
);
585 r
= sd_event_source_set_priority(f
->stdout_event_source
, priority
);
589 r
= sd_event_source_set_priority(f
->master_event_source
, priority
);
593 r
= sd_event_source_set_priority(f
->sigwinch_event_source
, priority
);