X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/60a0f1af07d685c88f4ffa09370da5bd7514823e..5d0c7f4f9e2d1fed53fcb2733aede71c9dcaf871:/qemu-char.c diff --git a/qemu-char.c b/qemu-char.c index b73969ddbd..d8da1677ff 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -32,14 +32,14 @@ #include "sysemu/char.h" #include "hw/usb.h" #include "qmp-commands.h" -#include "qapi/qmp-input-visitor.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/clone-visitor.h" #include "qapi-visit.h" #include "qemu/base64.h" #include "io/channel-socket.h" #include "io/channel-file.h" #include "io/channel-tls.h" #include "sysemu/replay.h" +#include "qemu/help_option.h" #include @@ -89,6 +89,8 @@ #define READ_RETRIES 10 #define TCP_MAX_FDS 16 +typedef struct MuxDriver MuxDriver; + /***********************************************************/ /* Socket address helpers */ @@ -190,6 +192,8 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp) void qemu_chr_be_event(CharDriverState *s, int event) { + CharBackend *be = s->be; + /* Keep track if the char device is open */ switch (event) { case CHR_EVENT_OPENED: @@ -200,9 +204,11 @@ void qemu_chr_be_event(CharDriverState *s, int event) break; } - if (!s->chr_event) + if (!be || !be->chr_event) { return; - s->chr_event(s->handler_opaque, event); + } + + be->chr_event(be->opaque, event); } void qemu_chr_be_generic_open(CharDriverState *s) @@ -266,10 +272,15 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int return res; } -int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) +int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len) { + CharDriverState *s = be->chr; int ret; + if (!s) { + return 0; + } + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { int offset; replay_char_write_event_load(&ret, &offset); @@ -294,7 +305,7 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) return ret; } -int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) +static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len) { int offset; int res; @@ -318,15 +329,27 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) return offset; } -int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) +int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len) +{ + CharDriverState *s = be->chr; + + if (!s) { + return 0; + } + + return qemu_chr_write_all(s, buf, len); +} + +int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len) { + CharDriverState *s = be->chr; int offset = 0, counter = 10; int res; - if (!s->chr_sync_read) { + if (!s || !s->chr_sync_read) { return 0; } - + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { return replay_char_read_all_load(buf); } @@ -363,10 +386,12 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) return offset; } -int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg) +int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg) { + CharDriverState *s = be->chr; int res; - if (!s->chr_ioctl || s->replay) { + + if (!s || !s->chr_ioctl || s->replay) { res = -ENOTSUP; } else { res = s->chr_ioctl(s, cmd, arg); @@ -377,15 +402,21 @@ int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg) int qemu_chr_be_can_write(CharDriverState *s) { - if (!s->chr_can_read) + CharBackend *be = s->be; + + if (!be || !be->chr_can_read) { return 0; - return s->chr_can_read(s->handler_opaque); + } + + return be->chr_can_read(be->opaque); } void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len) { - if (s->chr_read) { - s->chr_read(s->handler_opaque, buf, len); + CharBackend *be = s->be; + + if (be && be->chr_read) { + be->chr_read(be->opaque, buf, len); } } @@ -401,11 +432,12 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len) } } -int qemu_chr_fe_get_msgfd(CharDriverState *s) +int qemu_chr_fe_get_msgfd(CharBackend *be) { + CharDriverState *s = be->chr; int fd; - int res = (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1; - if (s->replay) { + int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1; + if (s && s->replay) { fprintf(stderr, "Replay: get msgfd is not supported for serial devices yet\n"); exit(1); @@ -413,13 +445,25 @@ int qemu_chr_fe_get_msgfd(CharDriverState *s) return res; } -int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len) +int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len) { + CharDriverState *s = be->chr; + + if (!s) { + return -1; + } + return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1; } -int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num) +int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num) { + CharDriverState *s = be->chr; + + if (!s) { + return -1; + } + return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1; } @@ -428,56 +472,34 @@ int qemu_chr_add_client(CharDriverState *s, int fd) return s->chr_add_client ? s->chr_add_client(s, fd) : -1; } -void qemu_chr_accept_input(CharDriverState *s) +void qemu_chr_fe_accept_input(CharBackend *be) { + CharDriverState *s = be->chr; + + if (!s) { + return; + } + if (s->chr_accept_input) s->chr_accept_input(s); qemu_notify_event(); } -void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) +void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) { char buf[READ_BUF_LEN]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); - qemu_chr_fe_write(s, (uint8_t *)buf, strlen(buf)); + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(be, (uint8_t *)buf, strlen(buf)); va_end(ap); } static void remove_fd_in_watch(CharDriverState *chr); - -void qemu_chr_add_handlers(CharDriverState *s, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - void *opaque) -{ - int fe_open; - - if (!opaque && !fd_can_read && !fd_read && !fd_event) { - fe_open = 0; - remove_fd_in_watch(s); - } else { - fe_open = 1; - } - s->chr_can_read = fd_can_read; - s->chr_read = fd_read; - s->chr_event = fd_event; - s->handler_opaque = opaque; - if (fe_open && s->chr_update_read_handler) - s->chr_update_read_handler(s); - - if (!s->explicit_fe_open) { - qemu_chr_fe_set_open(s, fe_open); - } - - /* We're connecting to an already opened device, so let's make sure we - also get the open event */ - if (fe_open && s->be_open) { - qemu_chr_be_generic_open(s); - } -} +static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context); +static void mux_set_focus(CharDriverState *chr, int focus); static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { @@ -487,6 +509,7 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) static CharDriverState *qemu_chr_open_null(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -497,7 +520,7 @@ static CharDriverState *qemu_chr_open_null(const char *id, return NULL; } chr->chr_write = null_chr_write; - chr->explicit_be_open = true; + *be_opened = false; return chr; } @@ -505,12 +528,9 @@ static CharDriverState *qemu_chr_open_null(const char *id, #define MAX_MUX 4 #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) -typedef struct { - IOCanReadHandler *chr_can_read[MAX_MUX]; - IOReadHandler *chr_read[MAX_MUX]; - IOEventHandler *chr_event[MAX_MUX]; - void *ext_opaque[MAX_MUX]; - CharDriverState *drv; +struct MuxDriver { + CharBackend *backends[MAX_MUX]; + CharBackend chr; int focus; int mux_cnt; int term_got_escape; @@ -526,8 +546,7 @@ typedef struct { /* Protected by the CharDriverState chr_write_lock. */ int linestart; int64_t timestamps_start; -} MuxDriver; - +}; /* Called with chr_write_lock held. */ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) @@ -535,7 +554,7 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) MuxDriver *d = chr->opaque; int ret; if (!d->timestamps) { - ret = qemu_chr_fe_write(d->drv, buf, len); + ret = qemu_chr_fe_write(&d->chr, buf, len); } else { int i; @@ -557,10 +576,13 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) (secs / 60) % 60, secs % 60, (int)(ti % 1000)); - qemu_chr_fe_write(d->drv, (uint8_t *)buf1, strlen(buf1)); + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_fe_write_all(&d->chr, + (uint8_t *)buf1, strlen(buf1)); d->linestart = 0; } - ret += qemu_chr_fe_write(d->drv, buf+i, 1); + ret += qemu_chr_fe_write(&d->chr, buf + i, 1); if (buf[i] == '\n') { d->linestart = 1; } @@ -595,21 +617,26 @@ static void mux_print_help(CharDriverState *chr) "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r", term_escape_char); } - qemu_chr_fe_write(chr, (uint8_t *)cbuf, strlen(cbuf)); + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + qemu_chr_write_all(chr, (uint8_t *)cbuf, strlen(cbuf)); for (i = 0; mux_help[i] != NULL; i++) { for (j=0; mux_help[i][j] != '\0'; j++) { if (mux_help[i][j] == '%') - qemu_chr_fe_write(chr, (uint8_t *)ebuf, strlen(ebuf)); + qemu_chr_write_all(chr, (uint8_t *)ebuf, strlen(ebuf)); else - qemu_chr_fe_write(chr, (uint8_t *)&mux_help[i][j], 1); + qemu_chr_write_all(chr, (uint8_t *)&mux_help[i][j], 1); } } } static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event) { - if (d->chr_event[mux_nr]) - d->chr_event[mux_nr](d->ext_opaque[mux_nr], event); + CharBackend *be = d->backends[mux_nr]; + + if (be && be->chr_event) { + be->chr_event(be->opaque, event); + } } static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) @@ -626,7 +653,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) case 'x': { const char *term = "QEMU: Terminated\n\r"; - qemu_chr_fe_write(chr, (uint8_t *)term, strlen(term)); + qemu_chr_write_all(chr, (uint8_t *)term, strlen(term)); exit(0); break; } @@ -637,12 +664,9 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) qemu_chr_be_event(chr, CHR_EVENT_BREAK); break; case 'c': + assert(d->mux_cnt > 0); /* handler registered with first fe */ /* Switch to the next registered device */ - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); - d->focus++; - if (d->focus >= d->mux_cnt) - d->focus = 0; - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); + mux_set_focus(chr, (d->focus + 1) % d->mux_cnt); break; case 't': d->timestamps = !d->timestamps; @@ -663,12 +687,12 @@ static void mux_chr_accept_input(CharDriverState *chr) { MuxDriver *d = chr->opaque; int m = d->focus; + CharBackend *be = d->backends[m]; - while (d->prod[m] != d->cons[m] && - d->chr_can_read[m] && - d->chr_can_read[m](d->ext_opaque[m])) { - d->chr_read[m](d->ext_opaque[m], - &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1); + while (be && d->prod[m] != d->cons[m] && + be->chr_can_read && be->chr_can_read(be->opaque)) { + be->chr_read(be->opaque, + &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1); } } @@ -677,11 +701,16 @@ static int mux_chr_can_read(void *opaque) CharDriverState *chr = opaque; MuxDriver *d = chr->opaque; int m = d->focus; + CharBackend *be = d->backends[m]; - if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) + if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) { return 1; - if (d->chr_can_read[m]) - return d->chr_can_read[m](d->ext_opaque[m]); + } + + if (be && be->chr_can_read) { + return be->chr_can_read(be->opaque); + } + return 0; } @@ -690,59 +719,39 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size) CharDriverState *chr = opaque; MuxDriver *d = chr->opaque; int m = d->focus; + CharBackend *be = d->backends[m]; int i; - mux_chr_accept_input (opaque); + mux_chr_accept_input(opaque); - for(i = 0; i < size; i++) + for (i = 0; i < size; i++) if (mux_proc_byte(chr, d, buf[i])) { if (d->prod[m] == d->cons[m] && - d->chr_can_read[m] && - d->chr_can_read[m](d->ext_opaque[m])) - d->chr_read[m](d->ext_opaque[m], &buf[i], 1); + be && be->chr_can_read && + be->chr_can_read(be->opaque)) + be->chr_read(be->opaque, &buf[i], 1); else d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i]; } } +static bool muxes_realized; + static void mux_chr_event(void *opaque, int event) { CharDriverState *chr = opaque; MuxDriver *d = chr->opaque; int i; + if (!muxes_realized) { + return; + } + /* Send the event to all registered listeners */ for (i = 0; i < d->mux_cnt; i++) mux_chr_send_event(d, i, event); } -static void mux_chr_update_read_handler(CharDriverState *chr) -{ - MuxDriver *d = chr->opaque; - - if (d->mux_cnt >= MAX_MUX) { - fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n"); - return; - } - d->ext_opaque[d->mux_cnt] = chr->handler_opaque; - d->chr_can_read[d->mux_cnt] = chr->chr_can_read; - d->chr_read[d->mux_cnt] = chr->chr_read; - d->chr_event[d->mux_cnt] = chr->chr_event; - /* Fix up the real driver with mux routines */ - if (d->mux_cnt == 0) { - qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read, - mux_chr_event, chr); - } - if (d->focus != -1) { - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); - } - d->focus = d->mux_cnt; - d->mux_cnt++; - mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); -} - -static bool muxes_realized; - /** * Called after processing of default and command-line-specified * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached @@ -784,12 +793,60 @@ static Notifier muxes_realize_notify = { static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond) { MuxDriver *d = s->opaque; - return d->drv->chr_add_watch(d->drv, cond); + CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr); + + return chr->chr_add_watch(chr, cond); +} + +static void mux_chr_free(struct CharDriverState *chr) +{ + MuxDriver *d = chr->opaque; + int i; + + for (i = 0; i < d->mux_cnt; i++) { + CharBackend *be = d->backends[i]; + if (be) { + be->chr = NULL; + } + } + qemu_chr_fe_deinit(&d->chr); + g_free(d); +} + +static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context) +{ + MuxDriver *d = chr->opaque; + + /* Fix up the real driver with mux routines */ + qemu_chr_fe_set_handlers(&d->chr, + mux_chr_can_read, + mux_chr_read, + mux_chr_event, + chr, + context, true); +} + +static void mux_set_focus(CharDriverState *chr, int focus) +{ + MuxDriver *d = chr->opaque; + + assert(focus >= 0); + assert(focus < d->mux_cnt); + + if (d->focus != -1) { + mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); + } + + d->focus = focus; + chr->be = d->backends[focus]; + mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); } static CharDriverState *qemu_chr_open_mux(const char *id, ChardevBackend *backend, - ChardevReturn *ret, Error **errp) + ChardevReturn *ret, + bool *be_opened, + Error **errp) { ChardevMux *mux = backend->u.mux.data; CharDriverState *chr, *drv; @@ -809,10 +866,9 @@ static CharDriverState *qemu_chr_open_mux(const char *id, d = g_new0(MuxDriver, 1); chr->opaque = d; - d->drv = drv; d->focus = -1; + chr->chr_free = mux_chr_free; chr->chr_write = mux_chr_write; - chr->chr_update_read_handler = mux_chr_update_read_handler; chr->chr_accept_input = mux_chr_accept_input; /* Frontend guest-open / -close notification is not support with muxes */ chr->chr_set_fe_open = NULL; @@ -822,12 +878,135 @@ static CharDriverState *qemu_chr_open_mux(const char *id, /* only default to opened state if we've realized the initial * set of muxes */ - chr->explicit_be_open = muxes_realized ? 0 : 1; + *be_opened = muxes_realized; chr->is_mux = 1; + if (!qemu_chr_fe_init(&d->chr, drv, errp)) { + qemu_chr_free(chr); + return NULL; + } return chr; } +CharDriverState *qemu_chr_fe_get_driver(CharBackend *be) +{ + return be->chr; +} + +bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp) +{ + int tag = 0; + + if (s->is_mux) { + MuxDriver *d = s->opaque; + + if (d->mux_cnt >= MAX_MUX) { + goto unavailable; + } + + d->backends[d->mux_cnt] = b; + tag = d->mux_cnt++; + } else if (s->be) { + goto unavailable; + } else { + s->be = b; + } + + b->fe_open = false; + b->tag = tag; + b->chr = s; + return true; + +unavailable: + error_setg(errp, QERR_DEVICE_IN_USE, s->label); + return false; +} + +static bool qemu_chr_is_busy(CharDriverState *s) +{ + if (s->is_mux) { + MuxDriver *d = s->opaque; + return d->mux_cnt >= 0; + } else { + return s->be != NULL; + } +} + +void qemu_chr_fe_deinit(CharBackend *b) +{ + assert(b); + + if (b->chr) { + qemu_chr_fe_set_handlers(b, NULL, NULL, NULL, NULL, NULL, true); + if (b->chr->be == b) { + b->chr->be = NULL; + } + if (b->chr->is_mux) { + MuxDriver *d = b->chr->opaque; + d->backends[b->tag] = NULL; + } + b->chr = NULL; + } +} + +void qemu_chr_fe_set_handlers(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque, + GMainContext *context, + bool set_open) +{ + CharDriverState *s; + int fe_open; + + s = b->chr; + if (!s) { + return; + } + + if (!opaque && !fd_can_read && !fd_read && !fd_event) { + fe_open = 0; + remove_fd_in_watch(s); + } else { + fe_open = 1; + } + b->chr_can_read = fd_can_read; + b->chr_read = fd_read; + b->chr_event = fd_event; + b->opaque = opaque; + if (s->chr_update_read_handler) { + s->chr_update_read_handler(s, context); + } + + if (set_open) { + qemu_chr_fe_set_open(b, fe_open); + } + + if (fe_open) { + qemu_chr_fe_take_focus(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); + } + } + + if (s->is_mux) { + mux_chr_set_handlers(s, context); + } +} + +void qemu_chr_fe_take_focus(CharBackend *b) +{ + if (!b->chr) { + return; + } + + if (b->chr->is_mux) { + mux_set_focus(b->chr, b->tag); + } +} typedef struct IOWatchPoll { @@ -839,6 +1018,7 @@ typedef struct IOWatchPoll IOCanReadHandler *fd_can_read; GSourceFunc fd_read; void *opaque; + GMainContext *context; } IOWatchPoll; static IOWatchPoll *io_watch_poll_from_source(GSource *source) @@ -846,7 +1026,8 @@ static IOWatchPoll *io_watch_poll_from_source(GSource *source) return container_of(source, IOWatchPoll, parent); } -static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_) +static gboolean io_watch_poll_prepare(GSource *source, + gint *timeout_) { IOWatchPoll *iwp = io_watch_poll_from_source(source); bool now_active = iwp->fd_can_read(iwp->opaque) > 0; @@ -859,7 +1040,7 @@ static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_) iwp->src = qio_channel_create_watch( iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL); g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL); - g_source_attach(iwp->src, NULL); + g_source_attach(iwp->src, iwp->context); } else { g_source_destroy(iwp->src); g_source_unref(iwp->src); @@ -903,22 +1084,31 @@ static GSourceFuncs io_watch_poll_funcs = { }; /* Can only be used for read */ -static guint io_add_watch_poll(QIOChannel *ioc, +static guint io_add_watch_poll(CharDriverState *chr, + QIOChannel *ioc, IOCanReadHandler *fd_can_read, QIOChannelFunc fd_read, - gpointer user_data) + gpointer user_data, + GMainContext *context) { IOWatchPoll *iwp; int tag; + char *name; - iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll)); + iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, + sizeof(IOWatchPoll)); iwp->fd_can_read = fd_can_read; iwp->opaque = user_data; iwp->ioc = ioc; iwp->fd_read = (GSourceFunc) fd_read; iwp->src = NULL; + iwp->context = context; - tag = g_source_attach(&iwp->parent, NULL); + name = g_strdup_printf("chardev-iowatch-%s", chr->label); + g_source_set_name((GSource *)iwp, name); + g_free(name); + + tag = g_source_attach(&iwp->parent, context); g_source_unref(&iwp->parent); return tag; } @@ -1050,19 +1240,21 @@ static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond) return qio_channel_create_watch(s->ioc_out, cond); } -static void fd_chr_update_read_handler(CharDriverState *chr) +static void fd_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { FDCharDriver *s = chr->opaque; remove_fd_in_watch(chr); if (s->ioc_in) { - chr->fd_in_tag = io_add_watch_poll(s->ioc_in, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in, fd_chr_read_poll, - fd_chr_read, chr); + fd_chr_read, chr, + context); } } -static void fd_chr_close(struct CharDriverState *chr) +static void fd_chr_free(struct CharDriverState *chr) { FDCharDriver *s = chr->opaque; @@ -1084,6 +1276,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, { CharDriverState *chr; FDCharDriver *s; + char *name; chr = qemu_chr_alloc(backend, errp); if (!chr) { @@ -1091,14 +1284,20 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, } s = g_new0(FDCharDriver, 1); s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in)); + name = g_strdup_printf("chardev-file-in-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name); + g_free(name); s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out)); + name = g_strdup_printf("chardev-file-out-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name); + g_free(name); qemu_set_nonblock(fd_out); s->chr = chr; chr->opaque = s; chr->chr_add_watch = fd_chr_add_watch; chr->chr_write = fd_chr_write; chr->chr_update_read_handler = fd_chr_update_read_handler; - chr->chr_close = fd_chr_close; + chr->chr_free = fd_chr_free; return chr; } @@ -1106,6 +1305,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, static CharDriverState *qemu_chr_open_pipe(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *opts = backend->u.pipe.data; @@ -1179,15 +1379,16 @@ static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) tcsetattr (0, TCSANOW, &tty); } -static void qemu_chr_close_stdio(struct CharDriverState *chr) +static void qemu_chr_free_stdio(struct CharDriverState *chr) { term_exit(); - fd_chr_close(chr); + fd_chr_free(chr); } static CharDriverState *qemu_chr_open_stdio(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevStdio *opts = backend->u.stdio.data; @@ -1216,12 +1417,15 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, sigaction(SIGCONT, &act, NULL); chr = qemu_chr_open_fd(0, 1, common, errp); - chr->chr_close = qemu_chr_close_stdio; + if (!chr) { + return NULL; + } + chr->chr_free = qemu_chr_free_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio; if (opts->has_signal) { stdio_allow_signal = opts->signal; } - qemu_chr_fe_set_echo(chr, false); + qemu_chr_set_echo_stdio(chr, false); return chr; } @@ -1266,6 +1470,7 @@ static gboolean pty_chr_timer(gpointer opaque) static void pty_chr_rearm_timer(CharDriverState *chr, int ms) { PtyCharDriver *s = chr->opaque; + char *name; if (s->timer_tag) { g_source_remove(s->timer_tag); @@ -1273,10 +1478,14 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms) } if (ms == 1000) { + name = g_strdup_printf("pty-timer-secs-%s", chr->label); s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr); } else { + name = g_strdup_printf("pty-timer-ms-%s", chr->label); s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr); } + g_source_set_name_by_id(s->timer_tag, name); + g_free(name); } /* Called with chr_write_lock held. */ @@ -1302,7 +1511,8 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr) } } -static void pty_chr_update_read_handler(CharDriverState *chr) +static void pty_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { qemu_mutex_lock(&chr->chr_write_lock); pty_chr_update_read_handler_locked(chr); @@ -1404,14 +1614,15 @@ static void pty_chr_state(CharDriverState *chr, int connected) s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr); } if (!chr->fd_in_tag) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc, pty_chr_read_poll, - pty_chr_read, chr); + pty_chr_read, + chr, NULL); } } } -static void pty_chr_close(struct CharDriverState *chr) +static void pty_chr_free(struct CharDriverState *chr) { PtyCharDriver *s = chr->opaque; @@ -1430,6 +1641,7 @@ static void pty_chr_close(struct CharDriverState *chr) static CharDriverState *qemu_chr_open_pty(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -1437,6 +1649,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, int master_fd, slave_fd; char pty_name[PATH_MAX]; ChardevCommon *common = backend->u.pty.data; + char *name; master_fd = qemu_openpty_raw(&slave_fd, pty_name); if (master_fd < 0) { @@ -1464,11 +1677,14 @@ static CharDriverState *qemu_chr_open_pty(const char *id, chr->opaque = s; chr->chr_write = pty_chr_write; chr->chr_update_read_handler = pty_chr_update_read_handler; - chr->chr_close = pty_chr_close; + chr->chr_free = pty_chr_free; chr->chr_add_watch = pty_chr_add_watch; - chr->explicit_be_open = true; + *be_opened = false; s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); + name = g_strdup_printf("chardev-pty-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(s->ioc), name); + g_free(name); s->timer_tag = 0; return chr; @@ -1659,21 +1875,25 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static void qemu_chr_close_tty(CharDriverState *chr) +static void qemu_chr_free_tty(CharDriverState *chr) { - fd_chr_close(chr); + fd_chr_free(chr); } static CharDriverState *qemu_chr_open_tty_fd(int fd, ChardevCommon *backend, + bool *be_opened, Error **errp) { CharDriverState *chr; tty_serial_init(fd, 115200, 'N', 8, 1); chr = qemu_chr_open_fd(fd, fd, backend, errp); + if (!chr) { + return NULL; + } chr->chr_ioctl = tty_serial_ioctl; - chr->chr_close = qemu_chr_close_tty; + chr->chr_free = qemu_chr_free_tty; return chr; } #endif /* __linux__ || __sun__ */ @@ -1779,7 +1999,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static void pp_close(CharDriverState *chr) +static void pp_free(CharDriverState *chr) { ParallelCharDriver *drv = chr->opaque; int fd = drv->fd; @@ -1793,6 +2013,7 @@ static void pp_close(CharDriverState *chr) static CharDriverState *qemu_chr_open_pp_fd(int fd, ChardevCommon *backend, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -1813,7 +2034,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd, chr->opaque = drv; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - chr->chr_close = pp_close; + chr->chr_free = pp_free; drv->fd = fd; drv->mode = IEEE1284_MODE_COMPAT; @@ -1865,6 +2086,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) static CharDriverState *qemu_chr_open_pp_fd(int fd, ChardevCommon *backend, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -1876,7 +2098,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd, chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - chr->explicit_be_open = true; + *be_opened = false; return chr; } #endif @@ -1912,7 +2134,7 @@ typedef struct { static int win_chr_poll(void *opaque); static int win_chr_pipe_poll(void *opaque); -static void win_chr_close(CharDriverState *chr) +static void win_chr_free(CharDriverState *chr) { WinCharState *s = chr->opaque; @@ -1999,7 +2221,7 @@ static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp return 0; fail: - win_chr_close(chr); + win_chr_free(chr); return -1; } @@ -2111,7 +2333,7 @@ static CharDriverState *qemu_chr_open_win_path(const char *filename, s = g_new0(WinCharState, 1); chr->opaque = s; chr->chr_write = win_chr_write; - chr->chr_close = win_chr_close; + chr->chr_free = win_chr_free; if (win_chr_init(chr, filename, errp) < 0) { g_free(s); @@ -2197,7 +2419,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename, return 0; fail: - win_chr_close(chr); + win_chr_free(chr); return -1; } @@ -2205,6 +2427,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename, static CharDriverState *qemu_chr_open_pipe(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *opts = backend->u.pipe.data; @@ -2220,7 +2443,7 @@ static CharDriverState *qemu_chr_open_pipe(const char *id, s = g_new0(WinCharState, 1); chr->opaque = s; chr->chr_write = win_chr_write; - chr->chr_close = win_chr_close; + chr->chr_free = win_chr_free; if (win_chr_pipe_init(chr, filename, errp) < 0) { g_free(s); @@ -2251,6 +2474,7 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out, static CharDriverState *qemu_chr_open_win_con(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevCommon *common = backend->u.console.data; @@ -2376,7 +2600,7 @@ static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo) } } -static void win_stdio_close(CharDriverState *chr) +static void win_stdio_free(CharDriverState *chr) { WinStdioCharState *stdio = chr->opaque; @@ -2391,12 +2615,12 @@ static void win_stdio_close(CharDriverState *chr) } g_free(chr->opaque); - g_free(chr); } static CharDriverState *qemu_chr_open_stdio(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -2421,7 +2645,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, chr->opaque = stdio; chr->chr_write = win_stdio_write; - chr->chr_close = win_stdio_close; + chr->chr_free = win_stdio_free; if (is_console) { if (qemu_add_wait_object(stdio->hStdIn, @@ -2464,7 +2688,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, SetConsoleMode(stdio->hStdIn, dwMode); chr->chr_set_echo = qemu_chr_set_echo_win_stdio; - qemu_chr_fe_set_echo(chr, false); + qemu_chr_set_echo_win_stdio(chr, false); return chr; @@ -2479,7 +2703,6 @@ err1: } #endif /* !_WIN32 */ - /***********************************************************/ /* UDP Net console */ @@ -2545,19 +2768,21 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } -static void udp_chr_update_read_handler(CharDriverState *chr) +static void udp_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { NetCharDriver *s = chr->opaque; remove_fd_in_watch(chr); if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc, udp_chr_read_poll, - udp_chr_read, chr); + udp_chr_read, chr, + context); } } -static void udp_chr_close(CharDriverState *chr) +static void udp_chr_free(CharDriverState *chr) { NetCharDriver *s = chr->opaque; @@ -2571,6 +2796,7 @@ static void udp_chr_close(CharDriverState *chr) static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc, ChardevCommon *backend, + bool *be_opened, Error **errp) { CharDriverState *chr = NULL; @@ -2588,9 +2814,9 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc, chr->opaque = s; chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; - chr->chr_close = udp_chr_close; + chr->chr_free = udp_chr_free; /* be isn't opened until we get a connection */ - chr->explicit_be_open = true; + *be_opened = false; return chr; } @@ -2627,9 +2853,13 @@ static gboolean socket_reconnect_timeout(gpointer opaque); static void qemu_chr_socket_restart_timer(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; + char *name; assert(s->connected == 0); s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time, socket_reconnect_timeout, chr); + name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label); + g_source_set_name_by_id(s->reconnect_timer, name); + g_free(name); } static void check_report_connect_error(CharDriverState *chr, @@ -2761,13 +2991,16 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num) { TCPCharDriver *s = chr->opaque; - if (!qio_channel_has_feature(s->ioc, - QIO_CHANNEL_FEATURE_FD_PASS)) { - return -1; - } /* clear old pending fd array */ g_free(s->write_msgfds); s->write_msgfds = NULL; + s->write_msgfds_num = 0; + + if (!s->connected || + !qio_channel_has_feature(s->ioc, + QIO_CHANNEL_FEATURE_FD_PASS)) { + return -1; + } if (num) { s->write_msgfds = g_new(int, num); @@ -2842,19 +3075,24 @@ static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond) return qio_channel_create_watch(s->ioc, cond); } -static void tcp_chr_disconnect(CharDriverState *chr) +static void tcp_chr_free_connection(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; + int i; if (!s->connected) { return; } - s->connected = 0; - if (s->listen_ioc) { - s->listen_tag = qio_channel_add_watch( - QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); + if (s->read_msgfds_num) { + for (i = 0; i < s->read_msgfds_num; i++) { + close(s->read_msgfds[i]); + } + g_free(s->read_msgfds); + s->read_msgfds = NULL; + s->read_msgfds_num = 0; } + tcp_set_msgfds(chr, NULL, 0); remove_fd_in_watch(chr); object_unref(OBJECT(s->sioc)); @@ -2862,6 +3100,24 @@ static void tcp_chr_disconnect(CharDriverState *chr) object_unref(OBJECT(s->ioc)); s->ioc = NULL; g_free(chr->filename); + chr->filename = NULL; + s->connected = 0; +} + +static void tcp_chr_disconnect(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + + if (!s->connected) { + return; + } + + tcp_chr_free_connection(chr); + + if (s->listen_ioc) { + 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); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); @@ -2928,14 +3184,16 @@ static void tcp_chr_connect(void *opaque) s->connected = 1; if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc, tcp_chr_read_poll, - tcp_chr_read, chr); + tcp_chr_read, + chr, NULL); } qemu_chr_be_generic_open(chr); } -static void tcp_chr_update_read_handler(CharDriverState *chr) +static void tcp_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { TCPCharDriver *s = chr->opaque; @@ -2945,9 +3203,10 @@ static void tcp_chr_update_read_handler(CharDriverState *chr) remove_fd_in_watch(chr); if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag = io_add_watch_poll(chr, s->ioc, tcp_chr_read_poll, - tcp_chr_read, chr); + tcp_chr_read, chr, + context); } } @@ -3018,14 +3277,13 @@ static void tcp_chr_telnet_init(CharDriverState *chr) } -static void tcp_chr_tls_handshake(Object *source, - Error *err, +static void tcp_chr_tls_handshake(QIOTask *task, gpointer user_data) { CharDriverState *chr = user_data; TCPCharDriver *s = chr->opaque; - if (err) { + if (qio_task_propagate_error(task, NULL)) { tcp_chr_disconnect(chr); } else { if (s->do_telnetopt) { @@ -3042,6 +3300,7 @@ static void tcp_chr_tls_init(CharDriverState *chr) TCPCharDriver *s = chr->opaque; QIOChannelTLS *tioc; Error *err = NULL; + gchar *name; if (s->is_listen) { tioc = qio_channel_tls_new_server( @@ -3057,7 +3316,13 @@ static void tcp_chr_tls_init(CharDriverState *chr) if (tioc == NULL) { error_free(err); tcp_chr_disconnect(chr); + return; } + name = g_strdup_printf("chardev-tls-%s-%s", + s->is_listen ? "server" : "client", + chr->label); + qio_channel_set_name(QIO_CHANNEL(tioc), name); + g_free(name); object_unref(OBJECT(s->ioc)); s->ioc = QIO_CHANNEL(tioc); @@ -3068,6 +3333,19 @@ static void tcp_chr_tls_init(CharDriverState *chr) } +static void tcp_chr_set_client_ioc_name(CharDriverState *chr, + QIOChannelSocket *sioc) +{ + TCPCharDriver *s = chr->opaque; + char *name; + name = g_strdup_printf("chardev-tcp-%s-%s", + s->is_listen ? "server" : "client", + chr->label); + qio_channel_set_name(QIO_CHANNEL(sioc), name); + g_free(name); + +} + static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc) { TCPCharDriver *s = chr->opaque; @@ -3113,6 +3391,7 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd) if (!sioc) { return -1; } + tcp_chr_set_client_ioc_name(chr, sioc); ret = tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); return ret; @@ -3138,20 +3417,65 @@ static gboolean tcp_chr_accept(QIOChannel *channel, return TRUE; } -static void tcp_chr_close(CharDriverState *chr) +static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp) { TCPCharDriver *s = chr->opaque; - int i; + QIOChannelSocket *sioc; + + /* It can't wait on s->connected, since it is set asynchronously + * in TLS and telnet cases, only wait for an accepted socket */ + while (!s->ioc) { + if (s->is_listen) { + fprintf(stderr, "QEMU waiting for connection on: %s\n", + chr->filename); + qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL); + tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr); + qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL); + } else { + sioc = qio_channel_socket_new(); + tcp_chr_set_client_ioc_name(chr, sioc); + if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { + object_unref(OBJECT(sioc)); + return -1; + } + tcp_chr_new_client(chr, sioc); + object_unref(OBJECT(sioc)); + } + } + + return 0; +} + +static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp) +{ + if (chr->chr_wait_connected) { + return chr->chr_wait_connected(chr, errp); + } + + return 0; +} + +int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp) +{ + if (!be->chr) { + error_setg(errp, "missing associated backend"); + return -1; + } + + return qemu_chr_wait_connected(be->chr, errp); +} + +static void tcp_chr_free(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + + tcp_chr_free_connection(chr); if (s->reconnect_timer) { g_source_remove(s->reconnect_timer); s->reconnect_timer = 0; } qapi_free_SocketAddress(s->addr); - remove_fd_in_watch(chr); - if (s->ioc) { - object_unref(OBJECT(s->ioc)); - } if (s->listen_tag) { g_source_remove(s->listen_tag); s->listen_tag = 0; @@ -3159,37 +3483,31 @@ static void tcp_chr_close(CharDriverState *chr) if (s->listen_ioc) { object_unref(OBJECT(s->listen_ioc)); } - if (s->read_msgfds_num) { - for (i = 0; i < s->read_msgfds_num; i++) { - close(s->read_msgfds[i]); - } - g_free(s->read_msgfds); - } if (s->tls_creds) { object_unref(OBJECT(s->tls_creds)); } - if (s->write_msgfds_num) { - g_free(s->write_msgfds); - } g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque) +static void qemu_chr_socket_connected(QIOTask *task, void *opaque) { - QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(src); + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; + Error *err = NULL; - if (err) { + if (qio_task_propagate_error(task, &err)) { check_report_connect_error(chr, err); - object_unref(src); - return; + error_free(err); + goto cleanup; } s->connect_err_reported = false; tcp_chr_new_client(chr, sioc); + + cleanup: object_unref(OBJECT(sioc)); } @@ -3228,7 +3546,7 @@ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len) } } - return 0; + return len; } static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) @@ -3245,7 +3563,7 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) return i; } -static void ringbuf_chr_close(struct CharDriverState *chr) +static void ringbuf_chr_free(struct CharDriverState *chr) { RingBufCharDriver *d = chr->opaque; @@ -3257,6 +3575,7 @@ static void ringbuf_chr_close(struct CharDriverState *chr) static CharDriverState *qemu_chr_open_ringbuf(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevRingbuf *opts = backend->u.ringbuf.data; @@ -3284,7 +3603,7 @@ static CharDriverState *qemu_chr_open_ringbuf(const char *id, chr->opaque = d; chr->chr_write = ringbuf_chr_write; - chr->chr_close = ringbuf_chr_close; + chr->chr_free = ringbuf_chr_free; return chr; @@ -3785,17 +4104,14 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, typedef struct CharDriver { const char *name; ChardevBackendKind kind; - void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); - CharDriverState *(*create)(const char *id, ChardevBackend *backend, - ChardevReturn *ret, Error **errp); + CharDriverParse *parse; + CharDriverCreate *create; } CharDriver; static GSList *backends; void register_char_driver(const char *name, ChardevBackendKind kind, - void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp), - CharDriverState *(*create)(const char *id, ChardevBackend *backend, - ChardevReturn *ret, Error **errp)) + CharDriverParse *parse, CharDriverCreate *create) { CharDriver *s; @@ -3809,8 +4125,7 @@ void register_char_driver(const char *name, ChardevBackendKind kind, } CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, - void (*init)(struct CharDriverState *s), - Error **errp) + Error **errp) { Error *local_err = NULL; CharDriver *cd; @@ -3821,16 +4136,26 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, const char *id = qemu_opts_id(opts); char *bid = NULL; - if (id == NULL) { - error_setg(errp, "chardev: no id specified"); - goto err; - } - if (qemu_opt_get(opts, "backend") == NULL) { error_setg(errp, "chardev: \"%s\" missing backend", qemu_opts_id(opts)); goto err; } + + if (is_help_option(qemu_opt_get(opts, "backend"))) { + fprintf(stderr, "Available chardev backend types:\n"); + for (i = backends; i; i = i->next) { + cd = i->data; + fprintf(stderr, "%s\n", cd->name); + } + exit(!is_help_option(qemu_opt_get(opts, "backend"))); + } + + if (id == NULL) { + error_setg(errp, "chardev: no id specified"); + goto err; + } + for (i = backends; i; i = i->next) { cd = i->data; @@ -3886,7 +4211,6 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, } chr = qemu_chr_find(id); - chr->opts = opts; qapi_out: qapi_free_ChardevBackend(backend); @@ -3895,12 +4219,10 @@ qapi_out: return chr; err: - qemu_opts_del(opts); return NULL; } -CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename, - void (*init)(struct CharDriverState *s)) +CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename) { const char *p; CharDriverState *chr; @@ -3915,21 +4237,21 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename, if (!opts) return NULL; - chr = qemu_chr_new_from_opts(opts, init, &err); + chr = qemu_chr_new_from_opts(opts, &err); if (err) { error_report_err(err); } if (chr && qemu_opt_get_bool(opts, "mux", 0)) { - qemu_chr_fe_claim_no_fail(chr); monitor_init(chr, MONITOR_USE_READLINE); } + qemu_opts_del(opts); return chr; } -CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s)) +CharDriverState *qemu_chr_new(const char *label, const char *filename) { CharDriverState *chr; - chr = qemu_chr_new_noreplay(label, filename, init); + chr = qemu_chr_new_noreplay(label, filename); if (chr) { chr->replay = replay_mode != REPLAY_MODE_NONE; if (chr->replay && chr->chr_ioctl) { @@ -3941,38 +4263,40 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in return chr; } -void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo) +void qemu_chr_fe_set_echo(CharBackend *be, bool echo) { - if (chr->chr_set_echo) { + CharDriverState *chr = be->chr; + + if (chr && chr->chr_set_echo) { chr->chr_set_echo(chr, echo); } } -void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open) +void qemu_chr_fe_set_open(CharBackend *be, int fe_open) { - if (chr->fe_open == fe_open) { + CharDriverState *chr = be->chr; + + if (!chr) { return; } - chr->fe_open = fe_open; + + if (be->fe_open == fe_open) { + return; + } + be->fe_open = fe_open; if (chr->chr_set_fe_open) { chr->chr_set_fe_open(chr, fe_open); } } -void qemu_chr_fe_event(struct CharDriverState *chr, int event) -{ - if (chr->chr_fe_event) { - chr->chr_fe_event(chr, event); - } -} - -guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, +guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, GIOFunc func, void *user_data) { + CharDriverState *s = be->chr; GSource *src; guint tag; - if (s->chr_add_watch == NULL) { + if (!s || s->chr_add_watch == NULL) { return 0; } @@ -3988,41 +4312,22 @@ guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, return tag; } -int qemu_chr_fe_claim(CharDriverState *s) +void qemu_chr_fe_disconnect(CharBackend *be) { - if (s->avail_connections < 1) { - return -1; - } - s->avail_connections--; - return 0; -} + CharDriverState *chr = be->chr; -void qemu_chr_fe_claim_no_fail(CharDriverState *s) -{ - if (qemu_chr_fe_claim(s) != 0) { - fprintf(stderr, "%s: error chardev \"%s\" already used\n", - __func__, s->label); - exit(1); - } -} - -void qemu_chr_fe_release(CharDriverState *s) -{ - s->avail_connections++; -} - -void qemu_chr_disconnect(CharDriverState *chr) -{ - if (chr->chr_disconnect) { + if (chr && chr->chr_disconnect) { chr->chr_disconnect(chr); } } static void qemu_chr_free_common(CharDriverState *chr) { + if (chr->be) { + chr->be->chr = NULL; + } g_free(chr->filename); g_free(chr->label); - qemu_opts_del(chr->opts); if (chr->logfd != -1) { close(chr->logfd); } @@ -4032,8 +4337,8 @@ static void qemu_chr_free_common(CharDriverState *chr) void qemu_chr_free(CharDriverState *chr) { - if (chr->chr_close) { - chr->chr_close(chr); + if (chr->chr_free) { + chr->chr_free(chr); } qemu_chr_free_common(chr); } @@ -4054,7 +4359,7 @@ ChardevInfoList *qmp_query_chardev(Error **errp) 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->fe_open; + info->value->frontend_open = chr->be && chr->be->fe_open; info->next = chr_list; chr_list = info; @@ -4193,19 +4498,32 @@ QemuOptsList qemu_chardev_opts = { static CharDriverState *qmp_chardev_open_file(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevFile *file = backend->u.file.data; ChardevCommon *common = qapi_ChardevFile_base(file); HANDLE out; + DWORD accessmode; + DWORD flags; if (file->has_in) { error_setg(errp, "input file not supported"); return NULL; } - out = CreateFile(file->out, GENERIC_WRITE, FILE_SHARE_READ, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file->has_append && file->append) { + /* Append to file if it already exists. */ + accessmode = FILE_GENERIC_WRITE & ~FILE_WRITE_DATA; + flags = OPEN_ALWAYS; + } else { + /* Truncate file if it already exists. */ + accessmode = GENERIC_WRITE; + flags = CREATE_ALWAYS; + } + + out = CreateFile(file->out, accessmode, FILE_SHARE_READ, NULL, flags, + FILE_ATTRIBUTE_NORMAL, NULL); if (out == INVALID_HANDLE_VALUE) { error_setg(errp, "open %s failed", file->out); return NULL; @@ -4216,6 +4534,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id, static CharDriverState *qmp_chardev_open_serial(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *serial = backend->u.serial.data; @@ -4240,6 +4559,7 @@ static int qmp_chardev_open_file_source(char *src, int flags, static CharDriverState *qmp_chardev_open_file(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevFile *file = backend->u.file.data; @@ -4274,6 +4594,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id, static CharDriverState *qmp_chardev_open_serial(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *serial = backend->u.serial.data; @@ -4285,7 +4606,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id, return NULL; } qemu_set_nonblock(fd); - return qemu_chr_open_tty_fd(fd, common, errp); + return qemu_chr_open_tty_fd(fd, common, be_opened, errp); } #endif @@ -4293,6 +4614,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id, static CharDriverState *qmp_chardev_open_parallel(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevHostdev *parallel = backend->u.parallel.data; @@ -4303,7 +4625,7 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id, if (fd < 0) { return NULL; } - return qemu_chr_open_pp_fd(fd, common, errp); + return qemu_chr_open_pp_fd(fd, common, be_opened, errp); } #endif @@ -4322,6 +4644,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque) } sioc = qio_channel_socket_new(); + tcp_chr_set_client_ioc_name(chr, sioc); qio_channel_socket_connect_async(sioc, s->addr, qemu_chr_socket_connected, chr, NULL); @@ -4332,6 +4655,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque) static CharDriverState *qmp_chardev_open_socket(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { CharDriverState *chr; @@ -4389,12 +4713,18 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, } } - qapi_copy_SocketAddress(&s->addr, sock->addr); + s->addr = QAPI_CLONE(SocketAddress, sock->addr); + + qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE); + if (s->is_unix) { + qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS); + } chr->opaque = s; + chr->chr_wait_connected = tcp_chr_wait_connected; chr->chr_write = tcp_chr_write; chr->chr_sync_read = tcp_chr_sync_read; - chr->chr_close = tcp_chr_close; + chr->chr_free = tcp_chr_free; chr->chr_disconnect = tcp_chr_disconnect; chr->get_msgfds = tcp_get_msgfds; chr->set_msgfds = tcp_set_msgfds; @@ -4402,7 +4732,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, chr->chr_add_watch = tcp_chr_add_watch; chr->chr_update_read_handler = tcp_chr_update_read_handler; /* be isn't opened until we get a connection */ - chr->explicit_be_open = true; + *be_opened = false; chr->filename = SocketAddress_to_str("disconnected:", addr, is_listen, is_telnet); @@ -4415,32 +4745,37 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, s->reconnect_time = reconnect; } - sioc = qio_channel_socket_new(); if (s->reconnect_time) { + sioc = qio_channel_socket_new(); + tcp_chr_set_client_ioc_name(chr, sioc); qio_channel_socket_connect_async(sioc, s->addr, qemu_chr_socket_connected, chr, NULL); - } else if (s->is_listen) { - if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) { - goto error; - } - s->listen_ioc = sioc; - if (is_waitconnect) { - fprintf(stderr, "QEMU waiting for connection on: %s\n", - chr->filename); - tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr); - } - qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL); - if (!s->ioc) { - s->listen_tag = qio_channel_add_watch( - QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); - } } else { - if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { + if (s->is_listen) { + char *name; + sioc = qio_channel_socket_new(); + + name = g_strdup_printf("chardev-tcp-listener-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(sioc), name); + g_free(name); + + if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) { + goto error; + } + s->listen_ioc = sioc; + if (is_waitconnect && + qemu_chr_wait_connected(chr, errp) < 0) { + goto error; + } + if (!s->ioc) { + s->listen_tag = qio_channel_add_watch( + QIO_CHANNEL(s->listen_ioc), G_IO_IN, + tcp_chr_accept, chr, NULL); + } + } else if (qemu_chr_wait_connected(chr, errp) < 0) { goto error; } - tcp_chr_new_client(chr, sioc); - object_unref(OBJECT(sioc)); } return chr; @@ -4460,11 +4795,14 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, static CharDriverState *qmp_chardev_open_udp(const char *id, ChardevBackend *backend, ChardevReturn *ret, + bool *be_opened, Error **errp) { ChardevUdp *udp = backend->u.udp.data; ChardevCommon *common = qapi_ChardevUdp_base(udp); QIOChannelSocket *sioc = qio_channel_socket_new(); + char *name; + CharDriverState *chr; if (qio_channel_socket_dgram_sync(sioc, udp->local, udp->remote, @@ -4472,7 +4810,26 @@ static CharDriverState *qmp_chardev_open_udp(const char *id, object_unref(OBJECT(sioc)); return NULL; } - return qemu_chr_open_udp(sioc, common, errp); + chr = qemu_chr_open_udp(sioc, common, be_opened, errp); + + name = g_strdup_printf("chardev-udp-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(sioc), name); + g_free(name); + + return chr; +} + + +bool qemu_chr_has_feature(CharDriverState *chr, + CharDriverFeature feature) +{ + return test_bit(feature, chr->features); +} + +void qemu_chr_set_feature(CharDriverState *chr, + CharDriverFeature feature) +{ + return set_bit(feature, chr->features); } ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, @@ -4483,19 +4840,19 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, Error *local_err = NULL; GSList *i; CharDriver *cd; + bool be_opened = true; chr = qemu_chr_find(id); if (chr) { error_setg(errp, "Chardev '%s' already exists", id); - g_free(ret); - return NULL; + goto out_error; } for (i = backends; i; i = i->next) { cd = i->data; if (cd->kind == backend->type) { - chr = cd->create(id, backend, ret, &local_err); + chr = cd->create(id, backend, ret, &be_opened, &local_err); if (local_err) { error_propagate(errp, local_err); goto out_error; @@ -4511,12 +4868,10 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, } chr->label = g_strdup(id); - chr->avail_connections = - (backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; if (!chr->filename) { chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]); } - if (!chr->explicit_be_open) { + if (be_opened) { qemu_chr_be_event(chr, CHR_EVENT_OPENED); } QTAILQ_INSERT_TAIL(&chardevs, chr, next); @@ -4536,8 +4891,7 @@ void qmp_chardev_remove(const char *id, Error **errp) error_setg(errp, "Chardev '%s' not found", id); return; } - if (chr->chr_can_read || chr->chr_read || - chr->chr_event || chr->handler_opaque) { + if (qemu_chr_is_busy(chr)) { error_setg(errp, "Chardev '%s' is busy", id); return; } @@ -4549,7 +4903,7 @@ void qmp_chardev_remove(const char *id, Error **errp) qemu_chr_delete(chr); } -static void qemu_chr_cleanup(void) +void qemu_chr_cleanup(void) { CharDriverState *chr, *tmp; @@ -4604,8 +4958,6 @@ static void register_types(void) * is specified */ qemu_add_machine_init_done_notifier(&muxes_realize_notify); - - atexit(qemu_chr_cleanup); } type_init(register_types);