summaryrefslogtreecommitdiffstats
path: root/chardev
diff options
context:
space:
mode:
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.c122
-rw-r--r--chardev/char-udp.c26
-rw-r--r--chardev/char.c159
6 files changed, 197 insertions, 125 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 581ab34278..35a175d796 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 d8de0518c5..3bda89a1fa 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -55,6 +55,7 @@ typedef struct {
SocketAddress *addr;
bool is_listen;
bool is_telnet;
+ bool is_tn3270;
guint reconnect_timer;
int64_t reconnect_time;
@@ -141,19 +142,25 @@ static int tcp_chr_read_poll(void *opaque)
return s->max_size;
}
-#define IAC 255
-#define IAC_BREAK 243
static void tcp_chr_process_IAC_bytes(Chardev *chr,
SocketChardev *s,
uint8_t *buf, int *size)
{
- /* Handle any telnet client's basic IAC options to satisfy char by
- * char mode with no echo. All IAC options will be removed from
- * the buf and the do_telnetopt variable will be used to track the
- * state of the width of the IAC information.
+ /* Handle any telnet or tn3270 client's basic IAC options.
+ * For telnet options, it satisfies char by char mode with no echo.
+ * For tn3270 options, it satisfies binary mode with EOR.
+ * All IAC options will be removed from the buf and the do_opt
+ * pointer will be used to track the state of the width of the
+ * IAC information.
*
- * IAC commands come in sets of 3 bytes with the exception of the
- * "IAC BREAK" command and the double IAC.
+ * RFC854: "All TELNET commands consist of at least a two byte sequence.
+ * The commands dealing with option negotiation are three byte sequences,
+ * the third byte being the code for the option referenced."
+ * "IAC BREAK", "IAC IP", "IAC NOP" and the double IAC are two bytes.
+ * "IAC SB", "IAC SE" and "IAC EOR" are saved to split up data boundary
+ * for tn3270.
+ * NOP, Break and Interrupt Process(IP) might be encountered during a TN3270
+ * session, and NOP and IP need to be done later.
*/
int i;
@@ -174,6 +181,18 @@ static void tcp_chr_process_IAC_bytes(Chardev *chr,
/* Handle IAC break commands by sending a serial break */
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
s->do_telnetopt++;
+ } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_EOR
+ || (unsigned char)buf[i] == IAC_SB
+ || (unsigned char)buf[i] == IAC_SE)
+ && s->do_telnetopt == 2) {
+ buf[j++] = IAC;
+ buf[j++] = buf[i];
+ s->do_telnetopt++;
+ } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_IP
+ || (unsigned char)buf[i] == IAC_NOP)
+ && s->do_telnetopt == 2) {
+ /* TODO: IP and NOP need to be implemented later. */
+ s->do_telnetopt++;
}
s->do_telnetopt++;
}
@@ -366,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);
@@ -380,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);
@@ -489,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,
@@ -512,7 +539,7 @@ static void tcp_chr_update_read_handler(Chardev *chr,
typedef struct {
Chardev *chr;
- char buf[12];
+ char buf[21];
size_t buflen;
} TCPChardevTelnetInit;
@@ -550,9 +577,6 @@ static void tcp_chr_telnet_init(Chardev *chr)
TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
size_t n = 0;
- init->chr = chr;
- init->buflen = 12;
-
#define IACSET(x, a, b, c) \
do { \
x[n++] = a; \
@@ -560,12 +584,26 @@ static void tcp_chr_telnet_init(Chardev *chr)
x[n++] = c; \
} while (0)
- /* Prep the telnet negotion to put telnet in binary,
- * no echo, single char mode */
- IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
- IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
- IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
- IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
+ init->chr = chr;
+ if (!s->is_tn3270) {
+ init->buflen = 12;
+ /* Prep the telnet negotion to put telnet in binary,
+ * no echo, single char mode */
+ IACSET(init->buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
+ IACSET(init->buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
+ IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
+ IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
+ } else {
+ init->buflen = 21;
+ /* Prep the TN3270 negotion based on RFC1576 */
+ IACSET(init->buf, 0xff, 0xfd, 0x19); /* IAC DO EOR */
+ IACSET(init->buf, 0xff, 0xfb, 0x19); /* IAC WILL EOR */
+ IACSET(init->buf, 0xff, 0xfd, 0x00); /* IAC DO BINARY */
+ IACSET(init->buf, 0xff, 0xfb, 0x00); /* IAC WILL BINARY */
+ IACSET(init->buf, 0xff, 0xfd, 0x18); /* IAC DO TERMINAL TYPE */
+ IACSET(init->buf, 0xff, 0xfa, 0x18); /* IAC SB TERMINAL TYPE */
+ IACSET(init->buf, 0x01, 0xff, 0xf0); /* SEND IAC SE */
+ }
#undef IACSET
@@ -585,7 +623,8 @@ static void tcp_chr_tls_handshake(QIOTask *task,
if (qio_task_propagate_error(task, NULL)) {
tcp_chr_disconnect(chr);
} else {
- if (s->do_telnetopt) {
+ /* tn3270 does not support TLS yet */
+ if (s->do_telnetopt && !s->is_tn3270) {
tcp_chr_telnet_init(chr);
} else {
tcp_chr_connect(chr);
@@ -824,12 +863,14 @@ static void qmp_chardev_open_socket(Chardev *chr,
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
bool is_listen = sock->has_server ? sock->server : true;
bool is_telnet = sock->has_telnet ? sock->telnet : false;
+ bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false;
bool is_waitconnect = sock->has_wait ? sock->wait : false;
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
QIOChannelSocket *sioc = NULL;
s->is_listen = is_listen;
s->is_telnet = is_telnet;
+ s->is_tn3270 = is_tn3270;
s->do_nodelay = do_nodelay;
if (sock->tls_creds) {
Object *creds;
@@ -875,11 +916,10 @@ 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) {
+ if (is_telnet || is_tn3270) {
s->do_telnetopt = 1;
}
} else if (reconnect > 0) {
@@ -904,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) {
@@ -933,6 +978,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
bool is_listen = qemu_opt_get_bool(opts, "server", false);
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
+ bool is_tn3270 = qemu_opt_get_bool(opts, "tn3270", false);
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
const char *path = qemu_opt_get(opts, "path");
@@ -968,6 +1014,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
sock->server = is_listen;
sock->has_telnet = true;
sock->telnet = is_telnet;
+ sock->has_tn3270 = true;
+ sock->tn3270 = is_tn3270;
sock->has_wait = true;
sock->wait = is_waitconnect;
sock->has_reconnect = true;
@@ -997,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);
@@ -1012,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 12240e528a..76ef83f930 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 54cd5f4081..4e24dc39af 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);
}
}
@@ -696,7 +690,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
return opts;
}
if (strstart(filename, "tcp:", &p) ||
- strstart(filename, "telnet:", &p)) {
+ strstart(filename, "telnet:", &p) ||
+ strstart(filename, "tn3270:", &p)) {
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
host[0] = 0;
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
@@ -712,8 +707,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
goto fail;
}
}
- if (strstart(filename, "telnet:", &p))
+ if (strstart(filename, "telnet:", &p)) {
qemu_opt_set(opts, "telnet", "on", &error_abort);
+ } else if (strstart(filename, "tn3270:", &p)) {
+ qemu_opt_set(opts, "tn3270", "on", &error_abort);
+ }
return opts;
}
if (strstart(filename, "udp:", &p)) {
@@ -770,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);
@@ -805,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;
@@ -941,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;
}
@@ -955,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;
}
@@ -1071,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;
}
@@ -1119,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 = {
@@ -1177,6 +1153,9 @@ QemuOptsList qemu_chardev_opts = {
.name = "telnet",
.type = QEMU_OPT_BOOL,
},{
+ .name = "tn3270",
+ .type = QEMU_OPT_BOOL,
+ },{
.name = "tls-creds",
.type = QEMU_OPT_STRING,
},{
@@ -1236,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) {
@@ -1261,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;
}
@@ -1276,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;
@@ -1309,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)