diff options
-rw-r--r-- | include/sysemu/char.h | 11 | ||||
-rw-r--r-- | qemu-char.c | 39 |
2 files changed, 40 insertions, 10 deletions
diff --git a/include/sysemu/char.h b/include/sysemu/char.h index b81a6ff185..9be57716b2 100644 --- a/include/sysemu/char.h +++ b/include/sysemu/char.h @@ -61,6 +61,7 @@ struct CharDriverState { int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); int (*get_msgfd)(struct CharDriverState *s); int (*chr_add_client)(struct CharDriverState *chr, int fd); + int (*chr_del_client)(struct CharDriverState *chr); IOEventHandler *chr_event; IOCanReadHandler *chr_can_read; IOReadHandler *chr_read; @@ -286,6 +287,16 @@ void qemu_chr_add_handlers(CharDriverState *s, void qemu_chr_be_generic_open(CharDriverState *s); void qemu_chr_accept_input(CharDriverState *s); int qemu_chr_add_client(CharDriverState *s, int fd); + +/** + * @qemu_chr_del_client: + * + * Delete (disconnect) the current client from the char backend, + * as if the remote end had closed itself. This is most useful for TCP + * listening sockets, where it means that we will close the connection to + * the current client but remain listening for new ones. + */ +int qemu_chr_del_client(CharDriverState *s); void qemu_chr_info_print(Monitor *mon, const QObject *ret_data); void qemu_chr_info(Monitor *mon, QObject **ret_data); CharDriverState *qemu_chr_find(const char *name); diff --git a/qemu-char.c b/qemu-char.c index 22ba162ff7..1f4208dad1 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -176,6 +176,11 @@ int qemu_chr_add_client(CharDriverState *s, int fd) return s->chr_add_client ? s->chr_add_client(s, fd) : -1; } +int qemu_chr_del_client(CharDriverState *s) +{ + return s->chr_del_client ? s->chr_del_client(s) : -1; +} + void qemu_chr_accept_input(CharDriverState *s) { if (s->chr_accept_input) @@ -2454,6 +2459,28 @@ static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond) return g_io_create_watch(s->chan, cond); } +static int tcp_chr_del_client(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + + if (!s->connected) { + return -1; + } + + s->connected = 0; + if (s->listen_chan) { + s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, + tcp_chr_accept, chr); + } + remove_fd_in_watch(chr); + g_io_channel_unref(s->chan); + s->chan = NULL; + closesocket(s->fd); + s->fd = -1; + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); + return 0; +} + static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; @@ -2470,16 +2497,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) size = tcp_chr_recv(chr, (void *)buf, len); if (size == 0) { /* connection closed */ - s->connected = 0; - if (s->listen_chan) { - s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); - } - remove_fd_in_watch(chr); - g_io_channel_unref(s->chan); - s->chan = NULL; - closesocket(s->fd); - s->fd = -1; - qemu_chr_be_event(chr, CHR_EVENT_CLOSED); + tcp_chr_del_client(chr); } else if (size > 0) { if (s->do_telnetopt) tcp_chr_process_IAC_bytes(chr, s, buf, &size); @@ -2675,6 +2693,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, chr->chr_close = tcp_chr_close; chr->get_msgfd = tcp_get_msgfd; chr->chr_add_client = tcp_chr_add_client; + chr->chr_del_client = tcp_chr_del_client; chr->chr_add_watch = tcp_chr_add_watch; chr->chr_update_read_handler = tcp_chr_update_read_handler; /* be isn't opened until we get a connection */ |