Exemplo n.º 1
0
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);
    }
}
Exemplo n.º 2
0
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");
    }
}
Exemplo n.º 3
0
void handle_vchan_error(libvchan_t *vchan, const char *op)
{
    if (!libvchan_is_open(vchan)) {
        fprintf(stderr, "EOF\n");
        exit(0);
    } else {
        fprintf(stderr, "Error while vchan %s\n, terminating", op);
        exit(1);
    }
}
Exemplo n.º 4
0
int libvchan_read(struct libvchan *ctrl, void *data, size_t size)
{
    while (1) {
        int avail = libvchan_data_ready(ctrl);
        if (avail && size > avail)
            size = avail;
        if (avail)
            return do_recv(ctrl, data, size);
        if (!libvchan_is_open(ctrl))
            return -1;
        if (!ctrl->blocking)
            return 0;
        if (libvchan_wait(ctrl))
            return -1;
    }
}
Exemplo n.º 5
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;
    }
}
Exemplo n.º 6
0
void init()
{
    mode_t old_umask;
    /* FIXME: This 0 is remote domain ID */
    ctrl_vchan = libvchan_server_init(0, VCHAN_BASE_PORT, 4096, 4096);
    if (!ctrl_vchan)
        handle_vchan_error("server_init");
    if (handle_handshake(ctrl_vchan) < 0)
        exit(1);
    old_umask = umask(0);
    trigger_fd = get_server_socket(QREXEC_AGENT_TRIGGER_PATH);
    umask(old_umask);
    register_exec_func(do_exec);

    /* wait for qrexec daemon */
    while (!libvchan_is_open(ctrl_vchan))
        libvchan_wait(ctrl_vchan);
}
Exemplo n.º 7
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;
}
Exemplo n.º 8
0
int main(int argc, char **argv)
{
    int opt;
    char *domname = NULL;
    libvchan_t *data_vchan = NULL;
    int data_port;
    int data_domain;
    int msg_type;
    int s;
    int just_exec = 0;
    int connect_existing = 0;
    char *local_cmdline = NULL;
    char *remote_cmdline = NULL;
    char *request_id;
    char *src_domain_name = NULL;
    int src_domain_id = 0; /* if not -c given, the process is run in dom0 */
    struct service_params svc_params;
    while ((opt = getopt(argc, argv, "d:l:ec:tT")) != -1) {
        switch (opt) {
            case 'd':
                domname = strdup(optarg);
                break;
            case 'l':
                local_cmdline = strdup(optarg);
                break;
            case 'e':
                just_exec = 1;
                break;
            case 'c':
                parse_connect(optarg, &request_id, &src_domain_name, &src_domain_id);
                connect_existing = 1;
                is_service = 1;
                break;
            case 't':
                replace_esc_stdout = 1;
                break;
            case 'T':
                replace_esc_stderr = 1;
                break;
            default:
                usage(argv[0]);
        }
    }
    if (optind >= argc || !domname)
        usage(argv[0]);
    remote_cmdline = argv[optind];

    register_exec_func(&do_exec);

    if (just_exec + connect_existing + (local_cmdline != 0) > 1) {
        fprintf(stderr, "ERROR: only one of -e, -l, -c can be specified\n");
        usage(argv[0]);
    }

    if (strcmp(domname, "dom0") == 0 && !connect_existing) {
        fprintf(stderr, "ERROR: when target domain is 'dom0', -c must be specified\n");
        usage(argv[0]);
    }

    if (strcmp(domname, "dom0") == 0) {
        if (connect_existing) {
            msg_type = MSG_SERVICE_CONNECT;
            strncpy(svc_params.ident, request_id, sizeof(svc_params.ident));
        } else if (just_exec)
            msg_type = MSG_JUST_EXEC;
        else
            msg_type = MSG_EXEC_CMDLINE;
        assert(src_domain_name);
        setenv("QREXEC_REMOTE_DOMAIN", src_domain_name, 1);
        s = connect_unix_socket(src_domain_name);
        negotiate_connection_params(s,
                0, /* dom0 */
                msg_type,
                connect_existing ? (void*)&svc_params : (void*)remote_cmdline,
                connect_existing ? sizeof(svc_params) : strlen(remote_cmdline) + 1,
                &data_domain,
                &data_port);

        prepare_local_fds(remote_cmdline);
        if (connect_existing)
            data_vchan = libvchan_client_init(data_domain, data_port);
        else {
            data_vchan = libvchan_server_init(data_domain, data_port,
                    VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE);
            while (data_vchan && libvchan_is_open(data_vchan) == VCHAN_WAITING)
                libvchan_wait(data_vchan);
        }
        if (!data_vchan || !libvchan_is_open(data_vchan)) {
            fprintf(stderr, "Failed to open data vchan connection\n");
            do_exit(1);
        }
        if (handle_agent_handshake(data_vchan, connect_existing) < 0)
            do_exit(1);
        select_loop(data_vchan);
    } else {
        if (just_exec)
            msg_type = MSG_JUST_EXEC;
        else
            msg_type = MSG_EXEC_CMDLINE;
        s = connect_unix_socket(domname);
        negotiate_connection_params(s,
                src_domain_id,
                msg_type,
                remote_cmdline,
                strlen(remote_cmdline) + 1,
                &data_domain,
                &data_port);
        close(s);
        setenv("QREXEC_REMOTE_DOMAIN", domname, 1);
        prepare_local_fds(local_cmdline);
        if (connect_existing) {
            s = connect_unix_socket(src_domain_name);
            send_service_connect(s, request_id, data_domain, data_port);
            close(s);
        } else {
            data_vchan = libvchan_server_init(data_domain, data_port,
                    VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE);
            if (!data_vchan) {
                fprintf(stderr, "Failed to start data vchan server\n");
                do_exit(1);
            }
            while (libvchan_is_open(data_vchan) == VCHAN_WAITING)
                libvchan_wait(data_vchan);
            if (!libvchan_is_open(data_vchan)) {
                fprintf(stderr, "Failed to open data vchan connection\n");
                do_exit(1);
            }
            if (handle_agent_handshake(data_vchan, 0) < 0)
                do_exit(1);
            select_loop(data_vchan);
        }
    }
    return 0;
}
Exemplo n.º 9
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);
    }
}