X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-char.c;h=64e824d0acb1a0f773060b45c9b5f7eea0f43b93;hb=fd8192a5a868ddbb711abbf4a871b09bbb1dae04;hp=cf02cabba2d879951b954e9e967b3b79fcea5837;hpb=5ab8211b9e1215ed136164c6d9622f2c928f7a8d;p=qemu.git diff --git a/qemu-char.c b/qemu-char.c index cf02cabba..64e824d0a 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -26,7 +26,7 @@ #include "ui/console.h" #include "sysemu/sysemu.h" #include "qemu/timer.h" -#include "char/char.h" +#include "sysemu/char.h" #include "hw/usb.h" #include "qmp-commands.h" @@ -108,10 +108,10 @@ void qemu_chr_be_event(CharDriverState *s, int event) /* Keep track if the char device is open */ switch (event) { case CHR_EVENT_OPENED: - s->opened = 1; + s->be_open = 1; break; case CHR_EVENT_CLOSED: - s->opened = 0; + s->be_open = 0; break; } @@ -120,7 +120,7 @@ void qemu_chr_be_event(CharDriverState *s, int event) s->chr_event(s->handler_opaque, event); } -static gboolean qemu_chr_generic_open_bh(gpointer opaque) +static gboolean qemu_chr_be_generic_open_bh(gpointer opaque) { CharDriverState *s = opaque; qemu_chr_be_event(s, CHR_EVENT_OPENED); @@ -128,10 +128,10 @@ static gboolean qemu_chr_generic_open_bh(gpointer opaque) return FALSE; } -void qemu_chr_generic_open(CharDriverState *s) +void qemu_chr_be_generic_open(CharDriverState *s) { if (s->idle_tag == 0) { - s->idle_tag = g_idle_add(qemu_chr_generic_open_bh, s); + s->idle_tag = g_idle_add(qemu_chr_be_generic_open_bh, s); } } @@ -140,6 +140,33 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) return s->chr_write(s, buf, len); } +int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) +{ + int offset = 0; + int res; + + while (offset < len) { + do { + res = s->chr_write(s, buf + offset, len - offset); + if (res == -1 && errno == EAGAIN) { + g_usleep(100); + } + } while (res == -1 && errno == EAGAIN); + + if (res == 0) { + break; + } + + if (res < 0) { + return res; + } + + offset += res; + } + + return offset; +} + int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg) { if (!s->chr_ioctl) @@ -194,9 +221,12 @@ void qemu_chr_add_handlers(CharDriverState *s, IOEventHandler *fd_event, void *opaque) { + int fe_open; + if (!opaque && !fd_can_read && !fd_read && !fd_event) { - /* chr driver being released. */ - ++s->avail_connections; + fe_open = 0; + } else { + fe_open = 1; } s->chr_can_read = fd_can_read; s->chr_read = fd_read; @@ -205,10 +235,14 @@ void qemu_chr_add_handlers(CharDriverState *s, if (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 (s->opened) { - qemu_chr_generic_open(s); + if (fe_open && s->be_open) { + qemu_chr_be_generic_open(s); } } @@ -217,7 +251,7 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static CharDriverState *qemu_chr_open_null(QemuOpts *opts) +static CharDriverState *qemu_chr_open_null(void) { CharDriverState *chr; @@ -478,11 +512,10 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) 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_guest_open = NULL; - chr->chr_guest_close = NULL; + chr->chr_set_fe_open = NULL; /* Muxes are always open on creation */ - qemu_chr_generic_open(chr); + qemu_chr_be_generic_open(chr); return chr; } @@ -533,71 +566,95 @@ int send_all(int fd, const void *_buf, int len1) } return len1 - len; } -#endif /* !_WIN32 */ -#ifndef _WIN32 +int recv_all(int fd, void *_buf, int len1, bool single_read) +{ + int ret, len; + uint8_t *buf = _buf; + + len = len1; + while ((len > 0) && (ret = read(fd, buf, len)) != 0) { + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) { + return -1; + } + continue; + } else { + if (single_read) { + return ret; + } + buf += ret; + len -= ret; + } + } + return len1 - len; +} + +#endif /* !_WIN32 */ typedef struct IOWatchPoll { + GSource parent; + + GIOChannel *channel; GSource *src; - int max_size; IOCanReadHandler *fd_can_read; + GSourceFunc fd_read; void *opaque; - - QTAILQ_ENTRY(IOWatchPoll) node; } IOWatchPoll; -static QTAILQ_HEAD(, IOWatchPoll) io_watch_poll_list = - QTAILQ_HEAD_INITIALIZER(io_watch_poll_list); - static IOWatchPoll *io_watch_poll_from_source(GSource *source) { - IOWatchPoll *i; - - QTAILQ_FOREACH(i, &io_watch_poll_list, node) { - if (i->src == source) { - return i; - } - } - - return NULL; + return container_of(source, IOWatchPoll, parent); } static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_) { IOWatchPoll *iwp = io_watch_poll_from_source(source); - - iwp->max_size = iwp->fd_can_read(iwp->opaque); - if (iwp->max_size == 0) { + bool now_active = iwp->fd_can_read(iwp->opaque) > 0; + bool was_active = iwp->src != NULL; + if (was_active == now_active) { return FALSE; } - return g_io_watch_funcs.prepare(source, timeout_); + if (now_active) { + iwp->src = g_io_create_watch(iwp->channel, G_IO_IN | G_IO_ERR | G_IO_HUP); + g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL); + g_source_attach(iwp->src, NULL); + } else { + g_source_destroy(iwp->src); + g_source_unref(iwp->src); + iwp->src = NULL; + } + return FALSE; } static gboolean io_watch_poll_check(GSource *source) { - IOWatchPoll *iwp = io_watch_poll_from_source(source); - - if (iwp->max_size == 0) { - return FALSE; - } - - return g_io_watch_funcs.check(source); + return FALSE; } static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { - return g_io_watch_funcs.dispatch(source, callback, user_data); + abort(); } static void io_watch_poll_finalize(GSource *source) { + /* Due to a glib bug, removing the last reference to a source + * inside a finalize callback causes recursive locking (and a + * deadlock). This is not a problem inside other callbacks, + * including dispatch callbacks, so we call io_remove_watch_poll + * to remove this source. At this point, iwp->src must + * be NULL, or we would leak it. + * + * This would be solved much more elegantly by child sources, + * but we support older glib versions that do not have them. + */ IOWatchPoll *iwp = io_watch_poll_from_source(source); - QTAILQ_REMOVE(&io_watch_poll_list, iwp, node); - g_io_watch_funcs.finalize(source); + assert(iwp->src == NULL); } static GSourceFuncs io_watch_poll_funcs = { @@ -614,26 +671,40 @@ static guint io_add_watch_poll(GIOChannel *channel, gpointer user_data) { IOWatchPoll *iwp; - GSource *src; - guint tag; - - src = g_io_create_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP); - g_source_set_funcs(src, &io_watch_poll_funcs); - g_source_set_callback(src, (GSourceFunc)fd_read, user_data, NULL); - tag = g_source_attach(src, NULL); - g_source_unref(src); + int tag; - iwp = g_malloc0(sizeof(*iwp)); - iwp->src = src; - iwp->max_size = 0; + iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll)); iwp->fd_can_read = fd_can_read; iwp->opaque = user_data; + iwp->channel = channel; + iwp->fd_read = (GSourceFunc) fd_read; + iwp->src = NULL; - QTAILQ_INSERT_HEAD(&io_watch_poll_list, iwp, node); - + tag = g_source_attach(&iwp->parent, NULL); + g_source_unref(&iwp->parent); return tag; } +static void io_remove_watch_poll(guint tag) +{ + GSource *source; + IOWatchPoll *iwp; + + g_return_if_fail (tag > 0); + + source = g_main_context_find_source_by_id(NULL, tag); + g_return_if_fail (source != NULL); + + iwp = io_watch_poll_from_source(source); + if (iwp->src) { + g_source_destroy(iwp->src); + g_source_unref(iwp->src); + iwp->src = NULL; + } + g_source_destroy(&iwp->parent); +} + +#ifndef _WIN32 static GIOChannel *io_channel_from_fd(int fd) { GIOChannel *chan; @@ -649,6 +720,7 @@ static GIOChannel *io_channel_from_fd(int fd) return chan; } +#endif static GIOChannel *io_channel_from_socket(int fd) { @@ -670,35 +742,41 @@ static GIOChannel *io_channel_from_socket(int fd) return chan; } -static int io_channel_send_all(GIOChannel *fd, const void *_buf, int len1) +static int io_channel_send(GIOChannel *fd, const void *buf, size_t len) { GIOStatus status; - gsize bytes_written; - int len; - const uint8_t *buf = _buf; + size_t offset; - len = len1; - while (len > 0) { - status = g_io_channel_write_chars(fd, (const gchar *)buf, len, + offset = 0; + while (offset < len) { + gsize bytes_written; + + status = g_io_channel_write_chars(fd, buf + offset, len - offset, &bytes_written, NULL); if (status != G_IO_STATUS_NORMAL) { if (status == G_IO_STATUS_AGAIN) { + /* If we've written any data, return a partial write. */ + if (offset) { + break; + } errno = EAGAIN; - return -1; } else { errno = EINVAL; - return -1; } + + return -1; } else if (status == G_IO_STATUS_EOF) { break; - } else { - buf += bytes_written; - len -= bytes_written; } + + offset += bytes_written; } - return len1 - len; + + return offset; } +#ifndef _WIN32 + typedef struct FDCharDriver { CharDriverState *chr; GIOChannel *fd_in, *fd_out; @@ -711,7 +789,7 @@ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { FDCharDriver *s = chr->opaque; - return io_channel_send_all(s->fd_out, buf, len); + return io_channel_send(s->fd_out, buf, len); } static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) @@ -728,12 +806,16 @@ static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) len = s->max_size; } if (len == 0) { - return FALSE; + return TRUE; } status = g_io_channel_read_chars(chan, (gchar *)buf, len, &bytes_read, NULL); if (status == G_IO_STATUS_EOF) { + if (s->fd_in_tag) { + io_remove_watch_poll(s->fd_in_tag); + s->fd_in_tag = 0; + } qemu_chr_be_event(chr, CHR_EVENT_CLOSED); return FALSE; } @@ -764,7 +846,8 @@ static void fd_chr_update_read_handler(CharDriverState *chr) FDCharDriver *s = chr->opaque; if (s->fd_in_tag) { - g_source_remove(s->fd_in_tag); + io_remove_watch_poll(s->fd_in_tag); + s->fd_in_tag = 0; } if (s->fd_in) { @@ -777,7 +860,7 @@ static void fd_chr_close(struct CharDriverState *chr) FDCharDriver *s = chr->opaque; if (s->fd_in_tag) { - g_source_remove(s->fd_in_tag); + io_remove_watch_poll(s->fd_in_tag); s->fd_in_tag = 0; } @@ -810,28 +893,16 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) chr->chr_update_read_handler = fd_chr_update_read_handler; chr->chr_close = fd_chr_close; - qemu_chr_generic_open(chr); + qemu_chr_be_generic_open(chr); return chr; } -static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) -{ - int fd_out; - - TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"), - O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); - if (fd_out < 0) { - return NULL; - } - return qemu_chr_open_fd(-1, fd_out); -} - -static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) +static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) { int fd_in, fd_out; char filename_in[256], filename_out[256]; - const char *filename = qemu_opt_get(opts, "path"); + const char *filename = opts->device; if (filename == NULL) { fprintf(stderr, "chardev: pipe: no filename given\n"); @@ -894,7 +965,7 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr) fd_chr_close(chr); } -static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) +static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) { CharDriverState *chr; @@ -910,8 +981,10 @@ static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) chr = qemu_chr_open_fd(0, 1); chr->chr_close = qemu_chr_close_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio; - stdio_allow_signal = qemu_opt_get_bool(opts, "signal", - display_type != DT_NOGRAPHIC); + stdio_allow_signal = display_type != DT_NOGRAPHIC; + if (opts->has_signal) { + stdio_allow_signal = opts->signal; + } qemu_chr_fe_set_echo(chr, false); return chr; @@ -984,7 +1057,6 @@ typedef struct { GIOChannel *fd; guint fd_tag; int connected; - int polling; int read_bytes; guint timer_tag; } PtyCharDriver; @@ -1000,17 +1072,12 @@ static gboolean pty_chr_timer(gpointer opaque) if (s->connected) { goto out; } - if (s->polling) { - /* If we arrive here without polling being cleared due - * read returning -EIO, then we are (re-)connected */ - pty_chr_state(chr, 1); - goto out; - } /* Next poll ... */ pty_chr_update_read_handler(chr); out: + s->timer_tag = 0; return FALSE; } @@ -1039,7 +1106,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) pty_chr_update_read_handler(chr); return 0; } - return io_channel_send_all(s->fd, buf, len); + return io_channel_send(s->fd, buf, len); } static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond) @@ -1068,8 +1135,9 @@ static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) len = sizeof(buf); if (len > s->read_bytes) len = s->read_bytes; - if (len == 0) - return FALSE; + if (len == 0) { + return TRUE; + } status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL); if (status != G_IO_STATUS_NORMAL) { pty_chr_state(chr, 0); @@ -1084,22 +1152,17 @@ static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) static void pty_chr_update_read_handler(CharDriverState *chr) { PtyCharDriver *s = chr->opaque; + GPollFD pfd; - if (s->fd_tag) { - g_source_remove(s->fd_tag); - } - - s->fd_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr); - s->polling = 1; - /* - * Short timeout here: just need wait long enougth that qemu makes - * it through the poll loop once. When reconnected we want a - * short timeout so we notice it almost instantly. Otherwise - * read() gives us -EIO instantly, making pty_chr_state() reset the - * timeout to the normal (much longer) poll interval before the - * timer triggers. - */ - pty_chr_rearm_timer(chr, 10); + pfd.fd = g_io_channel_unix_get_fd(s->fd); + pfd.events = G_IO_OUT; + pfd.revents = 0; + g_poll(&pfd, 1, 0); + if (pfd.revents & G_IO_HUP) { + pty_chr_state(chr, 0); + } else { + pty_chr_state(chr, 1); + } } static void pty_chr_state(CharDriverState *chr, int connected) @@ -1107,18 +1170,25 @@ static void pty_chr_state(CharDriverState *chr, int connected) PtyCharDriver *s = chr->opaque; if (!connected) { - g_source_remove(s->fd_tag); - s->fd_tag = 0; + if (s->fd_tag) { + io_remove_watch_poll(s->fd_tag); + s->fd_tag = 0; + } s->connected = 0; - s->polling = 0; /* (re-)connect poll interval for idle guests: once per second. * We check more frequently in case the guests sends data to * the virtual device linked to our pty. */ pty_chr_rearm_timer(chr, 1000); } else { - if (!s->connected) - qemu_chr_generic_open(chr); - s->connected = 1; + if (s->timer_tag) { + g_source_remove(s->timer_tag); + s->timer_tag = 0; + } + if (!s->connected) { + qemu_chr_be_generic_open(chr); + s->connected = 1; + s->fd_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr); + } } } @@ -1129,25 +1199,27 @@ static void pty_chr_close(struct CharDriverState *chr) int fd; if (s->fd_tag) { - g_source_remove(s->fd_tag); + io_remove_watch_poll(s->fd_tag); + s->fd_tag = 0; } fd = g_io_channel_unix_get_fd(s->fd); g_io_channel_unref(s->fd); close(fd); if (s->timer_tag) { g_source_remove(s->timer_tag); + s->timer_tag = 0; } g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) +static CharDriverState *qemu_chr_open_pty(const char *id, + ChardevReturn *ret) { CharDriverState *chr; PtyCharDriver *s; struct termios tty; - const char *label; - int master_fd, slave_fd, len; + int master_fd, slave_fd; #if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; #define q_ptsname(x) pty_name @@ -1168,17 +1240,12 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) chr = g_malloc0(sizeof(CharDriverState)); - len = strlen(q_ptsname(master_fd)) + 5; - chr->filename = g_malloc(len); - snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd)); - qemu_opt_set(opts, "path", q_ptsname(master_fd)); + chr->filename = g_strdup_printf("pty:%s", q_ptsname(master_fd)); + ret->pty = g_strdup(q_ptsname(master_fd)); + ret->has_pty = true; - label = qemu_opts_id(opts); - fprintf(stderr, "char device redirected to %s%s%s%s\n", - q_ptsname(master_fd), - label ? " (label " : "", - label ? label : "", - label ? ")" : ""); + fprintf(stderr, "char device redirected to %s (label %s)\n", + q_ptsname(master_fd), id); s = g_malloc0(sizeof(PtyCharDriver)); chr->opaque = s; @@ -1403,18 +1470,6 @@ static CharDriverState *qemu_chr_open_tty_fd(int fd) chr->chr_close = qemu_chr_close_tty; return chr; } - -static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) -{ - const char *filename = qemu_opt_get(opts, "path"); - int fd; - - TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK)); - if (fd < 0) { - return NULL; - } - return qemu_chr_open_tty_fd(fd); -} #endif /* __linux__ || __sun__ */ #if defined(__linux__) @@ -1550,7 +1605,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd) chr->chr_close = pp_close; chr->opaque = drv; - qemu_chr_generic_open(chr); + qemu_chr_be_generic_open(chr); return chr; } @@ -1835,15 +1890,10 @@ static CharDriverState *qemu_chr_open_win_path(const char *filename) g_free(chr); return NULL; } - qemu_chr_generic_open(chr); + qemu_chr_be_generic_open(chr); return chr; } -static CharDriverState *qemu_chr_open_win(QemuOpts *opts) -{ - return qemu_chr_open_win_path(qemu_opt_get(opts, "path")); -} - static int win_chr_pipe_poll(void *opaque) { CharDriverState *chr = opaque; @@ -1923,9 +1973,9 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) } -static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) +static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) { - const char *filename = qemu_opt_get(opts, "path"); + const char *filename = opts->device; CharDriverState *chr; WinCharState *s; @@ -1940,7 +1990,7 @@ static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) g_free(chr); return NULL; } - qemu_chr_generic_open(chr); + qemu_chr_be_generic_open(chr); return chr; } @@ -1954,29 +2004,15 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) s->hcom = fd_out; chr->opaque = s; chr->chr_write = win_chr_write; - qemu_chr_generic_open(chr); + qemu_chr_be_generic_open(chr); return chr; } -static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts) +static CharDriverState *qemu_chr_open_win_con(void) { return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); } -static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) -{ - const char *file_out = qemu_opt_get(opts, "path"); - HANDLE fd_out; - - fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd_out == INVALID_HANDLE_VALUE) { - return NULL; - } - - return qemu_chr_open_win_file(fd_out); -} - static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len) { HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); @@ -2114,7 +2150,7 @@ static void win_stdio_close(CharDriverState *chr) g_free(chr); } -static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) +static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) { CharDriverState *chr; WinStdioCharState *stdio; @@ -2233,13 +2269,18 @@ static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) gsize bytes_read = 0; GIOStatus status; - if (s->max_size == 0) - return FALSE; + if (s->max_size == 0) { + return TRUE; + } status = g_io_channel_read_chars(s->chan, (gchar *)s->buf, sizeof(s->buf), &bytes_read, NULL); s->bufcnt = bytes_read; s->bufptr = s->bufcnt; if (status != G_IO_STATUS_NORMAL) { + if (s->tag) { + io_remove_watch_poll(s->tag); + s->tag = 0; + } return FALSE; } @@ -2258,7 +2299,7 @@ static void udp_chr_update_read_handler(CharDriverState *chr) NetCharDriver *s = chr->opaque; if (s->tag) { - g_source_remove(s->tag); + io_remove_watch_poll(s->tag); s->tag = 0; } @@ -2271,7 +2312,8 @@ static void udp_chr_close(CharDriverState *chr) { NetCharDriver *s = chr->opaque; if (s->tag) { - g_source_remove(s->tag); + io_remove_watch_poll(s->tag); + s->tag = 0; } if (s->chan) { g_io_channel_unref(s->chan); @@ -2281,21 +2323,14 @@ static void udp_chr_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) +static CharDriverState *qemu_chr_open_udp_fd(int fd) { CharDriverState *chr = NULL; NetCharDriver *s = NULL; - Error *local_err = NULL; - int fd = -1; chr = g_malloc0(sizeof(CharDriverState)); s = g_malloc0(sizeof(NetCharDriver)); - fd = inet_dgram_opts(opts, &local_err); - if (fd < 0) { - goto return_err; - } - s->fd = fd; s->chan = io_channel_from_socket(s->fd); s->bufcnt = 0; @@ -2305,18 +2340,18 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_close = udp_chr_close; return chr; +} -return_err: - if (local_err) { - qerror_report_err(local_err); - error_free(local_err); - } - g_free(chr); - g_free(s); - if (fd >= 0) { - closesocket(fd); +static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) +{ + Error *local_err = NULL; + int fd = -1; + + fd = inet_dgram_opts(opts, &local_err); + if (fd < 0) { + return NULL; } - return NULL; + return qemu_chr_open_udp_fd(fd); } /***********************************************************/ @@ -2341,7 +2376,7 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { TCPCharDriver *s = chr->opaque; if (s->connected) { - return io_channel_send_all(s->chan, buf, len); + return io_channel_send(s->chan, buf, len); } else { /* XXX: indicate an error ? */ return len; @@ -2434,6 +2469,9 @@ static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg) if (fd < 0) continue; + /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ + qemu_set_block(fd); + #ifndef MSG_CMSG_CLOEXEC qemu_set_cloexec(fd); #endif @@ -2495,7 +2533,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) int len, size; if (!s->connected || s->max_size <= 0) { - return FALSE; + return TRUE; } len = sizeof(buf); if (len > s->max_size) @@ -2507,8 +2545,10 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) if (s->listen_chan) { s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); } - g_source_remove(s->tag); - s->tag = 0; + if (s->tag) { + io_remove_watch_poll(s->tag); + s->tag = 0; + } g_io_channel_unref(s->chan); s->chan = NULL; closesocket(s->fd); @@ -2540,7 +2580,7 @@ static void tcp_chr_connect(void *opaque) if (s->chan) { s->tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, tcp_chr_read, chr); } - qemu_chr_generic_open(chr); + qemu_chr_be_generic_open(chr); } #define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c; @@ -2564,13 +2604,15 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd) if (s->fd != -1) return -1; - socket_set_nonblock(fd); + qemu_set_nonblock(fd); if (s->do_nodelay) socket_set_nodelay(fd); s->fd = fd; s->chan = io_channel_from_socket(fd); - g_source_remove(s->listen_tag); - s->listen_tag = 0; + if (s->listen_tag) { + g_source_remove(s->listen_tag); + s->listen_tag = 0; + } tcp_chr_connect(chr); return 0; @@ -2601,6 +2643,7 @@ static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opa } fd = qemu_accept(s->listen_fd, addr, &len); if (fd < 0 && errno != EINTR) { + s->listen_tag = 0; return FALSE; } else if (fd >= 0) { if (s->do_telnetopt) @@ -2619,7 +2662,8 @@ static void tcp_chr_close(CharDriverState *chr) TCPCharDriver *s = chr->opaque; if (s->fd >= 0) { if (s->tag) { - g_source_remove(s->tag); + io_remove_watch_poll(s->tag); + s->tag = 0; } if (s->chan) { g_io_channel_unref(s->chan); @@ -2629,6 +2673,7 @@ static void tcp_chr_close(CharDriverState *chr) if (s->listen_fd >= 0) { if (s->listen_tag) { g_source_remove(s->listen_tag); + s->listen_tag = 0; } if (s->listen_chan) { g_io_channel_unref(s->listen_chan); @@ -2683,7 +2728,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, s->do_nodelay = do_nodelay; getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); - snprintf(chr->filename, 256, "%s:%s:%s%s%s%s", + snprintf(chr->filename, 256, "%s:%s%s%s:%s%s", is_telnet ? "telnet" : "tcp", left, host, right, serv, is_listen ? ",server" : ""); @@ -2716,7 +2761,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, printf("QEMU waiting for connection on: %s\n", chr->filename); tcp_chr_accept(s->listen_chan, G_IO_IN, chr); - socket_set_nonblock(s->listen_fd); + qemu_set_nonblock(s->listen_fd); } return chr; } @@ -2758,7 +2803,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) } if (!is_waitconnect) - socket_set_nonblock(fd); + qemu_set_nonblock(fd); chr = qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, is_telnet, is_waitconnect, &local_err); @@ -2783,70 +2828,6 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) return NULL; } -/***********************************************************/ -/* Memory chardev */ -typedef struct { - size_t outbuf_size; - size_t outbuf_capacity; - uint8_t *outbuf; -} MemoryDriver; - -static int mem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) -{ - MemoryDriver *d = chr->opaque; - - /* TODO: the QString implementation has the same code, we should - * introduce a generic way to do this in cutils.c */ - if (d->outbuf_capacity < d->outbuf_size + len) { - /* grow outbuf */ - d->outbuf_capacity += len; - d->outbuf_capacity *= 2; - d->outbuf = g_realloc(d->outbuf, d->outbuf_capacity); - } - - memcpy(d->outbuf + d->outbuf_size, buf, len); - d->outbuf_size += len; - - return len; -} - -void qemu_chr_init_mem(CharDriverState *chr) -{ - MemoryDriver *d; - - d = g_malloc(sizeof(*d)); - d->outbuf_size = 0; - d->outbuf_capacity = 4096; - d->outbuf = g_malloc0(d->outbuf_capacity); - - memset(chr, 0, sizeof(*chr)); - chr->opaque = d; - chr->chr_write = mem_chr_write; -} - -QString *qemu_chr_mem_to_qs(CharDriverState *chr) -{ - MemoryDriver *d = chr->opaque; - return qstring_from_substr((char *) d->outbuf, 0, d->outbuf_size - 1); -} - -/* NOTE: this driver can not be closed with qemu_chr_delete()! */ -void qemu_chr_close_mem(CharDriverState *chr) -{ - MemoryDriver *d = chr->opaque; - - g_free(d->outbuf); - g_free(chr->opaque); - chr->opaque = NULL; - chr->chr_write = NULL; -} - -size_t qemu_chr_mem_osize(const CharDriverState *chr) -{ - const MemoryDriver *d = chr->opaque; - return d->outbuf_size; -} - /*********************************************************/ /* Ring buffer chardev */ @@ -2904,7 +2885,8 @@ static void ringbuf_chr_close(struct CharDriverState *chr) chr->opaque = NULL; } -static CharDriverState *qemu_chr_open_ringbuf(QemuOpts *opts) +static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts, + Error **errp) { CharDriverState *chr; RingBufCharDriver *d; @@ -2912,14 +2894,11 @@ static CharDriverState *qemu_chr_open_ringbuf(QemuOpts *opts) chr = g_malloc0(sizeof(CharDriverState)); d = g_malloc(sizeof(*d)); - d->size = qemu_opt_get_size(opts, "size", 0); - if (d->size == 0) { - d->size = 65536; - } + d->size = opts->has_size ? opts->size : 65536; /* The size must be power of 2 */ if (d->size & (d->size - 1)) { - error_report("size of ringbuf device must be power of two"); + error_setg(errp, "size of ringbuf chardev must be power of two"); goto fail; } @@ -3160,25 +3139,88 @@ fail: return NULL; } -#ifdef HAVE_CHARDEV_PARPORT +static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *path = qemu_opt_get(opts, "path"); + + if (path == NULL) { + error_setg(errp, "chardev: file: no filename given"); + return; + } + backend->file = g_new0(ChardevFile, 1); + backend->file->out = g_strdup(path); +} -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend, + Error **errp) { - const char *filename = qemu_opt_get(opts, "path"); - int fd; + backend->stdio = g_new0(ChardevStdio, 1); + backend->stdio->has_signal = true; + backend->stdio->signal = + qemu_opt_get_bool(opts, "signal", display_type != DT_NOGRAPHIC); +} - fd = qemu_open(filename, O_RDWR); - if (fd < 0) { - return NULL; +static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *device = qemu_opt_get(opts, "path"); + + if (device == NULL) { + error_setg(errp, "chardev: serial/tty: no device path given"); + return; } - return qemu_chr_open_pp_fd(fd); + backend->serial = g_new0(ChardevHostdev, 1); + backend->serial->device = g_strdup(device); } -#endif +static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *device = qemu_opt_get(opts, "path"); + + if (device == NULL) { + error_setg(errp, "chardev: parallel: no device path given"); + return; + } + backend->parallel = g_new0(ChardevHostdev, 1); + backend->parallel->device = g_strdup(device); +} + +static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *device = qemu_opt_get(opts, "path"); + + if (device == NULL) { + error_setg(errp, "chardev: pipe: no device path given"); + return; + } + backend->pipe = g_new0(ChardevHostdev, 1); + backend->pipe->device = g_strdup(device); +} + +static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + int val; + + backend->memory = g_new0(ChardevRingbuf, 1); + + val = qemu_opt_get_number(opts, "size", 0); + if (val != 0) { + backend->memory->has_size = true; + backend->memory->size = val; + } +} typedef struct CharDriver { const char *name; + /* old, pre qapi */ CharDriverState *(*open)(QemuOpts *opts); + /* new, qapi-based */ + int kind; + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); } CharDriver; static GSList *backends; @@ -3194,6 +3236,19 @@ void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *) backends = g_slist_append(backends, s); } +void register_char_driver_qapi(const char *name, int kind, + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp)) +{ + CharDriver *s; + + s = g_malloc0(sizeof(*s)); + s->name = g_strdup(name); + s->kind = kind; + s->parse = parse; + + backends = g_slist_append(backends, s); +} + CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, void (*init)(struct CharDriverState *s), Error **errp) @@ -3225,6 +3280,51 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, return NULL; } + if (!cd->open) { + /* using new, qapi init */ + ChardevBackend *backend = g_new0(ChardevBackend, 1); + ChardevReturn *ret = NULL; + const char *id = qemu_opts_id(opts); + const char *bid = NULL; + + if (qemu_opt_get_bool(opts, "mux", 0)) { + bid = g_strdup_printf("%s-base", id); + } + + chr = NULL; + backend->kind = cd->kind; + if (cd->parse) { + cd->parse(opts, backend, errp); + if (error_is_set(errp)) { + goto qapi_out; + } + } + ret = qmp_chardev_add(bid ? bid : id, backend, errp); + if (error_is_set(errp)) { + goto qapi_out; + } + + if (bid) { + qapi_free_ChardevBackend(backend); + qapi_free_ChardevReturn(ret); + backend = g_new0(ChardevBackend, 1); + backend->mux = g_new0(ChardevMux, 1); + backend->kind = CHARDEV_BACKEND_KIND_MUX; + backend->mux->chardev = g_strdup(bid); + ret = qmp_chardev_add(id, backend, errp); + if (error_is_set(errp)) { + goto qapi_out; + } + } + + chr = qemu_chr_find(id); + + qapi_out: + qapi_free_ChardevBackend(backend); + qapi_free_ChardevReturn(ret); + return chr; + } + chr = cd->open(opts); if (!chr) { error_setg(errp, "chardev: opening backend \"%s\" failed", @@ -3279,6 +3379,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in error_free(err); } if (chr && qemu_opt_get_bool(opts, "mux", 0)) { + qemu_chr_fe_claim_no_fail(chr); monitor_init(chr, MONITOR_USE_READLINE); } return chr; @@ -3291,22 +3392,19 @@ void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo) } } -void qemu_chr_fe_open(struct CharDriverState *chr) +void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open) { - if (chr->chr_guest_open) { - chr->chr_guest_open(chr); + if (chr->fe_open == fe_open) { + return; } -} - -void qemu_chr_fe_close(struct CharDriverState *chr) -{ - if (chr->chr_guest_close) { - chr->chr_guest_close(chr); + chr->fe_open = fe_open; + if (chr->chr_set_fe_open) { + chr->chr_set_fe_open(chr, fe_open); } } -guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, - GIOFunc func, void *user_data) +int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, + GIOFunc func, void *user_data) { GSource *src; guint tag; @@ -3323,6 +3421,29 @@ guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, return tag; } +int qemu_chr_fe_claim(CharDriverState *s) +{ + if (s->avail_connections < 1) { + return -1; + } + s->avail_connections--; + return 0; +} + +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_delete(CharDriverState *chr) { QTAILQ_REMOVE(&chardevs, chr, next); @@ -3371,9 +3492,16 @@ CharDriverState *qemu_chr_find(const char *name) CharDriverState *qemu_char_get_next_serial(void) { static int next_serial; + CharDriverState *chr; /* FIXME: This function needs to go away: use chardev properties! */ - return serial_hds[next_serial++]; + + while (next_serial < MAX_SERIAL_PORTS && serial_hds[next_serial]) { + chr = serial_hds[next_serial++]; + qemu_chr_fe_claim_no_fail(chr); + return chr; + } + return NULL; } QemuOptsList qemu_chardev_opts = { @@ -3531,7 +3659,7 @@ static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial, if (error_is_set(errp)) { return NULL; } - socket_set_nonblock(fd); + qemu_set_nonblock(fd); return qemu_chr_open_tty_fd(fd); #else error_setg(errp, "character device backend type 'serial' not supported"); @@ -3580,11 +3708,23 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, is_telnet, is_waitconnect, errp); } +static CharDriverState *qmp_chardev_open_dgram(ChardevDgram *dgram, + Error **errp) +{ + int fd; + + fd = socket_dgram(dgram->remote, dgram->local, errp); + if (error_is_set(errp)) { + return NULL; + } + return qemu_chr_open_udp_fd(fd); +} + ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, Error **errp) { ChardevReturn *ret = g_new0(ChardevReturn, 1); - CharDriverState *chr = NULL; + CharDriverState *base, *chr = NULL; chr = qemu_chr_find(id); if (chr) { @@ -3603,24 +3743,61 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, case CHARDEV_BACKEND_KIND_PARALLEL: chr = qmp_chardev_open_parallel(backend->parallel, errp); break; + case CHARDEV_BACKEND_KIND_PIPE: + chr = qemu_chr_open_pipe(backend->pipe); + break; case CHARDEV_BACKEND_KIND_SOCKET: chr = qmp_chardev_open_socket(backend->socket, errp); break; + case CHARDEV_BACKEND_KIND_DGRAM: + chr = qmp_chardev_open_dgram(backend->dgram, errp); + break; #ifdef HAVE_CHARDEV_TTY case CHARDEV_BACKEND_KIND_PTY: - { - /* qemu_chr_open_pty sets "path" in opts */ - QemuOpts *opts; - opts = qemu_opts_create_nofail(qemu_find_opts("chardev")); - chr = qemu_chr_open_pty(opts); - ret->pty = g_strdup(qemu_opt_get(opts, "path")); - ret->has_pty = true; - qemu_opts_del(opts); + chr = qemu_chr_open_pty(id, ret); break; - } #endif case CHARDEV_BACKEND_KIND_NULL: - chr = qemu_chr_open_null(NULL); + chr = qemu_chr_open_null(); + break; + case CHARDEV_BACKEND_KIND_MUX: + base = qemu_chr_find(backend->mux->chardev); + if (base == NULL) { + error_setg(errp, "mux: base chardev %s not found", + backend->mux->chardev); + break; + } + chr = qemu_chr_open_mux(base); + break; + case CHARDEV_BACKEND_KIND_MSMOUSE: + chr = qemu_chr_open_msmouse(); + break; +#ifdef CONFIG_BRLAPI + case CHARDEV_BACKEND_KIND_BRAILLE: + chr = chr_baum_init(); + break; +#endif + case CHARDEV_BACKEND_KIND_STDIO: + chr = qemu_chr_open_stdio(backend->stdio); + break; +#ifdef _WIN32 + case CHARDEV_BACKEND_KIND_CONSOLE: + chr = qemu_chr_open_win_con(); + break; +#endif +#ifdef CONFIG_SPICE + case CHARDEV_BACKEND_KIND_SPICEVMC: + chr = qemu_chr_open_spice_vmc(backend->spicevmc->type); + break; + case CHARDEV_BACKEND_KIND_SPICEPORT: + chr = qemu_chr_open_spice_port(backend->spiceport->fqdn); + break; +#endif + case CHARDEV_BACKEND_KIND_VC: + chr = vc_init(backend->vc); + break; + case CHARDEV_BACKEND_KIND_MEMORY: + chr = qemu_chr_open_ringbuf(backend->memory, errp); break; default: error_setg(errp, "unknown chardev backend (%d)", backend->kind); @@ -3632,7 +3809,8 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, } if (chr) { chr->label = g_strdup(id); - chr->avail_connections = 1; + chr->avail_connections = + (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; QTAILQ_INSERT_TAIL(&chardevs, chr, next); return ret; } else { @@ -3660,31 +3838,27 @@ void qmp_chardev_remove(const char *id, Error **errp) static void register_types(void) { - register_char_driver("null", qemu_chr_open_null); + register_char_driver_qapi("null", CHARDEV_BACKEND_KIND_NULL, NULL); register_char_driver("socket", qemu_chr_open_socket); register_char_driver("udp", qemu_chr_open_udp); - register_char_driver("vc", vc_init); - register_char_driver("memory", qemu_chr_open_ringbuf); -#ifdef _WIN32 - register_char_driver("file", qemu_chr_open_win_file_out); - register_char_driver("pipe", qemu_chr_open_win_pipe); - register_char_driver("console", qemu_chr_open_win_con); - register_char_driver("serial", qemu_chr_open_win); - register_char_driver("stdio", qemu_chr_open_win_stdio); -#else - register_char_driver("file", qemu_chr_open_file_out); - register_char_driver("pipe", qemu_chr_open_pipe); - register_char_driver("stdio", qemu_chr_open_stdio); -#endif -#ifdef HAVE_CHARDEV_TTY - register_char_driver("tty", qemu_chr_open_tty); - register_char_driver("serial", qemu_chr_open_tty); - register_char_driver("pty", qemu_chr_open_pty); -#endif -#ifdef HAVE_CHARDEV_PARPORT - register_char_driver("parallel", qemu_chr_open_pp); - register_char_driver("parport", qemu_chr_open_pp); -#endif + register_char_driver_qapi("memory", CHARDEV_BACKEND_KIND_MEMORY, + qemu_chr_parse_ringbuf); + register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE, + qemu_chr_parse_file_out); + register_char_driver_qapi("stdio", CHARDEV_BACKEND_KIND_STDIO, + qemu_chr_parse_stdio); + register_char_driver_qapi("serial", CHARDEV_BACKEND_KIND_SERIAL, + qemu_chr_parse_serial); + register_char_driver_qapi("tty", CHARDEV_BACKEND_KIND_SERIAL, + qemu_chr_parse_serial); + register_char_driver_qapi("parallel", CHARDEV_BACKEND_KIND_PARALLEL, + qemu_chr_parse_parallel); + register_char_driver_qapi("parport", CHARDEV_BACKEND_KIND_PARALLEL, + qemu_chr_parse_parallel); + register_char_driver_qapi("pty", CHARDEV_BACKEND_KIND_PTY, NULL); + register_char_driver_qapi("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL); + register_char_driver_qapi("pipe", CHARDEV_BACKEND_KIND_PIPE, + qemu_chr_parse_pipe); } type_init(register_types);