diff mbox series

[2/4] qemu-sockets: implement non-blocking connect interface

Message ID 20200720180715.10521-3-vsementsov@virtuozzo.com
State New
Headers show
Series non-blocking connect | expand

Commit Message

Vladimir Sementsov-Ogievskiy July 20, 2020, 6:07 p.m. UTC
We are going to implement non-blocking connect in io/channel-socket.

non-blocking connect includes three phases:

    1. connect() call
    2. wait until socket is ready
    3. check result

io/channel-socket has wait-on-socket API (qio_channel_yield(),
qio_channel_wait()), so it's a good place for [2].

Still, the whole thing is not simple, because socket connect in case of
inet socket includes several connect() calls, as SocketAddress may be
parsed into a list of inet addresses. And after each non-blocking
connect() upper layer should have a possibility to wait on the socket.

We may try to implement a kind of abstract list or iterator for
"sub" addresses of SocketAddress, but all this appears to be too
complex and not worth doing (as actually, only inet sockets has such a
"multiple" SocketAddress).

So, let's instead make public API for inet sockets themselves, to be
handled in separate in upper layer, if it needs non-blocking connect.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 include/qemu/sockets.h |  6 ++++++
 util/qemu-sockets.c    | 45 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 46 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 7d1f813576..7389d6be55 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -34,6 +34,12 @@  int inet_ai_family_from_address(InetSocketAddress *addr,
 int inet_parse(InetSocketAddress *addr, const char *str, Error **errp);
 int inet_connect(const char *str, Error **errp);
 int inet_connect_saddr(InetSocketAddress *saddr, Error **errp);
+int inet_connect_addr(InetSocketAddress *saddr, struct addrinfo *addr,
+                      bool blocking, bool *in_progress, Error **errp);
+struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr,
+                                          Error **errp);
+
+int socket_check(int fd, Error **errp);
 
 NetworkAddressFamily inet_netfamily(int family);
 
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 8ccf4088c2..a02d00f342 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -354,11 +354,17 @@  listen_ok:
     ((rc) == -EINPROGRESS)
 #endif
 
-static int inet_connect_addr(InetSocketAddress *saddr,
-                             struct addrinfo *addr, Error **errp)
+int inet_connect_addr(InetSocketAddress *saddr, struct addrinfo *addr,
+                      bool blocking, bool *in_progress, Error **errp)
 {
     int sock, rc;
 
+    assert(blocking == !in_progress);
+
+    if (in_progress) {
+        *in_progress = false;
+    }
+
     sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
     if (sock < 0) {
         error_setg_errno(errp, errno, "Failed to create socket");
@@ -366,6 +372,10 @@  static int inet_connect_addr(InetSocketAddress *saddr,
     }
     socket_set_fast_reuse(sock);
 
+    if (!blocking) {
+        qemu_set_nonblock(sock);
+    }
+
     /* connect to peer */
     do {
         rc = 0;
@@ -374,6 +384,13 @@  static int inet_connect_addr(InetSocketAddress *saddr,
         }
     } while (rc == -EINTR);
 
+    if (!blocking && rc == -EINPROGRESS) {
+        if (in_progress) {
+            *in_progress = true;
+        }
+        return sock;
+    }
+
     if (rc < 0) {
         error_setg_errno(errp, errno, "Failed to connect socket");
         closesocket(sock);
@@ -395,8 +412,26 @@  static int inet_connect_addr(InetSocketAddress *saddr,
     return sock;
 }
 
-static struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr,
-                                                 Error **errp)
+int socket_check(int fd, Error **errp)
+{
+    int optval;
+    socklen_t optlen = sizeof(optval);
+    if (qemu_getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
+        error_setg_errno(errp, errno, "Unable to check connection");
+        return -1;
+    }
+
+    if (optval != 0) {
+        error_setg_errno(errp, errno, "Connection failed");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr,
+                                          Error **errp)
 {
     struct addrinfo ai, *res;
     int rc;
@@ -466,7 +501,7 @@  int inet_connect_saddr(InetSocketAddress *saddr, Error **errp)
     for (e = res; e != NULL; e = e->ai_next) {
         error_free(local_err);
         local_err = NULL;
-        sock = inet_connect_addr(saddr, e, &local_err);
+        sock = inet_connect_addr(saddr, e, true, NULL, &local_err);
         if (sock >= 0) {
             break;
         }