X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-char.c;h=64e824d0acb1a0f773060b45c9b5f7eea0f43b93;hb=9e1c2ec8fd8d9a9ee299ea86c5f6c986fe25e838;hp=160decc2f07311d8b37c107266468765c92490c7;hpb=b1c07f06ed0e2094197274e636a90ad33674653c;p=qemu.git diff --git a/qemu-char.c b/qemu-char.c index 160decc2f..64e824d0a 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -26,10 +26,8 @@ #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 "hw/baum.h" -#include "hw/msmouse.h" #include "qmp-commands.h" #include @@ -110,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; } @@ -122,20 +120,18 @@ void qemu_chr_be_event(CharDriverState *s, int event) s->chr_event(s->handler_opaque, event); } -static void qemu_chr_fire_open_event(void *opaque) +static gboolean qemu_chr_be_generic_open_bh(gpointer opaque) { CharDriverState *s = opaque; qemu_chr_be_event(s, CHR_EVENT_OPENED); - qemu_free_timer(s->open_timer); - s->open_timer = NULL; + s->idle_tag = 0; + return FALSE; } -void qemu_chr_generic_open(CharDriverState *s) +void qemu_chr_be_generic_open(CharDriverState *s) { - if (s->open_timer == NULL) { - s->open_timer = qemu_new_timer_ms(rt_clock, - qemu_chr_fire_open_event, s); - qemu_mod_timer(s->open_timer, qemu_get_clock_ms(rt_clock) - 1); + if (s->idle_tag == 0) { + s->idle_tag = g_idle_add(qemu_chr_be_generic_open_bh, s); } } @@ -144,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) @@ -198,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; @@ -209,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); } } @@ -221,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; @@ -482,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; } @@ -537,68 +566,292 @@ int send_all(int fd, const void *_buf, int len1) } return len1 - len; } + +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 */ -#define STDIO_MAX_CLIENTS 1 -static int stdio_nb_clients; +typedef struct IOWatchPoll +{ + GSource parent; + + GIOChannel *channel; + GSource *src; + + IOCanReadHandler *fd_can_read; + GSourceFunc fd_read; + void *opaque; +} IOWatchPoll; + +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_) +{ + IOWatchPoll *iwp = io_watch_poll_from_source(source); + bool now_active = iwp->fd_can_read(iwp->opaque) > 0; + bool was_active = iwp->src != NULL; + if (was_active == now_active) { + return FALSE; + } + + 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) +{ + return FALSE; +} + +static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback, + gpointer 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); + assert(iwp->src == NULL); +} + +static GSourceFuncs io_watch_poll_funcs = { + .prepare = io_watch_poll_prepare, + .check = io_watch_poll_check, + .dispatch = io_watch_poll_dispatch, + .finalize = io_watch_poll_finalize, +}; + +/* Can only be used for read */ +static guint io_add_watch_poll(GIOChannel *channel, + IOCanReadHandler *fd_can_read, + GIOFunc fd_read, + gpointer user_data) +{ + IOWatchPoll *iwp; + int tag; + + 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; + + 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; -typedef struct { - int fd_in, fd_out; - int max_size; -} FDCharDriver; + if (fd == -1) { + return NULL; + } + chan = g_io_channel_unix_new(fd); -static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) + g_io_channel_set_encoding(chan, NULL, NULL); + g_io_channel_set_buffered(chan, FALSE); + + return chan; +} +#endif + +static GIOChannel *io_channel_from_socket(int fd) { - FDCharDriver *s = chr->opaque; - return send_all(s->fd_out, buf, len); + GIOChannel *chan; + + if (fd == -1) { + return NULL; + } + +#ifdef _WIN32 + chan = g_io_channel_win32_new_socket(fd); +#else + chan = g_io_channel_unix_new(fd); +#endif + + g_io_channel_set_encoding(chan, NULL, NULL); + g_io_channel_set_buffered(chan, FALSE); + + return chan; } -static int fd_chr_read_poll(void *opaque) +static int io_channel_send(GIOChannel *fd, const void *buf, size_t len) { - CharDriverState *chr = opaque; - FDCharDriver *s = chr->opaque; + GIOStatus status; + size_t offset; - s->max_size = qemu_chr_be_can_write(chr); - return s->max_size; + 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; + } else { + errno = EINVAL; + } + + return -1; + } else if (status == G_IO_STATUS_EOF) { + break; + } + + offset += bytes_written; + } + + return offset; } -static void fd_chr_read(void *opaque) +#ifndef _WIN32 + +typedef struct FDCharDriver { + CharDriverState *chr; + GIOChannel *fd_in, *fd_out; + guint fd_in_tag; + int max_size; + QTAILQ_ENTRY(FDCharDriver) node; +} FDCharDriver; + +static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + FDCharDriver *s = chr->opaque; + + return io_channel_send(s->fd_out, buf, len); +} + +static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; FDCharDriver *s = chr->opaque; - int size, len; + int len; uint8_t buf[READ_BUF_LEN]; + GIOStatus status; + gsize bytes_read; len = sizeof(buf); - if (len > s->max_size) + if (len > s->max_size) { len = s->max_size; - if (len == 0) - return; - size = read(s->fd_in, buf, len); - if (size == 0) { - /* FD has been closed. Remove it from the active list. */ - qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); + } + if (len == 0) { + 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; + return FALSE; } - if (size > 0) { - qemu_chr_be_write(chr, buf, size); + if (status == G_IO_STATUS_NORMAL) { + qemu_chr_be_write(chr, buf, bytes_read); } + + return TRUE; +} + +static int fd_chr_read_poll(void *opaque) +{ + CharDriverState *chr = opaque; + FDCharDriver *s = chr->opaque; + + s->max_size = qemu_chr_be_can_write(chr); + return s->max_size; +} + +static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond) +{ + FDCharDriver *s = chr->opaque; + return g_io_create_watch(s->fd_out, cond); } static void fd_chr_update_read_handler(CharDriverState *chr) { FDCharDriver *s = chr->opaque; - if (s->fd_in >= 0) { - if (display_type == DT_NOGRAPHIC && s->fd_in == 0) { - } else { - qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll, - fd_chr_read, NULL, chr); - } + if (s->fd_in_tag) { + io_remove_watch_poll(s->fd_in_tag); + s->fd_in_tag = 0; + } + + if (s->fd_in) { + s->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll, fd_chr_read, chr); } } @@ -606,11 +859,16 @@ static void fd_chr_close(struct CharDriverState *chr) { FDCharDriver *s = chr->opaque; - if (s->fd_in >= 0) { - if (display_type == DT_NOGRAPHIC && s->fd_in == 0) { - } else { - qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); - } + if (s->fd_in_tag) { + io_remove_watch_poll(s->fd_in_tag); + s->fd_in_tag = 0; + } + + if (s->fd_in) { + g_io_channel_unref(s->fd_in); + } + if (s->fd_out) { + g_io_channel_unref(s->fd_out); } g_free(s); @@ -625,35 +883,26 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) chr = g_malloc0(sizeof(CharDriverState)); s = g_malloc0(sizeof(FDCharDriver)); - s->fd_in = fd_in; - s->fd_out = fd_out; + s->fd_in = io_channel_from_fd(fd_in); + s->fd_out = io_channel_from_fd(fd_out); + fcntl(fd_out, F_SETFL, O_NONBLOCK); + 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; - 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"); @@ -677,53 +926,6 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) return qemu_chr_open_fd(fd_in, fd_out); } - -/* for STDIO, we handle the case where several clients use it - (nographic mode) */ - -#define TERM_FIFO_MAX_SIZE 1 - -static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; -static int term_fifo_size; - -static int stdio_read_poll(void *opaque) -{ - CharDriverState *chr = opaque; - - /* try to flush the queue if needed */ - if (term_fifo_size != 0 && qemu_chr_be_can_write(chr) > 0) { - qemu_chr_be_write(chr, term_fifo, 1); - term_fifo_size = 0; - } - /* see if we can absorb more chars */ - if (term_fifo_size == 0) - return 1; - else - return 0; -} - -static void stdio_read(void *opaque) -{ - int size; - uint8_t buf[1]; - CharDriverState *chr = opaque; - - size = read(0, buf, 1); - if (size == 0) { - /* stdin has been closed. Remove it from the active list. */ - qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); - qemu_chr_be_event(chr, CHR_EVENT_CLOSED); - return; - } - if (size > 0) { - if (qemu_chr_be_can_write(chr) > 0) { - qemu_chr_be_write(chr, buf, 1); - } else if (term_fifo_size == 0) { - term_fifo[term_fifo_size++] = buf[0]; - } - } -} - /* init terminal so that we can grab keys */ static struct termios oldtty; static int old_fd0_flags; @@ -760,36 +962,29 @@ static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) static void qemu_chr_close_stdio(struct CharDriverState *chr) { term_exit(); - stdio_nb_clients--; - qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); fd_chr_close(chr); } -static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) +static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) { CharDriverState *chr; - if (stdio_nb_clients >= STDIO_MAX_CLIENTS) { - return NULL; - } if (is_daemonized()) { error_report("cannot use stdio with -daemonize"); return NULL; } - if (stdio_nb_clients == 0) { - old_fd0_flags = fcntl(0, F_GETFL); - tcgetattr (0, &oldtty); - fcntl(0, F_SETFL, O_NONBLOCK); - atexit(term_exit); - } + old_fd0_flags = fcntl(0, F_GETFL); + tcgetattr (0, &oldtty); + fcntl(0, F_SETFL, O_NONBLOCK); + atexit(term_exit); chr = qemu_chr_open_fd(0, 1); chr->chr_close = qemu_chr_close_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio; - qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr); - stdio_nb_clients++; - 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; @@ -859,16 +1054,49 @@ static void cfmakeraw (struct termios *termios_p) #define HAVE_CHARDEV_TTY 1 typedef struct { - int fd; + GIOChannel *fd; + guint fd_tag; int connected; - int polling; int read_bytes; - QEMUTimer *timer; + guint timer_tag; } PtyCharDriver; static void pty_chr_update_read_handler(CharDriverState *chr); static void pty_chr_state(CharDriverState *chr, int connected); +static gboolean pty_chr_timer(gpointer opaque) +{ + struct CharDriverState *chr = opaque; + PtyCharDriver *s = chr->opaque; + + if (s->connected) { + goto out; + } + + /* Next poll ... */ + pty_chr_update_read_handler(chr); + +out: + s->timer_tag = 0; + return FALSE; +} + +static void pty_chr_rearm_timer(CharDriverState *chr, int ms) +{ + PtyCharDriver *s = chr->opaque; + + if (s->timer_tag) { + g_source_remove(s->timer_tag); + s->timer_tag = 0; + } + + if (ms == 1000) { + s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr); + } else { + s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr); + } +} + static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { PtyCharDriver *s = chr->opaque; @@ -878,7 +1106,13 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) pty_chr_update_read_handler(chr); return 0; } - return send_all(s->fd, buf, len); + return io_channel_send(s->fd, buf, len); +} + +static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond) +{ + PtyCharDriver *s = chr->opaque; + return g_io_create_watch(s->fd, cond); } static int pty_chr_read_poll(void *opaque) @@ -890,46 +1124,45 @@ static int pty_chr_read_poll(void *opaque) return s->read_bytes; } -static void pty_chr_read(void *opaque) +static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; PtyCharDriver *s = chr->opaque; - int size, len; + gsize size, len; uint8_t buf[READ_BUF_LEN]; + GIOStatus status; len = sizeof(buf); if (len > s->read_bytes) len = s->read_bytes; - if (len == 0) - return; - size = read(s->fd, buf, len); - if ((size == -1 && errno == EIO) || - (size == 0)) { - pty_chr_state(chr, 0); - return; + if (len == 0) { + return TRUE; } - if (size > 0) { + status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL); + if (status != G_IO_STATUS_NORMAL) { + pty_chr_state(chr, 0); + return FALSE; + } else { pty_chr_state(chr, 1); qemu_chr_be_write(chr, buf, size); } + return TRUE; } static void pty_chr_update_read_handler(CharDriverState *chr) { PtyCharDriver *s = chr->opaque; + GPollFD pfd; - qemu_set_fd_handler2(s->fd, pty_chr_read_poll, - pty_chr_read, NULL, 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. - */ - qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 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) @@ -937,57 +1170,56 @@ static void pty_chr_state(CharDriverState *chr, int connected) PtyCharDriver *s = chr->opaque; if (!connected) { - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + 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. */ - qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 1000); + 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); + } } } -static void pty_chr_timer(void *opaque) -{ - struct CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; - - if (s->connected) - return; - 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); - return; - } - - /* Next poll ... */ - pty_chr_update_read_handler(chr); -} static void pty_chr_close(struct CharDriverState *chr) { PtyCharDriver *s = chr->opaque; + int fd; - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); - close(s->fd); - qemu_del_timer(s->timer); - qemu_free_timer(s->timer); + if (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 @@ -1008,26 +1240,22 @@ 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; chr->chr_write = pty_chr_write; chr->chr_update_read_handler = pty_chr_update_read_handler; chr->chr_close = pty_chr_close; + chr->chr_add_watch = pty_chr_add_watch; - s->fd = master_fd; - s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); + s->fd = io_channel_from_fd(master_fd); + s->timer_tag = 0; return chr; } @@ -1155,22 +1383,24 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) case CHR_IOCTL_SERIAL_SET_PARAMS: { QEMUSerialSetParams *ssp = arg; - tty_serial_init(s->fd_in, ssp->speed, ssp->parity, + tty_serial_init(g_io_channel_unix_get_fd(s->fd_in), + ssp->speed, ssp->parity, ssp->data_bits, ssp->stop_bits); } break; case CHR_IOCTL_SERIAL_SET_BREAK: { int enable = *(int *)arg; - if (enable) - tcsendbreak(s->fd_in, 1); + if (enable) { + tcsendbreak(g_io_channel_unix_get_fd(s->fd_in), 1); + } } break; case CHR_IOCTL_SERIAL_GET_TIOCM: { int sarg = 0; int *targ = (int *)arg; - ioctl(s->fd_in, TIOCMGET, &sarg); + ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &sarg); *targ = 0; if (sarg & TIOCM_CTS) *targ |= CHR_TIOCM_CTS; @@ -1190,7 +1420,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) { int sarg = *(int *)arg; int targ = 0; - ioctl(s->fd_in, TIOCMGET, &targ); + ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &targ); targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS); if (sarg & CHR_TIOCM_CTS) @@ -1205,7 +1435,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) targ |= TIOCM_DTR; if (sarg & CHR_TIOCM_RTS) targ |= TIOCM_RTS; - ioctl(s->fd_in, TIOCMSET, &targ); + ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMSET, &targ); } break; default: @@ -1220,7 +1450,7 @@ static void qemu_chr_close_tty(CharDriverState *chr) int fd = -1; if (s) { - fd = s->fd_in; + fd = g_io_channel_unix_get_fd(s->fd_in); } fd_chr_close(chr); @@ -1240,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__) @@ -1387,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; } @@ -1448,8 +1666,6 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd) #else /* _WIN32 */ -static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS]; - typedef struct { int max_size; HANDLE hcom, hrecv, hsend; @@ -1674,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; @@ -1762,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; @@ -1779,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; } @@ -1793,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); @@ -1951,21 +2148,15 @@ static void win_stdio_close(CharDriverState *chr) g_free(chr->opaque); g_free(chr); - stdio_nb_clients--; } -static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) +static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) { CharDriverState *chr; WinStdioCharState *stdio; DWORD dwMode; int is_console = 0; - if (stdio_nb_clients >= STDIO_MAX_CLIENTS - || ((display_type != DT_NOGRAPHIC) && (stdio_nb_clients != 0))) { - return NULL; - } - chr = g_malloc0(sizeof(CharDriverState)); stdio = g_malloc0(sizeof(WinStdioCharState)); @@ -1981,37 +2172,34 @@ static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) chr->chr_write = win_stdio_write; chr->chr_close = win_stdio_close; - if (stdio_nb_clients == 0) { - if (is_console) { - if (qemu_add_wait_object(stdio->hStdIn, - win_stdio_wait_func, chr)) { - fprintf(stderr, "qemu_add_wait_object: failed\n"); - } - } else { - DWORD dwId; - - stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, - chr, 0, &dwId); - - if (stdio->hInputThread == INVALID_HANDLE_VALUE - || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE - || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) { - fprintf(stderr, "cannot create stdio thread or event\n"); - exit(1); - } - if (qemu_add_wait_object(stdio->hInputReadyEvent, - win_stdio_thread_wait_func, chr)) { - fprintf(stderr, "qemu_add_wait_object: failed\n"); - } + if (is_console) { + if (qemu_add_wait_object(stdio->hStdIn, + win_stdio_wait_func, chr)) { + fprintf(stderr, "qemu_add_wait_object: failed\n"); + } + } else { + DWORD dwId; + + stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, + chr, 0, &dwId); + + if (stdio->hInputThread == INVALID_HANDLE_VALUE + || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE + || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) { + fprintf(stderr, "cannot create stdio thread or event\n"); + exit(1); + } + if (qemu_add_wait_object(stdio->hInputReadyEvent, + win_stdio_thread_wait_func, chr)) { + fprintf(stderr, "qemu_add_wait_object: failed\n"); } } dwMode |= ENABLE_LINE_INPUT; - stdio_clients[stdio_nb_clients++] = chr; - if (stdio_nb_clients == 1 && is_console) { + if (is_console) { /* set the terminal in raw mode */ /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */ dwMode |= ENABLE_PROCESSED_INPUT; @@ -2026,11 +2214,14 @@ static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) } #endif /* !_WIN32 */ + /***********************************************************/ /* UDP Net console */ typedef struct { int fd; + GIOChannel *chan; + guint tag; uint8_t buf[READ_BUF_LEN]; int bufcnt; int bufptr; @@ -2040,8 +2231,17 @@ typedef struct { static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { NetCharDriver *s = chr->opaque; + gsize bytes_written; + GIOStatus status; + + status = g_io_channel_write_chars(s->chan, (const gchar *)buf, len, &bytes_written, NULL); + if (status == G_IO_STATUS_EOF) { + return 0; + } else if (status != G_IO_STATUS_NORMAL) { + return -1; + } - return send(s->fd, (const void *)buf, len, 0); + return bytes_written; } static int udp_chr_read_poll(void *opaque) @@ -2062,17 +2262,27 @@ static int udp_chr_read_poll(void *opaque) return s->max_size; } -static void udp_chr_read(void *opaque) +static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; NetCharDriver *s = chr->opaque; + gsize bytes_read = 0; + GIOStatus status; - if (s->max_size == 0) - return; - s->bufcnt = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0); + 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 (s->bufcnt <= 0) - return; + if (status != G_IO_STATUS_NORMAL) { + if (s->tag) { + io_remove_watch_poll(s->tag); + s->tag = 0; + } + return FALSE; + } s->bufptr = 0; while (s->max_size > 0 && s->bufptr < s->bufcnt) { @@ -2080,45 +2290,49 @@ static void udp_chr_read(void *opaque) s->bufptr++; s->max_size = qemu_chr_be_can_write(chr); } + + return TRUE; } static void udp_chr_update_read_handler(CharDriverState *chr) { NetCharDriver *s = chr->opaque; - if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, udp_chr_read_poll, - udp_chr_read, NULL, chr); + if (s->tag) { + io_remove_watch_poll(s->tag); + s->tag = 0; + } + + if (s->chan) { + s->tag = io_add_watch_poll(s->chan, udp_chr_read_poll, udp_chr_read, chr); } } static void udp_chr_close(CharDriverState *chr) { NetCharDriver *s = chr->opaque; - if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + if (s->tag) { + io_remove_watch_poll(s->tag); + s->tag = 0; + } + if (s->chan) { + g_io_channel_unref(s->chan); closesocket(s->fd); } g_free(s); 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; s->bufptr = 0; chr->opaque = s; @@ -2126,24 +2340,27 @@ 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); } /***********************************************************/ /* TCP Net console */ typedef struct { + + GIOChannel *chan, *listen_chan; + guint tag, listen_tag; int fd, listen_fd; int connected; int max_size; @@ -2153,13 +2370,13 @@ typedef struct { int msgfd; } TCPCharDriver; -static void tcp_chr_accept(void *opaque); +static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque); static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { TCPCharDriver *s = chr->opaque; if (s->connected) { - return send_all(s->fd, buf, len); + return io_channel_send(s->chan, buf, len); } else { /* XXX: indicate an error ? */ return len; @@ -2252,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 @@ -2299,15 +2519,22 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) } #endif -static void tcp_chr_read(void *opaque) +static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond) +{ + TCPCharDriver *s = chr->opaque; + return g_io_create_watch(s->chan, cond); +} + +static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; uint8_t buf[READ_BUF_LEN]; int len, size; - if (!s->connected || s->max_size <= 0) - return; + if (!s->connected || s->max_size <= 0) { + return TRUE; + } len = sizeof(buf); if (len > s->max_size) len = s->max_size; @@ -2315,10 +2542,15 @@ static void tcp_chr_read(void *opaque) if (size == 0) { /* connection closed */ s->connected = 0; - if (s->listen_fd >= 0) { - qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr); + if (s->listen_chan) { + s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); } - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + if (s->tag) { + io_remove_watch_poll(s->tag); + s->tag = 0; + } + g_io_channel_unref(s->chan); + s->chan = NULL; closesocket(s->fd); s->fd = -1; qemu_chr_be_event(chr, CHR_EVENT_CLOSED); @@ -2328,6 +2560,8 @@ static void tcp_chr_read(void *opaque) if (size > 0) qemu_chr_be_write(chr, buf, size); } + + return TRUE; } #ifndef _WIN32 @@ -2343,11 +2577,10 @@ static void tcp_chr_connect(void *opaque) TCPCharDriver *s = chr->opaque; s->connected = 1; - if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, tcp_chr_read_poll, - tcp_chr_read, NULL, chr); + 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; @@ -2365,29 +2598,27 @@ static void tcp_chr_telnet_init(int fd) send(fd, (char *)buf, 3, 0); } -static void socket_set_nodelay(int fd) -{ - int val = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); -} - static int tcp_chr_add_client(CharDriverState *chr, int fd) { TCPCharDriver *s = chr->opaque; 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; - qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL); + s->chan = io_channel_from_socket(fd); + if (s->listen_tag) { + g_source_remove(s->listen_tag); + s->listen_tag = 0; + } tcp_chr_connect(chr); return 0; } -static void tcp_chr_accept(void *opaque) +static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; @@ -2412,7 +2643,8 @@ static void tcp_chr_accept(void *opaque) } fd = qemu_accept(s->listen_fd, addr, &len); if (fd < 0 && errno != EINTR) { - return; + s->listen_tag = 0; + return FALSE; } else if (fd >= 0) { if (s->do_telnetopt) tcp_chr_telnet_init(fd); @@ -2421,17 +2653,31 @@ static void tcp_chr_accept(void *opaque) } if (tcp_chr_add_client(chr, fd) < 0) close(fd); + + return TRUE; } static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + if (s->tag) { + io_remove_watch_poll(s->tag); + s->tag = 0; + } + if (s->chan) { + g_io_channel_unref(s->chan); + } closesocket(s->fd); } if (s->listen_fd >= 0) { - qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL); + 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); + } closesocket(s->listen_fd); } g_free(s); @@ -2482,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" : ""); @@ -2494,10 +2740,12 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, chr->chr_close = tcp_chr_close; chr->get_msgfd = tcp_get_msgfd; chr->chr_add_client = tcp_chr_add_client; + chr->chr_add_watch = tcp_chr_add_watch; if (is_listen) { s->listen_fd = fd; - qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr); + s->listen_chan = io_channel_from_socket(s->listen_fd); + s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); if (is_telnet) { s->do_telnetopt = 1; } @@ -2505,14 +2753,15 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, s->connected = 1; s->fd = fd; socket_set_nodelay(fd); + s->chan = io_channel_from_socket(s->fd); tcp_chr_connect(chr); } if (is_listen && is_waitconnect) { printf("QEMU waiting for connection on: %s\n", chr->filename); - tcp_chr_accept(chr); - socket_set_nonblock(s->listen_fd); + tcp_chr_accept(s->listen_chan, G_IO_IN, chr); + qemu_set_nonblock(s->listen_fd); } return chr; } @@ -2554,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); @@ -2579,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 */ @@ -2700,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; @@ -2708,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; } @@ -2956,69 +3139,123 @@ 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"); -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) + 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 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"); -static const struct { + 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); -} backend_table[] = { - { .name = "null", .open = qemu_chr_open_null }, - { .name = "socket", .open = qemu_chr_open_socket }, - { .name = "udp", .open = qemu_chr_open_udp }, - { .name = "msmouse", .open = qemu_chr_open_msmouse }, - { .name = "vc", .open = vc_init }, - { .name = "memory", .open = qemu_chr_open_ringbuf }, -#ifdef _WIN32 - { .name = "file", .open = qemu_chr_open_win_file_out }, - { .name = "pipe", .open = qemu_chr_open_win_pipe }, - { .name = "console", .open = qemu_chr_open_win_con }, - { .name = "serial", .open = qemu_chr_open_win }, - { .name = "stdio", .open = qemu_chr_open_win_stdio }, -#else - { .name = "file", .open = qemu_chr_open_file_out }, - { .name = "pipe", .open = qemu_chr_open_pipe }, - { .name = "stdio", .open = qemu_chr_open_stdio }, -#endif -#ifdef CONFIG_BRLAPI - { .name = "braille", .open = chr_baum_init }, -#endif -#ifdef HAVE_CHARDEV_TTY - { .name = "tty", .open = qemu_chr_open_tty }, - { .name = "serial", .open = qemu_chr_open_tty }, - { .name = "pty", .open = qemu_chr_open_pty }, -#endif -#ifdef HAVE_CHARDEV_PARPORT - { .name = "parallel", .open = qemu_chr_open_pp }, - { .name = "parport", .open = qemu_chr_open_pp }, -#endif -#ifdef CONFIG_SPICE - { .name = "spicevmc", .open = qemu_chr_open_spice }, -#if SPICE_SERVER_VERSION >= 0x000c02 - { .name = "spiceport", .open = qemu_chr_open_spice_port }, -#endif -#endif -}; + /* new, qapi-based */ + int kind; + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); +} CharDriver; + +static GSList *backends; + +void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *)) +{ + CharDriver *s; + + s = g_malloc0(sizeof(*s)); + s->name = g_strdup(name); + s->open = open; + + 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) { + CharDriver *cd; CharDriverState *chr; - int i; + GSList *i; if (qemu_opts_id(opts) == NULL) { error_setg(errp, "chardev: no id specified"); @@ -3030,17 +3267,65 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, qemu_opts_id(opts)); goto err; } - for (i = 0; i < ARRAY_SIZE(backend_table); i++) { - if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0) + for (i = backends; i; i = i->next) { + cd = i->data; + + if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) { break; + } } - if (i == ARRAY_SIZE(backend_table)) { + if (i == NULL) { error_setg(errp, "chardev: backend \"%s\" not found", qemu_opt_get(opts, "backend")); - goto err; + 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 = backend_table[i].open(opts); + chr = cd->open(opts); if (!chr) { error_setg(errp, "chardev: opening backend \"%s\" failed", qemu_opt_get(opts, "backend")); @@ -3094,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; @@ -3106,20 +3392,58 @@ 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->fe_open == fe_open) { + return; + } + chr->fe_open = fe_open; + if (chr->chr_set_fe_open) { + chr->chr_set_fe_open(chr, fe_open); + } +} + +int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, + GIOFunc func, void *user_data) +{ + GSource *src; + guint tag; + + if (s->chr_add_watch == NULL) { + return -ENOSYS; + } + + src = s->chr_add_watch(s, cond); + g_source_set_callback(src, (GSourceFunc)func, user_data, NULL); + tag = g_source_attach(src, NULL); + g_source_unref(src); + + return tag; +} + +int qemu_chr_fe_claim(CharDriverState *s) { - if (chr->chr_guest_open) { - chr->chr_guest_open(chr); + if (s->avail_connections < 1) { + return -1; } + s->avail_connections--; + return 0; } -void qemu_chr_fe_close(struct CharDriverState *chr) +void qemu_chr_fe_claim_no_fail(CharDriverState *s) { - if (chr->chr_guest_close) { - chr->chr_guest_close(chr); + 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); @@ -3168,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 = { @@ -3328,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"); @@ -3377,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) { @@ -3400,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); @@ -3429,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 { @@ -3454,3 +3835,30 @@ void qmp_chardev_remove(const char *id, Error **errp) } qemu_chr_delete(chr); } + +static void register_types(void) +{ + 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_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);