/* Called when there is buffered data for this client, and select() reports that client's pipe is writable; so we should be able to flush some buffered data. */ void write_buffered_data_to_client(int client_id) { switch (flush_client_data (client_id, client_id, &clients[client_id].buffer)) { case WRITE_STDIN_OK: // no more buffered data clients[client_id].state &= ~CLIENT_OUTQ_FULL; break; case WRITE_STDIN_ERROR: terminate_client_and_flush_data(client_id); break; case WRITE_STDIN_BUFFERED: // no room for all data, don't clear CLIENT_OUTQ_FULL flag break; default: fprintf(stderr, "unknown flush_client_data?\n"); exit(1); } }
static void select_loop(libvchan_t *vchan) { fd_set select_set; fd_set wr_set; int max_fd; int ret; int vchan_fd; sigset_t selectmask; struct timespec zero_timeout = { 0, 0 }; struct timespec select_timeout = { 10, 0 }; struct buffer stdin_buf; sigemptyset(&selectmask); sigaddset(&selectmask, SIGCHLD); sigprocmask(SIG_BLOCK, &selectmask, NULL); sigemptyset(&selectmask); buffer_init(&stdin_buf); /* remember to set back to blocking mode before closing the FD - this may * be not the only copy and some processes may misbehave when get * nonblocking FD for input/output */ set_nonblock(local_stdin_fd); for (;;) { vchan_fd = libvchan_fd_for_select(vchan); FD_ZERO(&select_set); FD_ZERO(&wr_set); FD_SET(vchan_fd, &select_set); max_fd = vchan_fd; if (local_stdout_fd != -1 && (size_t)libvchan_buffer_space(vchan) > sizeof(struct msg_header)) { FD_SET(local_stdout_fd, &select_set); if (local_stdout_fd > max_fd) max_fd = local_stdout_fd; } if (child_exited && local_stdout_fd == -1) check_child_status(vchan); if (local_stdin_fd != -1 && buffer_len(&stdin_buf)) { FD_SET(local_stdin_fd, &wr_set); if (local_stdin_fd > max_fd) max_fd = local_stdin_fd; } if ((local_stdin_fd == -1 || buffer_len(&stdin_buf) == 0) && libvchan_data_ready(vchan) > 0) { /* check for other FDs, but exit immediately */ ret = pselect(max_fd + 1, &select_set, &wr_set, NULL, &zero_timeout, &selectmask); } else ret = pselect(max_fd + 1, &select_set, &wr_set, NULL, &select_timeout, &selectmask); if (ret < 0) { if (errno == EINTR && local_pid > 0) { continue; } else { perror("select"); do_exit(1); } } if (ret == 0) { if (!libvchan_is_open(vchan)) { /* remote disconnected witout a proper signaling */ do_exit(1); } } if (FD_ISSET(vchan_fd, &select_set)) libvchan_wait(vchan); if (buffer_len(&stdin_buf) && local_stdin_fd != -1 && FD_ISSET(local_stdin_fd, &wr_set)) { if (flush_client_data(local_stdin_fd, &stdin_buf) == WRITE_STDIN_ERROR) { perror("write stdin"); close(local_stdin_fd); local_stdin_fd = -1; } } while (libvchan_data_ready(vchan)) if (handle_vchan_data(vchan, &stdin_buf) != WRITE_STDIN_OK) break; if (local_stdout_fd != -1 && FD_ISSET(local_stdout_fd, &select_set)) handle_input(vchan); } }
static int handle_vchan_data(libvchan_t *vchan, struct buffer *stdin_buf) { int status; struct msg_header hdr; char buf[MAX_DATA_CHUNK]; if (local_stdin_fd != -1) { switch(flush_client_data(local_stdin_fd, stdin_buf)) { case WRITE_STDIN_ERROR: perror("write stdin"); close(local_stdin_fd); local_stdin_fd = -1; break; case WRITE_STDIN_BUFFERED: return WRITE_STDIN_BUFFERED; case WRITE_STDIN_OK: break; } } if (libvchan_recv(vchan, &hdr, sizeof hdr) < 0) { perror("read vchan"); do_exit(1); } if (hdr.len > MAX_DATA_CHUNK) { fprintf(stderr, "client_header.len=%d\n", hdr.len); do_exit(1); } if (!read_vchan_all(vchan, buf, hdr.len)) { perror("read daemon"); do_exit(1); } switch (hdr.type) { /* both directions because we can serve as either end of service call */ case MSG_DATA_STDIN: case MSG_DATA_STDOUT: if (local_stdin_fd == -1) break; if (replace_esc_stdout) do_replace_esc(buf, hdr.len); if (hdr.len == 0) { /* restore flags, as we may have not the only copy of this file descriptor */ if (local_stdin_fd != -1) set_block(local_stdin_fd); close(local_stdin_fd); local_stdin_fd = -1; } else { switch (write_stdin(local_stdin_fd, buf, hdr.len, stdin_buf)) { case WRITE_STDIN_BUFFERED: return WRITE_STDIN_BUFFERED; case WRITE_STDIN_ERROR: if (errno == EPIPE) { // local process have closed its stdin, handle data in oposite // direction (if any) before exit close(local_stdin_fd); local_stdin_fd = -1; } else { perror("write local stdout"); do_exit(1); } break; case WRITE_STDIN_OK: break; } } break; case MSG_DATA_STDERR: if (replace_esc_stderr) do_replace_esc(buf, hdr.len); write_all(2, buf, hdr.len); break; case MSG_DATA_EXIT_CODE: libvchan_close(vchan); if (hdr.len < sizeof(status)) status = 255; else memcpy(&status, buf, sizeof(status)); flush_client_data(local_stdin_fd, stdin_buf); do_exit(status); break; default: fprintf(stderr, "unknown msg %d\n", hdr.type); do_exit(1); } /* intentionally do not distinguish between _ERROR and _OK, because in case * of write error, we simply eat the data - no way to report it to the * other side */ return WRITE_STDIN_OK; }