On 2014/3/5 8:38, miny...@acm.org wrote: > From: Corey Minyard <cminy...@mvista.com> > > 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. > > Signed-off-by: Corey Minyard <cminy...@mvista.com> > --- > include/sysemu/char.h | 16 ++++- > qemu-char.c | 165 > +++++++++++++++++++++++++++++++++++++++++++++----- > qemu-options.hx | 11 +++- > 3 files changed, 173 insertions(+), 19 deletions(-) > > diff --git a/include/sysemu/char.h b/include/sysemu/char.h > index f6844dd..1800c54 100644 > --- a/include/sysemu/char.h > +++ b/include/sysemu/char.h > @@ -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)); > > diff --git a/qemu-char.c b/qemu-char.c > index fc688cf..d9838aa 100644 > --- a/qemu-char.c > +++ b/qemu-char.c > @@ -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, > diff --git a/qemu-options.hx b/qemu-options.hx > index 56e5fdf..1002a3d 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -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
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?