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:
Paolo Bonzini 2012-10-03 13:37:46 +02:00
parent 0c81470947
commit 1fc05adfa0
3 changed files with 72 additions and 17 deletions

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);