qemu-sockets: add nonblocking connect for Unix sockets
This patch mostly mimics what was done to TCP sockets, but simpler because there is only one address to try. It also includes a free EINTR bug fix. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
		
							parent
							
								
									0c81470947
								
							
						
					
					
						commit
						1fc05adfa0
					
				| 
						 | 
				
			
			@ -2450,7 +2450,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
 | 
			
		|||
        if (is_listen) {
 | 
			
		||||
            fd = unix_listen_opts(opts, NULL);
 | 
			
		||||
        } else {
 | 
			
		||||
            fd = unix_connect_opts(opts, NULL);
 | 
			
		||||
            fd = unix_connect_opts(opts, NULL, NULL, NULL);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (is_listen) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -252,16 +252,19 @@ static void wait_for_connect(void *opaque)
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    /* try to connect to the next address on the list */
 | 
			
		||||
    while (s->current_addr->ai_next != NULL && s->fd < 0) {
 | 
			
		||||
        s->current_addr = s->current_addr->ai_next;
 | 
			
		||||
        s->fd = inet_connect_addr(s->current_addr, &in_progress, s);
 | 
			
		||||
        /* connect in progress */
 | 
			
		||||
        if (in_progress) {
 | 
			
		||||
            return;
 | 
			
		||||
    if (s->current_addr) {
 | 
			
		||||
        while (s->current_addr->ai_next != NULL && s->fd < 0) {
 | 
			
		||||
            s->current_addr = s->current_addr->ai_next;
 | 
			
		||||
            s->fd = inet_connect_addr(s->current_addr, &in_progress, s);
 | 
			
		||||
            /* connect in progress */
 | 
			
		||||
            if (in_progress) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        freeaddrinfo(s->addr_list);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    freeaddrinfo(s->addr_list);
 | 
			
		||||
    if (s->callback) {
 | 
			
		||||
        s->callback(s->fd, s->opaque);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -701,11 +704,13 @@ err:
 | 
			
		|||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int unix_connect_opts(QemuOpts *opts, Error **errp)
 | 
			
		||||
int unix_connect_opts(QemuOpts *opts, Error **errp,
 | 
			
		||||
                      NonBlockingConnectHandler *callback, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    struct sockaddr_un un;
 | 
			
		||||
    const char *path = qemu_opt_get(opts, "path");
 | 
			
		||||
    int sock;
 | 
			
		||||
    ConnectState *connect_state = NULL;
 | 
			
		||||
    int sock, rc;
 | 
			
		||||
 | 
			
		||||
    if (NULL == path) {
 | 
			
		||||
        fprintf(stderr, "unix connect: no path specified\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -717,16 +722,44 @@ int unix_connect_opts(QemuOpts *opts, Error **errp)
 | 
			
		|||
        perror("socket(unix)");
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (callback != NULL) {
 | 
			
		||||
        connect_state = g_malloc0(sizeof(*connect_state));
 | 
			
		||||
        connect_state->callback = callback;
 | 
			
		||||
        connect_state->opaque = opaque;
 | 
			
		||||
        socket_set_nonblock(sock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(&un, 0, sizeof(un));
 | 
			
		||||
    un.sun_family = AF_UNIX;
 | 
			
		||||
    snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
 | 
			
		||||
    if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
 | 
			
		||||
        fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
 | 
			
		||||
        close(sock);
 | 
			
		||||
	return -1;
 | 
			
		||||
 | 
			
		||||
    /* connect to peer */
 | 
			
		||||
    do {
 | 
			
		||||
        rc = 0;
 | 
			
		||||
        if (connect(sock, (struct sockaddr *) &un, sizeof(un)) < 0) {
 | 
			
		||||
            rc = -socket_error();
 | 
			
		||||
        }
 | 
			
		||||
    } while (rc == -EINTR);
 | 
			
		||||
 | 
			
		||||
    if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) {
 | 
			
		||||
        connect_state->fd = sock;
 | 
			
		||||
        qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect,
 | 
			
		||||
                             connect_state);
 | 
			
		||||
        return sock;
 | 
			
		||||
    } else if (rc >= 0) {
 | 
			
		||||
        /* non blocking socket immediate success, call callback */
 | 
			
		||||
        if (callback != NULL) {
 | 
			
		||||
            callback(sock, opaque);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (rc < 0) {
 | 
			
		||||
        fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
 | 
			
		||||
        close(sock);
 | 
			
		||||
        sock = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_free(connect_state);
 | 
			
		||||
    return sock;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -739,7 +772,8 @@ int unix_listen_opts(QemuOpts *opts, Error **errp)
 | 
			
		|||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int unix_connect_opts(QemuOpts *opts, Error **errp)
 | 
			
		||||
int unix_connect_opts(QemuOpts *opts, Error **errp,
 | 
			
		||||
                      NonBlockingConnectHandler *callback, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    fprintf(stderr, "unix sockets are not available on windows\n");
 | 
			
		||||
    errno = ENOTSUP;
 | 
			
		||||
| 
						 | 
				
			
			@ -784,7 +818,24 @@ int unix_connect(const char *path, Error **errp)
 | 
			
		|||
 | 
			
		||||
    opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
 | 
			
		||||
    qemu_opt_set(opts, "path", path);
 | 
			
		||||
    sock = unix_connect_opts(opts, errp);
 | 
			
		||||
    sock = unix_connect_opts(opts, errp, NULL, NULL);
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
    return sock;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int unix_nonblocking_connect(const char *path,
 | 
			
		||||
                             NonBlockingConnectHandler *callback,
 | 
			
		||||
                             void *opaque, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
    int sock = -1;
 | 
			
		||||
 | 
			
		||||
    g_assert(callback != NULL);
 | 
			
		||||
 | 
			
		||||
    opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
 | 
			
		||||
    qemu_opt_set(opts, "path", path);
 | 
			
		||||
    sock = unix_connect_opts(opts, errp, callback, opaque);
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
    return sock;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,8 +58,12 @@ const char *inet_strfamily(int family);
 | 
			
		|||
 | 
			
		||||
int unix_listen_opts(QemuOpts *opts, Error **errp);
 | 
			
		||||
int unix_listen(const char *path, char *ostr, int olen, Error **errp);
 | 
			
		||||
int unix_connect_opts(QemuOpts *opts, Error **errp);
 | 
			
		||||
int unix_connect_opts(QemuOpts *opts, Error **errp,
 | 
			
		||||
                      NonBlockingConnectHandler *callback, void *opaque);
 | 
			
		||||
int unix_connect(const char *path, Error **errp);
 | 
			
		||||
int unix_nonblocking_connect(const char *str,
 | 
			
		||||
                             NonBlockingConnectHandler *callback,
 | 
			
		||||
                             void *opaque, Error **errp);
 | 
			
		||||
 | 
			
		||||
/* Old, ipv4 only bits.  Don't use for new code. */
 | 
			
		||||
int parse_host_port(struct sockaddr_in *saddr, const char *str);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue