]> Git Repo - qemu.git/blob - tests/test-io-channel-socket.c
block: Add "file" output parameter to block status query functions
[qemu.git] / tests / test-io-channel-socket.c
1 /*
2  * QEMU I/O channel sockets test
3  *
4  * Copyright (c) 2015 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "io/channel-socket.h"
22 #include "io-channel-helpers.h"
23 #ifdef HAVE_IFADDRS_H
24 #include <ifaddrs.h>
25 #endif
26
27 static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
28 {
29 #ifdef HAVE_IFADDRS_H
30     struct ifaddrs *ifaddr = NULL, *ifa;
31     struct addrinfo hints = { 0 };
32     struct addrinfo *ai = NULL;
33     int gaierr;
34
35     *has_ipv4 = *has_ipv6 = false;
36
37     if (getifaddrs(&ifaddr) < 0) {
38         g_printerr("Failed to lookup interface addresses: %s\n",
39                    strerror(errno));
40         return -1;
41     }
42
43     for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
44         if (!ifa->ifa_addr) {
45             continue;
46         }
47
48         if (ifa->ifa_addr->sa_family == AF_INET) {
49             *has_ipv4 = true;
50         }
51         if (ifa->ifa_addr->sa_family == AF_INET6) {
52             *has_ipv6 = true;
53         }
54     }
55
56     freeifaddrs(ifaddr);
57
58     hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
59     hints.ai_family = AF_INET6;
60     hints.ai_socktype = SOCK_STREAM;
61
62     gaierr = getaddrinfo("::1", NULL, &hints, &ai);
63     if (gaierr != 0) {
64         if (gaierr == EAI_ADDRFAMILY ||
65             gaierr == EAI_FAMILY ||
66             gaierr == EAI_NONAME) {
67             *has_ipv6 = false;
68         } else {
69             g_printerr("Failed to resolve ::1 address: %s\n",
70                        gai_strerror(gaierr));
71             return -1;
72         }
73     }
74
75     freeaddrinfo(ai);
76
77     return 0;
78 #else
79     *has_ipv4 = *has_ipv6 = false;
80
81     return -1;
82 #endif
83 }
84
85
86 static void test_io_channel_set_socket_bufs(QIOChannel *src,
87                                             QIOChannel *dst)
88 {
89     int buflen = 64 * 1024;
90
91     /*
92      * Make the socket buffers small so that we see
93      * the effects of partial reads/writes
94      */
95     setsockopt(((QIOChannelSocket *)src)->fd,
96                SOL_SOCKET, SO_SNDBUF,
97                (char *)&buflen,
98                sizeof(buflen));
99
100     setsockopt(((QIOChannelSocket *)dst)->fd,
101                SOL_SOCKET, SO_SNDBUF,
102                (char *)&buflen,
103                sizeof(buflen));
104 }
105
106
107 static void test_io_channel_setup_sync(SocketAddress *listen_addr,
108                                        SocketAddress *connect_addr,
109                                        QIOChannel **src,
110                                        QIOChannel **dst)
111 {
112     QIOChannelSocket *lioc;
113
114     lioc = qio_channel_socket_new();
115     qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort);
116
117     if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
118         SocketAddress *laddr = qio_channel_socket_get_local_address(
119             lioc, &error_abort);
120
121         g_free(connect_addr->u.inet->port);
122         connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
123
124         qapi_free_SocketAddress(laddr);
125     }
126
127     *src = QIO_CHANNEL(qio_channel_socket_new());
128     qio_channel_socket_connect_sync(
129         QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort);
130     qio_channel_set_delay(*src, false);
131
132     *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
133     g_assert(*dst);
134
135     test_io_channel_set_socket_bufs(*src, *dst);
136
137     object_unref(OBJECT(lioc));
138 }
139
140
141 struct TestIOChannelData {
142     bool err;
143     GMainLoop *loop;
144 };
145
146
147 static void test_io_channel_complete(Object *src,
148                                      Error *err,
149                                      gpointer opaque)
150 {
151     struct TestIOChannelData *data = opaque;
152     data->err = err != NULL;
153     g_main_loop_quit(data->loop);
154 }
155
156
157 static void test_io_channel_setup_async(SocketAddress *listen_addr,
158                                         SocketAddress *connect_addr,
159                                         QIOChannel **src,
160                                         QIOChannel **dst)
161 {
162     QIOChannelSocket *lioc;
163     struct TestIOChannelData data;
164
165     data.loop = g_main_loop_new(g_main_context_default(),
166                                 TRUE);
167
168     lioc = qio_channel_socket_new();
169     qio_channel_socket_listen_async(
170         lioc, listen_addr,
171         test_io_channel_complete, &data, NULL);
172
173     g_main_loop_run(data.loop);
174     g_main_context_iteration(g_main_context_default(), FALSE);
175
176     g_assert(!data.err);
177
178     if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
179         SocketAddress *laddr = qio_channel_socket_get_local_address(
180             lioc, &error_abort);
181
182         g_free(connect_addr->u.inet->port);
183         connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
184
185         qapi_free_SocketAddress(laddr);
186     }
187
188     *src = QIO_CHANNEL(qio_channel_socket_new());
189
190     qio_channel_socket_connect_async(
191         QIO_CHANNEL_SOCKET(*src), connect_addr,
192         test_io_channel_complete, &data, NULL);
193
194     g_main_loop_run(data.loop);
195     g_main_context_iteration(g_main_context_default(), FALSE);
196
197     g_assert(!data.err);
198
199     *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
200     g_assert(*dst);
201
202     qio_channel_set_delay(*src, false);
203     test_io_channel_set_socket_bufs(*src, *dst);
204
205     object_unref(OBJECT(lioc));
206
207     g_main_loop_unref(data.loop);
208 }
209
210
211 static void test_io_channel(bool async,
212                             SocketAddress *listen_addr,
213                             SocketAddress *connect_addr,
214                             bool passFD)
215 {
216     QIOChannel *src, *dst;
217     QIOChannelTest *test;
218     if (async) {
219         test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
220
221         g_assert(!passFD ||
222                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
223         g_assert(!passFD ||
224                  qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
225
226         test = qio_channel_test_new();
227         qio_channel_test_run_threads(test, true, src, dst);
228         qio_channel_test_validate(test);
229
230         object_unref(OBJECT(src));
231         object_unref(OBJECT(dst));
232
233         test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
234
235         g_assert(!passFD ||
236                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
237         g_assert(!passFD ||
238                  qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
239
240         test = qio_channel_test_new();
241         qio_channel_test_run_threads(test, false, src, dst);
242         qio_channel_test_validate(test);
243
244         object_unref(OBJECT(src));
245         object_unref(OBJECT(dst));
246     } else {
247         test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
248
249         g_assert(!passFD ||
250                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
251         g_assert(!passFD ||
252                  qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
253
254         test = qio_channel_test_new();
255         qio_channel_test_run_threads(test, true, src, dst);
256         qio_channel_test_validate(test);
257
258         object_unref(OBJECT(src));
259         object_unref(OBJECT(dst));
260
261         test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
262
263         g_assert(!passFD ||
264                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
265         g_assert(!passFD ||
266                  qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
267
268         test = qio_channel_test_new();
269         qio_channel_test_run_threads(test, false, src, dst);
270         qio_channel_test_validate(test);
271
272         object_unref(OBJECT(src));
273         object_unref(OBJECT(dst));
274     }
275 }
276
277
278 static void test_io_channel_ipv4(bool async)
279 {
280     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
281     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
282
283     listen_addr->type = SOCKET_ADDRESS_KIND_INET;
284     listen_addr->u.inet = g_new0(InetSocketAddress, 1);
285     listen_addr->u.inet->host = g_strdup("127.0.0.1");
286     listen_addr->u.inet->port = NULL; /* Auto-select */
287
288     connect_addr->type = SOCKET_ADDRESS_KIND_INET;
289     connect_addr->u.inet = g_new0(InetSocketAddress, 1);
290     connect_addr->u.inet->host = g_strdup("127.0.0.1");
291     connect_addr->u.inet->port = NULL; /* Filled in later */
292
293     test_io_channel(async, listen_addr, connect_addr, false);
294
295     qapi_free_SocketAddress(listen_addr);
296     qapi_free_SocketAddress(connect_addr);
297 }
298
299
300 static void test_io_channel_ipv4_sync(void)
301 {
302     return test_io_channel_ipv4(false);
303 }
304
305
306 static void test_io_channel_ipv4_async(void)
307 {
308     return test_io_channel_ipv4(true);
309 }
310
311
312 static void test_io_channel_ipv6(bool async)
313 {
314     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
315     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
316
317     listen_addr->type = SOCKET_ADDRESS_KIND_INET;
318     listen_addr->u.inet = g_new0(InetSocketAddress, 1);
319     listen_addr->u.inet->host = g_strdup("::1");
320     listen_addr->u.inet->port = NULL; /* Auto-select */
321
322     connect_addr->type = SOCKET_ADDRESS_KIND_INET;
323     connect_addr->u.inet = g_new0(InetSocketAddress, 1);
324     connect_addr->u.inet->host = g_strdup("::1");
325     connect_addr->u.inet->port = NULL; /* Filled in later */
326
327     test_io_channel(async, listen_addr, connect_addr, false);
328
329     qapi_free_SocketAddress(listen_addr);
330     qapi_free_SocketAddress(connect_addr);
331 }
332
333
334 static void test_io_channel_ipv6_sync(void)
335 {
336     return test_io_channel_ipv6(false);
337 }
338
339
340 static void test_io_channel_ipv6_async(void)
341 {
342     return test_io_channel_ipv6(true);
343 }
344
345
346 #ifndef _WIN32
347 static void test_io_channel_unix(bool async)
348 {
349     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
350     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
351
352 #define TEST_SOCKET "test-io-channel-socket.sock"
353     listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
354     listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
355     listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
356
357     connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
358     connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
359     connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
360
361     test_io_channel(async, listen_addr, connect_addr, true);
362
363     qapi_free_SocketAddress(listen_addr);
364     qapi_free_SocketAddress(connect_addr);
365     unlink(TEST_SOCKET);
366 }
367
368
369 static void test_io_channel_unix_sync(void)
370 {
371     return test_io_channel_unix(false);
372 }
373
374
375 static void test_io_channel_unix_async(void)
376 {
377     return test_io_channel_unix(true);
378 }
379
380 static void test_io_channel_unix_fd_pass(void)
381 {
382     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
383     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
384     QIOChannel *src, *dst;
385     int testfd;
386     int fdsend[3];
387     int *fdrecv = NULL;
388     size_t nfdrecv = 0;
389     size_t i;
390     char bufsend[12], bufrecv[12];
391     struct iovec iosend[1], iorecv[1];
392
393 #define TEST_SOCKET "test-io-channel-socket.sock"
394 #define TEST_FILE "test-io-channel-socket.txt"
395
396     testfd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT, 0700);
397     g_assert(testfd != -1);
398     fdsend[0] = testfd;
399     fdsend[1] = testfd;
400     fdsend[2] = testfd;
401
402     listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
403     listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
404     listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
405
406     connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
407     connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
408     connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
409
410     test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
411
412     memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend));
413
414     iosend[0].iov_base = bufsend;
415     iosend[0].iov_len = G_N_ELEMENTS(bufsend);
416
417     iorecv[0].iov_base = bufrecv;
418     iorecv[0].iov_len = G_N_ELEMENTS(bufrecv);
419
420     g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
421     g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
422
423     qio_channel_writev_full(src,
424                             iosend,
425                             G_N_ELEMENTS(iosend),
426                             fdsend,
427                             G_N_ELEMENTS(fdsend),
428                             &error_abort);
429
430     qio_channel_readv_full(dst,
431                            iorecv,
432                            G_N_ELEMENTS(iorecv),
433                            &fdrecv,
434                            &nfdrecv,
435                            &error_abort);
436
437     g_assert(nfdrecv == G_N_ELEMENTS(fdsend));
438     /* Each recvd FD should be different from sent FD */
439     for (i = 0; i < nfdrecv; i++) {
440         g_assert_cmpint(fdrecv[i], !=, testfd);
441     }
442     /* Each recvd FD should be different from each other */
443     g_assert_cmpint(fdrecv[0], !=, fdrecv[1]);
444     g_assert_cmpint(fdrecv[0], !=, fdrecv[2]);
445     g_assert_cmpint(fdrecv[1], !=, fdrecv[2]);
446
447     /* Check the I/O buf we sent at the same time matches */
448     g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0);
449
450     /* Write some data into the FD we received */
451     g_assert(write(fdrecv[0], bufsend, G_N_ELEMENTS(bufsend)) ==
452              G_N_ELEMENTS(bufsend));
453
454     /* Read data from the original FD and make sure it matches */
455     memset(bufrecv, 0, G_N_ELEMENTS(bufrecv));
456     g_assert(lseek(testfd, 0, SEEK_SET) == 0);
457     g_assert(read(testfd, bufrecv, G_N_ELEMENTS(bufrecv)) ==
458              G_N_ELEMENTS(bufrecv));
459     g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0);
460
461     object_unref(OBJECT(src));
462     object_unref(OBJECT(dst));
463     qapi_free_SocketAddress(listen_addr);
464     qapi_free_SocketAddress(connect_addr);
465     unlink(TEST_SOCKET);
466     unlink(TEST_FILE);
467     close(testfd);
468     for (i = 0; i < nfdrecv; i++) {
469         close(fdrecv[i]);
470     }
471     g_free(fdrecv);
472 }
473 #endif /* _WIN32 */
474
475
476 int main(int argc, char **argv)
477 {
478     bool has_ipv4, has_ipv6;
479
480     module_call_init(MODULE_INIT_QOM);
481
482     g_test_init(&argc, &argv, NULL);
483
484     /* We're creating actual IPv4/6 sockets, so we should
485      * check if the host running tests actually supports
486      * each protocol to avoid breaking tests on machines
487      * with either IPv4 or IPv6 disabled.
488      */
489     if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
490         return 1;
491     }
492
493     if (has_ipv4) {
494         g_test_add_func("/io/channel/socket/ipv4-sync",
495                         test_io_channel_ipv4_sync);
496         g_test_add_func("/io/channel/socket/ipv4-async",
497                         test_io_channel_ipv4_async);
498     }
499     if (has_ipv6) {
500         g_test_add_func("/io/channel/socket/ipv6-sync",
501                         test_io_channel_ipv6_sync);
502         g_test_add_func("/io/channel/socket/ipv6-async",
503                         test_io_channel_ipv6_async);
504     }
505
506 #ifndef _WIN32
507     g_test_add_func("/io/channel/socket/unix-sync",
508                     test_io_channel_unix_sync);
509     g_test_add_func("/io/channel/socket/unix-async",
510                     test_io_channel_unix_async);
511     g_test_add_func("/io/channel/socket/unix-fd-pass",
512                     test_io_channel_unix_fd_pass);
513 #endif /* _WIN32 */
514
515     return g_test_run();
516 }
This page took 0.051206 seconds and 4 git commands to generate.