Skip to contentSkip to conversation

Allow a client chardev to reconnect if disconnected

PATCH In review [email protected] 4 participants 14 messages Mar 5, 2014 – Mar 6, 2014

lore
Share & tools
lore
#0 lore raw

cover Allow a client chardev to reconnect if disconnected

This patch set was part of the IPMI patches, but people have asked it to be separated out because it's useful by itself and the IPMI patches are stuck in legal limbo.

I have left the patch to move allocating CharDriverState in one place (patch 1). I didn't get much opinion either way, but I think it is an improvement and a net reduction in code. I also added a patch (patch 6) that goes another step there, it makes the error handling in qmp_chardev_add() consistent and is also a net reduction in code.

The main patches to add the feature are patch 2 and patch 3.

The rest are little possible bug fixes that I noticed while working in the code.

I went through all the comments and I believe I fixed everything.

Thanks to everyone that reviewed this before.

#1 lore raw

PATCH 1/7 qemu-char: Allocate CharDriverState in qemu_chr_new_from_opts

From: Corey Minyard <[email protected]>

This allocates the CharDriverState structure and passes it in to the open routine. This allows a coming option to automatically attempt to reconnect a chardev if the connection fails. The chardev has to be kept around so a reconnect can be done on it.

---
 backends/baum.c         |   6 +-
 backends/msmouse.c      |   5 +-
 hw/misc/ivshmem.c       |  11 ++-
 include/sysemu/char.h   |   9 ++-
 include/ui/console.h    |   4 +-
 include/ui/qemu-spice.h |   9 ++-
 qemu-char.c             | 189 ++++++++++++++++++++++--------------------------
 spice-qemu-char.c       |  15 ++--
 ui/console.c            |  10 +--
 ui/gtk.c                |   5 +-
 10 files changed, 122 insertions(+), 141 deletions(-)
backends/baum.c +2 -4
index 1132899..9910a74 100644
@@ -561,10 +561,9 @@ static void baum_close(struct CharDriverState *chr)
     g_free(baum);
 }
 
-CharDriverState *chr_baum_init(void)
+CharDriverState *chr_baum_init(CharDriverState *chr)
 {
     BaumDriverState *baum;
-    CharDriverState *chr;
     brlapi_handle_t *handle;
 #ifdef CONFIG_SDL
     SDL_SysWMinfo info;
@@ -572,7 +571,7 @@ CharDriverState *chr_baum_init(void)
     int tty;
 
     baum = g_malloc0(sizeof(BaumDriverState));
-    baum->chr = chr = g_malloc0(sizeof(CharDriverState));
+    baum->chr = chr;
 
     chr->opaque = baum;
     chr->chr_write = baum_write;
@@ -618,7 +617,6 @@ fail:
     brlapi__closeConnection(handle);
 fail_handle:
     g_free(handle);
-    g_free(chr);
     g_free(baum);
     return NULL;
 }
backends/msmouse.c +1 -4
index c0dbfcd..b4e7a23 100644
@@ -63,11 +63,8 @@ static void msmouse_chr_close (struct CharDriverState *chr)
     g_free (chr);
 }
 
-CharDriverState *qemu_chr_open_msmouse(void)
+CharDriverState *qemu_chr_open_msmouse(CharDriverState *chr)
 {
-    CharDriverState *chr;
-
-    chr = g_malloc0(sizeof(CharDriverState));
     chr->chr_write = msmouse_chr_write;
     chr->chr_close = msmouse_chr_close;
     chr->explicit_be_open = true;
hw/misc/ivshmem.c +8 -3
index 8d144ba..75e83c3 100644
@@ -291,12 +291,17 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *
 {
     /* create a event character device based on the passed eventfd */
     IVShmemState *s = opaque;
-    CharDriverState * chr;
+    CharDriverState *chr, *newchr;
     int eventfd = event_notifier_get_fd(n);
 
-    chr = qemu_chr_open_eventfd(eventfd);
-
+    newchr = g_malloc0(sizeof(*chr));
+    if (newchr == NULL) {
+        fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
+        exit(-1);
+    }
+    chr = qemu_chr_open_eventfd(newchr, eventfd);
     if (chr == NULL) {
+        g_free(newchr);
         fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
         exit(-1);
     }
include/sysemu/char.h +5 -4
index b81a6ff..f6844dd 100644
@@ -292,21 +292,22 @@ CharDriverState *qemu_chr_find(const char *name);
 
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
-void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *));
+void register_char_driver(const char *name,
+        CharDriverState *(*open)(CharDriverState *, QemuOpts *));
 void register_char_driver_qapi(const char *name, ChardevBackendKind kind,
         void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp));
 
 /* add an eventfd to the qemu devices that are polled */
-CharDriverState *qemu_chr_open_eventfd(int eventfd);
+CharDriverState *qemu_chr_open_eventfd(CharDriverState *chr, int eventfd);
 
 extern int term_escape_char;
 
 CharDriverState *qemu_char_get_next_serial(void);
 
 /* msmouse */
-CharDriverState *qemu_chr_open_msmouse(void);
+CharDriverState *qemu_chr_open_msmouse(CharDriverState *chr);
 
 /* baum.c */
-CharDriverState *chr_baum_init(void);
+CharDriverState *chr_baum_init(CharDriverState *chr);
 
 #endif
include/ui/console.h +2 -2
index 4156a87..572a1ff 100644
@@ -299,9 +299,9 @@ void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
 DisplaySurface *qemu_console_surface(QemuConsole *con);
 DisplayState *qemu_console_displaystate(QemuConsole *console);
 
-typedef CharDriverState *(VcHandler)(ChardevVC *vc);
+typedef CharDriverState *(VcHandler)(CharDriverState *chr, ChardevVC *vc);
 
-CharDriverState *vc_init(ChardevVC *vc);
+CharDriverState *vc_init(CharDriverState *chr, ChardevVC *vc);
 void register_vc_handler(VcHandler *handler);
 
 /* sdl.c */
include/ui/qemu-spice.h +6 -3
index a93b4b2..15220a1 100644
@@ -48,12 +48,15 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
 void do_info_spice_print(Monitor *mon, const QObject *data);
 void do_info_spice(Monitor *mon, QObject **ret_data);
 
-CharDriverState *qemu_chr_open_spice_vmc(const char *type);
+CharDriverState *qemu_chr_open_spice_vmc(CharDriverState *chr,
+                                         const char *type);
 #if SPICE_SERVER_VERSION >= 0x000c02
-CharDriverState *qemu_chr_open_spice_port(const char *name);
+CharDriverState *qemu_chr_open_spice_port(CharDriverState *chr,
+                                          const char *name);
 void qemu_spice_register_ports(void);
 #else
-static inline CharDriverState *qemu_chr_open_spice_port(const char *name)
+static inline CharDriverState *qemu_chr_open_spice_port(CharDriverState *chr,
+                                                        const char *name)
 { return NULL; }
 #endif
 
qemu-char.c +88 -101

576 lines not on this page · raw message

spice-qemu-char.c +6 -9
index 6624559..c2f5375 100644
@@ -261,14 +261,11 @@ static void print_allowed_subtypes(void)
     fprintf(stderr, "\n");
 }
 
-static CharDriverState *chr_open(const char *subtype,
+static CharDriverState *chr_open(CharDriverState *chr, const char *subtype,
     void (*set_fe_open)(struct CharDriverState *, int))
-
 {
-    CharDriverState *chr;
     SpiceCharDriver *s;
 
-    chr = g_malloc0(sizeof(CharDriverState));
     s = g_malloc0(sizeof(SpiceCharDriver));
     s->chr = chr;
     s->active = false;
@@ -286,7 +283,7 @@ static CharDriverState *chr_open(const char *subtype,
     return chr;
 }
 
-CharDriverState *qemu_chr_open_spice_vmc(const char *type)
+CharDriverState *qemu_chr_open_spice_vmc(CharDriverState *chr, const char *type)
 {
     const char **psubtype = spice_server_char_device_recognized_subtypes();
 
@@ -306,13 +303,13 @@ CharDriverState *qemu_chr_open_spice_vmc(const char *type)
         return NULL;
     }
 
-    return chr_open(type, spice_vmc_set_fe_open);
+    return chr_open(chr, type, spice_vmc_set_fe_open);
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
-CharDriverState *qemu_chr_open_spice_port(const char *name)
+CharDriverState *qemu_chr_open_spice_port(CharDriverState *chr,
+                                          const char *name)
 {
-    CharDriverState *chr;
     SpiceCharDriver *s;
 
     if (name == NULL) {
@@ -320,7 +317,7 @@ CharDriverState *qemu_chr_open_spice_port(const char *name)
         return NULL;
     }
 
-    chr = chr_open("port", spice_port_set_fe_open);
+    chr = chr_open(chr, "port", spice_port_set_fe_open);
     s = chr->opaque;
     s->sin.portname = g_strdup(name);
 
ui/console.c +3 -7
index 502e160..0ac45c5 100644
@@ -1724,15 +1724,12 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
         chr->init(chr);
 }
 
-static CharDriverState *text_console_init(ChardevVC *vc)
+static CharDriverState *text_console_init(CharDriverState *chr, ChardevVC *vc)
 {
-    CharDriverState *chr;
     QemuConsole *s;
     unsigned width = 0;
     unsigned height = 0;
 
-    chr = g_malloc0(sizeof(CharDriverState));
-
     if (vc->has_width) {
         width = vc->width;
     } else if (vc->has_cols) {
@@ -1754,7 +1751,6 @@ static CharDriverState *text_console_init(ChardevVC *vc)
     }
 
     if (!s) {
-        g_free(chr);
         return NULL;
     }
 
@@ -1774,9 +1770,9 @@ static CharDriverState *text_console_init(ChardevVC *vc)
 
 static VcHandler *vc_handler = text_console_init;
 
-CharDriverState *vc_init(ChardevVC *vc)
+CharDriverState *vc_init(CharDriverState *chr, ChardevVC *vc)
 {
-    return vc_handler(vc);
+    return vc_handler(chr, vc);
 }
 
 void register_vc_handler(VcHandler *handler)
ui/gtk.c +1 -4
index a633d89..0ecc26a 100644
@@ -1125,11 +1125,8 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 static int nb_vcs;
 static CharDriverState *vcs[MAX_VCS];
 
-static CharDriverState *gd_vc_handler(ChardevVC *unused)
+static CharDriverState *gd_vc_handler(CharDriverState *chr, ChardevVC *unused)
 {
-    CharDriverState *chr;
-
-    chr = g_malloc0(sizeof(*chr));
     chr->chr_write = gd_vc_chr_write;
     /* defer OPENED events until our vc is fully initialized */
     chr->explicit_be_open = true;
Signature · 1 line
1.8.3.1
#2 lore raw

PATCH 2/7 qemu-char: Allow a chardev to reconnect if disconnected

In reply to [email protected]

From: Corey Minyard <[email protected]>

Allow a socket that connects to reconnect on a periodic basis if it fails to connect at startup or if the connection drops while in use.

---
 include/sysemu/char.h |  16 ++++-
 qemu-char.c           | 165 +++++++++++++++++++++++++++++++++++++++++++++-----
 qemu-options.hx       |  11 +++-
 3 files changed, 173 insertions(+), 19 deletions(-)
include/sysemu/char.h +15 -1
index f6844dd..1800c54 100644
@@ -53,6 +53,17 @@ typedef struct {
 
 typedef void IOEventHandler(void *opaque, int event);
 
+struct ReconData {
+    void *opaque;
+    uint64_t recon_time;
+    struct ReconHandlers *handlers;
+    void (*state_change)(void *open_opaque, int is_open);
+    void *state_change_opaque;
+    void (*reconnected)(struct ReconData *r);
+    void (*connection_lost)(struct ReconData *r);
+    void (*shutdown)(struct ReconData *r);
+};
+
 struct CharDriverState {
     void (*init)(struct CharDriverState *s);
     int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
@@ -82,6 +93,8 @@ struct CharDriverState {
     guint fd_in_tag;
     QemuOpts *opts;
     QTAILQ_ENTRY(CharDriverState) next;
+    GSList *backend;
+    struct ReconData *recon;
 };
 
 /**
@@ -293,7 +306,8 @@ CharDriverState *qemu_chr_find(const char *name);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
 void register_char_driver(const char *name,
-        CharDriverState *(*open)(CharDriverState *, QemuOpts *));
+        CharDriverState *(*open)(CharDriverState *, QemuOpts *),
+        int (*recon_setup)(CharDriverState *));
 void register_char_driver_qapi(const char *name, ChardevBackendKind kind,
         void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp));
 
qemu-char.c +150 -15

272 lines not on this page · raw message

qemu-options.hx +8 -3
index 56e5fdf..1002a3d 100644
@@ -1787,8 +1787,9 @@ ETEXI
 DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev null,id=id[,mux=on|off]\n"
     "-chardev socket,id=id[,host=host],port=host[,to=to][,ipv4][,ipv6][,nodelay]\n"
-    "         [,server][,nowait][,telnet][,mux=on|off] (tcp)\n"
-    "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] (unix)\n"
+    "         [,server][,nowait][,telnet][,mux=on|off][,reconnect=seconds] (tcp)\n"
+    "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,mux=on|off]\n"
+    "         [,reconnect=seconds] (unix)\n"
     "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
     "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
     "-chardev msmouse,id=id[,mux=on|off]\n"
@@ -1860,7 +1861,7 @@ Options to each backend are described below.
 A void device. This device will not emit any data, and will drop any data it
 receives. The null backend does not take any options.
 
-@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet]
+@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}]
 
 Create a two-way stream socket, which can be either a TCP or a unix socket. A
 unix socket will be created if @option{path} is specified. Behaviour is
@@ -1874,6 +1875,10 @@ connect to a listening socket.
 @option{telnet} specifies that traffic on the socket should interpret telnet
 escape sequences.
 
+@option{reconnect} specifies that if the client socket does not connect at
+startup, or if the client socket is closed for some reason (like the other
+end exited), wait the given number of seconds and attempt to reconnect.
+
 TCP and unix socket options are given below:
 
 @table @option
Signature · 1 line
1.8.3.1
#3 lore raw

Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected

Quoted 371 lines · On 2014/3/5 8:38, [email protected] wrote:

From: Corey Minyard <[email protected]>

Allow a socket that connects to reconnect on a periodic basis if it fails to connect at startup or if the connection drops while in use.

---
 include/sysemu/char.h |  16 ++++-
 qemu-char.c           | 165 +++++++++++++++++++++++++++++++++++++++++++++-----
 qemu-options.hx       |  11 +++-
 3 files changed, 173 insertions(+), 19 deletions(-)
include/sysemu/char.h +15 -1
index f6844dd..1800c54 100644
@@ -53,6 +53,17 @@ typedef struct {
 
 typedef void IOEventHandler(void *opaque, int event);
 
+struct ReconData {
+    void *opaque;
+    uint64_t recon_time;
+    struct ReconHandlers *handlers;
+    void (*state_change)(void *open_opaque, int is_open);
+    void *state_change_opaque;
+    void (*reconnected)(struct ReconData *r);
+    void (*connection_lost)(struct ReconData *r);
+    void (*shutdown)(struct ReconData *r);
+};
+
 struct CharDriverState {
     void (*init)(struct CharDriverState *s);
     int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
@@ -82,6 +93,8 @@ struct CharDriverState {
     guint fd_in_tag;
     QemuOpts *opts;
     QTAILQ_ENTRY(CharDriverState) next;
+    GSList *backend;
+    struct ReconData *recon;
 };
 
 /**
@@ -293,7 +306,8 @@ CharDriverState *qemu_chr_find(const char *name);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
 void register_char_driver(const char *name,
-        CharDriverState *(*open)(CharDriverState *, QemuOpts *));
+        CharDriverState *(*open)(CharDriverState *, QemuOpts *),
+        int (*recon_setup)(CharDriverState *));
 void register_char_driver_qapi(const char *name, ChardevBackendKind kind,
         void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp));
 
qemu-char.c +150 -15
index fc688cf..d9838aa 100644
@@ -88,6 +88,44 @@
 /***********************************************************/
 /* character device */
 
+struct GenericReconData {
+    QEMUTimer *recon_timer;
+};
+
+static void generic_recon_timeout(void *opaque)
+{
+    struct ReconData *r = opaque;
+    struct GenericReconData *d = r->opaque;
+
+    timer_mod(d->recon_timer,
+              (get_clock() + (r->recon_time * get_ticks_per_sec())));
+    r->state_change(r->state_change_opaque, 0);
+}
+
+static void generic_reconnected(struct ReconData *r)
+{
+    struct GenericReconData *d = r->opaque;
+
+    timer_del(d->recon_timer);
+}
+
+static void generic_connection_lost(struct ReconData *r)
+{
+    struct GenericReconData *d = r->opaque;
+
+    r->state_change(r->state_change_opaque, 1);
+    timer_mod(d->recon_timer,
+              (get_clock() + (r->recon_time * get_ticks_per_sec())));
+}
+
+static void generic_recon_shutdown(struct ReconData *r)
+{
+    struct GenericReconData *d = r->opaque;
+    timer_free(d->recon_timer);
+    free(d);
+    r->opaque = NULL;
+}
+
 static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
     QTAILQ_HEAD_INITIALIZER(chardevs);
 
@@ -96,9 +134,15 @@ void qemu_chr_be_event(CharDriverState *s, int event)
     /* Keep track if the char device is open */
     switch (event) {
         case CHR_EVENT_OPENED:
+            if (s->recon) {
+                s->recon->reconnected(s->recon);
+            }
             s->be_open = 1;
             break;
         case CHR_EVENT_CLOSED:
+            if (s->recon) {
+                s->recon->connection_lost(s->recon);
+            }
             s->be_open = 0;
             break;
     }
@@ -2582,6 +2626,7 @@ static void tcp_chr_close(CharDriverState *chr)
         closesocket(s->listen_fd);
     }
     g_free(s);
+    chr->opaque = NULL;
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -2641,8 +2686,6 @@ static CharDriverState *qemu_chr_open_socket_fd(CharDriverState *chr,
     chr->get_msgfd = tcp_get_msgfd;
     chr->chr_add_client = tcp_chr_add_client;
     chr->chr_add_watch = tcp_chr_add_watch;
-    /* be isn't opened until we get a connection */
-    chr->explicit_be_open = true;
 
     if (is_listen) {
         s->listen_fd = fd;
@@ -2680,6 +2723,9 @@ static CharDriverState *qemu_chr_open_socket(CharDriverState *chr,
     bool do_nodelay     = !qemu_opt_get_bool(opts, "delay", true);
     bool is_unix        = qemu_opt_get(opts, "path") != NULL;
 
+    /* be isn't opened until we get a connection */
+    chr->explicit_be_open = true;
+
     if (is_unix) {
         if (is_listen) {
             fd = unix_listen_opts(opts, &local_err);
@@ -2716,8 +2762,9 @@ static CharDriverState *qemu_chr_open_socket(CharDriverState *chr,
     if (fd >= 0) {
         closesocket(fd);
     }
-    if (chr) {
+    if (chr && chr->opaque) {
         g_free(chr->opaque);
+        chr->opaque = NULL;
     }
     return NULL;
 }
@@ -3128,6 +3175,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 
 typedef struct CharDriver {
     const char *name;
+    int (*recon_setup)(CharDriverState *);
     /* old, pre qapi */
     CharDriverState *(*open)(CharDriverState *chr, QemuOpts *opts);
     /* new, qapi-based */
@@ -3138,13 +3186,15 @@ typedef struct CharDriver {
 static GSList *backends;
 
 void register_char_driver(const char *name,
-                          CharDriverState *(*open)(CharDriverState*,QemuOpts *))
+                          CharDriverState *(*open)(CharDriverState*,QemuOpts *),
+                          int (*recon_setup)(CharDriverState *))
 {
     CharDriver *s;
 
     s = g_malloc0(sizeof(*s));
     s->name = g_strdup(name);
     s->open = open;
+    s->recon_setup = recon_setup;
 
     backends = g_slist_append(backends, s);
 }
@@ -3162,6 +3212,50 @@ void register_char_driver_qapi(const char *name, ChardevBackendKind kind,
     backends = g_slist_append(backends, s);
 }
 
+static void generic_recon_state_change(void *opaque, int is_open)
+{
+    struct CharDriverState *chr = opaque;
+
+    if (!is_open) {
+        struct CharDriver *cd = chr->backend->data;
+
+        if (chr->be_open) {
+            return;
+        }
+
+        cd->open(chr, chr->opts);
+    } else {
+        void (*chr_close)(struct CharDriverState *chr) = chr->chr_close;
+
+        if (chr_close) {
+            chr->chr_close = NULL;
+            chr_close(chr);
+        }
+    }
+}
+
+static int generic_recon_setup(struct CharDriverState *chr)
+{
+    struct GenericReconData *d;
+
+    d = g_malloc(sizeof(*d));
+    chr->recon->opaque = d;
+
+    chr->recon->reconnected = generic_reconnected;
+    chr->recon->connection_lost = generic_connection_lost;
+    chr->recon->shutdown = generic_recon_shutdown;
+    chr->recon->state_change = generic_recon_state_change;
+    chr->recon->state_change_opaque = chr;
+
+    d->recon_timer = timer_new(QEMU_CLOCK_REALTIME, SCALE_NS,
+                               generic_recon_timeout, chr->recon);
+
+    /* Make sure it connects in time. */
+    timer_mod(d->recon_timer,
+              (get_clock() + (chr->recon->recon_time * get_ticks_per_sec())));
+    return 1;
+}
+
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
                                     void (*init)(struct CharDriverState *s),
                                     Error **errp)
@@ -3169,6 +3263,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     CharDriver *cd;
     CharDriverState *chr;
     GSList *i;
+    uint64_t recon_time;
 
     if (qemu_opts_id(opts) == NULL) {
         error_setg(errp, "chardev: no id specified");
@@ -3244,11 +3339,41 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     }
 
     chr = g_malloc0(sizeof(CharDriverState));
-    chr = cd->open(chr, opts);
-    if (!chr) {
-        error_setg(errp, "chardev: opening backend \"%s\" failed",
-                   qemu_opt_get(opts, "backend"));
-        goto err;
+
+    chr->backend = i;
+    recon_time = qemu_opt_get_number(opts, "reconnect", 0);
+    if (recon_time) {
+        CharDriver *d = chr->backend->data;
+        if (!d->recon_setup) {
+            g_free(chr);
+            fprintf(stderr, "chardev: reconnect not supported on %s\n",
+                    qemu_opt_get(opts, "backend"));
+            return NULL;
+        }
+        if (qemu_opt_get_bool(opts, "server", 0)) {
+            g_free(chr);
+            fprintf(stderr, "chardev: server device cannot reconnect\n");
+            return NULL;
+        }
+        chr->opts = opts;
+        chr->recon = g_malloc(sizeof(*chr->recon));
+        chr->recon->recon_time = recon_time;
+        if (!d->recon_setup(chr)) {
+            g_free(chr->recon);
+            g_free(chr);
+            fprintf(stderr, "chardev: Unable to set up reconnect\n");
+            return NULL;
+        }
+    }
+
+    if (!cd->open(chr, opts)) {
+        if (!chr->recon) {
+            /* Reconnect is not enabled, give up */
+            fprintf(stderr, "chardev: opening backend \"%s\" failed\n",
+                    qemu_opt_get(opts, "backend"));
+            g_free(chr);
+            return NULL;
+        }
     }
 
     if (!chr->filename)
@@ -3267,7 +3392,8 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         int len = strlen(qemu_opts_id(opts)) + 6;
         base->label = g_malloc(len);
         snprintf(base->label, len, "%s-base", qemu_opts_id(opts));
-        chr = qemu_chr_open_mux(chr, base);
+        chr = g_malloc0(sizeof(CharDriverState));
+        qemu_chr_open_mux(chr, base);
         chr->filename = base->filename;
         chr->avail_connections = MAX_MUX;
         QTAILQ_INSERT_TAIL(&chardevs, chr, next);
@@ -3275,7 +3401,6 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         chr->avail_connections = 1;
     }
     chr->label = g_strdup(qemu_opts_id(opts));
-    chr->opts = opts;
     return chr;
 
 err:
@@ -3378,9 +3503,16 @@ void qemu_chr_fe_release(CharDriverState *s)
 
 void qemu_chr_delete(CharDriverState *chr)
 {
+    void (*chr_close)(struct CharDriverState *chr) = chr->chr_close;
+
     QTAILQ_REMOVE(&chardevs, chr, next);
-    if (chr->chr_close) {
-        chr->chr_close(chr);
+    if (chr_close) {
+        chr->chr_close = NULL;
+        chr_close(chr);
+    }
+    if (chr->recon) {
+        chr->recon->shutdown(chr->recon);
+        g_free(chr->recon);
     }
     g_free(chr->filename);
     g_free(chr->label);
@@ -3515,6 +3647,9 @@ QemuOptsList qemu_chardev_opts = {
             .name = "mux",
             .type = QEMU_OPT_BOOL,
         },{
+            .name = "reconnect",
+            .type = QEMU_OPT_NUMBER,
+        },{
             .name = "signal",
             .type = QEMU_OPT_BOOL,
         },{
@@ -3811,8 +3946,8 @@ void qmp_chardev_remove(const char *id, Error **errp)
 static void register_types(void)
 {
     register_char_driver_qapi("null", CHARDEV_BACKEND_KIND_NULL, NULL);
-    register_char_driver("socket", qemu_chr_open_socket);
-    register_char_driver("udp", qemu_chr_open_udp);
+    register_char_driver("socket", qemu_chr_open_socket, generic_recon_setup);
+    register_char_driver("udp", qemu_chr_open_udp, NULL);
     register_char_driver_qapi("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
                               qemu_chr_parse_ringbuf);
     register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE,
qemu-options.hx +4 -3
index 56e5fdf..1002a3d 100644
@@ -1787,8 +1787,9 @@ ETEXI
 DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev null,id=id[,mux=on|off]\n"
     "-chardev socket,id=id[,host=host],port=host[,to=to][,ipv4][,ipv6][,nodelay]\n"
-    "         [,server][,nowait][,telnet][,mux=on|off] (tcp)\n"
-    "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] (unix)\n"
+    "         [,server][,nowait][,telnet][,mux=on|off][,reconnect=seconds] (tcp)\n"
+    "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,mux=on|off]\n"
+    "         [,reconnect=seconds] (unix)\n"
     "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
     "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
     "-chardev msmouse,id=id[,mux=on|off]\n"
@@ -1860,7 +1861,7 @@ Options to each backend are described below.
 A void device. This device will not emit any data, and will drop any data it
 receives. The null backend does not take any options.
 
-@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet]
+@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}]
 
 Create a two-way stream socket, which can be either a TCP or a unix socket. A
 unix socket will be created if @option{path} is specified. Behaviour is

qemu-options.hx connect to a listening socket. · line 1884

Quoted 10 lines
qemu-options.hx +4 -0
@@ -1874,6 +1875,10 @@ connect to a listening socket.
 @option{telnet} specifies that traffic on the socket should interpret telnet
 escape sequences.
 
+@option{reconnect} specifies that if the client socket does not connect at
+startup, or if the client socket is closed for some reason (like the other
+end exited), wait the given number of seconds and attempt to reconnect.
+
 TCP and unix socket options are given below:
 
 @table @option

The client will reconnect for ever when the server is dead. Is it better that try to reconnect several times? Or add a option which specifies times of reconnect?

#4 lore raw

Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected

Quoted 9 lines · Weidong Huang

escape sequences.

+@option{reconnect} specifies that if the client socket does not connect at

qemu-options.hx +3 -0
+startup, or if the client socket is closed for some reason (like the other
+end exited), wait the given number of seconds and attempt to reconnect.
+
 TCP and unix socket options are given below:
 
 @table @option
Quoted 3 lines

The client will reconnect for ever when the server is dead. Is it better that try to reconnect several times? Or add a option which specifies times of reconnect?

I'm not really sure about this. For a remote IPMI BMC, you would want it to reconnect for forever, there's no point it having it stop trying after a while, since you want it to come back even if the BMC is down for a day. I would think the same if you wanted a remote console or something of that nature.

What's the use case where you would want it to stop trying? I can't think of any.

Thanks,

-corey

#5 lore raw

Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected

Quoted 19 lines · Corey Minyard
Quoted 9 lines · Weidong Huang

escape sequences.

+@option{reconnect} specifies that if the client socket does not connect at

qemu-options.hx +3 -0
+startup, or if the client socket is closed for some reason (like the other
+end exited), wait the given number of seconds and attempt to reconnect.
+
 TCP and unix socket options are given below:
 
 @table @option
Quoted 3 lines

The client will reconnect for ever when the server is dead. Is it better that try to reconnect several times? Or add a option which specifies times of reconnect?

I'm not really sure about this. For a remote IPMI BMC, you would want it to reconnect for forever, there's no point it having it stop trying after a while, since you want it to come back even if the BMC is down for a day. I would think the same if you wanted a remote console or something of that nature.

Quoted 6 lines

What's the use case where you would want it to stop trying? I can't think of any.

Thanks,

-corey

Depends on what happens after it fails. Imagine whoever was using the socket is gone. It might be useful to stop VM or exit after a while rather than just hang about.

Signature · 1 line
MST
#6 lore raw

Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected

In reply to [email protected]
Quoted 371 lines · On Tue, Mar 04, 2014 at 06:38:52PM -0600, [email protected] wrote:

From: Corey Minyard <[email protected]>

Allow a socket that connects to reconnect on a periodic basis if it fails to connect at startup or if the connection drops while in use.

---
 include/sysemu/char.h |  16 ++++-
 qemu-char.c           | 165 +++++++++++++++++++++++++++++++++++++++++++++-----
 qemu-options.hx       |  11 +++-
 3 files changed, 173 insertions(+), 19 deletions(-)
include/sysemu/char.h +15 -1
index f6844dd..1800c54 100644
@@ -53,6 +53,17 @@ typedef struct {
 
 typedef void IOEventHandler(void *opaque, int event);
 
+struct ReconData {
+    void *opaque;
+    uint64_t recon_time;
+    struct ReconHandlers *handlers;
+    void (*state_change)(void *open_opaque, int is_open);
+    void *state_change_opaque;
+    void (*reconnected)(struct ReconData *r);
+    void (*connection_lost)(struct ReconData *r);
+    void (*shutdown)(struct ReconData *r);
+};
+
 struct CharDriverState {
     void (*init)(struct CharDriverState *s);
     int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
@@ -82,6 +93,8 @@ struct CharDriverState {
     guint fd_in_tag;
     QemuOpts *opts;
     QTAILQ_ENTRY(CharDriverState) next;
+    GSList *backend;
+    struct ReconData *recon;
 };
 
 /**
@@ -293,7 +306,8 @@ CharDriverState *qemu_chr_find(const char *name);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
 void register_char_driver(const char *name,
-        CharDriverState *(*open)(CharDriverState *, QemuOpts *));
+        CharDriverState *(*open)(CharDriverState *, QemuOpts *),
+        int (*recon_setup)(CharDriverState *));
 void register_char_driver_qapi(const char *name, ChardevBackendKind kind,
         void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp));
 
qemu-char.c +150 -15
index fc688cf..d9838aa 100644
@@ -88,6 +88,44 @@
 /***********************************************************/
 /* character device */
 
+struct GenericReconData {
+    QEMUTimer *recon_timer;
+};
+
+static void generic_recon_timeout(void *opaque)
+{
+    struct ReconData *r = opaque;
+    struct GenericReconData *d = r->opaque;
+
+    timer_mod(d->recon_timer,
+              (get_clock() + (r->recon_time * get_ticks_per_sec())));
+    r->state_change(r->state_change_opaque, 0);
+}
+
+static void generic_reconnected(struct ReconData *r)
+{
+    struct GenericReconData *d = r->opaque;
+
+    timer_del(d->recon_timer);
+}
+
+static void generic_connection_lost(struct ReconData *r)
+{
+    struct GenericReconData *d = r->opaque;
+
+    r->state_change(r->state_change_opaque, 1);
+    timer_mod(d->recon_timer,
+              (get_clock() + (r->recon_time * get_ticks_per_sec())));
+}
+
+static void generic_recon_shutdown(struct ReconData *r)
+{
+    struct GenericReconData *d = r->opaque;
+    timer_free(d->recon_timer);
+    free(d);
+    r->opaque = NULL;
+}
+
 static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
     QTAILQ_HEAD_INITIALIZER(chardevs);
 
@@ -96,9 +134,15 @@ void qemu_chr_be_event(CharDriverState *s, int event)
     /* Keep track if the char device is open */
     switch (event) {
         case CHR_EVENT_OPENED:
+            if (s->recon) {
+                s->recon->reconnected(s->recon);
+            }
             s->be_open = 1;
             break;
         case CHR_EVENT_CLOSED:
+            if (s->recon) {
+                s->recon->connection_lost(s->recon);
+            }
             s->be_open = 0;
             break;
     }
@@ -2582,6 +2626,7 @@ static void tcp_chr_close(CharDriverState *chr)
         closesocket(s->listen_fd);
     }
     g_free(s);
+    chr->opaque = NULL;
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
@@ -2641,8 +2686,6 @@ static CharDriverState *qemu_chr_open_socket_fd(CharDriverState *chr,
     chr->get_msgfd = tcp_get_msgfd;
     chr->chr_add_client = tcp_chr_add_client;
     chr->chr_add_watch = tcp_chr_add_watch;
-    /* be isn't opened until we get a connection */
-    chr->explicit_be_open = true;
 
     if (is_listen) {
         s->listen_fd = fd;
@@ -2680,6 +2723,9 @@ static CharDriverState *qemu_chr_open_socket(CharDriverState *chr,
     bool do_nodelay     = !qemu_opt_get_bool(opts, "delay", true);
     bool is_unix        = qemu_opt_get(opts, "path") != NULL;
 
+    /* be isn't opened until we get a connection */
+    chr->explicit_be_open = true;
+
     if (is_unix) {
         if (is_listen) {
             fd = unix_listen_opts(opts, &local_err);
@@ -2716,8 +2762,9 @@ static CharDriverState *qemu_chr_open_socket(CharDriverState *chr,
     if (fd >= 0) {
         closesocket(fd);
     }
-    if (chr) {
+    if (chr && chr->opaque) {
         g_free(chr->opaque);
+        chr->opaque = NULL;
     }
     return NULL;
 }
@@ -3128,6 +3175,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
 
 typedef struct CharDriver {
     const char *name;
+    int (*recon_setup)(CharDriverState *);
     /* old, pre qapi */
     CharDriverState *(*open)(CharDriverState *chr, QemuOpts *opts);
     /* new, qapi-based */
@@ -3138,13 +3186,15 @@ typedef struct CharDriver {
 static GSList *backends;
 
 void register_char_driver(const char *name,
-                          CharDriverState *(*open)(CharDriverState*,QemuOpts *))
+                          CharDriverState *(*open)(CharDriverState*,QemuOpts *),
+                          int (*recon_setup)(CharDriverState *))
 {
     CharDriver *s;
 
     s = g_malloc0(sizeof(*s));
     s->name = g_strdup(name);
     s->open = open;
+    s->recon_setup = recon_setup;
 
     backends = g_slist_append(backends, s);
 }
@@ -3162,6 +3212,50 @@ void register_char_driver_qapi(const char *name, ChardevBackendKind kind,
     backends = g_slist_append(backends, s);
 }
 
+static void generic_recon_state_change(void *opaque, int is_open)
+{
+    struct CharDriverState *chr = opaque;
+
+    if (!is_open) {
+        struct CharDriver *cd = chr->backend->data;
+
+        if (chr->be_open) {
+            return;
+        }
+
+        cd->open(chr, chr->opts);
+    } else {
+        void (*chr_close)(struct CharDriverState *chr) = chr->chr_close;
+
+        if (chr_close) {
+            chr->chr_close = NULL;
+            chr_close(chr);
+        }
+    }
+}
+
+static int generic_recon_setup(struct CharDriverState *chr)
+{
+    struct GenericReconData *d;
+
+    d = g_malloc(sizeof(*d));
+    chr->recon->opaque = d;
+
+    chr->recon->reconnected = generic_reconnected;
+    chr->recon->connection_lost = generic_connection_lost;
+    chr->recon->shutdown = generic_recon_shutdown;
+    chr->recon->state_change = generic_recon_state_change;
+    chr->recon->state_change_opaque = chr;
+
+    d->recon_timer = timer_new(QEMU_CLOCK_REALTIME, SCALE_NS,
+                               generic_recon_timeout, chr->recon);
+
+    /* Make sure it connects in time. */
+    timer_mod(d->recon_timer,
+              (get_clock() + (chr->recon->recon_time * get_ticks_per_sec())));
+    return 1;
+}
+
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
                                     void (*init)(struct CharDriverState *s),
                                     Error **errp)
@@ -3169,6 +3263,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     CharDriver *cd;
     CharDriverState *chr;
     GSList *i;
+    uint64_t recon_time;
 
     if (qemu_opts_id(opts) == NULL) {
         error_setg(errp, "chardev: no id specified");
@@ -3244,11 +3339,41 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
     }
 
     chr = g_malloc0(sizeof(CharDriverState));
-    chr = cd->open(chr, opts);
-    if (!chr) {
-        error_setg(errp, "chardev: opening backend \"%s\" failed",
-                   qemu_opt_get(opts, "backend"));
-        goto err;
+
+    chr->backend = i;
+    recon_time = qemu_opt_get_number(opts, "reconnect", 0);
+    if (recon_time) {
+        CharDriver *d = chr->backend->data;
+        if (!d->recon_setup) {
+            g_free(chr);
+            fprintf(stderr, "chardev: reconnect not supported on %s\n",
+                    qemu_opt_get(opts, "backend"));
+            return NULL;
+        }
+        if (qemu_opt_get_bool(opts, "server", 0)) {
+            g_free(chr);
+            fprintf(stderr, "chardev: server device cannot reconnect\n");
+            return NULL;
+        }
+        chr->opts = opts;
+        chr->recon = g_malloc(sizeof(*chr->recon));
+        chr->recon->recon_time = recon_time;
+        if (!d->recon_setup(chr)) {
+            g_free(chr->recon);
+            g_free(chr);
+            fprintf(stderr, "chardev: Unable to set up reconnect\n");
+            return NULL;
+        }
+    }
+
+    if (!cd->open(chr, opts)) {
+        if (!chr->recon) {
+            /* Reconnect is not enabled, give up */
+            fprintf(stderr, "chardev: opening backend \"%s\" failed\n",
+                    qemu_opt_get(opts, "backend"));
+            g_free(chr);
+            return NULL;
+        }
     }
 
     if (!chr->filename)
@@ -3267,7 +3392,8 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         int len = strlen(qemu_opts_id(opts)) + 6;
         base->label = g_malloc(len);
         snprintf(base->label, len, "%s-base", qemu_opts_id(opts));
-        chr = qemu_chr_open_mux(chr, base);
+        chr = g_malloc0(sizeof(CharDriverState));
+        qemu_chr_open_mux(chr, base);
         chr->filename = base->filename;
         chr->avail_connections = MAX_MUX;
         QTAILQ_INSERT_TAIL(&chardevs, chr, next);
@@ -3275,7 +3401,6 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
         chr->avail_connections = 1;
     }
     chr->label = g_strdup(qemu_opts_id(opts));
-    chr->opts = opts;
     return chr;
 
 err:
@@ -3378,9 +3503,16 @@ void qemu_chr_fe_release(CharDriverState *s)
 
 void qemu_chr_delete(CharDriverState *chr)
 {
+    void (*chr_close)(struct CharDriverState *chr) = chr->chr_close;
+
     QTAILQ_REMOVE(&chardevs, chr, next);
-    if (chr->chr_close) {
-        chr->chr_close(chr);
+    if (chr_close) {
+        chr->chr_close = NULL;
+        chr_close(chr);
+    }
+    if (chr->recon) {
+        chr->recon->shutdown(chr->recon);
+        g_free(chr->recon);
     }
     g_free(chr->filename);
     g_free(chr->label);
@@ -3515,6 +3647,9 @@ QemuOptsList qemu_chardev_opts = {
             .name = "mux",
             .type = QEMU_OPT_BOOL,
         },{
+            .name = "reconnect",
+            .type = QEMU_OPT_NUMBER,
+        },{
             .name = "signal",
             .type = QEMU_OPT_BOOL,
         },{
@@ -3811,8 +3946,8 @@ void qmp_chardev_remove(const char *id, Error **errp)
 static void register_types(void)
 {
     register_char_driver_qapi("null", CHARDEV_BACKEND_KIND_NULL, NULL);
-    register_char_driver("socket", qemu_chr_open_socket);
-    register_char_driver("udp", qemu_chr_open_udp);
+    register_char_driver("socket", qemu_chr_open_socket, generic_recon_setup);
+    register_char_driver("udp", qemu_chr_open_udp, NULL);
     register_char_driver_qapi("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF,
                               qemu_chr_parse_ringbuf);
     register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE,
qemu-options.hx +4 -3
index 56e5fdf..1002a3d 100644
@@ -1787,8 +1787,9 @@ ETEXI
 DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev null,id=id[,mux=on|off]\n"
     "-chardev socket,id=id[,host=host],port=host[,to=to][,ipv4][,ipv6][,nodelay]\n"
-    "         [,server][,nowait][,telnet][,mux=on|off] (tcp)\n"
-    "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] (unix)\n"
+    "         [,server][,nowait][,telnet][,mux=on|off][,reconnect=seconds] (tcp)\n"
+    "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,mux=on|off]\n"
+    "         [,reconnect=seconds] (unix)\n"
     "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
     "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
     "-chardev msmouse,id=id[,mux=on|off]\n"
@@ -1860,7 +1861,7 @@ Options to each backend are described below.
 A void device. This device will not emit any data, and will drop any data it
 receives. The null backend does not take any options.
 
-@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet]
+@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}]
 
 Create a two-way stream socket, which can be either a TCP or a unix socket. A
 unix socket will be created if @option{path} is specified. Behaviour is

qemu-options.hx connect to a listening socket. · line 1884

Quoted 10 lines
qemu-options.hx +4 -0
@@ -1874,6 +1875,10 @@ connect to a listening socket.
 @option{telnet} specifies that traffic on the socket should interpret telnet
 escape sequences.
 
+@option{reconnect} specifies that if the client socket does not connect at
+startup, or if the client socket is closed for some reason (like the other
+end exited), wait the given number of seconds and attempt to reconnect.
+
 TCP and unix socket options are given below:
 
 @table @option

This looks useful but it bothers me that this new option can't be used together with others:
server and nowait, which limits its usefulness and would make it harder to extend in the future.

- I think that specifying a time-out should be separate from the option to retry.
E.g. it might be quite reasonable to retry connecting immediately.
- it seems to me that if we don't specify nowait, this
should stop the VM until reconnect.
- I think this can co-exist with server option.
E.g. if we detect that the other side closed the socket,
start listening again. Or even listen all the time
and accept if disconnected.

I'd like to suggest we generalize this a bit:

When we detect a disconnect, three thinkable things to do would be:
- keep going as if nothing happened (default for all sockets except monitor)
- exit qemu
(default for monitor)
- retry

When waiting for retry it seems reasonable to either stop the VM or keep going (nowait option).

When retrying, it seems reasonable to specify a timeout to make it fail.

All of the above seem to apply to server and client sockets, with or without nowait option.

For clients only, it might be somewhat useful to limit the network traffic caused by reconnects.

Thanks,
MST

Quoted 2 lines
Signature · 1 line
1.8.3.1
#7 lore raw

Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected

Quoted 13 lines · Michael S. Tsirkin

+@option{reconnect} specifies that if the client socket does not connect at

qemu-options.hx +3 -0
+startup, or if the client socket is closed for some reason (like the other
+end exited), wait the given number of seconds and attempt to reconnect.
+
 TCP and unix socket options are given below:
 
 @table @option

This looks useful but it bothers me that this new option can't be used together with others:
server and nowait, which limits its usefulness and would make it harder to extend in the future.

Quoted 3 lines

- I think that specifying a time-out should be separate from the option to retry.
E.g. it might be quite reasonable to retry connecting immediately.

Maybe, but that would basically disable qemu until the connection came back, because it would just be spinning trying to connect. That doesn't seem like a good idea to me.

Quoted 2 lines

- it seems to me that if we don't specify nowait, this should stop the VM until reconnect.

That's an interesting thought. Currently the option basically implies nowait. It's probably easy to add this, but I'm not sure of the use case. Anyone else have any opinions on this?

Quoted 4 lines

- I think this can co-exist with server option. E.g. if we detect that the other side closed the socket, start listening again. Or even listen all the time and accept if disconnected.

IIRC, that's what it does already, even without this patch. If in server mode, I believe you can disconnect and then reconnect a socket and it works fine. So I see no need for this function in server mode.

Quoted 10 lines

I'd like to suggest we generalize this a bit:

When we detect a disconnect, three thinkable things to do would be:
- keep going as if nothing happened (default for all sockets except monitor)
- exit qemu
(default for monitor)
- retry

There's also
- stop the VM until the socket reconnects.

I'm not sure why you would want to single out monitor as special. Especially if it is mux-ed into another connection.

Quoted 6 lines

When waiting for retry it seems reasonable to either stop the VM or keep going (nowait option).

When retrying, it seems reasonable to specify a timeout to make it fail.

I just don't see why you would want to do this. It means that the connection is gone until the VM starts back up again.

One thing I did think of was to add a monitor command to try to reconnect. Then I could understand giving up, since you can get into the monitor and kick it back off.

Thanks,

-corey

Quoted 11 lines

All of the above seem to apply to server and client sockets, with or without nowait option.

For clients only, it might be somewhat useful to limit the network traffic caused by reconnects.

Thanks,
MST

Quoted 2 lines
Signature · 1 line
1.8.3.1
#8 lore raw

Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected

Quoted 14 lines · Corey Minyard
Quoted 13 lines · Michael S. Tsirkin

+@option{reconnect} specifies that if the client socket does not connect at

qemu-options.hx +3 -0
+startup, or if the client socket is closed for some reason (like the other
+end exited), wait the given number of seconds and attempt to reconnect.
+
 TCP and unix socket options are given below:
 
 @table @option

This looks useful but it bothers me that this new option can't be used together with others:
server and nowait, which limits its usefulness and would make it harder to extend in the future.

Quoted 6 lines
Quoted 3 lines

- I think that specifying a time-out should be separate from the option to retry.
E.g. it might be quite reasonable to retry connecting immediately.

Maybe, but that would basically disable qemu until the connection came back, because it would just be spinning trying to connect.

Why spin? queue the request in bh.

Quoted 9 lines

That doesn't
seem like a good idea to me.

Quoted 2 lines

- it seems to me that if we don't specify nowait, this should stop the VM until reconnect.

That's an interesting thought. Currently the option basically implies nowait. It's probably easy to add this, but I'm not sure of the use case.

To avoid losing information that would have been sent there.

Quoted 21 lines

Anyone else have any opinions on this?

Quoted 4 lines

- I think this can co-exist with server option. E.g. if we detect that the other side closed the socket, start listening again. Or even listen all the time and accept if disconnected.

IIRC, that's what it does already, even without this patch. If in server mode, I believe you can disconnect and then reconnect a socket and it works fine. So I see no need for this function in server mode.

Quoted 10 lines

I'd like to suggest we generalize this a bit:

When we detect a disconnect, three thinkable things to do would be:
- keep going as if nothing happened (default for all sockets except monitor)
- exit qemu
(default for monitor)
- retry

Quoted 5 lines

There's also
- stop the VM until the socket reconnects.

I'm not sure why you would want to single out monitor as special. Especially if it is mux-ed into another connection.

it seems useful to make qemu exit when monitor closes. many programs behave this way, this makes sure if you lose connection,
the server does not hang around doing nothing. combine with keepalive socket option,
you get a way to make qemu exit if it loses networking, which just might help restore networking.

Quoted 9 lines
Quoted 6 lines

When waiting for retry it seems reasonable to either stop the VM or keep going (nowait option).

When retrying, it seems reasonable to specify a timeout to make it fail.

I just don't see why you would want to do this. It means that the connection is gone until the VM starts back up again.

e.g. if socket is used for serial, you might want to stop VM to avoid losing output.

Quoted 8 lines

One thing I did think of was to add a monitor command to try to reconnect. Then I could understand giving up, since you can get into the monitor and kick it back off.

Thanks,

-corey

if qemu exits on timeout, then that's also useful as a way of automatically cleaning up resources if you lose connection to qemu.
which might help host recover from e.g. oom situation, and become responsive again.

Quoted 11 lines
Quoted 11 lines

All of the above seem to apply to server and client sockets, with or without nowait option.

For clients only, it might be somewhat useful to limit the network traffic caused by reconnects.

Thanks,
MST

Quoted 2 lines
Signature · 1 line
1.8.3.1
#9 lore raw

PATCH 3/7 qemu-char: Wait until socket connect to report connected

In reply to [email protected]

From: Corey Minyard <[email protected]>

The socket code was reporting that a socket connection was connected as soon as the connect call was issued. If the connection failed, it would then report it was not connected. With the reconnect code, it's better to wait until the connection is actually operational before reporting that the socket is connected.

---
 qemu-char.c | 49 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 40 insertions(+), 9 deletions(-)
qemu-char.c +40 -9
index d9838aa..6d6dd36 100644
@@ -2310,9 +2310,10 @@ static CharDriverState *qemu_chr_open_udp(CharDriverState *chr, QemuOpts *opts)
 typedef struct {
 
     GIOChannel *chan, *listen_chan;
+    int waitsrc;
     guint listen_tag;
     int fd, listen_fd;
-    int connected;
+    enum { TCP_NOT_CONNECTED, TCP_WAITING_CONNECT, TCP_CONNECTED } state;
     int max_size;
     int do_telnetopt;
     int do_nodelay;
@@ -2325,7 +2326,7 @@ static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque
 static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     TCPCharDriver *s = chr->opaque;
-    if (s->connected) {
+    if (s->state == TCP_CONNECTED) {
         return io_channel_send(s->chan, buf, len);
     } else {
         /* XXX: indicate an error ? */
@@ -2337,7 +2338,7 @@ static int tcp_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
-    if (!s->connected)
+    if (s->state != TCP_CONNECTED)
         return 0;
     s->max_size = qemu_chr_be_can_write(chr);
     return s->max_size;
@@ -2482,7 +2483,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
-    if (!s->connected || s->max_size <= 0) {
+    if (s->state != TCP_CONNECTED || s->max_size <= 0) {
         return TRUE;
     }
     len = sizeof(buf);
@@ -2491,7 +2492,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;
+        s->state = TCP_NOT_CONNECTED;
         if (s->listen_chan) {
             s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
         }
@@ -2518,17 +2519,48 @@ CharDriverState *qemu_chr_open_eventfd(CharDriverState *chr, int eventfd)
 }
 #endif
 
-static void tcp_chr_connect(void *opaque)
+static gboolean tcp_wait_connect(GIOChannel *source,
+                                 GIOCondition condition,
+                                 gpointer opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
 
-    s->connected = 1;
+    if (s->state != TCP_WAITING_CONNECT) {
+        return FALSE;
+    }
+    if (condition & G_IO_ERR || condition & G_IO_HUP) {
+        /* The connected failed */
+        s->state = TCP_NOT_CONNECTED;
+        g_io_channel_unref(s->chan);
+        s->chan = NULL;
+        closesocket(s->fd);
+        s->fd = -1;
+        return FALSE;
+    }
+
+    s->state = TCP_CONNECTED;
     if (s->chan) {
         chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
                                            tcp_chr_read, chr);
     }
     qemu_chr_be_generic_open(chr);
+    return FALSE;
+}
+
+static void tcp_chr_connect(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+
+    s->state = TCP_WAITING_CONNECT;
+    if (s->chan) {
+        /* Wait until write becomes ready before reporting connected. */
+        s->waitsrc = g_io_add_watch(s->chan,
+                                    G_IO_OUT | G_IO_HUP | G_IO_ERR,
+                                    tcp_wait_connect,
+                                    chr);
+    }
 }
 
 #define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
@@ -2650,7 +2682,7 @@ static CharDriverState *qemu_chr_open_socket_fd(CharDriverState *chr,
 
     s = g_malloc0(sizeof(TCPCharDriver));
 
-    s->connected = 0;
+    s->state = TCP_NOT_CONNECTED;
     s->fd = -1;
     s->listen_fd = -1;
     s->msgfd = -1;
@@ -2695,7 +2727,6 @@ static CharDriverState *qemu_chr_open_socket_fd(CharDriverState *chr,
             s->do_telnetopt = 1;
         }
     } else {
-        s->connected = 1;
         s->fd = fd;
         socket_set_nodelay(fd);
         s->chan = io_channel_from_socket(s->fd);
Signature · 1 line
1.8.3.1
#10 lore raw

PATCH 4/7 qemu-char: remove free of chr from win_stdio_close

In reply to [email protected]

From: Corey Minyard <[email protected]>

This will result in a double free on close, because it's freed in qemu_chr_delete() right after calling the close function.

---
 qemu-char.c | 1 -
 1 file changed, 1 deletion(-)
qemu-char.c +0 -1
index 6d6dd36..4ac131d 100644
@@ -2105,7 +2105,6 @@ static void win_stdio_close(CharDriverState *chr)
     }
 
     g_free(chr->opaque);
-    g_free(chr);
 }
 
 static CharDriverState *qemu_chr_open_stdio(CharDriverState *chr,
Signature · 1 line
1.8.3.1
#11 lore raw

PATCH 5/7 qemu-char: Close fd at end of file

In reply to [email protected]

From: Corey Minyard <[email protected]>

The chardev backends that used qemu_chr_open_fd did not get their file descriptors closed at end of file or when the chardev was closed. This could result in a file descriptor leak.

---
 qemu-char.c | 35 +++++++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)
qemu-char.c +29 -6
index 4ac131d..427bb34 100644
@@ -851,6 +851,8 @@ typedef struct FDCharDriver {
     GIOChannel *fd_in, *fd_out;
     int max_size;
     QTAILQ_ENTRY(FDCharDriver) node;
+    int close_fdin;
+    int close_fdout;
 } FDCharDriver;
 
 static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
@@ -860,6 +862,18 @@ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return io_channel_send(s->fd_out, buf, len);
 }
 
+static void fd_close_fds(FDCharDriver *s)
+{
+    if ((s->close_fdin != s->close_fdout) && (s->close_fdout != -1)) {
+        close(s->close_fdout);
+    }
+    s->close_fdout = -1;
+    if (s->close_fdin != -1) {
+        close(s->close_fdin);
+    }
+    s->close_fdin = -1;
+}
+
 static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
@@ -881,6 +895,7 @@ static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
                                      len, &bytes_read, NULL);
     if (status == G_IO_STATUS_EOF) {
         remove_fd_in_watch(chr);
+        fd_close_fds(s);
         qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
         return FALSE;
     }
@@ -929,19 +944,27 @@ static void fd_chr_close(struct CharDriverState *chr)
         g_io_channel_unref(s->fd_out);
     }
 
+    fd_close_fds(s);
     g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
 /* open a character device to a unix fd */
 static CharDriverState *qemu_chr_open_fd(CharDriverState *chr,
-                                         int fd_in, int fd_out)
+                                         int fd_in, int fd_out,
+                                         int close_fds_on_close)
 {
     FDCharDriver *s;
 
     s = g_malloc0(sizeof(FDCharDriver));
     s->fd_in = io_channel_from_fd(fd_in);
     s->fd_out = io_channel_from_fd(fd_out);
+    if (close_fds_on_close) {
+        s->close_fdin = fd_in;
+        s->close_fdout = fd_out;
+    } else {
+        s->close_fdin = s->close_fdout = -1;
+    }
     fcntl(fd_out, F_SETFL, O_NONBLOCK);
     s->chr = chr;
     chr->opaque = s;
@@ -979,7 +1002,7 @@ static CharDriverState *qemu_chr_open_pipe(CharDriverState *chr,
             return NULL;
         }
     }
-    return qemu_chr_open_fd(chr, fd_in, fd_out);
+    return qemu_chr_open_fd(chr, fd_in, fd_out, TRUE);
 }
 
 /* init terminal so that we can grab keys */
@@ -1032,7 +1055,7 @@ static CharDriverState *qemu_chr_open_stdio(CharDriverState *chr,
     fcntl(0, F_SETFL, O_NONBLOCK);
     atexit(term_exit);
 
-    qemu_chr_open_fd(chr, 0, 1);
+    qemu_chr_open_fd(chr, 0, 1, FALSE);
     chr->chr_close = qemu_chr_close_stdio;
     chr->chr_set_echo = qemu_chr_set_echo_stdio;
     if (opts->has_signal) {
@@ -1438,7 +1461,7 @@ static void qemu_chr_close_tty(CharDriverState *chr)
 static CharDriverState *qemu_chr_open_tty_fd(CharDriverState *chr, int fd)
 {
     tty_serial_init(fd, 115200, 'N', 8, 1);
-    qemu_chr_open_fd(chr, fd, fd);
+    qemu_chr_open_fd(chr, fd, fd, TRUE);
     chr->chr_ioctl = tty_serial_ioctl;
     chr->chr_close = qemu_chr_close_tty;
     return chr;
@@ -2514,7 +2537,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
 #ifndef _WIN32
 CharDriverState *qemu_chr_open_eventfd(CharDriverState *chr, int eventfd)
 {
-    return qemu_chr_open_fd(chr, eventfd, eventfd);
+    return qemu_chr_open_fd(chr, eventfd, eventfd, FALSE);
 }
 #endif
 
@@ -3769,7 +3792,7 @@ static CharDriverState *qmp_chardev_open_file(CharDriverState *chr,
         }
     }
 
-    return qemu_chr_open_fd(chr, in, out);
+    return qemu_chr_open_fd(chr, in, out, TRUE);
 }
 
 static CharDriverState *qmp_chardev_open_serial(CharDriverState *chr,
Signature · 1 line
1.8.3.1
#12 lore raw

PATCH 6/7 qemu-char: Clean up error handling in qmp_chardev_add

In reply to [email protected]

From: Corey Minyard <[email protected]>

The open functions called by qmp_chardev_add could either return NULL or set errp to return an error. Modify to just return errp, as that can pass useful information where returning NULL cannot.

---
 backends/baum.c         |   6 +-
 backends/msmouse.c      |   4 +-
 hw/misc/ivshmem.c       |  11 +-
 include/sysemu/char.h   |  10 +-
 include/ui/console.h    |   4 +-
 include/ui/qemu-spice.h |  15 ++-
 qemu-char.c             | 336 ++++++++++++++++++++++--------------------------
 spice-qemu-char.c       |  15 ++-
 ui/console.c            |  10 +-
 ui/gtk.c                |   4 +-
 10 files changed, 190 insertions(+), 225 deletions(-)
backends/baum.c +3 -3
index 9910a74..56cd00f 100644
@@ -561,7 +561,7 @@ static void baum_close(struct CharDriverState *chr)
     g_free(baum);
 }
 
-CharDriverState *chr_baum_init(CharDriverState *chr)
+void chr_baum_init(CharDriverState *chr, Error **errp)
 {
     BaumDriverState *baum;
     brlapi_handle_t *handle;
@@ -610,15 +610,15 @@ CharDriverState *chr_baum_init(CharDriverState *chr)
 
     qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
 
-    return chr;
+    return;
 
 fail:
     timer_free(baum->cellCount_timer);
     brlapi__closeConnection(handle);
 fail_handle:
+    error_setg(errp, "Failed to initialize baum");
     g_free(handle);
     g_free(baum);
-    return NULL;
 }
 
 static void register_types(void)
backends/msmouse.c +1 -3
index b4e7a23..78998f9 100644
@@ -63,15 +63,13 @@ static void msmouse_chr_close (struct CharDriverState *chr)
     g_free (chr);
 }
 
-CharDriverState *qemu_chr_open_msmouse(CharDriverState *chr)
+void qemu_chr_open_msmouse(CharDriverState *chr, Error **errp)
 {
     chr->chr_write = msmouse_chr_write;
     chr->chr_close = msmouse_chr_close;
     chr->explicit_be_open = true;
 
     qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse");
-
-    return chr;
 }
 
 static void register_types(void)
hw/misc/ivshmem.c +3 -8
index 75e83c3..5813f79 100644
@@ -291,20 +291,15 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *
 {
     /* create a event character device based on the passed eventfd */
     IVShmemState *s = opaque;
-    CharDriverState *chr, *newchr;
+    CharDriverState *chr;
     int eventfd = event_notifier_get_fd(n);
 
-    newchr = g_malloc0(sizeof(*chr));
-    if (newchr == NULL) {
-        fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
-        exit(-1);
-    }
-    chr = qemu_chr_open_eventfd(newchr, eventfd);
+    chr = g_malloc0(sizeof(*chr));
     if (chr == NULL) {
-        g_free(newchr);
         fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
         exit(-1);
     }
+    qemu_chr_open_eventfd(chr, eventfd);
     qemu_chr_fe_claim_no_fail(chr);
 
     /* if MSI is supported we need multiple interrupts */
include/sysemu/char.h +5 -5
index 1800c54..8604041 100644
@@ -306,22 +306,22 @@ CharDriverState *qemu_chr_find(const char *name);
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
 
 void register_char_driver(const char *name,
-        CharDriverState *(*open)(CharDriverState *, QemuOpts *),
-        int (*recon_setup)(CharDriverState *));
+                          void (*open)(CharDriverState *, QemuOpts *, Error **),
+                          int (*recon_setup)(CharDriverState *));
 void register_char_driver_qapi(const char *name, ChardevBackendKind kind,
         void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp));
 
 /* add an eventfd to the qemu devices that are polled */
-CharDriverState *qemu_chr_open_eventfd(CharDriverState *chr, int eventfd);
+void qemu_chr_open_eventfd(CharDriverState *chr, int eventfd);
 
 extern int term_escape_char;
 
 CharDriverState *qemu_char_get_next_serial(void);
 
 /* msmouse */
-CharDriverState *qemu_chr_open_msmouse(CharDriverState *chr);
+void qemu_chr_open_msmouse(CharDriverState *chr, Error **errp);
 
 /* baum.c */
-CharDriverState *chr_baum_init(CharDriverState *chr);
+void chr_baum_init(CharDriverState *chr, Error **errp);
 
 #endif
include/ui/console.h +2 -2
index 572a1ff..18776d6 100644
@@ -299,9 +299,9 @@ void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
 DisplaySurface *qemu_console_surface(QemuConsole *con);
 DisplayState *qemu_console_displaystate(QemuConsole *console);
 
-typedef CharDriverState *(VcHandler)(CharDriverState *chr, ChardevVC *vc);
+typedef void (VcHandler)(CharDriverState *chr, ChardevVC *vc, Error **errp);
 
-CharDriverState *vc_init(CharDriverState *chr, ChardevVC *vc);
+void vc_init(CharDriverState *chr, ChardevVC *vc, Error **errp);
 void register_vc_handler(VcHandler *handler);
 
 /* sdl.c */
include/ui/qemu-spice.h +8 -7
index 15220a1..85b1be3 100644
@@ -48,16 +48,17 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
 void do_info_spice_print(Monitor *mon, const QObject *data);
 void do_info_spice(Monitor *mon, QObject **ret_data);
 
-CharDriverState *qemu_chr_open_spice_vmc(CharDriverState *chr,
-                                         const char *type);
+void qemu_chr_open_spice_vmc(CharDriverState *chr, const char *type,
+                             Error **errp);
 #if SPICE_SERVER_VERSION >= 0x000c02
-CharDriverState *qemu_chr_open_spice_port(CharDriverState *chr,
-                                          const char *name);
+void qemu_chr_open_spice_port(CharDriverState *chr, const char *name,
+                              Error **errp);
 void qemu_spice_register_ports(void);
 #else
-static inline CharDriverState *qemu_chr_open_spice_port(CharDriverState *chr,
-                                                        const char *name)
-{ return NULL; }
+static inline void qemu_chr_open_spice_port(CharDriverState *chr,
+                                            const char *name,
+                                            Error **errp)
+{ error_setg(errp, "open spice port not supported"); }
 #endif
 
 #else  /* CONFIG_SPICE */
qemu-char.c +154 -182

849 lines not on this page · raw message

spice-qemu-char.c +8 -7
index c2f5375..8d999f1 100644
@@ -261,8 +261,8 @@ static void print_allowed_subtypes(void)
     fprintf(stderr, "\n");
 }
 
-static CharDriverState *chr_open(CharDriverState *chr, const char *subtype,
-    void (*set_fe_open)(struct CharDriverState *, int))
+static void chr_open(CharDriverState *chr, const char *subtype,
+                     void (*set_fe_open)(struct CharDriverState *, int))
 {
     SpiceCharDriver *s;
 
@@ -279,18 +279,18 @@ static CharDriverState *chr_open(CharDriverState *chr, const char *subtype,
     chr->chr_fe_event = spice_chr_fe_event;
 
     QLIST_INSERT_HEAD(&spice_chars, s, next);
-
-    return chr;
 }
 
-CharDriverState *qemu_chr_open_spice_vmc(CharDriverState *chr, const char *type)
+void qemu_chr_open_spice_vmc(CharDriverState *chr, const char *type,
+                             Error *errp)
 {
     const char **psubtype = spice_server_char_device_recognized_subtypes();
 
     if (type == NULL) {
         fprintf(stderr, "spice-qemu-char: missing name parameter\n");
         print_allowed_subtypes();
-        return NULL;
+        error_setg(errp, "open spice vmc failed");
+        return;
     }
     for (; *psubtype != NULL; ++psubtype) {
         if (strcmp(type, *psubtype) == 0) {
@@ -300,7 +300,8 @@ CharDriverState *qemu_chr_open_spice_vmc(CharDriverState *chr, const char *type)
     if (*psubtype == NULL) {
         fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type);
         print_allowed_subtypes();
-        return NULL;
+        error_setg(errp, "open spice vmc failed");
+        return;
     }
 
     return chr_open(chr, type, spice_vmc_set_fe_open);
ui/console.c +5 -5
index 0ac45c5..a204ce2 100644
@@ -1724,7 +1724,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
         chr->init(chr);
 }
 
-static CharDriverState *text_console_init(CharDriverState *chr, ChardevVC *vc)
+static void text_console_init(CharDriverState *chr, ChardevVC *vc, Error **errp)
 {
     QemuConsole *s;
     unsigned width = 0;
@@ -1751,7 +1751,8 @@ static CharDriverState *text_console_init(CharDriverState *chr, ChardevVC *vc)
     }
 
     if (!s) {
-        return NULL;
+        error_setg(errp, "Unable to create a new vc console");
+        return;
     }
 
     s->chr = chr;
@@ -1765,14 +1766,13 @@ static CharDriverState *text_console_init(CharDriverState *chr, ChardevVC *vc)
     if (display_state) {
         text_console_do_init(chr, display_state);
     }
-    return chr;
 }
 
 static VcHandler *vc_handler = text_console_init;
 
-CharDriverState *vc_init(CharDriverState *chr, ChardevVC *vc)
+void vc_init(CharDriverState *chr, ChardevVC *vc, Error **errp)
 {
-    return vc_handler(chr, vc);
+    vc_handler(chr, vc, errp);
 }
 
 void register_vc_handler(VcHandler *handler)
ui/gtk.c +1 -3
index 0ecc26a..a970860 100644
@@ -1125,15 +1125,13 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 static int nb_vcs;
 static CharDriverState *vcs[MAX_VCS];
 
-static CharDriverState *gd_vc_handler(CharDriverState *chr, ChardevVC *unused)
+static void gd_vc_handler(CharDriverState *chr, ChardevVC *unused, Error **errp)
 {
     chr->chr_write = gd_vc_chr_write;
     /* defer OPENED events until our vc is fully initialized */
     chr->explicit_be_open = true;
 
     vcs[nb_vcs++] = chr;
-
-    return chr;
 }
 
 void early_gtk_display_init(void)
Signature · 1 line
1.8.3.1
#13 lore raw

PATCH 7/7 console: Don't use the console if NULL

In reply to [email protected]

From: Corey Minyard <[email protected]>

Two spots used an allocated console, even though new_console could return NULL. Check the return value first.

---
 ui/console.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
ui/console.c +6 -1
index a204ce2..8d4ca81 100644
@@ -1582,6 +1582,9 @@ QemuConsole *graphic_console_init(DeviceState *dev,
     ds = get_alloc_displaystate();
     trace_console_gfx_new();
     s = new_console(ds, GRAPHIC_CONSOLE);
+    if (!s) {
+        return NULL;
+    }
     s->hw_ops = hw_ops;
     s->hw = opaque;
     if (dev) {
@@ -1747,7 +1750,9 @@ static void text_console_init(CharDriverState *chr, ChardevVC *vc, Error **errp)
         s = new_console(NULL, TEXT_CONSOLE);
     } else {
         s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
-        s->surface = qemu_create_displaysurface(width, height);
+        if (s) {
+            s->surface = qemu_create_displaysurface(width, height);
+        }
     }
 
     if (!s) {
Signature · 1 line
1.8.3.1