-----BEGIN PGP SIGNATURE-----
iQIcBAABAgAGBQJZCxOSAAoJENro4Ql1lpzlGagQAIoff4XMbbh2l5eFobcv00hn JdVT4u/Ie/Y1CTjOr8lhtgkKolA+cxfynlXMJN2pW8RP7W4J8Xo7SBpO84OpEKrc BGUmn4BSJQM6oOM5/YaITUBvfN+ZUFJMWwlBbHNTpF54jYb0y7UqbA8G/pJV8lAm ivo6wDrlwaOQLVs9RZWIZclVQOoCQ4VdTGwra9yKFf9BqtorSOs1BsDGvNprKLkw 0B5aRcTfBVS1qv7C/eGqOwigmCyE8s1h+HN2FTm7FIxYWiztRBezv76Yt+sV721w MvXPn9XSxHqhNnml2hsyEgKMblnutdRooKbdSesgVLKFnlt290WuDCOhj02phNXx qj8wnJ49nt8VzuPLUON5PKDnyhJrNHr+Hw0/YlneBZvjOhBEqX9BxSMvz8t1sTYC eAxQXrd+pFdUzLjv1HRrLBLfh8PUPtCluKkhGDh6MznXXzEbvZEGSeVCnLqy62w7 4gKxwinwfpod/1HFPTgfoFQBZzGzhTN7CjM6kh6PsIluTgsW0ROA2oVJSosVmv1P QfONhnGZJJf/yjkTjea40qHbJftBsVs9UiOHZ4PZ1QYgX4ZOj+FKaj1l5XYi6AG/ FNWWpKtelyf/YlU2ogFiRTYY7Y9BTITfk/K4vpOMRlQ9XFLf45z6DP1PRqCKx2Ub fOuWoPQOHLtyeAx8lrJM =rxGe -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
dd1559bb26
@ -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];
|
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;
|
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;
|
int i;
|
||||||
|
|
||||||
if (!muxes_realized) {
|
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)
|
static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
|
||||||
{
|
{
|
||||||
MuxChardev *d = MUX_CHARDEV(s);
|
MuxChardev *d = MUX_CHARDEV(s);
|
||||||
|
@ -58,6 +58,6 @@ typedef struct MuxChardev {
|
|||||||
|
|
||||||
void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
|
void mux_chr_set_handlers(Chardev *chr, GMainContext *context);
|
||||||
void mux_set_focus(Chardev *chr, int focus);
|
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 */
|
#endif /* CHAR_MUX_H */
|
||||||
|
@ -185,7 +185,7 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque)
|
|||||||
PtyChardev *s = PTY_CHARDEV(opaque);
|
PtyChardev *s = PTY_CHARDEV(opaque);
|
||||||
|
|
||||||
s->open_tag = 0;
|
s->open_tag = 0;
|
||||||
qemu_chr_be_generic_open(chr);
|
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
static void tcp_chr_disconnect(Chardev *chr)
|
||||||
{
|
{
|
||||||
SocketChardev *s = SOCKET_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(
|
s->listen_tag = qio_channel_add_watch(
|
||||||
QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
|
QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
|
||||||
}
|
}
|
||||||
chr->filename = SocketAddress_to_str("disconnected:", s->addr,
|
update_disconnected_filename(s);
|
||||||
s->is_listen, s->is_telnet);
|
|
||||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||||
if (s->reconnect_time) {
|
if (s->reconnect_time) {
|
||||||
qemu_chr_socket_restart_timer(chr);
|
qemu_chr_socket_restart_timer(chr);
|
||||||
@ -508,7 +516,7 @@ static void tcp_chr_connect(void *opaque)
|
|||||||
tcp_chr_read,
|
tcp_chr_read,
|
||||||
chr, NULL);
|
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,
|
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 isn't opened until we get a connection */
|
||||||
*be_opened = false;
|
*be_opened = false;
|
||||||
|
|
||||||
chr->filename = SocketAddress_to_str("disconnected:",
|
update_disconnected_filename(s);
|
||||||
addr, is_listen, is_telnet);
|
|
||||||
|
|
||||||
if (is_listen) {
|
if (is_listen) {
|
||||||
if (is_telnet || is_tn3270) {
|
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) {
|
if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qapi_free_SocketAddress(s->addr);
|
||||||
|
s->addr = socket_local_address(sioc->fd, errp);
|
||||||
|
update_disconnected_filename(s);
|
||||||
|
|
||||||
s->listen_ioc = sioc;
|
s->listen_ioc = sioc;
|
||||||
if (is_waitconnect &&
|
if (is_waitconnect &&
|
||||||
qemu_chr_wait_connected(chr, errp) < 0) {
|
qemu_chr_wait_connected(chr, errp) < 0) {
|
||||||
@ -1033,6 +1045,23 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
|||||||
sock->addr = addr;
|
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)
|
static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
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_client = tcp_chr_add_client;
|
||||||
cc->chr_add_watch = tcp_chr_add_watch;
|
cc->chr_add_watch = tcp_chr_add_watch;
|
||||||
cc->chr_update_read_handler = tcp_chr_update_read_handler;
|
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 = {
|
static const TypeInfo char_socket_type_info = {
|
||||||
|
@ -51,6 +51,18 @@ static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
|||||||
s->ioc, (const char *)buf, len, NULL);
|
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)
|
static int udp_chr_read_poll(void *opaque)
|
||||||
{
|
{
|
||||||
Chardev *chr = CHARDEV(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
|
/* If there were any stray characters in the queue process them
|
||||||
* first
|
* first
|
||||||
*/
|
*/
|
||||||
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
udp_chr_flush_buffer(s);
|
||||||
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
|
||||||
s->bufptr++;
|
|
||||||
s->max_size = qemu_chr_be_can_write(chr);
|
|
||||||
}
|
|
||||||
return s->max_size;
|
return s->max_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,13 +94,8 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
s->bufcnt = ret;
|
s->bufcnt = ret;
|
||||||
|
|
||||||
s->bufptr = 0;
|
s->bufptr = 0;
|
||||||
while (s->max_size > 0 && s->bufptr < s->bufcnt) {
|
udp_chr_flush_buffer(s);
|
||||||
qemu_chr_be_write(chr, &s->buf[s->bufptr], 1);
|
|
||||||
s->bufptr++;
|
|
||||||
s->max_size = qemu_chr_be_can_write(chr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
150
chardev/char.c
150
chardev/char.c
@ -42,8 +42,10 @@
|
|||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* character device */
|
/* character device */
|
||||||
|
|
||||||
static QTAILQ_HEAD(ChardevHead, Chardev) chardevs =
|
static Object *get_chardevs_root(void)
|
||||||
QTAILQ_HEAD_INITIALIZER(chardevs);
|
{
|
||||||
|
return container_get(object_get_root(), "/chardevs");
|
||||||
|
}
|
||||||
|
|
||||||
void qemu_chr_be_event(Chardev *s, int event)
|
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);
|
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
|
/* Not reporting errors from writing to logfile, as logs are
|
||||||
* defined to be "best effort" only */
|
* defined to be "best effort" only */
|
||||||
static void qemu_chr_fe_write_log(Chardev *s,
|
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
|
* mux will receive CHR_EVENT_OPENED notifications for the BE
|
||||||
* immediately.
|
* immediately.
|
||||||
*/
|
*/
|
||||||
|
static int open_muxes(Object *child, void *opaque)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void muxes_realize_done(Notifier *notifier, void *unused)
|
static void muxes_realize_done(Notifier *notifier, void *unused)
|
||||||
{
|
{
|
||||||
Chardev *chr;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(chr, &chardevs, next) {
|
|
||||||
if (CHARDEV_IS_MUX(chr)) {
|
|
||||||
MuxChardev *d = MUX_CHARDEV(chr);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
muxes_realized = true;
|
muxes_realized = true;
|
||||||
|
object_child_foreach(get_chardevs_root(), open_muxes, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Notifier muxes_realize_notify = {
|
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
|
/* We're connecting to an already opened device, so let's make sure we
|
||||||
also get the open event */
|
also get the open event */
|
||||||
if (s->be_open) {
|
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");
|
const char *logfile = qemu_opt_get(opts, "logfile");
|
||||||
|
|
||||||
backend->has_logfile = logfile != NULL;
|
backend->has_logfile = logfile != NULL;
|
||||||
backend->logfile = logfile ? g_strdup(logfile) : NULL;
|
backend->logfile = g_strdup(logfile);
|
||||||
|
|
||||||
backend->has_logappend = true;
|
backend->has_logappend = true;
|
||||||
backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
|
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;
|
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 {
|
static const struct ChardevAlias {
|
||||||
const char *typename;
|
const char *typename;
|
||||||
const char *alias;
|
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 */
|
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)),
|
object_class_get_name(OBJECT_CLASS(cc)),
|
||||||
backend, errp);
|
backend, errp);
|
||||||
|
|
||||||
if (chr == NULL) {
|
if (chr == NULL) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -959,9 +934,9 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts,
|
|||||||
backend->type = CHARDEV_BACKEND_KIND_MUX;
|
backend->type = CHARDEV_BACKEND_KIND_MUX;
|
||||||
backend->u.mux.data = g_new0(ChardevMux, 1);
|
backend->u.mux.data = g_new0(ChardevMux, 1);
|
||||||
backend->u.mux.data->chardev = g_strdup(bid);
|
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) {
|
if (mux == NULL) {
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
chr = NULL;
|
chr = NULL;
|
||||||
goto out;
|
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);
|
Chardev *chr = CHARDEV(obj);
|
||||||
object_unref(OBJECT(chr));
|
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 *qmp_query_chardev(Error **errp)
|
||||||
{
|
{
|
||||||
ChardevInfoList *chr_list = NULL;
|
ChardevInfoList *chr_list = NULL;
|
||||||
Chardev *chr;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(chr, &chardevs, next) {
|
object_child_foreach(get_chardevs_root(),
|
||||||
ChardevInfoList *info = g_malloc0(sizeof(*info));
|
qmp_query_chardev_foreach, &chr_list);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return chr_list;
|
return chr_list;
|
||||||
}
|
}
|
||||||
@ -1123,14 +1100,9 @@ ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp)
|
|||||||
|
|
||||||
Chardev *qemu_chr_find(const char *name)
|
Chardev *qemu_chr_find(const char *name)
|
||||||
{
|
{
|
||||||
Chardev *chr;
|
Object *obj = object_resolve_path_component(get_chardevs_root(), name);
|
||||||
|
|
||||||
QTAILQ_FOREACH(chr, &chardevs, next) {
|
return obj ? CHARDEV(obj) : NULL;
|
||||||
if (strcmp(chr->label, name) != 0)
|
|
||||||
continue;
|
|
||||||
return chr;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QemuOptsList qemu_chardev_opts = {
|
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,
|
Chardev *qemu_chardev_new(const char *id, const char *typename,
|
||||||
ChardevBackend *backend, Error **errp)
|
ChardevBackend *backend,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
Object *obj;
|
||||||
Chardev *chr = NULL;
|
Chardev *chr = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
bool be_opened = true;
|
bool be_opened = true;
|
||||||
|
|
||||||
assert(g_str_has_prefix(typename, "chardev-"));
|
assert(g_str_has_prefix(typename, "chardev-"));
|
||||||
|
|
||||||
chr = CHARDEV(object_new(typename));
|
obj = object_new(typename);
|
||||||
|
chr = CHARDEV(obj);
|
||||||
chr->label = g_strdup(id);
|
chr->label = g_strdup(id);
|
||||||
|
|
||||||
qemu_char_open(chr, backend, &be_opened, &local_err);
|
qemu_char_open(chr, backend, &be_opened, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
goto end;
|
||||||
object_unref(OBJECT(chr));
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chr->filename) {
|
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);
|
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;
|
return chr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1283,7 +1271,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
|
|||||||
return NULL;
|
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);
|
backend, errp);
|
||||||
if (!chr) {
|
if (!chr) {
|
||||||
return NULL;
|
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);
|
"Chardev '%s' cannot be unplugged in record/replay mode", id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_chr_cleanup(void)
|
void qemu_chr_cleanup(void)
|
||||||
{
|
{
|
||||||
Chardev *chr, *tmp;
|
object_unparent(get_chardevs_root());
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
|
|
||||||
qemu_chr_delete(chr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void register_types(void)
|
static void register_types(void)
|
||||||
|
@ -1611,7 +1611,7 @@ void gdb_exit(CPUArchState *env, int code)
|
|||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
qemu_chr_fe_deinit(&s->chr);
|
qemu_chr_fe_deinit(&s->chr);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1912,7 +1912,7 @@ int gdbserver_start(const char *device)
|
|||||||
monitor_init(mon_chr, 0);
|
monitor_init(mon_chr, 0);
|
||||||
} else {
|
} else {
|
||||||
if (qemu_chr_fe_get_driver(&s->chr)) {
|
if (qemu_chr_fe_get_driver(&s->chr)) {
|
||||||
qemu_chr_delete(qemu_chr_fe_get_driver(&s->chr));
|
object_unparent(OBJECT(qemu_chr_fe_get_driver(&s->chr)));
|
||||||
}
|
}
|
||||||
mon_chr = s->mon_chr;
|
mon_chr = s->mon_chr;
|
||||||
memset(s, 0, sizeof(GDBState));
|
memset(s, 0, sizeof(GDBState));
|
||||||
|
@ -267,7 +267,7 @@ static void ccid_card_vscard_drop_connection(PassthruState *card)
|
|||||||
Chardev *chr = qemu_chr_fe_get_driver(&card->cs);
|
Chardev *chr = qemu_chr_fe_get_driver(&card->cs);
|
||||||
|
|
||||||
qemu_chr_fe_deinit(&card->cs);
|
qemu_chr_fe_deinit(&card->cs);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
card->vscard_in_pos = card->vscard_in_hdr = 0;
|
card->vscard_in_pos = card->vscard_in_hdr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1433,7 +1433,7 @@ static void usbredir_unrealize(USBDevice *udev, Error **errp)
|
|||||||
Chardev *chr = qemu_chr_fe_get_driver(&dev->cs);
|
Chardev *chr = qemu_chr_fe_get_driver(&dev->cs);
|
||||||
|
|
||||||
qemu_chr_fe_deinit(&dev->cs);
|
qemu_chr_fe_deinit(&dev->cs);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
|
|
||||||
/* Note must be done after qemu_chr_close, as that causes a close event */
|
/* Note must be done after qemu_chr_close, as that causes a close event */
|
||||||
qemu_bh_delete(dev->chardev_close_bh);
|
qemu_bh_delete(dev->chardev_close_bh);
|
||||||
|
@ -38,7 +38,7 @@ static int store_dev_info(int domid, Chardev *cs, const char *string)
|
|||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
/* Only continue if we're talking to a pty. */
|
/* Only continue if we're talking to a pty. */
|
||||||
if (strncmp(cs->filename, "pty:", 4)) {
|
if (!CHARDEV_IS_PTY(cs)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
pts = cs->filename + 4;
|
pts = cs->filename + 4;
|
||||||
|
@ -103,7 +103,6 @@ struct Chardev {
|
|||||||
int be_open;
|
int be_open;
|
||||||
guint fd_in_tag;
|
guint fd_in_tag;
|
||||||
DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
|
DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
|
||||||
QTAILQ_ENTRY(Chardev) next;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,14 +177,6 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp);
|
|||||||
*/
|
*/
|
||||||
Chardev *qemu_chr_new_noreplay(const char *label, const char *filename);
|
Chardev *qemu_chr_new_noreplay(const char *label, const char *filename);
|
||||||
|
|
||||||
/**
|
|
||||||
* @qemu_chr_delete:
|
|
||||||
*
|
|
||||||
* Destroy a character backend and remove it from the list of
|
|
||||||
* identified character backends.
|
|
||||||
*/
|
|
||||||
void qemu_chr_delete(Chardev *chr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @qemu_chr_fe_set_echo:
|
* @qemu_chr_fe_set_echo:
|
||||||
*
|
*
|
||||||
@ -435,7 +426,6 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
|
|||||||
*/
|
*/
|
||||||
void qemu_chr_fe_take_focus(CharBackend *b);
|
void qemu_chr_fe_take_focus(CharBackend *b);
|
||||||
|
|
||||||
void qemu_chr_be_generic_open(Chardev *s);
|
|
||||||
void qemu_chr_fe_accept_input(CharBackend *be);
|
void qemu_chr_fe_accept_input(CharBackend *be);
|
||||||
int qemu_chr_add_client(Chardev *s, int fd);
|
int qemu_chr_add_client(Chardev *s, int fd);
|
||||||
Chardev *qemu_chr_find(const char *name);
|
Chardev *qemu_chr_find(const char *name);
|
||||||
|
@ -154,7 +154,7 @@ static void vhost_user_cleanup(NetClientState *nc)
|
|||||||
Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
|
Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
|
||||||
|
|
||||||
qemu_chr_fe_deinit(&s->chr);
|
qemu_chr_fe_deinit(&s->chr);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_purge_queued_packets(nc);
|
qemu_purge_queued_packets(nc);
|
||||||
|
@ -40,6 +40,7 @@ Object *container_get(Object *root, const char *path)
|
|||||||
if (!child) {
|
if (!child) {
|
||||||
child = object_new("container");
|
child = object_new("container");
|
||||||
object_property_add_child(obj, parts[i], child, NULL);
|
object_property_add_child(obj, parts[i], child, NULL);
|
||||||
|
object_unref(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,35 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
|
#include "qemu/sockets.h"
|
||||||
#include "sysemu/char.h"
|
#include "sysemu/char.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
#include "qom/qom-qobject.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
|
|
||||||
|
static bool quit;
|
||||||
|
|
||||||
typedef struct FeHandler {
|
typedef struct FeHandler {
|
||||||
int read_count;
|
int read_count;
|
||||||
int last_event;
|
int last_event;
|
||||||
char read_buf[128];
|
char read_buf[128];
|
||||||
} FeHandler;
|
} FeHandler;
|
||||||
|
|
||||||
|
static void main_loop(void)
|
||||||
|
{
|
||||||
|
bool nonblocking;
|
||||||
|
int last_io = 0;
|
||||||
|
|
||||||
|
quit = false;
|
||||||
|
do {
|
||||||
|
nonblocking = last_io > 0;
|
||||||
|
last_io = main_loop_wait(nonblocking);
|
||||||
|
} while (!quit);
|
||||||
|
}
|
||||||
|
|
||||||
static int fe_can_read(void *opaque)
|
static int fe_can_read(void *opaque)
|
||||||
{
|
{
|
||||||
FeHandler *h = opaque;
|
FeHandler *h = opaque;
|
||||||
@ -28,6 +45,7 @@ static void fe_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
|
|
||||||
memcpy(h->read_buf + h->read_count, buf, size);
|
memcpy(h->read_buf + h->read_count, buf, size);
|
||||||
h->read_count += size;
|
h->read_count += size;
|
||||||
|
quit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fe_event(void *opaque, int event)
|
static void fe_event(void *opaque, int event)
|
||||||
@ -35,9 +53,36 @@ static void fe_event(void *opaque, int event)
|
|||||||
FeHandler *h = opaque;
|
FeHandler *h = opaque;
|
||||||
|
|
||||||
h->last_event = event;
|
h->last_event = event;
|
||||||
|
quit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
|
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
|
||||||
|
#ifdef _WIN32
|
||||||
|
static void char_console_test_subprocess(void)
|
||||||
|
{
|
||||||
|
QemuOpts *opts;
|
||||||
|
Chardev *chr;
|
||||||
|
|
||||||
|
opts = qemu_opts_create(qemu_find_opts("chardev"), "console-label",
|
||||||
|
1, &error_abort);
|
||||||
|
qemu_opt_set(opts, "backend", "console", &error_abort);
|
||||||
|
|
||||||
|
chr = qemu_chr_new_from_opts(opts, NULL);
|
||||||
|
g_assert_nonnull(chr);
|
||||||
|
|
||||||
|
qemu_chr_write_all(chr, (const uint8_t *)"CONSOLE", 7);
|
||||||
|
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
object_unparent(OBJECT(chr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void char_console_test(void)
|
||||||
|
{
|
||||||
|
g_test_trap_subprocess("/char/console/subprocess", 0, 0);
|
||||||
|
g_test_trap_assert_passed();
|
||||||
|
g_test_trap_assert_stdout("CONSOLE");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
static void char_stdio_test_subprocess(void)
|
static void char_stdio_test_subprocess(void)
|
||||||
{
|
{
|
||||||
Chardev *chr;
|
Chardev *chr;
|
||||||
@ -53,7 +98,7 @@ static void char_stdio_test_subprocess(void)
|
|||||||
g_assert_cmpint(ret, ==, 4);
|
g_assert_cmpint(ret, ==, 4);
|
||||||
|
|
||||||
qemu_chr_fe_deinit(&be);
|
qemu_chr_fe_deinit(&be);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void char_stdio_test(void)
|
static void char_stdio_test(void)
|
||||||
@ -64,7 +109,6 @@ static void char_stdio_test(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void char_ringbuf_test(void)
|
static void char_ringbuf_test(void)
|
||||||
{
|
{
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
@ -103,7 +147,17 @@ static void char_ringbuf_test(void)
|
|||||||
g_free(data);
|
g_free(data);
|
||||||
|
|
||||||
qemu_chr_fe_deinit(&be);
|
qemu_chr_fe_deinit(&be);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
|
|
||||||
|
/* check alias */
|
||||||
|
opts = qemu_opts_create(qemu_find_opts("chardev"), "memory-label",
|
||||||
|
1, &error_abort);
|
||||||
|
qemu_opt_set(opts, "backend", "memory", &error_abort);
|
||||||
|
qemu_opt_set(opts, "size", "2", &error_abort);
|
||||||
|
chr = qemu_chr_new_from_opts(opts, NULL);
|
||||||
|
g_assert_nonnull(chr);
|
||||||
|
object_unparent(OBJECT(chr));
|
||||||
|
qemu_opts_del(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void char_mux_test(void)
|
static void char_mux_test(void)
|
||||||
@ -179,7 +233,296 @@ static void char_mux_test(void)
|
|||||||
|
|
||||||
qemu_chr_fe_deinit(&chr_be1);
|
qemu_chr_fe_deinit(&chr_be1);
|
||||||
qemu_chr_fe_deinit(&chr_be2);
|
qemu_chr_fe_deinit(&chr_be2);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct SocketIdleData {
|
||||||
|
GMainLoop *loop;
|
||||||
|
Chardev *chr;
|
||||||
|
bool conn_expected;
|
||||||
|
CharBackend *be;
|
||||||
|
CharBackend *client_be;
|
||||||
|
} SocketIdleData;
|
||||||
|
|
||||||
|
static gboolean char_socket_test_idle(gpointer user_data)
|
||||||
|
{
|
||||||
|
SocketIdleData *data = user_data;
|
||||||
|
|
||||||
|
if (object_property_get_bool(OBJECT(data->chr), "connected", NULL)
|
||||||
|
== data->conn_expected) {
|
||||||
|
quit = true;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void socket_read(void *opaque, const uint8_t *buf, int size)
|
||||||
|
{
|
||||||
|
SocketIdleData *data = opaque;
|
||||||
|
|
||||||
|
g_assert_cmpint(size, ==, 1);
|
||||||
|
g_assert_cmpint(*buf, ==, 'Z');
|
||||||
|
|
||||||
|
size = qemu_chr_fe_write(data->be, (const uint8_t *)"hello", 5);
|
||||||
|
g_assert_cmpint(size, ==, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int socket_can_read(void *opaque)
|
||||||
|
{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void socket_read_hello(void *opaque, const uint8_t *buf, int size)
|
||||||
|
{
|
||||||
|
g_assert_cmpint(size, ==, 5);
|
||||||
|
g_assert(strncmp((char *)buf, "hello", 5) == 0);
|
||||||
|
|
||||||
|
quit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int socket_can_read_hello(void *opaque)
|
||||||
|
{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void char_socket_test(void)
|
||||||
|
{
|
||||||
|
Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait");
|
||||||
|
Chardev *chr_client;
|
||||||
|
QObject *addr;
|
||||||
|
QDict *qdict, *data;
|
||||||
|
const char *port;
|
||||||
|
SocketIdleData d = { .chr = chr };
|
||||||
|
CharBackend be;
|
||||||
|
CharBackend client_be;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
d.be = &be;
|
||||||
|
d.client_be = &be;
|
||||||
|
|
||||||
|
g_assert_nonnull(chr);
|
||||||
|
g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort));
|
||||||
|
|
||||||
|
addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort);
|
||||||
|
qdict = qobject_to_qdict(addr);
|
||||||
|
data = qdict_get_qdict(qdict, "data");
|
||||||
|
port = qdict_get_str(data, "port");
|
||||||
|
tmp = g_strdup_printf("tcp:127.0.0.1:%s", port);
|
||||||
|
QDECREF(qdict);
|
||||||
|
|
||||||
|
qemu_chr_fe_init(&be, chr, &error_abort);
|
||||||
|
qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read,
|
||||||
|
NULL, &d, NULL, true);
|
||||||
|
|
||||||
|
chr_client = qemu_chr_new("client", tmp);
|
||||||
|
qemu_chr_fe_init(&client_be, chr_client, &error_abort);
|
||||||
|
qemu_chr_fe_set_handlers(&client_be, socket_can_read_hello,
|
||||||
|
socket_read_hello,
|
||||||
|
NULL, &d, NULL, true);
|
||||||
|
g_free(tmp);
|
||||||
|
|
||||||
|
d.conn_expected = true;
|
||||||
|
guint id = g_idle_add(char_socket_test_idle, &d);
|
||||||
|
g_source_set_name_by_id(id, "test-idle");
|
||||||
|
g_assert_cmpint(id, >, 0);
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort));
|
||||||
|
g_assert(object_property_get_bool(OBJECT(chr_client),
|
||||||
|
"connected", &error_abort));
|
||||||
|
|
||||||
|
qemu_chr_write_all(chr_client, (const uint8_t *)"Z", 1);
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
object_unparent(OBJECT(chr_client));
|
||||||
|
|
||||||
|
d.conn_expected = false;
|
||||||
|
g_idle_add(char_socket_test_idle, &d);
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
object_unparent(OBJECT(chr));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
static void char_pipe_test(void)
|
||||||
|
{
|
||||||
|
gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL);
|
||||||
|
gchar *tmp, *in, *out, *pipe = g_build_filename(tmp_path, "pipe", NULL);
|
||||||
|
Chardev *chr;
|
||||||
|
CharBackend be;
|
||||||
|
int ret, fd;
|
||||||
|
char buf[10];
|
||||||
|
FeHandler fe = { 0, };
|
||||||
|
|
||||||
|
in = g_strdup_printf("%s.in", pipe);
|
||||||
|
if (mkfifo(in, 0600) < 0) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
out = g_strdup_printf("%s.out", pipe);
|
||||||
|
if (mkfifo(out, 0600) < 0) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = g_strdup_printf("pipe:%s", pipe);
|
||||||
|
chr = qemu_chr_new("pipe", tmp);
|
||||||
|
g_assert_nonnull(chr);
|
||||||
|
g_free(tmp);
|
||||||
|
|
||||||
|
qemu_chr_fe_init(&be, chr, &error_abort);
|
||||||
|
|
||||||
|
ret = qemu_chr_fe_write(&be, (void *)"pipe-out", 9);
|
||||||
|
g_assert_cmpint(ret, ==, 9);
|
||||||
|
|
||||||
|
fd = open(out, O_RDWR);
|
||||||
|
ret = read(fd, buf, sizeof(buf));
|
||||||
|
g_assert_cmpint(ret, ==, 9);
|
||||||
|
g_assert_cmpstr(buf, ==, "pipe-out");
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
fd = open(in, O_WRONLY);
|
||||||
|
ret = write(fd, "pipe-in", 8);
|
||||||
|
g_assert_cmpint(ret, ==, 8);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
qemu_chr_fe_set_handlers(&be,
|
||||||
|
fe_can_read,
|
||||||
|
fe_read,
|
||||||
|
fe_event,
|
||||||
|
&fe,
|
||||||
|
NULL, true);
|
||||||
|
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
g_assert_cmpint(fe.read_count, ==, 8);
|
||||||
|
g_assert_cmpstr(fe.read_buf, ==, "pipe-in");
|
||||||
|
|
||||||
|
qemu_chr_fe_deinit(&be);
|
||||||
|
object_unparent(OBJECT(chr));
|
||||||
|
|
||||||
|
g_assert(g_unlink(in) == 0);
|
||||||
|
g_assert(g_unlink(out) == 0);
|
||||||
|
g_assert(g_rmdir(tmp_path) == 0);
|
||||||
|
g_free(in);
|
||||||
|
g_free(out);
|
||||||
|
g_free(tmp_path);
|
||||||
|
g_free(pipe);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void char_udp_test(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr = { 0, }, other;
|
||||||
|
SocketIdleData d = { 0, };
|
||||||
|
Chardev *chr;
|
||||||
|
CharBackend be;
|
||||||
|
socklen_t alen = sizeof(addr);
|
||||||
|
int ret, sock = qemu_socket(PF_INET, SOCK_DGRAM, 0);
|
||||||
|
char buf[10];
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
g_assert_cmpint(sock, >, 0);
|
||||||
|
addr.sin_family = AF_INET ;
|
||||||
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
addr.sin_port = 0;
|
||||||
|
ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
|
||||||
|
g_assert_cmpint(ret, ==, 0);
|
||||||
|
ret = getsockname(sock, (struct sockaddr *)&addr, &alen);
|
||||||
|
g_assert_cmpint(ret, ==, 0);
|
||||||
|
|
||||||
|
tmp = g_strdup_printf("udp:127.0.0.1:%d",
|
||||||
|
ntohs(addr.sin_port));
|
||||||
|
chr = qemu_chr_new("client", tmp);
|
||||||
|
g_assert_nonnull(chr);
|
||||||
|
|
||||||
|
d.chr = chr;
|
||||||
|
qemu_chr_fe_init(&be, chr, &error_abort);
|
||||||
|
qemu_chr_fe_set_handlers(&be, socket_can_read_hello, socket_read_hello,
|
||||||
|
NULL, &d, NULL, true);
|
||||||
|
ret = qemu_chr_write_all(chr, (uint8_t *)"hello", 5);
|
||||||
|
g_assert_cmpint(ret, ==, 5);
|
||||||
|
|
||||||
|
alen = sizeof(addr);
|
||||||
|
ret = recvfrom(sock, buf, sizeof(buf), 0,
|
||||||
|
(struct sockaddr *)&other, &alen);
|
||||||
|
g_assert_cmpint(ret, ==, 5);
|
||||||
|
ret = sendto(sock, buf, 5, 0, (struct sockaddr *)&other, alen);
|
||||||
|
g_assert_cmpint(ret, ==, 5);
|
||||||
|
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
close(sock);
|
||||||
|
g_free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void char_file_test(void)
|
||||||
|
{
|
||||||
|
char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL);
|
||||||
|
char *out = g_build_filename(tmp_path, "out", NULL);
|
||||||
|
char *contents = NULL;
|
||||||
|
ChardevFile file = { .out = out };
|
||||||
|
ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE,
|
||||||
|
.u.file.data = &file };
|
||||||
|
Chardev *chr;
|
||||||
|
gsize length;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend,
|
||||||
|
&error_abort);
|
||||||
|
ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6);
|
||||||
|
g_assert_cmpint(ret, ==, 6);
|
||||||
|
object_unref(OBJECT(chr));
|
||||||
|
|
||||||
|
ret = g_file_get_contents(out, &contents, &length, NULL);
|
||||||
|
g_assert(ret == TRUE);
|
||||||
|
g_assert_cmpint(length, ==, 6);
|
||||||
|
g_assert(strncmp(contents, "hello!", 6) == 0);
|
||||||
|
g_free(contents);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
{
|
||||||
|
CharBackend be;
|
||||||
|
FeHandler fe = { 0, };
|
||||||
|
char *fifo = g_build_filename(tmp_path, "fifo", NULL);
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (mkfifo(fifo, 0600) < 0) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(fifo, O_RDWR);
|
||||||
|
ret = write(fd, "fifo-in", 8);
|
||||||
|
g_assert_cmpint(ret, ==, 8);
|
||||||
|
|
||||||
|
file.in = fifo;
|
||||||
|
file.has_in = true;
|
||||||
|
chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend,
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
qemu_chr_fe_init(&be, chr, &error_abort);
|
||||||
|
qemu_chr_fe_set_handlers(&be,
|
||||||
|
fe_can_read,
|
||||||
|
fe_read,
|
||||||
|
fe_event,
|
||||||
|
&fe, NULL, true);
|
||||||
|
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
g_assert_cmpint(fe.read_count, ==, 8);
|
||||||
|
g_assert_cmpstr(fe.read_buf, ==, "fifo-in");
|
||||||
|
qemu_chr_fe_deinit(&be);
|
||||||
|
object_unref(OBJECT(chr));
|
||||||
|
g_unlink(fifo);
|
||||||
|
g_free(fifo);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_unlink(out);
|
||||||
|
g_rmdir(tmp_path);
|
||||||
|
g_free(tmp_path);
|
||||||
|
g_free(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void char_null_test(void)
|
static void char_null_test(void)
|
||||||
@ -222,7 +565,7 @@ static void char_null_test(void)
|
|||||||
g_assert_cmpint(ret, ==, 4);
|
g_assert_cmpint(ret, ==, 4);
|
||||||
|
|
||||||
qemu_chr_fe_deinit(&be);
|
qemu_chr_fe_deinit(&be);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void char_invalid_test(void)
|
static void char_invalid_test(void)
|
||||||
@ -235,6 +578,9 @@ static void char_invalid_test(void)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
qemu_init_main_loop(&error_abort);
|
||||||
|
socket_init();
|
||||||
|
|
||||||
g_test_init(&argc, &argv, NULL);
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
|
||||||
module_call_init(MODULE_INIT_QOM);
|
module_call_init(MODULE_INIT_QOM);
|
||||||
@ -245,9 +591,19 @@ int main(int argc, char **argv)
|
|||||||
g_test_add_func("/char/ringbuf", char_ringbuf_test);
|
g_test_add_func("/char/ringbuf", char_ringbuf_test);
|
||||||
g_test_add_func("/char/mux", char_mux_test);
|
g_test_add_func("/char/mux", char_mux_test);
|
||||||
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
|
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
|
||||||
|
#ifdef _WIN32
|
||||||
|
g_test_add_func("/char/console/subprocess", char_console_test_subprocess);
|
||||||
|
g_test_add_func("/char/console", char_console_test);
|
||||||
|
#endif
|
||||||
g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess);
|
g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess);
|
||||||
g_test_add_func("/char/stdio", char_stdio_test);
|
g_test_add_func("/char/stdio", char_stdio_test);
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef _WIN32
|
||||||
|
g_test_add_func("/char/pipe", char_pipe_test);
|
||||||
|
#endif
|
||||||
|
g_test_add_func("/char/file", char_file_test);
|
||||||
|
g_test_add_func("/char/socket", char_socket_test);
|
||||||
|
g_test_add_func("/char/udp", char_udp_test);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
@ -491,7 +491,7 @@ static gboolean _test_server_free(TestServer *server)
|
|||||||
Chardev *chr = qemu_chr_fe_get_driver(&server->chr);
|
Chardev *chr = qemu_chr_fe_get_driver(&server->chr);
|
||||||
|
|
||||||
qemu_chr_fe_deinit(&server->chr);
|
qemu_chr_fe_deinit(&server->chr);
|
||||||
qemu_chr_delete(chr);
|
object_unparent(OBJECT(chr));
|
||||||
|
|
||||||
for (i = 0; i < server->fds_num; i++) {
|
for (i = 0; i < server->fds_num; i++) {
|
||||||
close(server->fds[i]);
|
close(server->fds[i]);
|
||||||
|
@ -2076,7 +2076,7 @@ static void text_console_do_init(Chardev *chr, DisplayState *ds)
|
|||||||
s->t_attrib = s->t_attrib_default;
|
s->t_attrib = s->t_attrib_default;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_chr_be_generic_open(chr);
|
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vc_chr_open(Chardev *chr,
|
static void vc_chr_open(Chardev *chr,
|
||||||
|
2
ui/gtk.c
2
ui/gtk.c
@ -1868,7 +1868,7 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
|
|||||||
gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
|
gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
|
||||||
gtk_label_new(vc->label));
|
gtk_label_new(vc->label));
|
||||||
|
|
||||||
qemu_chr_be_generic_open(vc->vte.chr);
|
qemu_chr_be_event(vc->vte.chr, CHR_EVENT_OPENED);
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user