X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/df2c35902e315c772ca97ca29cb6398ddc4a5861..2f4aa2329945c741e10fd5b6631e99a4be25ea60:/qemu-char.c diff --git a/qemu-char.c b/qemu-char.c index a50b8fb3a3..fb456cec34 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -39,6 +39,7 @@ #include "io/channel-file.h" #include "io/channel-tls.h" #include "sysemu/replay.h" +#include "qemu/help_option.h" #include @@ -440,17 +441,20 @@ void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) 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(s, (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) +void qemu_chr_add_handlers_full(CharDriverState *s, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque, + GMainContext *context) { int fe_open; @@ -464,8 +468,9 @@ void qemu_chr_add_handlers(CharDriverState *s, 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 (fe_open && s->chr_update_read_handler) { + s->chr_update_read_handler(s, context); + } if (!s->explicit_fe_open) { qemu_chr_fe_set_open(s, fe_open); @@ -478,6 +483,16 @@ void qemu_chr_add_handlers(CharDriverState *s, } } +void qemu_chr_add_handlers(CharDriverState *s, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque) +{ + qemu_chr_add_handlers_full(s, fd_can_read, fd_read, + fd_event, opaque, NULL); +} + static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { return len; @@ -556,7 +571,9 @@ 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->drv, (uint8_t *)buf1, strlen(buf1)); d->linestart = 0; } ret += qemu_chr_fe_write(d->drv, buf+i, 1); @@ -594,13 +611,15 @@ 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_fe_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_fe_write_all(chr, (uint8_t *)ebuf, strlen(ebuf)); else - qemu_chr_fe_write(chr, (uint8_t *)&mux_help[i][j], 1); + qemu_chr_fe_write_all(chr, (uint8_t *)&mux_help[i][j], 1); } } } @@ -625,7 +644,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_fe_write_all(chr, (uint8_t *)term, strlen(term)); exit(0); break; } @@ -715,7 +734,8 @@ static void mux_chr_event(void *opaque, int event) mux_chr_send_event(d, i, event); } -static void mux_chr_update_read_handler(CharDriverState *chr) +static void mux_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { MuxDriver *d = chr->opaque; @@ -729,8 +749,10 @@ static void mux_chr_update_read_handler(CharDriverState *chr) 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); + qemu_chr_add_handlers_full(d->drv, mux_chr_can_read, + mux_chr_read, + mux_chr_event, + chr, context); } if (d->focus != -1) { mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); @@ -786,6 +808,13 @@ static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond) return d->drv->chr_add_watch(d->drv, cond); } +static void mux_chr_close(struct CharDriverState *chr) +{ + MuxDriver *d = chr->opaque; + + g_free(d); +} + static CharDriverState *qemu_chr_open_mux(const char *id, ChardevBackend *backend, ChardevReturn *ret, Error **errp) @@ -810,6 +839,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id, chr->opaque = d; d->drv = drv; d->focus = -1; + chr->chr_close = mux_chr_close; chr->chr_write = mux_chr_write; chr->chr_update_read_handler = mux_chr_update_read_handler; chr->chr_accept_input = mux_chr_accept_input; @@ -838,6 +868,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) @@ -845,7 +876,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; @@ -858,7 +890,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); @@ -905,19 +937,22 @@ static GSourceFuncs io_watch_poll_funcs = { static guint io_add_watch_poll(QIOChannel *ioc, IOCanReadHandler *fd_can_read, QIOChannelFunc fd_read, - gpointer user_data) + gpointer user_data, + GMainContext *context) { IOWatchPoll *iwp; int tag; - 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); + tag = g_source_attach(&iwp->parent, context); g_source_unref(&iwp->parent); return tag; } @@ -1049,7 +1084,8 @@ 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; @@ -1057,7 +1093,8 @@ static void fd_chr_update_read_handler(CharDriverState *chr) if (s->ioc_in) { chr->fd_in_tag = io_add_watch_poll(s->ioc_in, fd_chr_read_poll, - fd_chr_read, chr); + fd_chr_read, chr, + context); } } @@ -1215,6 +1252,9 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, sigaction(SIGCONT, &act, NULL); chr = qemu_chr_open_fd(0, 1, common, errp); + if (!chr) { + return NULL; + } chr->chr_close = qemu_chr_close_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio; if (opts->has_signal) { @@ -1301,7 +1341,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); @@ -1405,7 +1446,8 @@ static void pty_chr_state(CharDriverState *chr, int connected) if (!chr->fd_in_tag) { chr->fd_in_tag = io_add_watch_poll(s->ioc, pty_chr_read_poll, - pty_chr_read, chr); + pty_chr_read, + chr, NULL); } } } @@ -1671,6 +1713,9 @@ static CharDriverState *qemu_chr_open_tty_fd(int fd, 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; return chr; @@ -2544,7 +2589,8 @@ 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; @@ -2552,7 +2598,8 @@ static void udp_chr_update_read_handler(CharDriverState *chr) if (s->ioc) { chr->fd_in_tag = io_add_watch_poll(s->ioc, udp_chr_read_poll, - udp_chr_read, chr); + udp_chr_read, chr, + context); } } @@ -2763,6 +2810,7 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num) /* 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, @@ -2843,19 +2891,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)); @@ -2863,6 +2916,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); @@ -2931,12 +3002,14 @@ static void tcp_chr_connect(void *opaque) if (s->ioc) { chr->fd_in_tag = io_add_watch_poll(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; @@ -2948,7 +3021,8 @@ static void tcp_chr_update_read_handler(CharDriverState *chr) if (s->ioc) { chr->fd_in_tag = io_add_watch_poll(s->ioc, tcp_chr_read_poll, - tcp_chr_read, chr); + tcp_chr_read, chr, + context); } } @@ -3144,7 +3218,9 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp) TCPCharDriver *s = chr->opaque; QIOChannelSocket *sioc; - while (!s->connected) { + /* 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); @@ -3177,17 +3253,14 @@ int qemu_chr_wait_connected(CharDriverState *chr, Error **errp) static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; - int i; + + 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; @@ -3195,18 +3268,9 @@ 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); } @@ -3857,16 +3921,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; @@ -4234,14 +4308,26 @@ static CharDriverState *qmp_chardev_open_file(const char *id, 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;