X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-char.c;h=e00f84c8e971c690e21fa8e569a22ec4229209bf;hb=394cfa39ba24dd838ace1308ae24961243947fb8;hp=4f8382e540f72b7f28321d33797d6cea506e08fc;hpb=fd469df97ab4277411ecdd4032a2f045a3a87b2a;p=qemu.git diff --git a/qemu-char.c b/qemu-char.c index 4f8382e54..e00f84c8e 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -76,8 +76,6 @@ #include // must come after ip.h #include #include -#include -#include #endif #endif #endif @@ -110,19 +108,9 @@ void qemu_chr_be_event(CharDriverState *s, int event) s->chr_event(s->handler_opaque, event); } -static gboolean qemu_chr_be_generic_open_bh(gpointer opaque) -{ - CharDriverState *s = opaque; - qemu_chr_be_event(s, CHR_EVENT_OPENED); - s->idle_tag = 0; - return FALSE; -} - void qemu_chr_be_generic_open(CharDriverState *s) { - if (s->idle_tag == 0) { - s->idle_tag = g_idle_add(qemu_chr_be_generic_open_bh, s); - } + qemu_chr_be_event(s, CHR_EVENT_OPENED); } int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) @@ -205,6 +193,8 @@ void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) va_end(ap); } +static void remove_fd_in_watch(CharDriverState *chr); + void qemu_chr_add_handlers(CharDriverState *s, IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, @@ -215,6 +205,7 @@ void qemu_chr_add_handlers(CharDriverState *s, if (!opaque && !fd_can_read && !fd_read && !fd_event) { fe_open = 0; + remove_fd_in_watch(s); } else { fe_open = 1; } @@ -247,6 +238,7 @@ static CharDriverState *qemu_chr_open_null(void) chr = g_malloc0(sizeof(CharDriverState)); chr->chr_write = null_chr_write; + chr->explicit_be_open = true; return chr; } @@ -292,7 +284,7 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) int64_t ti; int secs; - ti = qemu_get_clock_ms(rt_clock); + ti = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); if (d->timestamps_start == -1) d->timestamps_start = ti; ti -= d->timestamps_start; @@ -487,6 +479,46 @@ static void mux_chr_update_read_handler(CharDriverState *chr) mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); } +static bool muxes_realized; + +/** + * Called after processing of default and command-line-specified + * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached + * to a mux chardev. This is done here to ensure that + * output/prompts/banners are only displayed for the FE that has + * focus when initial command-line processing/machine init is + * completed. + * + * After this point, any new FE attached to any new or existing + * mux will receive CHR_EVENT_OPENED notifications for the BE + * immediately. + */ +static void muxes_realize_done(Notifier *notifier, void *unused) +{ + CharDriverState *chr; + + QTAILQ_FOREACH(chr, &chardevs, next) { + if (chr->is_mux) { + MuxDriver *d = chr->opaque; + int i; + + /* send OPENED to all already-attached FEs */ + for (i = 0; i < d->mux_cnt; i++) { + mux_chr_send_event(d, i, CHR_EVENT_OPENED); + } + /* mark mux as OPENED so any new FEs will immediately receive + * OPENED event + */ + qemu_chr_be_generic_open(chr); + } + } + muxes_realized = true; +} + +static Notifier muxes_realize_notify = { + .notify = muxes_realize_done, +}; + static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) { CharDriverState *chr; @@ -503,9 +535,11 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) chr->chr_accept_input = mux_chr_accept_input; /* Frontend guest-open / -close notification is not support with muxes */ chr->chr_set_fe_open = NULL; - - /* Muxes are always open on creation */ - qemu_chr_be_generic_open(chr); + /* only default to opened state if we've realized the initial + * set of muxes + */ + chr->explicit_be_open = muxes_realized ? 0 : 1; + chr->is_mux = 1; return chr; } @@ -694,6 +728,14 @@ static void io_remove_watch_poll(guint tag) g_source_destroy(&iwp->parent); } +static void remove_fd_in_watch(CharDriverState *chr) +{ + if (chr->fd_in_tag) { + io_remove_watch_poll(chr->fd_in_tag); + chr->fd_in_tag = 0; + } +} + #ifndef _WIN32 static GIOChannel *io_channel_from_fd(int fd) { @@ -734,35 +776,32 @@ static GIOChannel *io_channel_from_socket(int fd) static int io_channel_send(GIOChannel *fd, const void *buf, size_t len) { - GIOStatus status; - size_t offset; + size_t offset = 0; + GIOStatus status = G_IO_STATUS_NORMAL; - offset = 0; - while (offset < len) { - gsize bytes_written; + while (offset < len && status == G_IO_STATUS_NORMAL) { + gsize bytes_written = 0; 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; + if (offset > 0) { + return offset; + } + switch (status) { + case G_IO_STATUS_NORMAL: + g_assert(len == 0); + return 0; + case G_IO_STATUS_AGAIN: + errno = EAGAIN; + return -1; + default: + break; + } + errno = EINVAL; + return -1; } #ifndef _WIN32 @@ -770,7 +809,6 @@ static int io_channel_send(GIOChannel *fd, const void *buf, size_t len) typedef struct FDCharDriver { CharDriverState *chr; GIOChannel *fd_in, *fd_out; - guint fd_in_tag; int max_size; QTAILQ_ENTRY(FDCharDriver) node; } FDCharDriver; @@ -802,10 +840,7 @@ static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) 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; - } + remove_fd_in_watch(chr); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); return FALSE; } @@ -835,13 +870,10 @@ static void fd_chr_update_read_handler(CharDriverState *chr) { FDCharDriver *s = chr->opaque; - if (s->fd_in_tag) { - io_remove_watch_poll(s->fd_in_tag); - s->fd_in_tag = 0; - } - + remove_fd_in_watch(chr); if (s->fd_in) { - s->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll, fd_chr_read, chr); + chr->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll, + fd_chr_read, chr); } } @@ -849,11 +881,7 @@ static void fd_chr_close(struct CharDriverState *chr) { FDCharDriver *s = chr->opaque; - if (s->fd_in_tag) { - io_remove_watch_poll(s->fd_in_tag); - s->fd_in_tag = 0; - } - + remove_fd_in_watch(chr); if (s->fd_in) { g_io_channel_unref(s->fd_in); } @@ -883,8 +911,6 @@ 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_be_generic_open(chr); - return chr; } @@ -942,7 +968,6 @@ static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 0; } - /* if graphical mode, we allow Ctrl-C handling */ if (!stdio_allow_signal) tty.c_lflag &= ~ISIG; @@ -971,7 +996,6 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *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 = display_type != DT_NOGRAPHIC; if (opts->has_signal) { stdio_allow_signal = opts->signal; } @@ -980,63 +1004,6 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) return chr; } -#ifdef __sun__ -/* Once Solaris has openpty(), this is going to be removed. */ -static int openpty(int *amaster, int *aslave, char *name, - struct termios *termp, struct winsize *winp) -{ - const char *slave; - int mfd = -1, sfd = -1; - - *amaster = *aslave = -1; - - mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); - if (mfd < 0) - goto err; - - if (grantpt(mfd) == -1 || unlockpt(mfd) == -1) - goto err; - - if ((slave = ptsname(mfd)) == NULL) - goto err; - - if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1) - goto err; - - if (ioctl(sfd, I_PUSH, "ptem") == -1 || - (termp != NULL && tcgetattr(sfd, termp) < 0)) - goto err; - - if (amaster) - *amaster = mfd; - if (aslave) - *aslave = sfd; - if (winp) - ioctl(sfd, TIOCSWINSZ, winp); - - return 0; - -err: - if (sfd != -1) - close(sfd); - close(mfd); - return -1; -} - -static void cfmakeraw (struct termios *termios_p) -{ - termios_p->c_iflag &= - ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); - termios_p->c_oflag &= ~OPOST; - termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); - termios_p->c_cflag &= ~(CSIZE|PARENB); - termios_p->c_cflag |= CS8; - - termios_p->c_cc[VMIN] = 0; - termios_p->c_cc[VTIME] = 0; -} -#endif - #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ || defined(__GLIBC__) @@ -1045,7 +1012,6 @@ static void cfmakeraw (struct termios *termios_p) typedef struct { GIOChannel *fd; - guint fd_tag; int connected; int read_bytes; guint timer_tag; @@ -1059,15 +1025,11 @@ 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; + if (!s->connected) { + /* Next poll ... */ + pty_chr_update_read_handler(chr); + } return FALSE; } @@ -1160,10 +1122,7 @@ static void pty_chr_state(CharDriverState *chr, int connected) PtyCharDriver *s = chr->opaque; if (!connected) { - if (s->fd_tag) { - io_remove_watch_poll(s->fd_tag); - s->fd_tag = 0; - } + remove_fd_in_watch(chr); s->connected = 0; /* (re-)connect poll interval for idle guests: once per second. * We check more frequently in case the guests sends data to @@ -1175,9 +1134,10 @@ static void pty_chr_state(CharDriverState *chr, int connected) 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); + qemu_chr_be_generic_open(chr); + chr->fd_in_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, + pty_chr_read, chr); } } } @@ -1188,10 +1148,7 @@ static void pty_chr_close(struct CharDriverState *chr) PtyCharDriver *s = chr->opaque; int fd; - if (s->fd_tag) { - io_remove_watch_poll(s->fd_tag); - s->fd_tag = 0; - } + remove_fd_in_watch(chr); fd = g_io_channel_unix_get_fd(s->fd); g_io_channel_unref(s->fd); close(fd); @@ -1208,34 +1165,24 @@ static CharDriverState *qemu_chr_open_pty(const char *id, { CharDriverState *chr; PtyCharDriver *s; - struct termios tty; int master_fd, slave_fd; -#if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; -#define q_ptsname(x) pty_name -#else - char *pty_name = NULL; -#define q_ptsname(x) ptsname(x) -#endif - if (openpty(&master_fd, &slave_fd, pty_name, NULL, NULL) < 0) { + master_fd = qemu_openpty_raw(&slave_fd, pty_name); + if (master_fd < 0) { return NULL; } - /* Set raw attributes on the pty. */ - tcgetattr(slave_fd, &tty); - cfmakeraw(&tty); - tcsetattr(slave_fd, TCSAFLUSH, &tty); close(slave_fd); chr = g_malloc0(sizeof(CharDriverState)); - chr->filename = g_strdup_printf("pty:%s", q_ptsname(master_fd)); - ret->pty = g_strdup(q_ptsname(master_fd)); + chr->filename = g_strdup_printf("pty:%s", pty_name); + ret->pty = g_strdup(pty_name); ret->has_pty = true; fprintf(stderr, "char device redirected to %s (label %s)\n", - q_ptsname(master_fd), id); + pty_name, id); s = g_malloc0(sizeof(PtyCharDriver)); chr->opaque = s; @@ -1243,6 +1190,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, chr->chr_update_read_handler = pty_chr_update_read_handler; chr->chr_close = pty_chr_close; chr->chr_add_watch = pty_chr_add_watch; + chr->explicit_be_open = true; s->fd = io_channel_from_fd(master_fd); s->timer_tag = 0; @@ -1595,8 +1543,6 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd) chr->chr_close = pp_close; chr->opaque = drv; - qemu_chr_be_generic_open(chr); - return chr; } #endif /* __linux__ */ @@ -1650,6 +1596,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd) chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; + chr->explicit_be_open = true; return chr; } #endif @@ -1880,7 +1827,6 @@ static CharDriverState *qemu_chr_open_win_path(const char *filename) g_free(chr); return NULL; } - qemu_chr_be_generic_open(chr); return chr; } @@ -1980,7 +1926,6 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) g_free(chr); return NULL; } - qemu_chr_be_generic_open(chr); return chr; } @@ -1994,7 +1939,6 @@ 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_be_generic_open(chr); return chr; } @@ -2211,7 +2155,6 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) typedef struct { int fd; GIOChannel *chan; - guint tag; uint8_t buf[READ_BUF_LEN]; int bufcnt; int bufptr; @@ -2267,10 +2210,7 @@ static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) 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; - } + remove_fd_in_watch(chr); return FALSE; } @@ -2288,23 +2228,18 @@ static void udp_chr_update_read_handler(CharDriverState *chr) { NetCharDriver *s = chr->opaque; - if (s->tag) { - io_remove_watch_poll(s->tag); - s->tag = 0; - } - + remove_fd_in_watch(chr); if (s->chan) { - s->tag = io_add_watch_poll(s->chan, udp_chr_read_poll, udp_chr_read, chr); + chr->fd_in_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->tag) { - io_remove_watch_poll(s->tag); - s->tag = 0; - } + + remove_fd_in_watch(chr); if (s->chan) { g_io_channel_unref(s->chan); closesocket(s->fd); @@ -2329,6 +2264,8 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd) chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_close = udp_chr_close; + /* be isn't opened until we get a connection */ + chr->explicit_be_open = true; return chr; } @@ -2339,6 +2276,8 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) fd = inet_dgram_opts(opts, &local_err); if (fd < 0) { + qerror_report_err(local_err); + error_free(local_err); return NULL; } return qemu_chr_open_udp_fd(fd); @@ -2350,7 +2289,7 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) typedef struct { GIOChannel *chan, *listen_chan; - guint tag, listen_tag; + guint listen_tag; int fd, listen_fd; int connected; int max_size; @@ -2535,10 +2474,7 @@ 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); } - if (s->tag) { - io_remove_watch_poll(s->tag); - s->tag = 0; - } + remove_fd_in_watch(chr); g_io_channel_unref(s->chan); s->chan = NULL; closesocket(s->fd); @@ -2568,7 +2504,8 @@ static void tcp_chr_connect(void *opaque) s->connected = 1; if (s->chan) { - s->tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, tcp_chr_read, chr); + chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, + tcp_chr_read, chr); } qemu_chr_be_generic_open(chr); } @@ -2651,10 +2588,7 @@ static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; if (s->fd >= 0) { - if (s->tag) { - io_remove_watch_poll(s->tag); - s->tag = 0; - } + remove_fd_in_watch(chr); if (s->chan) { g_io_channel_unref(s->chan); } @@ -2688,7 +2622,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, memset(&ss, 0, ss_len); if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) { - error_setg(errp, "getsockname: %s", strerror(errno)); + error_setg_errno(errp, errno, "getsockname"); return NULL; } @@ -2731,6 +2665,8 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, chr->get_msgfd = tcp_get_msgfd; chr->chr_add_client = tcp_chr_add_client; chr->chr_add_watch = tcp_chr_add_watch; + /* be isn't opened until we get a connection */ + chr->explicit_be_open = true; if (is_listen) { s->listen_fd = fd; @@ -2748,8 +2684,8 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, } if (is_listen && is_waitconnect) { - printf("QEMU waiting for connection on: %s\n", - chr->filename); + fprintf(stderr, "QEMU waiting for connection on: %s\n", + chr->filename); tcp_chr_accept(s->listen_chan, G_IO_IN, chr); qemu_set_nonblock(s->listen_fd); } @@ -2761,19 +2697,12 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) CharDriverState *chr = NULL; Error *local_err = NULL; int fd = -1; - int is_listen; - int is_waitconnect; - int do_nodelay; - int is_unix; - int is_telnet; - is_listen = qemu_opt_get_bool(opts, "server", 0); - is_waitconnect = qemu_opt_get_bool(opts, "wait", 1); - is_telnet = qemu_opt_get_bool(opts, "telnet", 0); - do_nodelay = !qemu_opt_get_bool(opts, "delay", 1); - is_unix = qemu_opt_get(opts, "path") != NULL; - if (!is_listen) - is_waitconnect = 0; + bool is_listen = qemu_opt_get_bool(opts, "server", false); + bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true); + bool is_telnet = qemu_opt_get_bool(opts, "telnet", false); + bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true); + bool is_unix = qemu_opt_get(opts, "path") != NULL; if (is_unix) { if (is_listen) { @@ -2875,8 +2804,8 @@ static void ringbuf_chr_close(struct CharDriverState *chr) chr->opaque = NULL; } -static CharDriverState *qemu_chr_open_memory(ChardevMemory *opts, - Error **errp) +static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts, + Error **errp) { CharDriverState *chr; RingBufCharDriver *d; @@ -2888,7 +2817,7 @@ static CharDriverState *qemu_chr_open_memory(ChardevMemory *opts, /* The size must be power of 2 */ if (d->size & (d->size - 1)) { - error_setg(errp, "size of memory chardev must be power of two"); + error_setg(errp, "size of ringbuf chardev must be power of two"); goto fail; } @@ -3019,6 +2948,14 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) if (strstart(filename, "mon:", &p)) { filename = p; qemu_opt_set(opts, "mux", "on"); + if (strcmp(filename, "stdio") == 0) { + /* Monitor is muxed to stdio: do not exit on Ctrl+C by default + * but pass it to the guest. Handle this only for compat syntax, + * for -chardev syntax we have special option for this. + * This is what -nographic did, redirecting+muxing serial+monitor + * to stdio causing Ctrl+C to be passed to guest. */ + qemu_opt_set(opts, "signal", "off"); + } } if (strcmp(filename, "null") == 0 || @@ -3032,11 +2969,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) if (strstart(filename, "vc", &p)) { qemu_opt_set(opts, "backend", "vc"); if (*p == ':') { - if (sscanf(p+1, "%8[0-9]x%8[0-9]", width, height) == 2) { + if (sscanf(p+1, "%7[0-9]x%7[0-9]", width, height) == 2) { /* pixels */ qemu_opt_set(opts, "width", width); qemu_opt_set(opts, "height", height); - } else if (sscanf(p+1, "%8[0-9]Cx%8[0-9]C", width, height) == 2) { + } else if (sscanf(p+1, "%7[0-9]Cx%7[0-9]C", width, height) == 2) { /* chars */ qemu_opt_set(opts, "cols", width); qemu_opt_set(opts, "rows", height); @@ -3147,8 +3084,7 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend, { backend->stdio = g_new0(ChardevStdio, 1); backend->stdio->has_signal = true; - backend->stdio->signal = - qemu_opt_get_bool(opts, "signal", display_type != DT_NOGRAPHIC); + backend->stdio->signal = qemu_opt_get_bool(opts, "signal", true); } static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, @@ -3190,18 +3126,31 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend, backend->pipe->device = g_strdup(device); } -static void qemu_chr_parse_memory(QemuOpts *opts, ChardevBackend *backend, - Error **errp) +static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, + Error **errp) { int val; - backend->memory = g_new0(ChardevMemory, 1); + backend->ringbuf = g_new0(ChardevRingbuf, 1); - val = qemu_opt_get_number(opts, "size", 0); + val = qemu_opt_get_size(opts, "size", 0); if (val != 0) { - backend->memory->has_size = true; - backend->memory->size = val; + backend->ringbuf->has_size = true; + backend->ringbuf->size = val; + } +} + +static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *chardev = qemu_opt_get(opts, "chardev"); + + if (chardev == NULL) { + error_setg(errp, "chardev: mux: no chardev given"); + return; } + backend->mux = g_new0(ChardevMux, 1); + backend->mux->chardev = g_strdup(chardev); } typedef struct CharDriver { @@ -3209,7 +3158,7 @@ typedef struct CharDriver { /* old, pre qapi */ CharDriverState *(*open)(QemuOpts *opts); /* new, qapi-based */ - int kind; + ChardevBackendKind kind; void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); } CharDriver; @@ -3226,7 +3175,7 @@ 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 register_char_driver_qapi(const char *name, ChardevBackendKind kind, void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp)) { CharDriver *s; @@ -3267,7 +3216,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, if (i == NULL) { error_setg(errp, "chardev: backend \"%s\" not found", qemu_opt_get(opts, "backend")); - return NULL; + goto err; } if (!cd->open) { @@ -3275,8 +3224,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, ChardevBackend *backend = g_new0(ChardevBackend, 1); ChardevReturn *ret = NULL; const char *id = qemu_opts_id(opts); - const char *bid = NULL; - char *filename = g_strdup(qemu_opt_get(opts, "backend")); + char *bid = NULL; if (qemu_opt_get_bool(opts, "mux", 0)) { bid = g_strdup_printf("%s-base", id); @@ -3304,16 +3252,20 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, backend->mux->chardev = g_strdup(bid); ret = qmp_chardev_add(id, backend, errp); if (error_is_set(errp)) { + chr = qemu_chr_find(bid); + qemu_chr_delete(chr); + chr = NULL; goto qapi_out; } } chr = qemu_chr_find(id); - chr->filename = filename; + chr->opts = opts; qapi_out: qapi_free_ChardevBackend(backend); qapi_free_ChardevReturn(ret); + g_free(bid); return chr; } @@ -3327,6 +3279,12 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, if (!chr->filename) chr->filename = g_strdup(qemu_opt_get(opts, "backend")); chr->init = init; + /* if we didn't create the chardev via qmp_chardev_add, we + * need to send the OPENED event here + */ + if (!chr->explicit_be_open) { + qemu_chr_be_event(chr, CHR_EVENT_OPENED); + } QTAILQ_INSERT_TAIL(&chardevs, chr, next); if (qemu_opt_get_bool(opts, "mux", 0)) { @@ -3367,7 +3325,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in chr = qemu_chr_new_from_opts(opts, init, &err); if (error_is_set(&err)) { - fprintf(stderr, "%s\n", error_get_pretty(err)); + error_report("%s", error_get_pretty(err)); error_free(err); } if (chr && qemu_opt_get_bool(opts, "mux", 0)) { @@ -3567,6 +3525,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "size", .type = QEMU_OPT_SIZE, + },{ + .name = "chardev", + .type = QEMU_OPT_STRING, }, { /* end of list */ } }, @@ -3578,7 +3539,7 @@ static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) { HANDLE out; - if (file->in) { + if (file->has_in) { error_setg(errp, "input file not supported"); return NULL; } @@ -3614,7 +3575,7 @@ static int qmp_chardev_open_file_source(char *src, int flags, TFR(fd = qemu_open(src, flags, 0666)); if (fd == -1) { - error_setg(errp, "open %s: %s", src, strerror(errno)); + error_setg_file_open(errp, errno, src); } return fd; } @@ -3629,7 +3590,7 @@ static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) return NULL; } - if (file->in) { + if (file->has_in) { flags = O_RDONLY; in = qmp_chardev_open_file_source(file->in, flags, errp); if (error_is_set(errp)) { @@ -3788,8 +3749,9 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, case CHARDEV_BACKEND_KIND_VC: chr = vc_init(backend->vc); break; + case CHARDEV_BACKEND_KIND_RINGBUF: case CHARDEV_BACKEND_KIND_MEMORY: - chr = qemu_chr_open_memory(backend->memory, errp); + chr = qemu_chr_open_ringbuf(backend->ringbuf, errp); break; default: error_setg(errp, "unknown chardev backend (%d)", backend->kind); @@ -3803,6 +3765,12 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, chr->label = g_strdup(id); chr->avail_connections = (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; + if (!chr->filename) { + chr->filename = g_strdup(ChardevBackendKind_lookup[backend->kind]); + } + if (!chr->explicit_be_open) { + qemu_chr_be_event(chr, CHR_EVENT_OPENED); + } QTAILQ_INSERT_TAIL(&chardevs, chr, next); return ret; } else { @@ -3833,8 +3801,8 @@ 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_memory); + register_char_driver_qapi("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF, + 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, @@ -3851,6 +3819,16 @@ static void register_types(void) register_char_driver_qapi("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL); register_char_driver_qapi("pipe", CHARDEV_BACKEND_KIND_PIPE, qemu_chr_parse_pipe); + register_char_driver_qapi("mux", CHARDEV_BACKEND_KIND_MUX, + qemu_chr_parse_mux); + /* Bug-compatibility: */ + register_char_driver_qapi("memory", CHARDEV_BACKEND_KIND_MEMORY, + qemu_chr_parse_ringbuf); + /* this must be done after machine init, since we register FEs with muxes + * as part of realize functions like serial_isa_realizefn when -nographic + * is specified + */ + qemu_add_machine_init_done_notifier(&muxes_realize_notify); } type_init(register_types);