@@ -127,6 +127,7 @@ check-unit-y += tests/test-bufferiszero$(EXESUF)
gcov-files-check-bufferiszero-y = util/bufferiszero.c
check-unit-y += tests/test-uuid$(EXESUF)
check-unit-y += tests/ptimer-test$(EXESUF)
+#check-unit-y += tests/test-listen$(EXESUF)
gcov-files-ptimer-test-y = hw/core/ptimer.c
check-unit-y += tests/test-qapi-util$(EXESUF)
gcov-files-test-qapi-util-y = qapi/qapi-util.c
@@ -760,6 +761,7 @@ tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
tests/numa-test$(EXESUF): tests/numa-test.o
+tests/test-listen$(EXESUF): tests/test-listen.o $(test-util-obj-y)
tests/migration/stress$(EXESUF): tests/migration/stress.o
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
new file mode 100644
@@ -0,0 +1,141 @@
+/*
+ * Test parallel port listen configuration with
+ * dynamic port allocation
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu-common.h"
+#include "qemu/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+
+#define NAME_LEN 1024
+#define PORT_LEN 16
+
+struct thr_info {
+ QemuThread thread;
+ int to_port;
+ int got_port;
+ int eno;
+ int fd;
+ const char *errstr;
+};
+
+static char hostname[NAME_LEN + 1];
+static char port[PORT_LEN + 1];
+
+static void *listener_thread(void *arg)
+{
+ struct thr_info *thr = (struct thr_info *)arg;
+ SocketAddress addr = {
+ .type = SOCKET_ADDRESS_TYPE_INET,
+ .u = {
+ .inet = {
+ .host = hostname,
+ .port = port,
+ .ipv4 = true,
+ .has_to = true,
+ .to = thr->to_port,
+ },
+ },
+ };
+ Error *err = NULL;
+ int fd;
+
+ fd = socket_listen(&addr, &err);
+ if (fd < 0) {
+ thr->eno = errno;
+ thr->errstr = error_get_pretty(err);
+ } else {
+ struct sockaddr_in a;
+ socklen_t a_len = sizeof(a);
+ g_assert_cmpint(getsockname(fd, (struct sockaddr *)&a, &a_len), ==, 0);
+ thr->got_port = ntohs(a.sin_port);
+ thr->fd = fd;
+ }
+ return arg;
+}
+
+
+static void listen_compete_nthr(bool threaded, int nthreads,
+ int start_port, int max_offset)
+{
+ int i;
+ int failed_listens = 0;
+ size_t alloc_sz = sizeof(struct thr_info) * nthreads;
+ struct thr_info *thr = g_malloc(alloc_sz);
+ int used[max_offset + 1];
+ memset(used, 0, sizeof(used));
+ g_assert_nonnull(thr);
+ g_assert_cmpint(gethostname(hostname, NAME_LEN), == , 0);
+ snprintf(port, PORT_LEN, "%d", start_port);
+ memset(thr, 0, alloc_sz);
+
+ for (i = 0; i < nthreads; i++) {
+ thr[i].to_port = start_port + max_offset;
+ if (threaded) {
+ qemu_thread_create(&thr[i].thread, "listener",
+ listener_thread, &thr[i],
+ QEMU_THREAD_JOINABLE);
+ } else {
+ listener_thread(&thr[i]);
+ }
+ }
+
+ if (threaded) {
+ for (i = 0; i < nthreads; i++) {
+ qemu_thread_join(&thr[i].thread);
+ }
+ }
+ for (i = 0; i < nthreads; i++) {
+ if (thr[i].got_port) {
+ closesocket(thr[i].fd);
+ }
+ }
+
+ for (i = 0; i < nthreads; i++) {
+ if (thr[i].eno != 0) {
+ const char *m;
+ printf("** Failed to assign a port to thread %d (errno = %d)\n",
+ i, thr[i].eno);
+ /* This is what we are interested in capturing -
+ * catch and report details if something unexpected happens:
+ */
+ m = strstr(thr[i].errstr, "Failed to listen on socket");
+ if (m != NULL) {
+ g_assert_cmpstr(thr[i].errstr, ==,
+ "Failed to listen on socket: Address already in use");
+ }
+ failed_listens++;
+ } else {
+ int assigned_port = thr[i].got_port;
+ g_assert_cmpint(assigned_port, <= , thr[i].to_port);
+ g_assert_cmpint(used[assigned_port - start_port], == , 0);
+ }
+ }
+ g_assert_cmpint(failed_listens, ==, 0);
+ free(thr);
+}
+
+
+static void listen_compete(void)
+{
+ listen_compete_nthr(true, 200, 5920, 300);
+}
+
+static void listen_serial(void)
+{
+ listen_compete_nthr(false, 200, 6300, 300);
+}
+
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/socket/listen-serial", listen_serial);
+ g_test_add_func("/socket/listen-compete", listen_compete);
+
+ return g_test_run();
+}