diff options
-rw-r--r-- | chardev/Makefile.objs | 3 | ||||
-rw-r--r-- | chardev/char-fe.c | 2 | ||||
-rw-r--r-- | chardev/char-mux.c | 2 | ||||
-rw-r--r-- | chardev/char-socket.c | 5 | ||||
-rw-r--r-- | chardev/char.c | 43 | ||||
-rw-r--r-- | chardev/chardev-internal.h (renamed from include/chardev/char-mux.h) | 10 | ||||
-rw-r--r-- | chardev/chardev-sysemu.c | 69 | ||||
-rw-r--r-- | monitor/misc.c | 1 | ||||
-rw-r--r-- | tests/test-char.c | 122 |
9 files changed, 195 insertions, 62 deletions
diff --git a/chardev/Makefile.objs b/chardev/Makefile.objs index 3a58c9d329..3783dadc4c 100644 --- a/chardev/Makefile.objs +++ b/chardev/Makefile.objs @@ -1,4 +1,5 @@ chardev-obj-y += char.o +chardev-obj-$(CONFIG_SOFTMMU) += chardev-sysemu.o chardev-obj-$(CONFIG_WIN32) += char-console.o chardev-obj-$(CONFIG_POSIX) += char-fd.o chardev-obj-y += char-fe.o @@ -17,7 +18,7 @@ chardev-obj-y += char-udp.o chardev-obj-$(CONFIG_WIN32) += char-win.o chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o -common-obj-y += msmouse.o wctablet.o testdev.o +common-obj-$(CONFIG_SOFTMMU) += msmouse.o wctablet.o testdev.o ifeq ($(CONFIG_BRLAPI),y) common-obj-m += baum.o diff --git a/chardev/char-fe.c b/chardev/char-fe.c index f3530a90e6..474715c5a9 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -29,7 +29,7 @@ #include "chardev/char-fe.h" #include "chardev/char-io.h" -#include "chardev/char-mux.h" +#include "chardev-internal.h" int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len) { diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 46c44af67c..6f980bb836 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -29,7 +29,7 @@ #include "chardev/char.h" #include "sysemu/block-backend.h" #include "sysemu/sysemu.h" -#include "chardev/char-mux.h" +#include "chardev-internal.h" /* MUX driver for serial I/O splitting */ diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 5758d9900f..ef62dbf3d7 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -490,7 +490,7 @@ static void tcp_chr_disconnect_locked(Chardev *chr) if (emit_close) { qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } - if (s->reconnect_time) { + if (s->reconnect_time && !s->reconnect_timer) { qemu_chr_socket_restart_timer(chr); } } @@ -1129,7 +1129,8 @@ static void tcp_chr_connect_client_async(Chardev *chr) */ s->connect_task = qio_task_new(OBJECT(sioc), qemu_chr_socket_connected, - chr, NULL); + object_ref(OBJECT(chr)), + (GDestroyNotify)object_unref); qio_task_run_in_thread(s->connect_task, tcp_chr_connect_client_task, s->addr, diff --git a/chardev/char.c b/chardev/char.c index e5b43cb4b8..77e7ec814f 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -40,12 +40,12 @@ #include "qemu/id.h" #include "qemu/coroutine.h" -#include "chardev/char-mux.h" +#include "chardev-internal.h" /***********************************************************/ /* character device */ -static Object *get_chardevs_root(void) +Object *get_chardevs_root(void) { return container_get(object_get_root(), "/chardevs"); } @@ -305,33 +305,6 @@ static const TypeInfo char_type_info = { .class_init = char_class_init, }; -static int chardev_machine_done_notify_one(Object *child, void *opaque) -{ - Chardev *chr = (Chardev *)child; - ChardevClass *class = CHARDEV_GET_CLASS(chr); - - if (class->chr_machine_done) { - return class->chr_machine_done(chr); - } - - return 0; -} - -static void chardev_machine_done_hook(Notifier *notifier, void *unused) -{ - int ret = object_child_foreach(get_chardevs_root(), - chardev_machine_done_notify_one, NULL); - - if (ret) { - error_report("Failed to call chardev machine_done hooks"); - exit(1); - } -} - -static Notifier chardev_machine_done_notify = { - .notify = chardev_machine_done_hook, -}; - static bool qemu_chr_is_busy(Chardev *s) { if (CHARDEV_IS_MUX(s)) { @@ -996,7 +969,11 @@ static Chardev *chardev_new(const char *id, const char *typename, } if (id) { - object_property_add_child(get_chardevs_root(), id, obj); + object_property_try_add_child(get_chardevs_root(), id, obj, + &local_err); + if (local_err) { + goto end; + } object_unref(obj); } @@ -1194,12 +1171,6 @@ void qemu_chr_cleanup(void) static void register_types(void) { type_register_static(&char_type_info); - - /* this must be done after machine init, since we register FEs with muxes - * as part of realize functions like serial_isa_realizefn when -nographic - * is specified - */ - qemu_add_machine_init_done_notifier(&chardev_machine_done_notify); } type_init(register_types); diff --git a/include/chardev/char-mux.h b/chardev/chardev-internal.h index 417fe32eed..f4d0429763 100644 --- a/include/chardev/char-mux.h +++ b/chardev/chardev-internal.h @@ -1,5 +1,5 @@ /* - * QEMU System Emulator + * QEMU Character device internals * * Copyright (c) 2003-2008 Fabrice Bellard * @@ -21,15 +21,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef CHAR_MUX_H -#define CHAR_MUX_H +#ifndef CHARDEV_INTERNAL_H +#define CHARDEV_INTERNAL_H #include "chardev/char.h" #include "chardev/char-fe.h" +#include "qom/object.h" #define MAX_MUX 4 #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) + typedef struct MuxChardev { Chardev parent; CharBackend *backends[MAX_MUX]; @@ -58,4 +60,6 @@ typedef struct MuxChardev { void mux_set_focus(Chardev *chr, int focus); void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event); +Object *get_chardevs_root(void); + #endif /* CHAR_MUX_H */ diff --git a/chardev/chardev-sysemu.c b/chardev/chardev-sysemu.c new file mode 100644 index 0000000000..eecdc615ee --- /dev/null +++ b/chardev/chardev-sysemu.c @@ -0,0 +1,69 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "chardev/char.h" +#include "qemu/error-report.h" +#include "chardev-internal.h" + +static int chardev_machine_done_notify_one(Object *child, void *opaque) +{ + Chardev *chr = (Chardev *)child; + ChardevClass *class = CHARDEV_GET_CLASS(chr); + + if (class->chr_machine_done) { + return class->chr_machine_done(chr); + } + + return 0; +} + +static void chardev_machine_done_hook(Notifier *notifier, void *unused) +{ + int ret = object_child_foreach(get_chardevs_root(), + chardev_machine_done_notify_one, NULL); + + if (ret) { + error_report("Failed to call chardev machine_done hooks"); + exit(1); + } +} + + +static Notifier chardev_machine_done_notify = { + .notify = chardev_machine_done_hook, +}; + +static void register_types(void) +{ + /* + * This must be done after machine init, since we register FEs with muxes + * as part of realize functions like serial_isa_realizefn when -nographic + * is specified. + */ + qemu_add_machine_init_done_notifier(&chardev_machine_done_notify); +} + +type_init(register_types); diff --git a/monitor/misc.c b/monitor/misc.c index 89bb970b00..e847b58a8c 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -33,7 +33,6 @@ #include "exec/gdbstub.h" #include "net/net.h" #include "net/slirp.h" -#include "chardev/char-mux.h" #include "ui/qemu-spice.h" #include "qemu/config-file.h" #include "qemu/ctype.h" diff --git a/tests/test-char.c b/tests/test-char.c index 3afc9b1b8d..614bdac2df 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); |