aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-07-13 09:34:24 +0100
committerPeter Maydell <peter.maydell@linaro.org>2020-07-13 09:34:24 +0100
commit6c87d9f311dba0641bdc2df556056938a8bf2a12 (patch)
treee85ca32e9a70b92ed8078c2029299489169605f5 /tests
parent9f526fce49c6ac48114ed04914b5a76e4db75785 (diff)
parent30827bad3852fd85d86995e7ccab429679442889 (diff)
downloadqemu-6c87d9f311dba0641bdc2df556056938a8bf2a12.zip
qemu-6c87d9f311dba0641bdc2df556056938a8bf2a12.tar.gz
qemu-6c87d9f311dba0641bdc2df556056938a8bf2a12.tar.bz2
Merge remote-tracking branch 'remotes/elmarco/tags/chardev-pull-request' into staging
# gpg: Signature made Mon 13 Jul 2020 09:23:19 BST # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * remotes/elmarco/tags/chardev-pull-request: chardev: Extract system emulation specific code chardev: Reduce "char-mux.h" scope, rename it "chardev-internal.h" chardev: Restrict msmouse / wctablet / testdev to system emulation tests/test-char: Remove unused "chardev/char-mux.h" include monitor/misc: Remove unused "chardev/char-mux.h" include char: fix use-after-free with dup chardev & reconnect chardev: don't abort on attempt to add duplicated chardev char-socket: initialize reconnect timer only when the timer doesn't start Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/test-char.c122
1 files changed, 105 insertions, 17 deletions
diff --git a/tests/test-char.c b/tests/test-char.c
index 3afc9b1..614bdac 100644
--- a/tests/test-char.c
+++ b/tests/test-char.c
@@ -6,7 +6,6 @@
#include "qemu/option.h"
#include "qemu/sockets.h"
#include "chardev/char-fe.h"
-#include "chardev/char-mux.h"
#include "sysemu/sysemu.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-char.h"
@@ -625,12 +624,14 @@ static void char_udp_test(void)
typedef struct {
int event;
bool got_pong;
+ CharBackend *be;
} CharSocketTestData;
#define SOCKET_PING "Hello"
#define SOCKET_PONG "World"
+typedef void (*char_socket_cb)(void *opaque, QEMUChrEvent event);
static void
char_socket_event(void *opaque, QEMUChrEvent event)
@@ -639,6 +640,27 @@ char_socket_event(void *opaque, QEMUChrEvent event)
data->event = event;
}
+static void
+char_socket_event_with_error(void *opaque, QEMUChrEvent event)
+{
+ static bool first_error;
+ CharSocketTestData *data = opaque;
+ CharBackend *be = data->be;
+ data->event = event;
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ if (!first_error) {
+ first_error = true;
+ qemu_chr_fe_disconnect(be);
+ }
+ return;
+ case CHR_EVENT_CLOSED:
+ return;
+ default:
+ return;
+ }
+}
+
static void
char_socket_read(void *opaque, const uint8_t *buf, int size)
@@ -699,19 +721,24 @@ char_socket_addr_to_opt_str(SocketAddress *addr, bool fd_pass,
}
-static void
-char_socket_ping_pong(QIOChannel *ioc)
+static int
+char_socket_ping_pong(QIOChannel *ioc, Error **errp)
{
char greeting[sizeof(SOCKET_PING)];
const char *response = SOCKET_PONG;
- qio_channel_read_all(ioc, greeting, sizeof(greeting), &error_abort);
+ int ret;
+ ret = qio_channel_read_all(ioc, greeting, sizeof(greeting), errp);
+ if (ret != 0) {
+ object_unref(OBJECT(ioc));
+ return -1;
+ }
g_assert(memcmp(greeting, SOCKET_PING, sizeof(greeting)) == 0);
- qio_channel_write_all(ioc, response, sizeof(SOCKET_PONG), &error_abort);
-
+ qio_channel_write_all(ioc, response, sizeof(SOCKET_PONG), errp);
object_unref(OBJECT(ioc));
+ return 0;
}
@@ -723,7 +750,7 @@ char_socket_server_client_thread(gpointer data)
qio_channel_socket_connect_sync(ioc, addr, &error_abort);
- char_socket_ping_pong(QIO_CHANNEL(ioc));
+ char_socket_ping_pong(QIO_CHANNEL(ioc), &error_abort);
return NULL;
}
@@ -783,6 +810,7 @@ static void char_socket_server_test(gconstpointer opaque)
reconnect:
data.event = -1;
+ data.be = &be;
qemu_chr_fe_set_handlers(&be, NULL, NULL,
char_socket_event, NULL,
&data, NULL, true);
@@ -855,10 +883,13 @@ char_socket_client_server_thread(gpointer data)
QIOChannelSocket *ioc = data;
QIOChannelSocket *cioc;
+retry:
cioc = qio_channel_socket_accept(ioc, &error_abort);
g_assert_nonnull(cioc);
- char_socket_ping_pong(QIO_CHANNEL(cioc));
+ if (char_socket_ping_pong(QIO_CHANNEL(cioc), NULL) != 0) {
+ goto retry;
+ }
return NULL;
}
@@ -869,12 +900,59 @@ typedef struct {
const char *reconnect;
bool wait_connected;
bool fd_pass;
+ char_socket_cb event_cb;
} CharSocketClientTestConfig;
+static void char_socket_client_dupid_test(gconstpointer opaque)
+{
+ const CharSocketClientTestConfig *config = opaque;
+ QIOChannelSocket *ioc;
+ char *optstr;
+ Chardev *chr1, *chr2;
+ SocketAddress *addr;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+
+ /*
+ * Setup a listener socket and determine get its address
+ * so we know the TCP port for the client later
+ */
+ ioc = qio_channel_socket_new();
+ g_assert_nonnull(ioc);
+ qio_channel_socket_listen_sync(ioc, config->addr, 1, &error_abort);
+ addr = qio_channel_socket_get_local_address(ioc, &error_abort);
+ g_assert_nonnull(addr);
+
+ /*
+ * Populate the chardev address based on what the server
+ * is actually listening on
+ */
+ optstr = char_socket_addr_to_opt_str(addr,
+ config->fd_pass,
+ config->reconnect,
+ false);
+
+ opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"),
+ optstr, true);
+ g_assert_nonnull(opts);
+ chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort);
+ g_assert_nonnull(chr1);
+
+ chr2 = qemu_chr_new_from_opts(opts, NULL, &local_err);
+ g_assert_null(chr2);
+ error_free_or_abort(&local_err);
+
+ object_unref(OBJECT(ioc));
+ qemu_opts_del(opts);
+ object_unparent(OBJECT(chr1));
+ qapi_free_SocketAddress(addr);
+ g_free(optstr);
+}
static void char_socket_client_test(gconstpointer opaque)
{
const CharSocketClientTestConfig *config = opaque;
+ const char_socket_cb event_cb = config->event_cb;
QIOChannelSocket *ioc;
char *optstr;
Chardev *chr;
@@ -938,8 +1016,9 @@ static void char_socket_client_test(gconstpointer opaque)
reconnect:
data.event = -1;
+ data.be = &be;
qemu_chr_fe_set_handlers(&be, NULL, NULL,
- char_socket_event, NULL,
+ event_cb, NULL,
&data, NULL, true);
if (config->reconnect) {
g_assert(data.event == -1);
@@ -977,7 +1056,7 @@ static void char_socket_client_test(gconstpointer opaque)
/* Setup a callback to receive the reply to our greeting */
qemu_chr_fe_set_handlers(&be, char_socket_can_read,
char_socket_read,
- char_socket_event, NULL,
+ event_cb, NULL,
&data, NULL, true);
g_assert(data.event == CHR_EVENT_OPENED);
data.event = -1;
@@ -1422,17 +1501,22 @@ int main(int argc, char **argv)
#define SOCKET_CLIENT_TEST(name, addr) \
static CharSocketClientTestConfig client1 ## name = \
- { addr, NULL, false, false }; \
+ { addr, NULL, false, false, char_socket_event }; \
static CharSocketClientTestConfig client2 ## name = \
- { addr, NULL, true, false }; \
+ { addr, NULL, true, false, char_socket_event }; \
static CharSocketClientTestConfig client3 ## name = \
- { addr, ",reconnect=1", false }; \
+ { addr, ",reconnect=1", false, false, char_socket_event }; \
static CharSocketClientTestConfig client4 ## name = \
- { addr, ",reconnect=1", true }; \
+ { addr, ",reconnect=1", true, false, char_socket_event }; \
static CharSocketClientTestConfig client5 ## name = \
- { addr, NULL, false, true }; \
+ { addr, NULL, false, true, char_socket_event }; \
static CharSocketClientTestConfig client6 ## name = \
- { addr, NULL, true, true }; \
+ { addr, NULL, true, true, char_socket_event }; \
+ static CharSocketClientTestConfig client7 ## name = \
+ { addr, ",reconnect=1", true, false, \
+ char_socket_event_with_error }; \
+ static CharSocketClientTestConfig client8 ## name = \
+ { addr, ",reconnect=1", false, false, char_socket_event }; \
g_test_add_data_func("/char/socket/client/mainloop/" # name, \
&client1 ##name, char_socket_client_test); \
g_test_add_data_func("/char/socket/client/wait-conn/" # name, \
@@ -1444,7 +1528,11 @@ int main(int argc, char **argv)
g_test_add_data_func("/char/socket/client/mainloop-fdpass/" # name, \
&client5 ##name, char_socket_client_test); \
g_test_add_data_func("/char/socket/client/wait-conn-fdpass/" # name, \
- &client6 ##name, char_socket_client_test)
+ &client6 ##name, char_socket_client_test); \
+ g_test_add_data_func("/char/socket/client/reconnect-error/" # name, \
+ &client7 ##name, char_socket_client_test); \
+ g_test_add_data_func("/char/socket/client/dupid-reconnect/" # name, \
+ &client8 ##name, char_socket_client_dupid_test)
if (has_ipv4) {
SOCKET_SERVER_TEST(tcp, &tcpaddr);