Пример #1
0
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;
}
Пример #3
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);
}
Пример #6
0
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);
    }
}