qemu-char: Add a chr_del_client method to char backends

Add a chr_del_client method to char backends. This makes sense
mostly for TCP, where it means "close the current connection and
then go back to listening for a new one"; the semantics are
as if the remote end had closed the connection.

This could for completeness be supported for other backends
(eg pipe, fd, tty), but this patch only supports it in TCP.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 832b7fe..b6b2785 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -65,6 +65,7 @@
     int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
     int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
     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;
@@ -339,6 +340,16 @@
 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);
 CharDriverState *qemu_chr_find(const char *name);
 bool chr_is_ringbuf(const CharDriverState *chr);
 
diff --git a/qemu-char.c b/qemu-char.c
index a8b01da..2b19e54 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -327,6 +327,11 @@
     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)
@@ -2802,10 +2807,14 @@
     return g_io_create_watch(s->chan, cond);
 }
 
-static void tcp_chr_disconnect(CharDriverState *chr)
+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,
@@ -2822,6 +2831,7 @@
     if (s->reconnect_time) {
         qemu_chr_socket_restart_timer(chr);
     }
+    return 0;
 }
 
 static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
@@ -2833,7 +2843,7 @@
 
     if (cond & G_IO_HUP) {
         /* connection closed */
-        tcp_chr_disconnect(chr);
+        tcp_chr_del_client(chr);
         return TRUE;
     }
 
@@ -2846,7 +2856,7 @@
     size = tcp_chr_recv(chr, (void *)buf, len);
     if (size == 0) {
         /* connection closed */
-        tcp_chr_disconnect(chr);
+        tcp_chr_del_client(chr);
     } else if (size > 0) {
         if (s->do_telnetopt)
             tcp_chr_process_IAC_bytes(chr, s, buf, &size);
@@ -2869,7 +2879,7 @@
     size = tcp_chr_recv(chr, (void *) buf, len);
     if (size == 0) {
         /* connection closed */
-        tcp_chr_disconnect(chr);
+        tcp_chr_del_client(chr);
     }
 
     return size;
@@ -4140,6 +4150,7 @@
     chr->get_msgfds = tcp_get_msgfds;
     chr->set_msgfds = tcp_set_msgfds;
     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 */