From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:45262) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UhoYK-0002us-OJ for qemu-devel@nongnu.org; Wed, 29 May 2013 18:09:12 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UhoYE-0004UC-Ci for qemu-devel@nongnu.org; Wed, 29 May 2013 18:09:04 -0400 Received: from vms173023pub.verizon.net ([206.46.173.23]:37477) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UhoYE-0004Sz-4q for qemu-devel@nongnu.org; Wed, 29 May 2013 18:08:58 -0400 Received: from wf-rch.minyard.home ([unknown] [173.74.121.95]) by vms173023.mailsrvcs.net (Sun Java(tm) System Messaging Server 7u2-7.02 32bit (built Apr 16 2009)) with ESMTPA id <0MNK00517YU3PC10@vms173023.mailsrvcs.net> for qemu-devel@nongnu.org; Wed, 29 May 2013 17:08:36 -0500 (CDT) From: minyard@acm.org Date: Wed, 29 May 2013 17:07:58 -0500 Message-id: <1369865296-19584-3-git-send-email-minyard@acm.org> In-reply-to: <1369865296-19584-1-git-send-email-minyard@acm.org> References: <1369865296-19584-1-git-send-email-minyard@acm.org> Subject: [Qemu-devel] [PATCH 02/20] qemu-char: Allow a chardev to reconnect if disconnected List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Corey Minyard , openipmi-developer@lists.sourceforge.net From: Corey Minyard 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 --- include/sysemu/char.h | 3 ++ qemu-char.c | 84 +++++++++++++++++++++++++++++++++++++++++++------ qemu-options.hx | 11 +++++-- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/include/sysemu/char.h b/include/sysemu/char.h index a8931c6..122193b 100644 --- a/include/sysemu/char.h +++ b/include/sysemu/char.h @@ -79,6 +79,9 @@ struct CharDriverState { int avail_connections; QemuOpts *opts; QTAILQ_ENTRY(CharDriverState) next; + GSList *backend; + QEMUTimer *recon_timer; + uint64_t recon_time; }; /** diff --git a/qemu-char.c b/qemu-char.c index 8dc1e0d..2c34224 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -98,9 +98,22 @@ 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_timer) { + qemu_del_timer(s->recon_timer); + } s->be_open = 1; break; case CHR_EVENT_CLOSED: + if (s->recon_timer) { + void (*chr_close)(struct CharDriverState *chr) = s->chr_close; + if (chr_close) { + s->chr_close = NULL; + chr_close(s); + } + qemu_mod_timer(s->recon_timer, + (qemu_get_clock_ns(vm_clock) + + (s->recon_time * 1000000000ULL))); + } s->be_open = 0; break; } @@ -2649,6 +2662,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); } @@ -2788,8 +2802,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; } @@ -3214,6 +3229,21 @@ void register_char_driver_qapi(const char *name, int kind, backends = g_slist_append(backends, s); } +static void recon_timeout(void *opaque) +{ + CharDriverState *chr = opaque; + CharDriver *cd = chr->backend->data; + + if (chr->be_open) { + return; + } + + qemu_mod_timer(chr->recon_timer, + (qemu_get_clock_ns(vm_clock) + + (chr->recon_time * 1000000000ULL))); + cd->open(chr, chr->opts); +} + CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, void (*init)(struct CharDriverState *s), Error **errp) @@ -3293,11 +3323,36 @@ 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; + chr->recon_time = qemu_opt_get_number(opts, "reconnect", 0); + if (chr->recon_time) { + if (strcmp(qemu_opt_get(opts, "backend"), "socket") != 0) { + g_free(chr); + fprintf(stderr, "chardev: reconnect only supported on sockets\n"); + 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_timer = qemu_new_timer_ns(vm_clock, recon_timeout, chr); + + /* Make sure it connects in time. */ + qemu_mod_timer(chr->recon_timer, + (qemu_get_clock_ns(vm_clock) + + (chr->recon_time * 1000000000ULL))); + } + + if (!cd->open(chr, opts)) { + if (!chr->recon_time) { + /* Reconnect is not enabled, give up */ + fprintf(stderr, "chardev: opening backend \"%s\" failed\n", + qemu_opt_get(opts, "backend")); + return NULL; + } } if (!chr->filename) @@ -3310,7 +3365,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); @@ -3318,7 +3374,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: @@ -3414,9 +3469,15 @@ 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_timer) { + qemu_free_timer(chr->recon_timer); } g_free(chr->filename); g_free(chr->label); @@ -3532,6 +3593,9 @@ QemuOptsList qemu_chardev_opts = { .name = "mux", .type = QEMU_OPT_BOOL, },{ + .name = "reconnect", + .type = QEMU_OPT_NUMBER, + },{ .name = "signal", .type = QEMU_OPT_BOOL, },{ diff --git a/qemu-options.hx b/qemu-options.hx index bf94862..df5488f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1772,8 +1772,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" @@ -1845,7 +1846,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 @@ -1859,6 +1860,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 socket does not come up at startup, +or if the 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 -- 1.7.9.5