summaryrefslogtreecommitdiff
path: root/chardev
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2017-05-05 17:04:07 +0100
committerStefan Hajnoczi <stefanha@redhat.com>2017-05-05 17:07:55 +0100
commitdd1559bb267becbb838de41132ef60771d183e5d (patch)
tree82524f290a879e29173cefab8c16d4de92f4904e /chardev
parentf03f9f0c10dcfadee5811d43240f0a6af230f1ce (diff)
parent79c8db5a13c0f4b73f930be0fcff9bbc1237c258 (diff)
downloadqemu-dd1559bb267becbb838de41132ef60771d183e5d.tar.gz
Merge remote-tracking branch 'elmarco/tags/chr-tests-pull-request' into staging
# gpg: Signature made Thu 04 May 2017 12:42:10 PM BST # gpg: using RSA key 0xDAE8E10975969CE5 # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * elmarco/tags/chr-tests-pull-request: (21 commits) tests: add /char/console test tests: add /char/udp test tests: add /char/socket test tests: add /char/file test tests: add /char/pipe test tests: add alias check in /char/ringbuf char-udp: flush as much buffer as possible char-socket: add 'connected' property char-socket: add 'addr' property char-socket: update local address after listen char-socket: introduce update_disconnected_filename() char: useless NULL check char: remove chardevs list char: remove qemu_chardev_add char: use /chardevs container instead of chardevs list vl: add todo note about root container cleanup char: add a /chardevs container container: don't leak container reference xen: use a better chardev type check mux: simplfy muxes_realize_done ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'chardev')
-rw-r--r--chardev/char-mux.c11
-rw-r--r--chardev/char-mux.h2
-rw-r--r--chardev/char-pty.c2
-rw-r--r--chardev/char-socket.c46
-rw-r--r--chardev/char-udp.c26
-rw-r--r--chardev/char.c148
6 files changed, 132 insertions, 103 deletions
diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index 5547a36a0a..37d42c65c6 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -114,7 +114,7 @@ static void mux_print_help(Chardev *chr)
}
}
-void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
+static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
{
CharBackend *be = d->backends[mux_nr];
@@ -222,9 +222,9 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
bool muxes_realized;
-static void mux_chr_event(void *opaque, int event)
+void mux_chr_send_all_event(Chardev *chr, int event)
{
- MuxChardev *d = MUX_CHARDEV(opaque);
+ MuxChardev *d = MUX_CHARDEV(chr);
int i;
if (!muxes_realized) {
@@ -237,6 +237,11 @@ static void mux_chr_event(void *opaque, int event)
}
}
+static void mux_chr_event(void *opaque, int event)
+{
+ mux_chr_send_all_event(CHARDEV(opaque), event);
+}
+
static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
{
MuxChardev *d = MUX_CHARDEV(s);
diff --git a/chardev/char-mux.h b/chardev/char-mux.h
index 9a2fffce91..3f41dfcfd2 100644
--- a/chardev/char-mux.h
+++ b/chardev/char-mux.h
@@ -58,6 +58,6 @@ typedef struct MuxChardev {
void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
void mux_set_focus(Chardev *chr, int focus);
-void mux_chr_send_event(MuxChardev *d, int mux_nr, int event);
+void mux_chr_send_all_event(Chardev *chr, int event);
#endif /* CHAR_MUX_H */
diff --git a/chardev/char-pty.c b/chardev/char-pty.c
index a6337be8aa..aa9d0cb2c3 100644
--- a/chardev/char-pty.c
+++ b/chardev/char-pty.c
@@ -185,7 +185,7 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
PtyChardev *s = PTY_CHARDEV(opaque);
s->open_tag = 0;
- qemu_chr_be_generic_open(chr);
+ qemu_chr_be_event(chr, CHR_EVENT_OPENED);
return FALSE;
}
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 175fb8c3ec..9d5f062c62 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -385,6 +385,15 @@ static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
}
}
+static void update_disconnected_filename(SocketChardev *s)
+{
+ Chardev *chr = CHARDEV(s);
+
+ g_free(chr->filename);
+ chr->filename = SocketAddress_to_str("disconnected:", s->addr,
+ s->is_listen, s->is_telnet);
+}
+
static void tcp_chr_disconnect(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
@@ -399,8 +408,7 @@ static void tcp_chr_disconnect(Chardev *chr)
s->listen_tag = qio_channel_add_watch(
QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
}
- chr->filename = SocketAddress_to_str("disconnected:", s->addr,
- s->is_listen, s->is_telnet);
+ update_disconnected_filename(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
if (s->reconnect_time) {
qemu_chr_socket_restart_timer(chr);
@@ -508,7 +516,7 @@ static void tcp_chr_connect(void *opaque)
tcp_chr_read,
chr, NULL);
}
- qemu_chr_be_generic_open(chr);
+ qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
static void tcp_chr_update_read_handler(Chardev *chr,
@@ -908,8 +916,7 @@ static void qmp_chardev_open_socket(Chardev *chr,
/* be isn't opened until we get a connection */
*be_opened = false;
- chr->filename = SocketAddress_to_str("disconnected:",
- addr, is_listen, is_telnet);
+ update_disconnected_filename(s);
if (is_listen) {
if (is_telnet || is_tn3270) {
@@ -937,6 +944,11 @@ static void qmp_chardev_open_socket(Chardev *chr,
if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
goto error;
}
+
+ qapi_free_SocketAddress(s->addr);
+ s->addr = socket_local_address(sioc->fd, errp);
+ update_disconnected_filename(s);
+
s->listen_ioc = sioc;
if (is_waitconnect &&
qemu_chr_wait_connected(chr, errp) < 0) {
@@ -1033,6 +1045,23 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
sock->addr = addr;
}
+static void
+char_socket_get_addr(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ SocketChardev *s = SOCKET_CHARDEV(obj);
+
+ visit_type_SocketAddress(v, name, &s->addr, errp);
+}
+
+static bool
+char_socket_get_connected(Object *obj, Error **errp)
+{
+ SocketChardev *s = SOCKET_CHARDEV(obj);
+
+ return s->connected;
+}
+
static void char_socket_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@@ -1048,6 +1077,13 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
cc->chr_add_client = tcp_chr_add_client;
cc->chr_add_watch = tcp_chr_add_watch;
cc->chr_update_read_handler = tcp_chr_update_read_handler;
+
+ object_class_property_add(oc, "addr", "SocketAddress",
+ char_socket_get_addr, NULL,
+ NULL, NULL, &error_abort);
+
+ object_class_property_add_bool(oc, "connected", char_socket_get_connected,
+ NULL, &error_abort);
}
static const TypeInfo char_socket_type_info = {
diff --git a/chardev/char-udp.c b/chardev/char-udp.c
index 804bd22efa..1958c36de4 100644
--- a/chardev/char-udp.c
+++ b/chardev/char-udp.c
@@ -51,6 +51,18 @@ static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
s->ioc, (const char *)buf, len, NULL);
}
+static void udp_chr_flush_buffer(UdpChardev *s)
+{
+ Chardev *chr = CHARDEV(s);
+
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ int n = MIN(s->max_size, s->bufcnt - s->bufptr);
+ qemu_chr_be_write(chr, &s->buf[s->bufptr], n);
+ s->bufptr += n;
+ s->max_size = qemu_chr_be_can_write(chr);
+ }
+}
+
static int udp_chr_read_poll(void *opaque)
{
Chardev *chr = CHARDEV(opaque);
@@ -61,11 +73,8 @@ static int udp_chr_read_poll(void *opaque)
/* If there were any stray characters in the queue process them
* first
*/
- while (s->max_size > 0 && s->bufptr < s->bufcnt) {
- qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
- s->bufptr++;
- s->max_size = qemu_chr_be_can_write(chr);
- }
+ udp_chr_flush_buffer(s);
+
return s->max_size;
}
@@ -85,13 +94,8 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
return FALSE;
}
s->bufcnt = ret;
-
s->bufptr = 0;
- while (s->max_size > 0 && s->bufptr < s->bufcnt) {
- qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
- s->bufptr++;
- s->max_size = qemu_chr_be_can_write(chr);
- }
+ udp_chr_flush_buffer(s);
return TRUE;
}
diff --git a/chardev/char.c b/chardev/char.c
index 309734f2b7..fadfedb836 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -42,8 +42,10 @@
/***********************************************************/
/* character device */
-static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
- QTAILQ_HEAD_INITIALIZER(chardevs);
+static Object *get_chardevs_root(void)
+{
+ return container_get(object_get_root(), "/chardevs");
+}
void qemu_chr_be_event(Chardev *s, int event)
{
@@ -66,12 +68,6 @@ void qemu_chr_be_event(Chardev *s, int event)
be->chr_event(be->opaque, event);
}
-void qemu_chr_be_generic_open(Chardev *s)
-{
- qemu_chr_be_event(s, CHR_EVENT_OPENED);
-}
-
-
/* Not reporting errors from writing to logfile, as logs are
* defined to be "best effort" only */
static void qemu_chr_fe_write_log(Chardev *s,
@@ -453,26 +449,24 @@ static const TypeInfo char_type_info = {
* mux will receive CHR_EVENT_OPENED notifications for the BE
* immediately.
*/
-static void muxes_realize_done(Notifier *notifier, void *unused)
+static int open_muxes(Object *child, void *opaque)
{
- Chardev *chr;
+ if (CHARDEV_IS_MUX(child)) {
+ /* send OPENED to all already-attached FEs */
+ mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED);
+ /* mark mux as OPENED so any new FEs will immediately receive
+ * OPENED event
+ */
+ qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED);
+ }
- QTAILQ_FOREACH(chr, &chardevs, next) {
- if (CHARDEV_IS_MUX(chr)) {
- MuxChardev *d = MUX_CHARDEV(chr);
- int i;
+ return 0;
+}
- /* send OPENED to all already-attached FEs */
- for (i = 0; i < d->mux_cnt; i++) {
- mux_chr_send_event(d, i, CHR_EVENT_OPENED);
- }
- /* mark mux as OPENED so any new FEs will immediately receive
- * OPENED event
- */
- qemu_chr_be_generic_open(chr);
- }
- }
+static void muxes_realize_done(Notifier *notifier, void *unused)
+{
muxes_realized = true;
+ object_child_foreach(get_chardevs_root(), open_muxes, NULL);
}
static Notifier muxes_realize_notify = {
@@ -581,7 +575,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
/* We're connecting to an already opened device, so let's make sure we
also get the open event */
if (s->be_open) {
- qemu_chr_be_generic_open(s);
+ qemu_chr_be_event(s, CHR_EVENT_OPENED);
}
}
@@ -774,7 +768,7 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
const char *logfile = qemu_opt_get(opts, "logfile");
backend->has_logfile = logfile != NULL;
- backend->logfile = logfile ? g_strdup(logfile) : NULL;
+ backend->logfile = g_strdup(logfile);
backend->has_logappend = true;
backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
@@ -809,26 +803,6 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp)
return cc;
}
-static Chardev *qemu_chardev_add(const char *id, const char *typename,
- ChardevBackend *backend, Error **errp)
-{
- Chardev *chr;
-
- chr = qemu_chr_find(id);
- if (chr) {
- error_setg(errp, "Chardev '%s' already exists", id);
- return NULL;
- }
-
- chr = qemu_chardev_new(id, typename, backend, errp);
- if (!chr) {
- return NULL;
- }
-
- QTAILQ_INSERT_TAIL(&chardevs, chr, next);
- return chr;
-}
-
static const struct ChardevAlias {
const char *typename;
const char *alias;
@@ -945,9 +919,10 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
backend->u.null.data = ccom; /* Any ChardevCommon member would work */
}
- chr = qemu_chardev_add(bid ? bid : id,
+ chr = qemu_chardev_new(bid ? bid : id,
object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
+
if (chr == NULL) {
goto out;
}
@@ -959,9 +934,9 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
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_add(id, TYPE_CHARDEV_MUX, backend, errp);
+ mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, errp);
if (mux == NULL) {
- qemu_chr_delete(chr);
+ object_unparent(OBJECT(chr));
chr = NULL;
goto out;
}
@@ -1075,27 +1050,29 @@ void qemu_chr_fe_disconnect(CharBackend *be)
}
}
-void qemu_chr_delete(Chardev *chr)
+static int qmp_query_chardev_foreach(Object *obj, void *data)
{
- QTAILQ_REMOVE(&chardevs, chr, next);
- object_unref(OBJECT(chr));
+ Chardev *chr = CHARDEV(obj);
+ ChardevInfoList **list = data;
+ ChardevInfoList *info = g_malloc0(sizeof(*info));
+
+ 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;
+
+ info->next = *list;
+ *list = info;
+
+ return 0;
}
ChardevInfoList *qmp_query_chardev(Error **errp)
{
ChardevInfoList *chr_list = NULL;
- Chardev *chr;
- QTAILQ_FOREACH(chr, &chardevs, next) {
- ChardevInfoList *info = g_malloc0(sizeof(*info));
- 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;
-
- info->next = chr_list;
- chr_list = info;
- }
+ object_child_foreach(get_chardevs_root(),
+ qmp_query_chardev_foreach, &chr_list);
return chr_list;
}
@@ -1123,14 +1100,9 @@ ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
Chardev *qemu_chr_find(const char *name)
{
- Chardev *chr;
+ Object *obj = object_resolve_path_component(get_chardevs_root(), name);
- QTAILQ_FOREACH(chr, &chardevs, next) {
- if (strcmp(chr->label, name) != 0)
- continue;
- return chr;
- }
- return NULL;
+ return obj ? CHARDEV(obj) : NULL;
}
QemuOptsList qemu_chardev_opts = {
@@ -1243,22 +1215,23 @@ void qemu_chr_set_feature(Chardev *chr,
}
Chardev *qemu_chardev_new(const char *id, const char *typename,
- ChardevBackend *backend, Error **errp)
+ ChardevBackend *backend,
+ Error **errp)
{
+ Object *obj;
Chardev *chr = NULL;
Error *local_err = NULL;
bool be_opened = true;
assert(g_str_has_prefix(typename, "chardev-"));
- chr = CHARDEV(object_new(typename));
+ obj = object_new(typename);
+ chr = CHARDEV(obj);
chr->label = g_strdup(id);
qemu_char_open(chr, backend, &be_opened, &local_err);
if (local_err) {
- error_propagate(errp, local_err);
- object_unref(OBJECT(chr));
- return NULL;
+ goto end;
}
if (!chr->filename) {
@@ -1268,6 +1241,21 @@ Chardev *qemu_chardev_new(const char *id, const char *typename,
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
+ if (id) {
+ object_property_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;
}
@@ -1283,7 +1271,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
return NULL;
}
- chr = qemu_chardev_add(id, object_class_get_name(OBJECT_CLASS(cc)),
+ chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
backend, errp);
if (!chr) {
return NULL;
@@ -1316,16 +1304,12 @@ void qmp_chardev_remove(const char *id, Error **errp)
"Chardev '%s' cannot be unplugged in record/replay mode", id);
return;
}
- qemu_chr_delete(chr);
+ object_unparent(OBJECT(chr));
}
void qemu_chr_cleanup(void)
{
- Chardev *chr, *tmp;
-
- QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
- qemu_chr_delete(chr);
- }
+ object_unparent(get_chardevs_root());
}
static void register_types(void)