X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/7b27a769f139102c906c8d11d844d51dce068a6a..5a82362ad0bf06bba3377d63ca0ecd05fb74f322:/qemu-char.c?ds=sidebyside diff --git a/qemu-char.c b/qemu-char.c index a3d6e64967..fb13b28454 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -28,7 +28,6 @@ #include "sysemu.h" #include "qemu-timer.h" #include "qemu-char.h" -#include "block.h" #include "hw/usb.h" #include "hw/baum.h" #include "hw/msmouse.h" @@ -36,7 +35,6 @@ #include #include -#include #include #include #include @@ -98,6 +96,7 @@ #endif #include "qemu_socket.h" +#include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 @@ -109,6 +108,16 @@ static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = static void qemu_chr_event(CharDriverState *s, int event) { + /* Keep track if the char device is open */ + switch (event) { + case CHR_EVENT_OPENED: + s->opened = 1; + break; + case CHR_EVENT_CLOSED: + s->opened = 0; + break; + } + if (!s->chr_event) return; s->chr_event(s->handler_opaque, event); @@ -187,12 +196,22 @@ void qemu_chr_add_handlers(CharDriverState *s, IOEventHandler *fd_event, void *opaque) { + if (!opaque && !fd_can_read && !fd_read && !fd_event) { + /* chr driver being released. */ + ++s->avail_connections; + } s->chr_can_read = fd_can_read; s->chr_read = fd_read; s->chr_event = fd_event; s->handler_opaque = opaque; if (s->chr_update_read_handler) s->chr_update_read_handler(s); + + /* 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); + } } static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) @@ -251,7 +270,7 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) int64_t ti; int secs; - ti = qemu_get_clock(rt_clock); + ti = qemu_get_clock_ms(rt_clock); if (d->timestamps_start == -1) d->timestamps_start = ti; ti -= d->timestamps_start; @@ -336,12 +355,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) break; } case 's': - { - DriveInfo *dinfo; - QTAILQ_FOREACH(dinfo, &drives, next) { - bdrv_commit(dinfo->bdrv); - } - } + bdrv_commit_all(); break; case 'b': qemu_chr_event(chr, CHR_EVENT_BREAK); @@ -465,6 +479,13 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv) chr->chr_write = mux_chr_write; chr->chr_update_read_handler = mux_chr_update_read_handler; chr->chr_accept_input = mux_chr_accept_input; + /* Frontend guest-open / -close notification is not support with muxes */ + chr->chr_guest_open = NULL; + chr->chr_guest_close = NULL; + + /* Muxes are always open on creation */ + qemu_chr_generic_open(chr); + return chr; } @@ -494,9 +515,10 @@ int send_all(int fd, const void *buf, int len1) #else -static int unix_write(int fd, const uint8_t *buf, int len1) +int send_all(int fd, const void *_buf, int len1) { int ret, len; + const uint8_t *buf = _buf; len = len1; while (len > 0) { @@ -513,11 +535,6 @@ static int unix_write(int fd, const uint8_t *buf, int len1) } return len1 - len; } - -int send_all(int fd, const void *buf, int len1) -{ - return unix_write(fd, buf, len1); -} #endif /* !_WIN32 */ #ifndef _WIN32 @@ -705,7 +722,7 @@ static void stdio_read(void *opaque) /* init terminal so that we can grab keys */ static struct termios oldtty; static int old_fd0_flags; -static int term_atexit_done; +static bool stdio_allow_signal; static void term_exit(void) { @@ -713,32 +730,26 @@ static void term_exit(void) fcntl(0, F_SETFL, old_fd0_flags); } -static void term_init(QemuOpts *opts) +static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) { struct termios tty; - tcgetattr (0, &tty); - oldtty = tty; - old_fd0_flags = fcntl(0, F_GETFL); - - tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + tty = oldtty; + if (!echo) { + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP |INLCR|IGNCR|ICRNL|IXON); - tty.c_oflag |= OPOST; - tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + } /* if graphical mode, we allow Ctrl-C handling */ - if (!qemu_opt_get_bool(opts, "signal", display_type != DT_NOGRAPHIC)) + if (!stdio_allow_signal) tty.c_lflag &= ~ISIG; - tty.c_cflag &= ~(CSIZE|PARENB); - tty.c_cflag |= CS8; - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; tcsetattr (0, TCSANOW, &tty); - - if (!term_atexit_done++) - atexit(term_exit); - - fcntl(0, F_SETFL, O_NONBLOCK); } static void qemu_chr_close_stdio(struct CharDriverState *chr) @@ -755,11 +766,21 @@ static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) if (stdio_nb_clients >= STDIO_MAX_CLIENTS) 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); + } + 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++; - term_init(opts); + stdio_allow_signal = qemu_opt_get_bool(opts, "signal", + display_type != DT_NOGRAPHIC); + qemu_chr_set_echo(chr, false); return chr; } @@ -896,7 +917,7 @@ static void pty_chr_update_read_handler(CharDriverState *chr) * timeout to the normal (much longer) poll interval before the * timer triggers. */ - qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 10); } static void pty_chr_state(CharDriverState *chr, int connected) @@ -910,7 +931,7 @@ static void pty_chr_state(CharDriverState *chr, int connected) /* (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(rt_clock) + 1000); + qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 1000); } else { if (!s->connected) qemu_chr_generic_open(chr); @@ -986,7 +1007,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) chr->chr_update_read_handler = pty_chr_update_read_handler; chr->chr_close = pty_chr_close; - s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr); + s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); return chr; } @@ -1002,7 +1023,6 @@ static void tty_serial_init(int fd, int speed, speed, parity, data_bits, stop_bits); #endif tcgetattr (fd, &tty); - oldtty = tty; #define check_speed(val) if (speed <= val) { spd = B##val; break; } speed = speed * 10 / 11; @@ -1174,11 +1194,6 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static void tty_exit(void) -{ - tcsetattr(0, TCSANOW, &oldtty); -} - static void qemu_chr_close_tty(CharDriverState *chr) { FDCharDriver *s = chr->opaque; @@ -1213,12 +1228,10 @@ static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) } chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; - if (!term_atexit_done++) - atexit(tty_exit); return chr; } #else /* ! __linux__ && ! __sun__ */ -static CharDriverState *qemu_chr_open_pty(void) +static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) { return NULL; } @@ -1369,7 +1382,7 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) { - int fd = (int)chr->opaque; + int fd = (int)(intptr_t)chr->opaque; uint8_t b; switch(cmd) { @@ -1415,7 +1428,7 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) return NULL; chr = qemu_mallocz(sizeof(CharDriverState)); - chr->opaque = (void *)fd; + chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; return chr; @@ -1978,8 +1991,9 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr, static int tcp_get_msgfd(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; - - return s->msgfd; + int fd = s->msgfd; + s->msgfd = -1; + return fd; } #ifndef _WIN32 @@ -2067,13 +2081,16 @@ static void tcp_chr_read(void *opaque) tcp_chr_process_IAC_bytes(chr, s, buf, &size); if (size > 0) qemu_chr_read(chr, buf, size); - if (s->msgfd != -1) { - close(s->msgfd); - s->msgfd = -1; - } } } +#ifndef _WIN32 +CharDriverState *qemu_chr_open_eventfd(int eventfd) +{ + return qemu_chr_open_fd(eventfd, eventfd); +} +#endif + static void tcp_chr_connect(void *opaque) { CharDriverState *chr = opaque; @@ -2259,6 +2276,70 @@ 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 = qemu_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 = qemu_malloc(sizeof(*d)); + d->outbuf_size = 0; + d->outbuf_capacity = 4096; + d->outbuf = qemu_mallocz(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_close()! */ +void qemu_chr_close_mem(CharDriverState *chr) +{ + MemoryDriver *d = chr->opaque; + + qemu_free(d->outbuf); + qemu_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; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2266,7 +2347,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) const char *p; QemuOpts *opts; - opts = qemu_opts_create(&qemu_chardev_opts, label, 1); + opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1); if (NULL == opts) return NULL; @@ -2415,6 +2496,9 @@ static const struct { || defined(__FreeBSD_kernel__) { .name = "parport", .open = qemu_chr_open_pp }, #endif +#ifdef CONFIG_SPICE + { .name = "spicevmc", .open = qemu_chr_open_spice }, +#endif }; CharDriverState *qemu_chr_open_opts(QemuOpts *opts, @@ -2428,6 +2512,11 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, return NULL; } + if (qemu_opt_get(opts, "backend") == NULL) { + fprintf(stderr, "chardev: \"%s\" missing backend\n", + qemu_opts_id(opts)); + return NULL; + } for (i = 0; i < ARRAY_SIZE(backend_table); i++) { if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0) break; @@ -2457,7 +2546,10 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, snprintf(base->label, len, "%s-base", qemu_opts_id(opts)); chr = qemu_chr_open_mux(base); chr->filename = base->filename; + chr->avail_connections = MAX_MUX; QTAILQ_INSERT_TAIL(&chardevs, chr, next); + } else { + chr->avail_connections = 1; } chr->label = qemu_strdup(qemu_opts_id(opts)); return chr; @@ -2481,9 +2573,31 @@ CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*i if (chr && qemu_opt_get_bool(opts, "mux", 0)) { monitor_init(chr, MONITOR_USE_READLINE); } + qemu_opts_del(opts); return chr; } +void qemu_chr_set_echo(struct CharDriverState *chr, bool echo) +{ + if (chr->chr_set_echo) { + chr->chr_set_echo(chr, echo); + } +} + +void qemu_chr_guest_open(struct CharDriverState *chr) +{ + if (chr->chr_guest_open) { + chr->chr_guest_open(chr); + } +} + +void qemu_chr_guest_close(struct CharDriverState *chr) +{ + if (chr->chr_guest_close) { + chr->chr_guest_close(chr); + } +} + void qemu_chr_close(CharDriverState *chr) { QTAILQ_REMOVE(&chardevs, chr, next); @@ -2509,22 +2623,6 @@ void qemu_chr_info_print(Monitor *mon, const QObject *ret_data) qlist_iter(qobject_to_qlist(ret_data), qemu_chr_qlist_iter, mon); } -/** - * qemu_chr_info(): Character devices information - * - * Each device is represented by a QDict. The returned QObject is a QList - * of all devices. - * - * The QDict contains the following: - * - * - "label": device's label - * - "filename": device's file - * - * Example: - * - * [ { "label": "monitor", "filename", "stdio" }, - * { "label": "serial0", "filename": "vc" } ] - */ void qemu_chr_info(Monitor *mon, QObject **ret_data) { QList *chr_list;