int libvchan_write(struct libvchan *ctrl, const void *data, size_t size) { int avail; if (!libvchan_is_open(ctrl)) return -1; if (ctrl->blocking) { size_t pos = 0; while (1) { avail = libvchan_buffer_space(ctrl); if (pos + avail > size) avail = size - pos; if (avail) pos += do_send(ctrl, data + pos, avail); if (pos == size) return pos; if (libvchan_wait(ctrl)) return -1; if (!libvchan_is_open(ctrl)) return -1; } } else { avail = libvchan_buffer_space(ctrl); if (size > avail) size = avail; if (size == 0) return 0; return do_send(ctrl, data, size); } }
int main() { fd_set rdset, wrset; int max; sigset_t chld_set; init(); signal(SIGCHLD, sigchld_handler); signal(SIGPIPE, SIG_IGN); sigemptyset(&chld_set); sigaddset(&chld_set, SIGCHLD); for (;;) { sigprocmask(SIG_BLOCK, &chld_set, NULL); if (child_exited) reap_children(); max = fill_fds_for_select(&rdset, &wrset); if (libvchan_buffer_space(ctrl_vchan) <= (int)sizeof(struct msg_header)) FD_ZERO(&rdset); wait_for_vchan_or_argfd(ctrl_vchan, max, &rdset, &wrset); sigprocmask(SIG_UNBLOCK, &chld_set, NULL); while (libvchan_data_ready(ctrl_vchan)) handle_server_cmd(); if (FD_ISSET(trigger_fd, &rdset)) handle_trigger_io(); handle_terminated_fork_client(&rdset); } }
static void handle_input(libvchan_t *vchan) { char buf[MAX_DATA_CHUNK]; int ret; size_t max_len; struct msg_header hdr; max_len = libvchan_buffer_space(vchan)-sizeof(hdr); if (max_len > sizeof(buf)) max_len = sizeof(buf); if (max_len == 0) return; ret = read(local_stdout_fd, buf, max_len); if (ret < 0) { perror("read"); do_exit(1); } hdr.type = is_service ? MSG_DATA_STDOUT : MSG_DATA_STDIN; hdr.len = ret; if (libvchan_send(vchan, &hdr, sizeof(hdr)) != sizeof(hdr)) { fprintf(stderr, "Failed to write STDIN data to the agent\n"); do_exit(1); } if (ret == 0) { close(local_stdout_fd); local_stdout_fd = -1; if (local_stdin_fd == -1) { // if not a remote end of service call, wait for exit status if (is_service) { // if pipe in opposite direction already closed, no need to stay alive if (local_pid == 0) { /* if this is "remote" service end and no real local process * exists (using own stdin/out) send also fake exit code */ send_exit_code(vchan, 0); do_exit(0); } } } } if (!write_vchan_all(vchan, buf, ret)) { if (!libvchan_is_open(vchan)) { // agent disconnected its end of socket, so no future data will be // send there; there is no sense to read from child stdout // // since vchan socket is buffered it doesn't mean all data was // received from the agent close(local_stdout_fd); local_stdout_fd = -1; if (local_stdin_fd == -1) { // since child does no longer accept data on its stdin, doesn't // make sense to process the data from the daemon // // we don't know real exit VM process code (exiting here, before // MSG_DATA_EXIT_CODE message) do_exit(1); } } else perror("write agent"); } }
int main(int argc, char **argv) { fd_set read_fdset, write_fdset; int i, opt; int max; sigset_t chld_set; while ((opt=getopt(argc, argv, "q")) != -1) { switch (opt) { case 'q': opt_quiet = 1; break; default: /* '?' */ fprintf(stderr, "usage: %s [-q] domainid domain-name [default user]\n", argv[0]); exit(1); } } if (argc - optind < 2 || argc - optind > 3) { fprintf(stderr, "usage: %s [-q] domainid domain-name [default user]\n", argv[0]); exit(1); } remote_domain_id = atoi(argv[optind]); remote_domain_name = argv[optind+1]; if (argc - optind >= 3) default_user = argv[optind+2]; init(remote_domain_id); sigemptyset(&chld_set); sigaddset(&chld_set, SIGCHLD); signal(SIGCHLD, sigchld_handler); /* * The main event loop. Waits for one of the following events: * - message from client * - message from agent * - new client * - child exited */ for (;;) { max = fill_fdsets_for_select(&read_fdset, &write_fdset); if (libvchan_buffer_space(vchan) <= (int)sizeof(struct msg_header)) FD_ZERO(&read_fdset); // vchan full - don't read from clients sigprocmask(SIG_BLOCK, &chld_set, NULL); if (child_exited) reap_children(); wait_for_vchan_or_argfd(vchan, max, &read_fdset, &write_fdset); sigprocmask(SIG_UNBLOCK, &chld_set, NULL); if (FD_ISSET(qrexec_daemon_unix_socket_fd, &read_fdset)) handle_new_client(); while (libvchan_data_ready(vchan)) handle_message_from_agent(); for (i = 0; i <= max_client_fd; i++) if (clients[i].state != CLIENT_INVALID && FD_ISSET(i, &read_fdset)) handle_message_from_client(i); } }
int write_data(libvchan_t *vchan, char *buf, int size) { int count; if (!double_buffered) return write_data_exact(vchan, buf, size); // this may block double_buffer_append(buf, size); count = libvchan_buffer_space(vchan); if (count > double_buffer_datacount()) count = double_buffer_datacount(); // below, we write only as much data as possible without // blocking; remainder of data stays in the double buffer write_data_exact(vchan, double_buffer_data(), count); double_buffer_substract(count); return size; }
static int process_sink_render(struct userdata *u) { pa_assert(u); if (u->memchunk_sink.length <= 0) pa_sink_render(u->sink, libvchan_buffer_space(u->play_ctrl), &u->memchunk_sink); pa_assert(u->memchunk_sink.length > 0); for (;;) { ssize_t l; void *p; p = pa_memblock_acquire(u->memchunk_sink.memblock); l = write_to_vchan(u->play_ctrl, (char *) p + u->memchunk_sink.index, u->memchunk_sink.length); pa_memblock_release(u->memchunk_sink.memblock); pa_assert(l != 0); if (l < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) return 0; else { pa_log ("Failed to write data to VCHAN: %s", pa_cstrerror(errno)); return -1; } } else { u->memchunk_sink.index += (size_t) l; u->memchunk_sink.length -= (size_t) l; if (u->memchunk_sink.length <= 0) { pa_memblock_unref(u->memchunk_sink.memblock); pa_memchunk_reset(&u->memchunk_sink); } } return 0; } }
/** * returns 0 if no buffer space is available, -1 on error, or size on success */ int libvchan_send(struct libvchan *ctrl, const void *data, size_t size) { int avail; while (1) { if (!libvchan_is_open(ctrl)) return -1; avail = libvchan_buffer_space(ctrl); if (size <= avail) return do_send(ctrl, data, size); if (!ctrl->blocking) return 0; if (size > wr_ring_size(ctrl)) return -1; if (libvchan_wait(ctrl)) return -1; } }
/** may sleep (only if no buffer space available); may write less data than requested; returns the amount of data processed, -1 on error or peer close */ int libvchan_write(struct libvchan *ctrl, const char *data, int size) { int avail, avail_contig; int real_idx; while ((avail = libvchan_buffer_space(ctrl)) == 0) if (libvchan_wait(ctrl) < 0) return -1; if (avail > size) avail = size; real_idx = (*ctrl->wr_prod) & (ctrl->wr_ring_size - 1); avail_contig = ctrl->wr_ring_size - real_idx; if (avail_contig < avail) avail = avail_contig; memcpy(ctrl->wr_ring + real_idx, data, avail); *ctrl->wr_prod += avail; if (do_notify(ctrl) < 0) return -1; return avail; }
static int write_to_vchan(libvchan_t *ctrl, char *buf, int size) { static int all = 0, waited = 0, nonwaited = 0, full = 0; ssize_t l; fd_set rfds; struct timeval tv = { 0, 0 }; int ret, fd = libvchan_fd_for_select(ctrl); FD_ZERO(&rfds); FD_SET(fd, &rfds); all++; ret = select(fd + 1, &rfds, NULL, NULL, &tv); if (ret == -1) { pa_log("Failed to select() in vchan: %s", pa_cstrerror(errno)); return -1; } if (ret) { if (libvchan_wait(ctrl) < 0) { pa_log("Failed libvchan_wait"); return -1; } waited++; } else nonwaited++; if (libvchan_buffer_space(ctrl)) { l = libvchan_write(ctrl, buf, size); } else { l = -1; errno = EAGAIN; full++; } if ((all % 8000) == 0) { pa_log ("write_to_vchan: all=%d waited=%d nonwaited=%d full=%d\n", all, waited, nonwaited, full); } return l; }
static void thread_func(void *userdata) { struct userdata *u = userdata; char buf[2048]; // max ring buffer size pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); for (;;) { struct pollfd *play_pollfd; struct pollfd *rec_pollfd; int ret; play_pollfd = pa_rtpoll_item_get_pollfd(u->play_rtpoll_item, NULL); rec_pollfd = pa_rtpoll_item_get_pollfd(u->rec_rtpoll_item, NULL); if (play_pollfd->revents & POLLIN) { if (libvchan_wait(u->play_ctrl) < 0) goto fail; play_pollfd->revents = 0; } if (rec_pollfd->revents & POLLIN) { if (libvchan_wait(u->rec_ctrl) < 0) goto fail; rec_pollfd->revents = 0; } /* Render some data and write it to the fifo */ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { if (u->sink->thread_info.rewind_requested) pa_sink_process_rewind(u->sink, 0); if (libvchan_buffer_space(u->play_ctrl)) { if (process_sink_render(u) < 0) goto fail; } } if (u->source->thread_info.state == PA_SOURCE_RUNNING) { while (libvchan_data_ready(u->rec_ctrl)) { if (process_source_data(u) < 0) goto fail; } } else { /* discard the data */ if (libvchan_data_ready(u->rec_ctrl)) if (libvchan_read(u->rec_ctrl, buf, sizeof(buf)) < 0) goto fail; } /* Hmm, nothing to do. Let's sleep */ play_pollfd->events = POLLIN; rec_pollfd->events = POLLIN; #if PA_CHECK_VERSION(6,0,0) if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) #else if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0) #endif goto fail; if (ret == 0) goto finish; } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
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); } }