]> git.ipfire.org Git - thirdparty/qemu.git/blobdiff - tests/test-io-channel-socket.c
Merge remote-tracking branch 'remotes/mcayland/tags/qemu-openbios-20191028' into...
[thirdparty/qemu.git] / tests / test-io-channel-socket.c
index c5c131479c26d852a64411f68905b4a3badd2dac..d43083a766713df3910da3fc1d24d23b6cd99535 100644 (file)
 #include "io/channel-socket.h"
 #include "io/channel-util.h"
 #include "io-channel-helpers.h"
+#include "socket-helpers.h"
 #include "qapi/error.h"
-
-#ifndef AI_ADDRCONFIG
-# define AI_ADDRCONFIG 0
-#endif
-#ifndef EAI_ADDRFAMILY
-# define EAI_ADDRFAMILY 0
-#endif
-
-static int check_bind(const char *hostname, bool *has_proto)
-{
-    int fd = -1;
-    struct addrinfo ai, *res = NULL;
-    int rc;
-    int ret = -1;
-
-    memset(&ai, 0, sizeof(ai));
-    ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
-    ai.ai_family = AF_UNSPEC;
-    ai.ai_socktype = SOCK_STREAM;
-
-    /* lookup */
-    rc = getaddrinfo(hostname, NULL, &ai, &res);
-    if (rc != 0) {
-        if (rc == EAI_ADDRFAMILY ||
-            rc == EAI_FAMILY) {
-            *has_proto = false;
-            goto done;
-        }
-        goto cleanup;
-    }
-
-    fd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-    if (fd < 0) {
-        goto cleanup;
-    }
-
-    if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
-        if (errno == EADDRNOTAVAIL) {
-            *has_proto = false;
-            goto done;
-        }
-        goto cleanup;
-    }
-
-    *has_proto = true;
- done:
-    ret = 0;
-
- cleanup:
-    if (fd != -1) {
-        close(fd);
-    }
-    if (res) {
-        freeaddrinfo(res);
-    }
-    return ret;
-}
-
-static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
-{
-    if (check_bind("127.0.0.1", has_ipv4) < 0) {
-        return -1;
-    }
-    if (check_bind("::1", has_ipv6) < 0) {
-        return -1;
-    }
-
-    return 0;
-}
+#include "qemu/module.h"
 
 
 static void test_io_channel_set_socket_bufs(QIOChannel *src,
@@ -117,20 +50,21 @@ static void test_io_channel_set_socket_bufs(QIOChannel *src,
 
 static void test_io_channel_setup_sync(SocketAddress *listen_addr,
                                        SocketAddress *connect_addr,
+                                       QIOChannel **srv,
                                        QIOChannel **src,
                                        QIOChannel **dst)
 {
     QIOChannelSocket *lioc;
 
     lioc = qio_channel_socket_new();
-    qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort);
+    qio_channel_socket_listen_sync(lioc, listen_addr, 1, &error_abort);
 
-    if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
+    if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) {
         SocketAddress *laddr = qio_channel_socket_get_local_address(
             lioc, &error_abort);
 
-        g_free(connect_addr->u.inet.data->port);
-        connect_addr->u.inet.data->port = g_strdup(laddr->u.inet.data->port);
+        g_free(connect_addr->u.inet.port);
+        connect_addr->u.inet.port = g_strdup(laddr->u.inet.port);
 
         qapi_free_SocketAddress(laddr);
     }
@@ -146,7 +80,7 @@ static void test_io_channel_setup_sync(SocketAddress *listen_addr,
 
     test_io_channel_set_socket_bufs(*src, *dst);
 
-    object_unref(OBJECT(lioc));
+    *srv = QIO_CHANNEL(lioc);
 }
 
 
@@ -167,6 +101,7 @@ static void test_io_channel_complete(QIOTask *task,
 
 static void test_io_channel_setup_async(SocketAddress *listen_addr,
                                         SocketAddress *connect_addr,
+                                        QIOChannel **srv,
                                         QIOChannel **src,
                                         QIOChannel **dst)
 {
@@ -178,20 +113,20 @@ static void test_io_channel_setup_async(SocketAddress *listen_addr,
 
     lioc = qio_channel_socket_new();
     qio_channel_socket_listen_async(
-        lioc, listen_addr,
-        test_io_channel_complete, &data, NULL);
+        lioc, listen_addr, 1,
+        test_io_channel_complete, &data, NULL, NULL);
 
     g_main_loop_run(data.loop);
     g_main_context_iteration(g_main_context_default(), FALSE);
 
     g_assert(!data.err);
 
-    if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
+    if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) {
         SocketAddress *laddr = qio_channel_socket_get_local_address(
             lioc, &error_abort);
 
-        g_free(connect_addr->u.inet.data->port);
-        connect_addr->u.inet.data->port = g_strdup(laddr->u.inet.data->port);
+        g_free(connect_addr->u.inet.port);
+        connect_addr->u.inet.port = g_strdup(laddr->u.inet.port);
 
         qapi_free_SocketAddress(laddr);
     }
@@ -200,7 +135,7 @@ static void test_io_channel_setup_async(SocketAddress *listen_addr,
 
     qio_channel_socket_connect_async(
         QIO_CHANNEL_SOCKET(*src), connect_addr,
-        test_io_channel_complete, &data, NULL);
+        test_io_channel_complete, &data, NULL, NULL);
 
     g_main_loop_run(data.loop);
     g_main_context_iteration(g_main_context_default(), FALSE);
@@ -214,21 +149,34 @@ static void test_io_channel_setup_async(SocketAddress *listen_addr,
     qio_channel_set_delay(*src, false);
     test_io_channel_set_socket_bufs(*src, *dst);
 
-    object_unref(OBJECT(lioc));
+    *srv = QIO_CHANNEL(lioc);
 
     g_main_loop_unref(data.loop);
 }
 
 
+static void test_io_channel_socket_path_exists(SocketAddress *addr,
+                                               bool expectExists)
+{
+    if (addr->type != SOCKET_ADDRESS_TYPE_UNIX) {
+        return;
+    }
+
+    g_assert(g_file_test(addr->u.q_unix.path,
+                         G_FILE_TEST_EXISTS) == expectExists);
+}
+
+
 static void test_io_channel(bool async,
                             SocketAddress *listen_addr,
                             SocketAddress *connect_addr,
                             bool passFD)
 {
-    QIOChannel *src, *dst;
+    QIOChannel *src, *dst, *srv;
     QIOChannelTest *test;
     if (async) {
-        test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
+        test_io_channel_setup_async(listen_addr, connect_addr,
+                                    &srv, &src, &dst);
 
         g_assert(!passFD ||
                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
@@ -237,14 +185,25 @@ static void test_io_channel(bool async,
         g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN));
         g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN));
 
+        test_io_channel_socket_path_exists(listen_addr, true);
+
         test = qio_channel_test_new();
         qio_channel_test_run_threads(test, true, src, dst);
         qio_channel_test_validate(test);
 
+        test_io_channel_socket_path_exists(listen_addr, true);
+
+        /* unref without close, to ensure finalize() cleans up */
+
         object_unref(OBJECT(src));
         object_unref(OBJECT(dst));
+        test_io_channel_socket_path_exists(listen_addr, true);
+
+        object_unref(OBJECT(srv));
+        test_io_channel_socket_path_exists(listen_addr, false);
 
-        test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
+        test_io_channel_setup_async(listen_addr, connect_addr,
+                                    &srv, &src, &dst);
 
         g_assert(!passFD ||
                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
@@ -257,10 +216,24 @@ static void test_io_channel(bool async,
         qio_channel_test_run_threads(test, false, src, dst);
         qio_channel_test_validate(test);
 
+        /* close before unref, to ensure finalize copes with already closed */
+
+        qio_channel_close(src, &error_abort);
+        qio_channel_close(dst, &error_abort);
+        test_io_channel_socket_path_exists(listen_addr, true);
+
         object_unref(OBJECT(src));
         object_unref(OBJECT(dst));
+        test_io_channel_socket_path_exists(listen_addr, true);
+
+        qio_channel_close(srv, &error_abort);
+        test_io_channel_socket_path_exists(listen_addr, false);
+
+        object_unref(OBJECT(srv));
+        test_io_channel_socket_path_exists(listen_addr, false);
     } else {
-        test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
+        test_io_channel_setup_sync(listen_addr, connect_addr,
+                                   &srv, &src, &dst);
 
         g_assert(!passFD ||
                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
@@ -269,14 +242,25 @@ static void test_io_channel(bool async,
         g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN));
         g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN));
 
+        test_io_channel_socket_path_exists(listen_addr, true);
+
         test = qio_channel_test_new();
         qio_channel_test_run_threads(test, true, src, dst);
         qio_channel_test_validate(test);
 
+        test_io_channel_socket_path_exists(listen_addr, true);
+
+        /* unref without close, to ensure finalize() cleans up */
+
         object_unref(OBJECT(src));
         object_unref(OBJECT(dst));
+        test_io_channel_socket_path_exists(listen_addr, true);
+
+        object_unref(OBJECT(srv));
+        test_io_channel_socket_path_exists(listen_addr, false);
 
-        test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
+        test_io_channel_setup_sync(listen_addr, connect_addr,
+                                   &srv, &src, &dst);
 
         g_assert(!passFD ||
                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
@@ -289,8 +273,23 @@ static void test_io_channel(bool async,
         qio_channel_test_run_threads(test, false, src, dst);
         qio_channel_test_validate(test);
 
+        test_io_channel_socket_path_exists(listen_addr, true);
+
+        /* close before unref, to ensure finalize copes with already closed */
+
+        qio_channel_close(src, &error_abort);
+        qio_channel_close(dst, &error_abort);
+        test_io_channel_socket_path_exists(listen_addr, true);
+
         object_unref(OBJECT(src));
         object_unref(OBJECT(dst));
+        test_io_channel_socket_path_exists(listen_addr, true);
+
+        qio_channel_close(srv, &error_abort);
+        test_io_channel_socket_path_exists(listen_addr, false);
+
+        object_unref(OBJECT(srv));
+        test_io_channel_socket_path_exists(listen_addr, false);
     }
 }
 
@@ -300,16 +299,14 @@ static void test_io_channel_ipv4(bool async)
     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
 
-    listen_addr->type = SOCKET_ADDRESS_KIND_INET;
-    listen_addr->u.inet.data = g_new(InetSocketAddress, 1);
-    *listen_addr->u.inet.data = (InetSocketAddress) {
+    listen_addr->type = SOCKET_ADDRESS_TYPE_INET;
+    listen_addr->u.inet = (InetSocketAddress) {
         .host = g_strdup("127.0.0.1"),
         .port = NULL, /* Auto-select */
     };
 
-    connect_addr->type = SOCKET_ADDRESS_KIND_INET;
-    connect_addr->u.inet.data = g_new(InetSocketAddress, 1);
-    *connect_addr->u.inet.data = (InetSocketAddress) {
+    connect_addr->type = SOCKET_ADDRESS_TYPE_INET;
+    connect_addr->u.inet = (InetSocketAddress) {
         .host = g_strdup("127.0.0.1"),
         .port = NULL, /* Filled in later */
     };
@@ -338,16 +335,14 @@ static void test_io_channel_ipv6(bool async)
     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
 
-    listen_addr->type = SOCKET_ADDRESS_KIND_INET;
-    listen_addr->u.inet.data = g_new(InetSocketAddress, 1);
-    *listen_addr->u.inet.data = (InetSocketAddress) {
+    listen_addr->type = SOCKET_ADDRESS_TYPE_INET;
+    listen_addr->u.inet = (InetSocketAddress) {
         .host = g_strdup("::1"),
         .port = NULL, /* Auto-select */
     };
 
-    connect_addr->type = SOCKET_ADDRESS_KIND_INET;
-    connect_addr->u.inet.data = g_new(InetSocketAddress, 1);
-    *connect_addr->u.inet.data = (InetSocketAddress) {
+    connect_addr->type = SOCKET_ADDRESS_TYPE_INET;
+    connect_addr->u.inet = (InetSocketAddress) {
         .host = g_strdup("::1"),
         .port = NULL, /* Filled in later */
     };
@@ -378,19 +373,16 @@ static void test_io_channel_unix(bool async)
     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
 
 #define TEST_SOCKET "test-io-channel-socket.sock"
-    listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    listen_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
-    listen_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET);
+    listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX;
+    listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET);
 
-    connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    connect_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
-    connect_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET);
+    connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX;
+    connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET);
 
     test_io_channel(async, listen_addr, connect_addr, true);
 
     qapi_free_SocketAddress(listen_addr);
     qapi_free_SocketAddress(connect_addr);
-    g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS) == FALSE);
 }
 
 
@@ -409,7 +401,7 @@ static void test_io_channel_unix_fd_pass(void)
 {
     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
-    QIOChannel *src, *dst;
+    QIOChannel *src, *dst, *srv;
     int testfd;
     int fdsend[3];
     int *fdrecv = NULL;
@@ -427,15 +419,13 @@ static void test_io_channel_unix_fd_pass(void)
     fdsend[1] = testfd;
     fdsend[2] = testfd;
 
-    listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    listen_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
-    listen_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET);
+    listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX;
+    listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET);
 
-    connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
-    connect_addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
-    connect_addr->u.q_unix.data->path = g_strdup(TEST_SOCKET);
+    connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX;
+    connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET);
 
-    test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
+    test_io_channel_setup_sync(listen_addr, connect_addr, &srv, &src, &dst);
 
     memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend));
 
@@ -488,6 +478,7 @@ static void test_io_channel_unix_fd_pass(void)
 
     object_unref(OBJECT(src));
     object_unref(OBJECT(dst));
+    object_unref(OBJECT(srv));
     qapi_free_SocketAddress(listen_addr);
     qapi_free_SocketAddress(connect_addr);
     unlink(TEST_SOCKET);
@@ -574,8 +565,9 @@ int main(int argc, char **argv)
      * each protocol to avoid breaking tests on machines
      * with either IPv4 or IPv6 disabled.
      */
-    if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
-        return 1;
+    if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
+        g_printerr("socket_check_protocol_support() failed\n");
+        goto end;
     }
 
     if (has_ipv4) {
@@ -604,5 +596,6 @@ int main(int argc, char **argv)
                     test_io_channel_unix_listen_cleanup);
 #endif /* _WIN32 */
 
+end:
     return g_test_run();
 }