ptyfwd.c revision dc75168823540076b354135f6e2de7a9a978fbca
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering This file is part of systemd.
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering Copyright 2010-2013 Lennart Poettering
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering systemd is free software; you can redistribute it and/or modify it
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering under the terms of the GNU Lesser General Public License as published by
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering (at your option) any later version.
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering systemd is distributed in the hope that it will be useful, but
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering Lesser General Public License for more details.
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering You should have received a copy of the GNU Lesser General Public License
4ba93280223ceb5de1bcedb196c38252f334521aLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering /* Continue reading after hangup? */
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poetteringstatic bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
04d39279245834494baccfdb9349db8bf80abd13Lennart Poettering const char *p;
04d39279245834494baccfdb9349db8bf80abd13Lennart Poettering /* Check for ^] */
04d39279245834494baccfdb9349db8bf80abd13Lennart Poettering if (*p == 0x1D) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering while ((f->stdin_readable && f->in_buffer_full <= 0) ||
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering (f->master_writable && f->in_buffer_full > 0) ||
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering (f->master_readable && f->out_buffer_full <= 0) ||
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering (f->stdout_writable && f->out_buffer_full > 0)) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if (f->stdin_readable && f->in_buffer_full < LINE_MAX) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering return sd_event_exit(f->event, EXIT_FAILURE);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering } else if (k == 0) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering /* EOF on stdin */
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering /* Check if ^] has been
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering * pressed three times within
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering * one second. If we get this
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering * we quite immediately. */
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering return sd_event_exit(f->event, EXIT_FAILURE);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if (f->master_writable && f->in_buffer_full > 0) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering k = write(f->master, f->in_buffer, f->in_buffer_full);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering else if (errno == EPIPE || errno == ECONNRESET) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering f->master_writable = f->master_readable = false;
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering f->master_event_source = sd_event_source_unref(f->master_event_source);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering return sd_event_exit(f->event, EXIT_FAILURE);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if (f->master_readable && f->out_buffer_full < LINE_MAX) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full);
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering /* Note that EIO on the master device
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering * might be caused by vhangup() or
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering * temporary closing of everything on
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering * the other side, we treat it like
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering * EAGAIN here and try again, unless
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering * ignore_vhangup is off. */
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering if (errno == EAGAIN || (errno == EIO && f->ignore_vhangup))
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering f->master_readable = f->master_writable = false;
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering f->master_event_source = sd_event_source_unref(f->master_event_source);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering return sd_event_exit(f->event, EXIT_FAILURE);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if (f->stdout_writable && f->out_buffer_full > 0) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering return sd_event_exit(f->event, EXIT_FAILURE);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering assert(f->out_buffer_full >= (size_t) k);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if (f->stdin_hangup || f->stdout_hangup || f->master_hangup) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering /* Exit the loop if any side hung up and if there's
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering * nothing more to write or nothing we could write. */
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if ((f->out_buffer_full <= 0 || f->stdout_hangup) &&
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering (f->in_buffer_full <= 0 || f->master_hangup))
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering return sd_event_exit(f->event, EXIT_SUCCESS);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poetteringstatic int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poetteringstatic int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poetteringstatic int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poetteringstatic int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *si, void *userdata) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering /* The window size changed, let's forward that. */
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
679bc6cb9016715339aac4ae6b2d5371c6262935Lennart Poettering (void) ioctl(f->master, TIOCSWINSZ, &ws);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering _cleanup_(pty_forward_freep) PTYForward *f = NULL;
eaf73b061604c028aa28f960870a9b46aab2f76aLuke Shumaker if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
dc75168823540076b354135f6e2de7a9a978fbcaZbigniew Jędrzejewski-Szmek (void) ioctl(master, TIOCSWINSZ, &ws);
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag;
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr);
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) {
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag;
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag;
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr);
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f);
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering if (r < 0 && r != -EPERM)
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering /* stdout without epoll support. Likely redirected to regular file. */
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering else if (r < 0)
9c857b9d160c10b4454fc9f83442c1878343422fLennart Poettering r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart PoetteringPTYForward *pty_forward_free(PTYForward *f) {
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering sd_event_source_unref(f->stdin_event_source);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering sd_event_source_unref(f->stdout_event_source);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering sd_event_source_unref(f->master_event_source);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
023fb90b83871a15ef7f57e8cd126e3426f99b9eLennart Poettering tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
d60473c7ba32f2325a13f0357b23fd8e25609650Lennart Poettering /* STDIN/STDOUT should not be nonblocking normally, so let's
d60473c7ba32f2325a13f0357b23fd8e25609650Lennart Poettering * unconditionally reset it */
0ec5543c4c0318552a4dcdd83210793347b93081Lennart Poetteringint pty_forward_get_last_char(PTYForward *f, char *ch) {
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poetteringint pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup) {
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering /* We shall now react to vhangup()s? Let's check
da054c3782f25b3b18243f6c76dcfcf90ba70274Lennart Poettering * immediately if we might be in one */