aboutsummaryrefslogtreecommitdiff
path: root/chardev
diff options
context:
space:
mode:
Diffstat (limited to 'chardev')
-rw-r--r--chardev/char-fe.c16
-rw-r--r--chardev/char-mux.c1
-rw-r--r--chardev/char-socket.c2
-rw-r--r--chardev/char.c164
4 files changed, 152 insertions, 31 deletions
diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index 3f90f0567c..f3af6ae584 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -179,9 +179,21 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...)
Chardev *qemu_chr_fe_get_driver(CharBackend *be)
{
+ /* this is unsafe for the users that support chardev hotswap */
+ assert(be->chr_be_change == NULL);
return be->chr;
}
+bool qemu_chr_fe_backend_connected(CharBackend *be)
+{
+ return !!be->chr;
+}
+
+bool qemu_chr_fe_backend_open(CharBackend *be)
+{
+ return be->chr && be->chr->be_open;
+}
+
bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
{
int tag = 0;
@@ -216,7 +228,7 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del)
assert(b);
if (b->chr) {
- qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true);
+ qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, NULL, true);
if (b->chr->be == b) {
b->chr->be = NULL;
}
@@ -235,6 +247,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
IOCanReadHandler *fd_can_read,
IOReadHandler *fd_read,
IOEventHandler *fd_event,
+ BackendChangeHandler *be_change,
void *opaque,
GMainContext *context,
bool set_open)
@@ -258,6 +271,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
b->chr_can_read = fd_can_read;
b->chr_read = fd_read;
b->chr_event = fd_event;
+ b->chr_be_change = be_change;
b->opaque = opaque;
if (cc->chr_update_read_handler) {
cc->chr_update_read_handler(s, context);
diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index 08570b915e..4cda5e7458 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -278,6 +278,7 @@ void mux_chr_set_handlers(Chardev *chr, GMainContext *context)
mux_chr_can_read,
mux_chr_read,
mux_chr_event,
+ NULL,
chr,
context, true);
}
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index a050a686ea..1ae730a4cb 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -454,7 +454,9 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
return 0;
}
+ qio_channel_set_blocking(s->ioc, true, NULL);
size = tcp_chr_recv(chr, (void *) buf, len);
+ qio_channel_set_blocking(s->ioc, false, NULL);
if (size == 0) {
/* connection closed */
tcp_chr_disconnect(chr);
diff --git a/chardev/char.c b/chardev/char.c
index 2b679a2295..c34b44abc9 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -556,17 +556,23 @@ help_string_append(const char *name, void *opaque)
g_string_append_printf(str, "\n%s", name);
}
-Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
- Error **errp)
+static const char *chardev_alias_translate(const char *name)
+{
+ int i;
+ for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
+ if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) {
+ return chardev_alias_table[i].typename;
+ }
+ }
+ return name;
+}
+
+ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp)
{
Error *local_err = NULL;
const ChardevClass *cc;
- Chardev *chr;
- int i;
ChardevBackend *backend = NULL;
- const char *name = qemu_opt_get(opts, "backend");
- const char *id = qemu_opts_id(opts);
- char *bid = NULL;
+ const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend"));
if (name == NULL) {
error_setg(errp, "chardev: \"%s\" missing backend",
@@ -574,7 +580,40 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
return NULL;
}
- if (is_help_option(name)) {
+ cc = char_get_class(name, errp);
+ if (cc == NULL) {
+ return NULL;
+ }
+
+ backend = g_new0(ChardevBackend, 1);
+ backend->type = CHARDEV_BACKEND_KIND_NULL;
+
+ if (cc->parse) {
+ cc->parse(opts, backend, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ qapi_free_ChardevBackend(backend);
+ return NULL;
+ }
+ } else {
+ ChardevCommon *ccom = g_new0(ChardevCommon, 1);
+ qemu_chr_parse_common(opts, ccom);
+ backend->u.null.data = ccom; /* Any ChardevCommon member would work */
+ }
+
+ return backend;
+}
+
+Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp)
+{
+ const ChardevClass *cc;
+ Chardev *chr = NULL;
+ ChardevBackend *backend = NULL;
+ const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend"));
+ const char *id = qemu_opts_id(opts);
+ char *bid = NULL;
+
+ if (name && is_help_option(name)) {
GString *str = g_string_new("");
chardev_name_foreach(help_string_append, str);
@@ -589,38 +628,20 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
return NULL;
}
- for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
- if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) {
- name = chardev_alias_table[i].typename;
- break;
- }
+ backend = qemu_chr_parse_opts(opts, errp);
+ if (backend == NULL) {
+ return NULL;
}
cc = char_get_class(name, errp);
if (cc == NULL) {
- return NULL;
+ goto out;
}
- backend = g_new0(ChardevBackend, 1);
- backend->type = CHARDEV_BACKEND_KIND_NULL;
-
if (qemu_opt_get_bool(opts, "mux", 0)) {
bid = g_strdup_printf("%s-base", id);
}
- chr = NULL;
- if (cc->parse) {
- cc->parse(opts, backend, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto out;
- }
- } else {
- ChardevCommon *ccom = g_new0(ChardevCommon, 1);
- qemu_chr_parse_common(opts, ccom);
- backend->u.null.data = ccom; /* Any ChardevCommon member would work */
- }
-
chr = qemu_chardev_new(bid ? bid : id,
object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
@@ -930,6 +951,89 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
return ret;
}
+ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
+ Error **errp)
+{
+ CharBackend *be;
+ const ChardevClass *cc;
+ Chardev *chr, *chr_new;
+ bool closed_sent = false;
+ ChardevReturn *ret;
+
+ chr = qemu_chr_find(id);
+ if (!chr) {
+ error_setg(errp, "Chardev '%s' does not exist", id);
+ return NULL;
+ }
+
+ if (CHARDEV_IS_MUX(chr)) {
+ error_setg(errp, "Mux device hotswap not supported yet");
+ return NULL;
+ }
+
+ if (qemu_chr_replay(chr)) {
+ error_setg(errp,
+ "Chardev '%s' cannot be changed in record/replay mode", id);
+ return NULL;
+ }
+
+ be = chr->be;
+ if (!be) {
+ /* easy case */
+ object_unparent(OBJECT(chr));
+ return qmp_chardev_add(id, backend, errp);
+ }
+
+ if (!be->chr_be_change) {
+ error_setg(errp, "Chardev user does not support chardev hotswap");
+ return NULL;
+ }
+
+ cc = char_get_class(ChardevBackendKind_lookup[backend->type], errp);
+ if (!cc) {
+ return NULL;
+ }
+
+ chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)),
+ backend, errp);
+ if (!chr_new) {
+ return NULL;
+ }
+ chr_new->label = g_strdup(id);
+
+ if (chr->be_open && !chr_new->be_open) {
+ qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
+ closed_sent = true;
+ }
+
+ chr->be = NULL;
+ qemu_chr_fe_init(be, chr_new, &error_abort);
+
+ if (be->chr_be_change(be->opaque) < 0) {
+ error_setg(errp, "Chardev '%s' change failed", chr_new->label);
+ chr_new->be = NULL;
+ qemu_chr_fe_init(be, chr, &error_abort);
+ if (closed_sent) {
+ qemu_chr_be_event(chr, CHR_EVENT_OPENED);
+ }
+ object_unref(OBJECT(chr_new));
+ return NULL;
+ }
+
+ object_unparent(OBJECT(chr));
+ object_property_add_child(get_chardevs_root(), chr_new->label,
+ OBJECT(chr_new), &error_abort);
+ object_unref(OBJECT(chr_new));
+
+ ret = g_new0(ChardevReturn, 1);
+ if (CHARDEV_IS_PTY(chr_new)) {
+ ret->pty = g_strdup(chr_new->filename + 4);
+ ret->has_pty = true;
+ }
+
+ return ret;
+}
+
void qmp_chardev_remove(const char *id, Error **errp)
{
Chardev *chr;