int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) { int r; assert(f); if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b) return 0; if (b) f->flags |= PTY_FORWARD_IGNORE_VHANGUP; else f->flags &= ~PTY_FORWARD_IGNORE_VHANGUP; if (!ignore_vhangup(f)) { /* We shall now react to vhangup()s? Let's check * immediately if we might be in one */ f->master_readable = true; r = shovel(f); if (r < 0) return r; } return 0; }
static int shovel(PTYForward *f) { ssize_t k; assert(f); while ((f->stdin_readable && f->in_buffer_full <= 0) || (f->master_writable && f->in_buffer_full > 0) || (f->master_readable && f->out_buffer_full <= 0) || (f->stdout_writable && f->out_buffer_full > 0)) { if (f->stdin_readable && f->in_buffer_full < LINE_MAX) { k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full); if (k < 0) { if (errno == EAGAIN) f->stdin_readable = false; else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { f->stdin_readable = false; f->stdin_hangup = true; f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); } else { log_error_errno(errno, "read(): %m"); return pty_forward_done(f, -errno); } } else if (k == 0) { /* EOF on stdin */ f->stdin_readable = false; f->stdin_hangup = true; f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); } else { /* Check if ^] has been pressed three times within one second. If we get this we quite * immediately. */ if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k)) return pty_forward_done(f, -ECANCELED); f->in_buffer_full += (size_t) k; } } if (f->master_writable && f->in_buffer_full > 0) { k = write(f->master, f->in_buffer, f->in_buffer_full); if (k < 0) { if (errno == EAGAIN || errno == EIO) f->master_writable = false; else if (errno == EPIPE || errno == ECONNRESET) { f->master_writable = f->master_readable = false; f->master_hangup = true; f->master_event_source = sd_event_source_unref(f->master_event_source); } else { log_error_errno(errno, "write(): %m"); return pty_forward_done(f, -errno); } } else { assert(f->in_buffer_full >= (size_t) k); memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k); f->in_buffer_full -= k; } } if (f->master_readable && f->out_buffer_full < LINE_MAX) { k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full); if (k < 0) { /* Note that EIO on the master device * might be caused by vhangup() or * temporary closing of everything on * the other side, we treat it like * EAGAIN here and try again, unless * ignore_vhangup is off. */ if (errno == EAGAIN || (errno == EIO && ignore_vhangup(f))) f->master_readable = false; else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) { f->master_readable = f->master_writable = false; f->master_hangup = true; f->master_event_source = sd_event_source_unref(f->master_event_source); } else { log_error_errno(errno, "read(): %m"); return pty_forward_done(f, -errno); } } else { f->read_from_master = true; f->out_buffer_full += (size_t) k; } } if (f->stdout_writable && f->out_buffer_full > 0) { k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full); if (k < 0) { if (errno == EAGAIN) f->stdout_writable = false; else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { f->stdout_writable = false; f->stdout_hangup = true; f->stdout_event_source = sd_event_source_unref(f->stdout_event_source); } else { log_error_errno(errno, "write(): %m"); return pty_forward_done(f, -errno); } } else { if (k > 0) { f->last_char = f->out_buffer[k-1]; f->last_char_set = true; } assert(f->out_buffer_full >= (size_t) k); memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k); f->out_buffer_full -= k; } } } if (f->stdin_hangup || f->stdout_hangup || f->master_hangup) { /* Exit the loop if any side hung up and if there's * nothing more to write or nothing we could write. */ if ((f->out_buffer_full <= 0 || f->stdout_hangup) && (f->in_buffer_full <= 0 || f->master_hangup)) return pty_forward_done(f, 0); } return 0; }