int libvchan_wait(struct libvchan *ctrl) { int ret; #ifndef CONFIG_STUBDOM ret = xc_evtchn_pending(ctrl->evfd); #else int vchan_fd = libvchan_fd_for_select(ctrl); fd_set rfds; libvchan_prepare_to_select(ctrl); while ((ret = xc_evtchn_pending(ctrl->evfd)) < 0) { FD_ZERO(&rfds); FD_SET(0, &rfds); FD_SET(vchan_fd, &rfds); ret = select(vchan_fd + 1, &rfds, NULL, NULL, NULL); if (ret < 0 && errno != EINTR) { perror("select"); return ret; } } #endif if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport)) return -1; if (ret!=-1 && libvchan_is_eof(ctrl)) return -1; return ret; }
static int do_conn(struct userdata *u) { int fd; /* FIXME: 0 is remote domain ID */ u->play_ctrl = libvchan_server_init(0, QUBES_PA_SINK_VCHAN_PORT, 128, 2048); if (!u->play_ctrl) { pa_log("libvchan_server_init play failed\n"); return -1; } /* FIXME: 0 is remote domain ID */ u->rec_ctrl = libvchan_server_init(0, QUBES_PA_SOURCE_VCHAN_PORT, 2048, 128); if (!u->rec_ctrl) { pa_log("libvchan_server_init rec failed\n"); return -1; } fd = libvchan_fd_for_select(u->play_ctrl); pa_log("play libvchan_fd_for_select=%d, ctrl=%p\n", fd, u->play_ctrl); fd = libvchan_fd_for_select(u->rec_ctrl); pa_log("rec libvchan_fd_for_select=%d, ctrl=%p\n", fd, u->rec_ctrl); return 0; }
int wait_for_vchan_or_argfd_once(libvchan_t *vchan, int nfd, int *fd, fd_set * retset) { fd_set rfds; int vfd, max = 0, ret, i; struct timeval tv = { 0, 1000000 }; write_data(vchan, NULL, 0); // trigger write of queued data, if any present vfd = libvchan_fd_for_select(vchan); FD_ZERO(&rfds); for (i = 0; i < nfd; i++) { int cfd = fd[i]; FD_SET(cfd, &rfds); if (cfd > max) max = cfd; } FD_SET(vfd, &rfds); if (vfd > max) max = vfd; max++; ret = select(max, &rfds, NULL, NULL, &tv); if (ret < 0 && errno == EINTR) return -1; if (ret < 0) { perror("select"); exit(1); } if (!libvchan_is_open(vchan)) { fprintf(stderr, "libvchan_is_eof\n"); libvchan_close(vchan); if (vchan_at_eof != NULL) { vchan_at_eof(); return -1; } else exit(0); } if (FD_ISSET(vfd, &rfds)) // the following will never block; we need to do this to // clear libvchan_fd pending state libvchan_wait(vchan); if (retset) *retset = rfds; return ret; }
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; }
int pa__init(pa_module * m) { struct userdata *u; pa_sample_spec ss; pa_channel_map map; pa_modargs *ma; struct pollfd *pollfd; pa_sink_new_data data_sink; pa_source_new_data data_source; pa_assert(m); pa_log("vchan module loading"); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; } ss = m->core->default_sample_spec; map = m->core->default_channel_map; if (pa_modargs_get_sample_spec_and_channel_map (ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { pa_log ("Invalid sample format specification or channel map"); goto fail; } u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; m->userdata = u; pa_memchunk_reset(&u->memchunk_sink); pa_memchunk_reset(&u->memchunk_source); u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); if ((do_conn(u)) < 0) { pa_log("get_early_allocated_vchan: %s", pa_cstrerror(errno)); goto fail; } /* SINK preparation */ pa_sink_new_data_init(&data_sink); data_sink.driver = __FILE__; data_sink.module = m; pa_sink_new_data_set_name(&data_sink, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_proplist_sets(data_sink.proplist, PA_PROP_DEVICE_STRING, DEFAULT_SINK_NAME); pa_proplist_setf(data_sink.proplist, PA_PROP_DEVICE_DESCRIPTION, "Qubes VCHAN sink"); pa_sink_new_data_set_sample_spec(&data_sink, &ss); pa_sink_new_data_set_channel_map(&data_sink, &map); if (pa_modargs_get_proplist (ma, "sink_properties", data_sink.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_sink_new_data_done(&data_sink); goto fail; } u->sink = pa_sink_new(m->core, &data_sink, PA_SINK_LATENCY); pa_sink_new_data_done(&data_sink); if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_max_request(u->sink, VCHAN_BUF); pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec (VCHAN_BUF, &u->sink->sample_spec)); u->play_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->play_rtpoll_item, NULL); pollfd->fd = libvchan_fd_for_select(u->play_ctrl); pollfd->events = POLLIN; pollfd->revents = 0; /* SOURCE preparation */ pa_source_new_data_init(&data_source); data_source.driver = __FILE__; data_source.module = m; pa_source_new_data_set_name(&data_source, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); pa_proplist_sets(data_source.proplist, PA_PROP_DEVICE_STRING, DEFAULT_SOURCE_NAME); pa_proplist_setf(data_source.proplist, PA_PROP_DEVICE_DESCRIPTION, "Qubes VCHAN source"); pa_source_new_data_set_sample_spec(&data_source, &ss); pa_source_new_data_set_channel_map(&data_source, &map); if (pa_modargs_get_proplist(ma, "source_properties", data_source.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_source_new_data_done(&data_source); goto fail; } u->source = pa_source_new(m->core, &data_source, PA_SOURCE_LATENCY); pa_source_new_data_done(&data_source); if (!u->source) { pa_log("Failed to create source."); goto fail; } u->source->parent.process_msg = source_process_msg; u->source->userdata = u; pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(PIPE_BUF, &u->source->sample_spec)); u->rec_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->rec_rtpoll_item, NULL); pollfd->fd = libvchan_fd_for_select(u->rec_ctrl); pollfd->events = POLLIN; pollfd->revents = 0; #if PA_CHECK_VERSION(0,9,22) if (!(u->thread = pa_thread_new("vchan-sink", thread_func, u))) { #else if (!(u->thread = pa_thread_new(thread_func, u))) { #endif pa_log("Failed to create thread."); goto fail; } pa_sink_put(u->sink); pa_source_put(u->source); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); pa__done(m); return -1; } int pa__get_n_used(pa_module * m) { struct userdata *u; pa_assert(m); pa_assert_se(u = m->userdata); return pa_sink_linked_by(u->sink); } void pa__done(pa_module * m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->sink) pa_sink_unlink(u->sink); if (u->source) pa_source_unlink(u->source); if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->sink) pa_sink_unref(u->sink); if (u->source) pa_source_unref(u->source); if (u->memchunk_sink.memblock) pa_memblock_unref(u->memchunk_sink.memblock); if (u->memchunk_source.memblock) pa_memblock_unref(u->memchunk_source.memblock); if (u->play_rtpoll_item) pa_rtpoll_item_free(u->play_rtpoll_item); if (u->rec_rtpoll_item) pa_rtpoll_item_free(u->rec_rtpoll_item); if (u->rtpoll) pa_rtpoll_free(u->rtpoll); if (u->play_ctrl) libvchan_close(u->play_ctrl); if (u->rec_ctrl) libvchan_close(u->rec_ctrl); pa_xfree(u); }
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); } }