[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disc
From: |
minyard |
Subject: |
[Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected |
Date: |
Tue, 4 Mar 2014 18:38:52 -0600 |
From: Corey Minyard <address@hidden>
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 <address@hidden>
---
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.
address@hidden -chardev socket ,address@hidden address@hidden options} or
@var{unix options}] [,server] [,nowait] [,telnet]
address@hidden -chardev socket ,address@hidden address@hidden options} or
@var{unix options}] [,server] [,nowait] [,telnet] [,address@hidden
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.
address@hidden 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
--
1.8.3.1
[Qemu-devel] [PATCH 3/7] qemu-char: Wait until socket connect to report connected, minyard, 2014/03/04
[Qemu-devel] [PATCH 4/7] qemu-char: remove free of chr from win_stdio_close, minyard, 2014/03/04
[Qemu-devel] [PATCH 5/7] qemu-char: Close fd at end of file, minyard, 2014/03/04
[Qemu-devel] [PATCH 6/7] qemu-char: Clean up error handling in qmp_chardev_add, minyard, 2014/03/04