#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"
#include <unistd.h>
#include <fcntl.h>
-#include <signal.h>
#include <time.h>
#include <errno.h>
#include <sys/time.h>
#endif
#include "qemu_socket.h"
+#include "ui/qemu-spice.h"
#define READ_BUF_LEN 4096
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);
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)
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;
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);
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;
}
#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) {
}
return len1 - len;
}
-
-int send_all(int fd, const void *buf, int len1)
-{
- return unix_write(fd, buf, len1);
-}
#endif /* !_WIN32 */
#ifndef _WIN32
/* 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)
{
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)
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;
}
* 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)
/* (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);
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;
}
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;
return 0;
}
-static void tty_exit(void)
-{
- tcsetattr(0, TCSANOW, &oldtty);
-}
-
static void qemu_chr_close_tty(CharDriverState *chr)
{
FDCharDriver *s = chr->opaque;
}
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;
}
#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) {
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;
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
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;
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];
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;
|| 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,
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;
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;
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);
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;