#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "monitor/monitor.h"
-#include "sysemu/sysemu.h"
+#include "monitor/qmp-helpers.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qemu/qemu-print.h"
#include "qemu/option.h"
#include "qemu/id.h"
#include "qemu/coroutine.h"
+#include "qemu/yank.h"
#include "chardev-internal.h"
}
}
if (*offset > 0) {
+ /*
+ * If some data was written by backend, we should
+ * only log what was actually written. This method
+ * may be invoked again to write the remaining
+ * method, thus we'll log the remainder at that time.
+ */
qemu_chr_write_log(s, buf, *offset);
+ } else if (res < 0) {
+ /*
+ * If a fatal error was reported by the backend,
+ * assume this method won't be invoked again with
+ * this buffer, so log it all right away.
+ */
+ qemu_chr_write_log(s, buf, len);
}
qemu_mutex_unlock(&s->chr_write_lock);
return be->chr_can_read(be->opaque);
}
-void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len)
+void qemu_chr_be_write_impl(Chardev *s, const uint8_t *buf, int len)
{
CharBackend *be = s->be;
}
}
-void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
+void qemu_chr_be_write(Chardev *s, const uint8_t *buf, int len)
{
if (qemu_chr_replay(s)) {
if (replay_mode == REPLAY_MODE_PLAY) {
/* Any ChardevCommon member would work */
ChardevCommon *common = backend ? backend->u.null.data : NULL;
- if (common && common->has_logfile) {
- int flags = O_WRONLY | O_CREAT;
+ if (common && common->logfile) {
+ int flags = O_WRONLY;
if (common->has_logappend &&
common->logappend) {
flags |= O_APPEND;
} else {
flags |= O_TRUNC;
}
- chr->logfd = qemu_open(common->logfile, flags, 0666);
+ chr->logfd = qemu_create(common->logfile, flags, 0666, errp);
if (chr->logfd < 0) {
- error_setg_errno(errp, errno,
- "Unable to open logfile %s",
- common->logfile);
return;
}
}
{
Chardev *chr = CHARDEV(obj);
+ chr->handover_yank_instance = false;
chr->logfd = -1;
qemu_mutex_init(&chr->chr_write_lock);
{
const char *logfile = qemu_opt_get(opts, "logfile");
- backend->has_logfile = logfile != NULL;
backend->logfile = g_strdup(logfile);
-
backend->has_logappend = true;
backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
}
if (object_class_is_abstract(oc)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
- "abstract device type");
+ "an abstract device type");
return NULL;
}
return cc;
}
-static const struct ChardevAlias {
- const char *typename;
- const char *alias;
-} chardev_alias_table[] = {
-#ifdef HAVE_CHARDEV_PARPORT
- { "parallel", "parport" },
-#endif
-#ifdef HAVE_CHARDEV_SERIAL
- { "serial", "tty" },
-#endif
-};
-
typedef struct ChadevClassFE {
void (*fn)(const char *name, void *opaque);
void *opaque;
}
static void
-chardev_name_foreach(void (*fn)(const char *name, void *opaque), void *opaque)
+chardev_name_foreach(void (*fn)(const char *name, void *opaque),
+ void *opaque)
{
ChadevClassFE fe = { .fn = fn, .opaque = opaque };
- int i;
object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe);
-
- for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) {
- fn(chardev_alias_table[i].alias, opaque);
- }
}
static void
g_string_append_printf(str, "\n %s", name);
}
-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;
ChardevBackend *backend = NULL;
- const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend"));
+ const char *name = qemu_opt_get(opts, "backend");
if (name == NULL) {
error_setg(errp, "chardev: \"%s\" missing backend",
const ChardevClass *cc;
Chardev *chr = NULL;
ChardevBackend *backend = NULL;
- const char *name = chardev_alias_translate(qemu_opt_get(opts, "backend"));
+ const char *name = qemu_opt_get(opts, "backend");
const char *id = qemu_opts_id(opts);
char *bid = NULL;
{
Chardev *chr = CHARDEV(obj);
ChardevInfoList **list = data;
- ChardevInfoList *info = g_malloc0(sizeof(*info));
+ ChardevInfo *value = g_malloc0(sizeof(*value));
- info->value = g_malloc0(sizeof(*info->value));
- info->value->label = g_strdup(chr->label);
- info->value->filename = g_strdup(chr->filename);
- info->value->frontend_open = chr->be && chr->be->fe_open;
+ value->label = g_strdup(chr->label);
+ value->filename = g_strdup(chr->filename);
+ value->frontend_open = chr->be && chr->be->fe_open;
- info->next = *list;
- *list = info;
+ QAPI_LIST_PREPEND(*list, value);
return 0;
}
qmp_prepend_backend(const char *name, void *opaque)
{
ChardevBackendInfoList **list = opaque;
- ChardevBackendInfoList *info = g_malloc0(sizeof(*info));
+ ChardevBackendInfo *value;
- info->value = g_malloc0(sizeof(*info->value));
- info->value->name = g_strdup(name);
- info->next = *list;
- *list = info;
+ value = g_new0(ChardevBackendInfo, 1);
+ value->name = g_strdup(name);
+ QAPI_LIST_PREPEND(*list, value);
}
ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
},{
.name = "delay",
.type = QEMU_OPT_BOOL,
+ },{
+ .name = "nodelay",
+ .type = QEMU_OPT_BOOL,
},{
.name = "reconnect",
.type = QEMU_OPT_NUMBER,
},{
.name = "logappend",
.type = QEMU_OPT_BOOL,
+ },{
+ .name = "mouse",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "clipboard",
+ .type = QEMU_OPT_BOOL,
+#ifdef CONFIG_LINUX
},{
.name = "tight",
.type = QEMU_OPT_BOOL,
},{
.name = "abstract",
.type = QEMU_OPT_BOOL,
+#endif
},
{ /* end of list */ }
},
static Chardev *chardev_new(const char *id, const char *typename,
ChardevBackend *backend,
GMainContext *gcontext,
+ bool handover_yank_instance,
Error **errp)
{
Object *obj;
bool be_opened = true;
assert(g_str_has_prefix(typename, "chardev-"));
+ assert(id);
obj = object_new(typename);
chr = CHARDEV(obj);
+ chr->handover_yank_instance = handover_yank_instance;
chr->label = g_strdup(id);
chr->gcontext = gcontext;
qemu_char_open(chr, backend, &be_opened, &local_err);
if (local_err) {
- goto end;
+ error_propagate(errp, local_err);
+ object_unref(obj);
+ return NULL;
}
if (!chr->filename) {
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
- if (id) {
- object_property_try_add_child(get_chardevs_root(), id, obj,
- &local_err);
- if (local_err) {
- goto end;
- }
- object_unref(obj);
- }
-
-end:
- if (local_err) {
- error_propagate(errp, local_err);
- object_unref(obj);
- return NULL;
- }
-
return chr;
}
GMainContext *gcontext,
Error **errp)
{
+ Chardev *chr;
g_autofree char *genid = NULL;
if (!id) {
id = genid;
}
- return chardev_new(id, typename, backend, gcontext, errp);
+ chr = chardev_new(id, typename, backend, gcontext, false, errp);
+ if (!chr) {
+ return NULL;
+ }
+
+ if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr),
+ errp)) {
+ object_unref(OBJECT(chr));
+ return NULL;
+ }
+ object_unref(OBJECT(chr));
+
+ return chr;
}
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
Error **errp)
{
+ ERRP_GUARD();
const ChardevClass *cc;
ChardevReturn *ret;
- Chardev *chr;
+ g_autoptr(Chardev) chr = NULL;
+
+ if (qemu_chr_find(id)) {
+ error_setg(errp, "Chardev with id '%s' already exists", id);
+ return NULL;
+ }
cc = char_get_class(ChardevBackendKind_str(backend->type), errp);
if (!cc) {
- return NULL;
+ goto err;
}
chr = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
- backend, NULL, errp);
+ backend, NULL, false, errp);
if (!chr) {
- return NULL;
+ goto err;
+ }
+
+ if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr),
+ errp)) {
+ goto err;
}
ret = g_new0(ChardevReturn, 1);
if (CHARDEV_IS_PTY(chr)) {
ret->pty = g_strdup(chr->filename + 4);
- ret->has_pty = true;
}
return ret;
+
+err:
+ error_prepend(errp, "Failed to add chardev '%s': ", id);
+ return NULL;
}
ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
Error **errp)
{
CharBackend *be;
- const ChardevClass *cc;
+ const ChardevClass *cc, *cc_new;
Chardev *chr, *chr_new;
bool closed_sent = false;
+ bool handover_yank_instance;
ChardevReturn *ret;
chr = qemu_chr_find(id);
return NULL;
}
- cc = char_get_class(ChardevBackendKind_str(backend->type), errp);
- if (!cc) {
+ cc = CHARDEV_GET_CLASS(chr);
+ cc_new = char_get_class(ChardevBackendKind_str(backend->type), errp);
+ if (!cc_new) {
return NULL;
}
- chr_new = chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)),
- backend, chr->gcontext, errp);
+ /*
+ * The new chardev should not register a yank instance if the current
+ * chardev has registered one already.
+ */
+ handover_yank_instance = cc->supports_yank && cc_new->supports_yank;
+
+ chr_new = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc_new)),
+ backend, chr->gcontext, handover_yank_instance, 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);
return NULL;
}
+ /* change successfull, clean up */
+ chr_new->handover_yank_instance = false;
+
+ /*
+ * When the old chardev is freed, it should not unregister the yank
+ * instance if the new chardev needs it.
+ */
+ chr->handover_yank_instance = handover_yank_instance;
+
object_unparent(OBJECT(chr));
object_property_add_child(get_chardevs_root(), chr_new->label,
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;
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
}
+bool qmp_add_client_char(int fd, bool has_skipauth, bool skipauth,
+ bool has_tls, bool tls, const char *protocol,
+ Error **errp)
+{
+ Chardev *s = qemu_chr_find(protocol);
+
+ if (!s) {
+ error_setg(errp, "protocol '%s' is invalid", protocol);
+ close(fd);
+ return false;
+ }
+ if (qemu_chr_add_client(s, fd) < 0) {
+ error_setg(errp, "failed to add client");
+ close(fd);
+ return false;
+ }
+ return true;
+}
+
/*
* Add a timeout callback for the chardev (in milliseconds), return
* the GSource object created. Please use this to add timeout hook for