Signed-off-by: Kevin Wolf <kw...@redhat.com> Reviewed-by: Paolo Bonzini <pbonz...@redhat.com> --- tests/libqtest.c | 123 ++++++++++++++++++++++++++++++++++++++++++------------ tests/libqtest.h | 17 +++++++ 2 files changed, 113 insertions(+), 27 deletions(-)
diff --git a/tests/libqtest.c b/tests/libqtest.c index 295c6d4..6d333ef 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -36,6 +36,7 @@ QTestState *global_qtest; struct QTestState { int fd; + int qmp_fd; bool irq_level[MAX_IRQ]; GString *rx; gchar *pid_file; @@ -45,48 +46,76 @@ struct QTestState g_assert_cmpint(ret, !=, -1); \ } while (0) +static int init_socket(const char *socket_path) +{ + struct sockaddr_un addr; + int sock; + int ret; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + g_assert_no_errno(sock); + + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); + qemu_set_cloexec(sock); + + do { + ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); + } while (ret == -1 && errno == EINTR); + g_assert_no_errno(ret); + listen(sock, 1); + + return sock; +} + +static int socket_accept(int sock) +{ + struct sockaddr_un addr; + socklen_t addrlen; + int ret; + + do { + ret = accept(sock, (struct sockaddr *)&addr, &addrlen); + } while (ret == -1 && errno == EINTR); + g_assert_no_errno(ret); + close(sock); + + return ret; +} + QTestState *qtest_init(const char *extra_args) { QTestState *s; - struct sockaddr_un addr; - int sock, ret, i; + int sock, qmpsock, ret, i; gchar *socket_path; + gchar *qmp_socket_path; gchar *pid_file; gchar *command; const char *qemu_binary; pid_t pid; - socklen_t addrlen; qemu_binary = getenv("QTEST_QEMU_BINARY"); g_assert(qemu_binary != NULL); socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid()); + qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid()); pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid()); s = g_malloc(sizeof(*s)); - sock = socket(PF_UNIX, SOCK_STREAM, 0); - g_assert_no_errno(sock); - - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); - qemu_set_cloexec(sock); - - do { - ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); - } while (ret == -1 && errno == EINTR); - g_assert_no_errno(ret); - listen(sock, 1); + sock = init_socket(socket_path); + qmpsock = init_socket(qmp_socket_path); pid = fork(); if (pid == 0) { command = g_strdup_printf("%s " "-qtest unix:%s,nowait " "-qtest-log /dev/null " + "-qmp unix:%s,nowait " "-pidfile %s " "-machine accel=qtest " "%s", qemu_binary, socket_path, - pid_file, + qmp_socket_path, pid_file, extra_args ?: ""); ret = system(command); @@ -94,13 +123,9 @@ QTestState *qtest_init(const char *extra_args) g_free(command); } - do { - ret = accept(sock, (struct sockaddr *)&addr, &addrlen); - } while (ret == -1 && errno == EINTR); - g_assert_no_errno(ret); - close(sock); + s->fd = socket_accept(sock); + s->qmp_fd = socket_accept(qmpsock); - s->fd = ret; s->rx = g_string_new(""); s->pid_file = pid_file; for (i = 0; i < MAX_IRQ; i++) { @@ -108,6 +133,11 @@ QTestState *qtest_init(const char *extra_args) } g_free(socket_path); + g_free(qmp_socket_path); + + /* Read the QMP greeting and then do the handshake */ + qtest_qmp(s, ""); + qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"); return s; } @@ -131,22 +161,19 @@ void qtest_quit(QTestState *s) } } -static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) +static void socket_sendf(int fd, const char *fmt, va_list ap) { - va_list ap; gchar *str; size_t size, offset; - va_start(ap, fmt); str = g_strdup_vprintf(fmt, ap); - va_end(ap); size = strlen(str); offset = 0; while (offset < size) { ssize_t len; - len = write(s->fd, str + offset, size - offset); + len = write(fd, str + offset, size - offset); if (len == -1 && errno == EINTR) { continue; } @@ -158,6 +185,15 @@ static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) } } +static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + socket_sendf(s->fd, fmt, ap); + va_end(ap); +} + static GString *qtest_recv_line(QTestState *s) { GString *line; @@ -233,6 +269,39 @@ redo: return words; } +void qtest_qmp(QTestState *s, const char *fmt, ...) +{ + va_list ap; + bool has_reply = false; + int nesting = 0; + + /* Send QMP request */ + va_start(ap, fmt); + socket_sendf(s->qmp_fd, fmt, ap); + va_end(ap); + + /* Receive reply */ + while (!has_reply || nesting > 0) { + ssize_t len; + char c; + + len = read(s->qmp_fd, &c, 1); + if (len == -1 && errno == EINTR) { + continue; + } + + switch (c) { + case '{': + nesting++; + has_reply = true; + break; + case '}': + nesting--; + break; + } + } +} + const char *qtest_get_arch(void) { const char *qemu = getenv("QTEST_QEMU_BINARY"); diff --git a/tests/libqtest.h b/tests/libqtest.h index 2ca85a9..c8ade85 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -38,6 +38,15 @@ QTestState *qtest_init(const char *extra_args); void qtest_quit(QTestState *s); /** + * qtest_qmp: + * @s: QTestState instance to operate on. + * @fmt...: QMP message to send to qemu + * + * Sends a QMP message to QEMU + */ +void qtest_qmp(QTestState *s, const char *fmt, ...); + +/** * qtest_get_irq: * @s: QTestState instance to operate on. * @num: Interrupt to observe. @@ -207,6 +216,14 @@ void qtest_add_func(const char *str, void (*fn)); ) /** + * qmp: + * @fmt...: QMP message to send to qemu + * + * Sends a QMP message to QEMU + */ +#define qmp(fmt, ...) qtest_qmp(global_qtest, fmt, ## __VA_ARGS__) + +/** * get_irq: * @num: Interrupt to observe. * -- 1.7.6.5