diff options
-rw-r--r-- | chardev/char.c | 83 | ||||
-rw-r--r-- | include/chardev/char.h | 9 | ||||
-rw-r--r-- | qapi-schema.json | 40 |
3 files changed, 132 insertions, 0 deletions
diff --git a/chardev/char.c b/chardev/char.c index 839eff6afd..d6b9d89e93 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -951,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; diff --git a/include/chardev/char.h b/include/chardev/char.h index 8a9ade4931..22fd734999 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -93,6 +93,15 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend); Chardev *qemu_chr_new(const char *label, const char *filename); /** + * @qemu_chr_change: + * + * Change an existing character backend + * + * @opts the new backend options + */ +void qemu_chr_change(QemuOpts *opts, Error **errp); + +/** * @qemu_chr_cleanup: * * Delete all chardevs (when leaving qemu) diff --git a/qapi-schema.json b/qapi-schema.json index 485767f1ab..ab438ead70 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -5090,6 +5090,46 @@ 'returns': 'ChardevReturn' } ## +# @chardev-change: +# +# Change a character device backend +# +# @id: the chardev's ID, must exist +# @backend: new backend type and parameters +# +# Returns: ChardevReturn. +# +# Since: 2.10 +# +# Example: +# +# -> { "execute" : "chardev-change", +# "arguments" : { "id" : "baz", +# "backend" : { "type" : "pty", "data" : {} } } } +# <- { "return": { "pty" : "/dev/pty/42" } } +# +# -> {"execute" : "chardev-change", +# "arguments" : { +# "id" : "charchannel2", +# "backend" : { +# "type" : "socket", +# "data" : { +# "addr" : { +# "type" : "unix" , +# "data" : { +# "path" : "/tmp/charchannel2.socket" +# } +# }, +# "server" : true, +# "wait" : false }}}} +# <- {"return": {}} +# +## +{ 'command': 'chardev-change', 'data': {'id' : 'str', + 'backend' : 'ChardevBackend' }, + 'returns': 'ChardevReturn' } + +## # @chardev-remove: # # Remove a character device backend |