#include "sysemu/sysemu.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
+#include "qemu/qemu-print.h"
#include "chardev/char.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-char.h"
#include "qapi/qmp/qerror.h"
#include "sysemu/replay.h"
#include "qemu/help_option.h"
+#include "qemu/module.h"
#include "qemu/option.h"
+#include "qemu/id.h"
+#include "qemu/coroutine.h"
#include "chardev/char-mux.h"
return container_get(object_get_root(), "/chardevs");
}
-static void chr_be_event(Chardev *s, int event)
+static void chr_be_event(Chardev *s, QEMUChrEvent event)
{
CharBackend *be = s->be;
be->chr_event(be->opaque, event);
}
-void qemu_chr_be_event(Chardev *s, int event)
+void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
{
/* Keep track if the char device is open */
switch (event) {
case CHR_EVENT_CLOSED:
s->be_open = 0;
break;
+ case CHR_EVENT_BREAK:
+ case CHR_EVENT_MUX_IN:
+ case CHR_EVENT_MUX_OUT:
+ /* Ignore */
+ break;
}
CHARDEV_GET_CLASS(s)->chr_be_event(s, event);
retry:
res = cc->chr_write(s, buf + *offset, len - *offset);
if (res < 0 && errno == EAGAIN && write_all) {
- g_usleep(100);
+ if (qemu_in_coroutine()) {
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
+ } else {
+ g_usleep(100);
+ }
goto retry;
}
{
ChardevClass *cc = CHARDEV_GET_CLASS(s);
+ assert(qemu_chr_has_feature(s, QEMU_CHAR_FEATURE_GCONTEXT)
+ || !context);
s->gcontext = context;
if (cc->chr_update_read_handler) {
cc->chr_update_read_handler(s);
chr->logfd = -1;
qemu_mutex_init(&chr->chr_write_lock);
+
+ /*
+ * Assume if chr_update_read_handler is implemented it will
+ * take the updated gcontext into account.
+ */
+ if (CHARDEV_GET_CLASS(chr)->chr_update_read_handler) {
+ qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT);
+ }
+
}
static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
return 0;
}
-QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
+QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
+ bool permit_mux_mon)
{
char host[65], port[33], width[8], height[8];
int pos;
}
if (strstart(filename, "mon:", &p)) {
+ if (!permit_mux_mon) {
+ error_report("mon: isn't supported in this context");
+ return NULL;
+ }
filename = p;
qemu_opt_set(opts, "mux", "on", &error_abort);
if (strcmp(filename, "stdio") == 0) {
}
if (strstart(filename, "tcp:", &p) ||
strstart(filename, "telnet:", &p) ||
- strstart(filename, "tn3270:", &p)) {
+ strstart(filename, "tn3270:", &p) ||
+ strstart(filename, "websocket:", &p)) {
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
qemu_opt_set(opts, "telnet", "on", &error_abort);
} else if (strstart(filename, "tn3270:", &p)) {
qemu_opt_set(opts, "tn3270", "on", &error_abort);
+ } else if (strstart(filename, "websocket:", &p)) {
+ qemu_opt_set(opts, "websocket", "on", &error_abort);
}
return opts;
}
return opts;
}
+ error_report("'%s' is not a valid char driver", filename);
+
fail:
qemu_opts_del(opts);
return NULL;
{
GString *str = opaque;
- g_string_append_printf(str, "\n%s", name);
+ g_string_append_printf(str, "\n %s", name);
}
static const char *chardev_alias_translate(const char *name)
return backend;
}
-Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp)
+Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context,
+ Error **errp)
{
const ChardevClass *cc;
Chardev *chr = NULL;
chardev_name_foreach(help_string_append, str);
- error_report("Available chardev backend types: %s", str->str);
+ qemu_printf("Available chardev backend types: %s\n", str->str);
g_string_free(str, true);
return NULL;
}
chr = qemu_chardev_new(bid ? bid : id,
object_class_get_name(OBJECT_CLASS(cc)),
- backend, errp);
+ backend, context, errp);
if (chr == NULL) {
goto out;
backend->type = CHARDEV_BACKEND_KIND_MUX;
backend->u.mux.data = g_new0(ChardevMux, 1);
backend->u.mux.data->chardev = g_strdup(bid);
- mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, errp);
+ mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, context, errp);
if (mux == NULL) {
object_unparent(OBJECT(chr));
chr = NULL;
return chr;
}
-Chardev *qemu_chr_new_noreplay(const char *label, const char *filename)
+Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
+ bool permit_mux_mon, GMainContext *context)
{
const char *p;
Chardev *chr;
return qemu_chr_find(p);
}
- opts = qemu_chr_parse_compat(label, filename);
+ opts = qemu_chr_parse_compat(label, filename, permit_mux_mon);
if (!opts)
return NULL;
- chr = qemu_chr_new_from_opts(opts, &err);
- if (err) {
+ chr = qemu_chr_new_from_opts(opts, context, &err);
+ if (!chr) {
error_report_err(err);
+ goto out;
}
- if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
- monitor_init(chr, MONITOR_USE_READLINE);
+
+ if (qemu_opt_get_bool(opts, "mux", 0)) {
+ assert(permit_mux_mon);
+ monitor_init_hmp(chr, true, &err);
+ if (err) {
+ error_report_err(err);
+ object_unparent(OBJECT(chr));
+ chr = NULL;
+ goto out;
+ }
}
+
+out:
qemu_opts_del(opts);
return chr;
}
-Chardev *qemu_chr_new(const char *label, const char *filename)
+static Chardev *qemu_chr_new_permit_mux_mon(const char *label,
+ const char *filename,
+ bool permit_mux_mon,
+ GMainContext *context)
{
Chardev *chr;
- chr = qemu_chr_new_noreplay(label, filename);
+ chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon, context);
if (chr) {
if (replay_mode != REPLAY_MODE_NONE) {
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
return chr;
}
+Chardev *qemu_chr_new(const char *label, const char *filename,
+ GMainContext *context)
+{
+ return qemu_chr_new_permit_mux_mon(label, filename, false, context);
+}
+
+Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename,
+ GMainContext *context)
+{
+ return qemu_chr_new_permit_mux_mon(label, filename, true, context);
+}
+
static int qmp_query_chardev_foreach(Object *obj, void *data)
{
Chardev *chr = CHARDEV(obj);
},{
.name = "tls-creds",
.type = QEMU_OPT_STRING,
+ },{
+ .name = "tls-authz",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "websocket",
+ .type = QEMU_OPT_BOOL,
},{
.name = "width",
.type = QEMU_OPT_NUMBER,
},{
.name = "logappend",
.type = QEMU_OPT_BOOL,
+ },{
+ .name = "tight",
+ .type = QEMU_OPT_BOOL,
+ .def_value_str = "on",
+ },{
+ .name = "abstract",
+ .type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},
return set_bit(feature, chr->features);
}
-Chardev *qemu_chardev_new(const char *id, const char *typename,
- ChardevBackend *backend,
- Error **errp)
+static Chardev *chardev_new(const char *id, const char *typename,
+ ChardevBackend *backend,
+ GMainContext *gcontext,
+ Error **errp)
{
Object *obj;
Chardev *chr = NULL;
obj = object_new(typename);
chr = CHARDEV(obj);
chr->label = g_strdup(id);
+ chr->gcontext = gcontext;
qemu_char_open(chr, backend, &be_opened, &local_err);
if (local_err) {
}
if (id) {
- object_property_add_child(get_chardevs_root(), id, obj, &local_err);
- if (local_err) {
- goto end;
- }
+ object_property_add_child(get_chardevs_root(), id, obj);
object_unref(obj);
}
return chr;
}
+Chardev *qemu_chardev_new(const char *id, const char *typename,
+ ChardevBackend *backend,
+ GMainContext *gcontext,
+ Error **errp)
+{
+ g_autofree char *genid = NULL;
+
+ if (!id) {
+ genid = id_generate(ID_CHR);
+ id = genid;
+ }
+
+ return chardev_new(id, typename, backend, gcontext, errp);
+}
+
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
Error **errp)
{
return NULL;
}
- chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
- backend, errp);
+ chr = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
+ backend, NULL, errp);
if (!chr) {
return NULL;
}
return NULL;
}
- chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)),
- backend, errp);
+ chr_new = chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)),
+ backend, chr->gcontext, errp);
if (!chr_new) {
return NULL;
}
object_unparent(OBJECT(chr));
object_property_add_child(get_chardevs_root(), chr_new->label,
- OBJECT(chr_new), &error_abort);
+ OBJECT(chr_new));
object_unref(OBJECT(chr_new));
ret = g_new0(ChardevReturn, 1);