#include "ui/console.h"
#include "sysemu/sysemu.h"
#include "qemu/timer.h"
-#include "char/char.h"
+#include "sysemu/char.h"
#include "hw/usb.h"
#include "qmp-commands.h"
#include <sys/select.h>
#ifdef CONFIG_BSD
#include <sys/stat.h>
-#if defined(__GLIBC__)
-#include <pty.h>
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-#include <libutil.h>
-#else
-#include <util.h>
-#endif
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <dev/ppbus/ppi.h>
#include <dev/ppbus/ppbconf.h>
#endif
#else
#ifdef __linux__
-#include <pty.h>
-
#include <linux/ppdev.h>
#include <linux/parport.h>
#endif
#include <netinet/tcp.h>
#include <net/if.h>
#include <syslog.h>
-#include <stropts.h>
#endif
#endif
#endif
typedef struct IOWatchPoll
{
+ GSource parent;
+
+ GIOChannel *channel;
GSource *src;
- int max_size;
IOCanReadHandler *fd_can_read;
+ GSourceFunc fd_read;
void *opaque;
-
- QTAILQ_ENTRY(IOWatchPoll) node;
} IOWatchPoll;
-static QTAILQ_HEAD(, IOWatchPoll) io_watch_poll_list =
- QTAILQ_HEAD_INITIALIZER(io_watch_poll_list);
-
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
{
- IOWatchPoll *i;
-
- QTAILQ_FOREACH(i, &io_watch_poll_list, node) {
- if (i->src == source) {
- return i;
- }
- }
-
- return NULL;
+ return container_of(source, IOWatchPoll, parent);
}
static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_)
{
IOWatchPoll *iwp = io_watch_poll_from_source(source);
-
- iwp->max_size = iwp->fd_can_read(iwp->opaque);
- if (iwp->max_size == 0) {
+ bool now_active = iwp->fd_can_read(iwp->opaque) > 0;
+ bool was_active = iwp->src != NULL;
+ if (was_active == now_active) {
return FALSE;
}
- return g_io_watch_funcs.prepare(source, timeout_);
+ if (now_active) {
+ iwp->src = g_io_create_watch(iwp->channel, G_IO_IN | G_IO_ERR | G_IO_HUP);
+ g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
+ g_source_attach(iwp->src, NULL);
+ } else {
+ g_source_destroy(iwp->src);
+ g_source_unref(iwp->src);
+ iwp->src = NULL;
+ }
+ return FALSE;
}
static gboolean io_watch_poll_check(GSource *source)
{
- IOWatchPoll *iwp = io_watch_poll_from_source(source);
-
- if (iwp->max_size == 0) {
- return FALSE;
- }
-
- return g_io_watch_funcs.check(source);
+ return FALSE;
}
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
gpointer user_data)
{
- return g_io_watch_funcs.dispatch(source, callback, user_data);
+ abort();
}
static void io_watch_poll_finalize(GSource *source)
{
+ /* Due to a glib bug, removing the last reference to a source
+ * inside a finalize callback causes recursive locking (and a
+ * deadlock). This is not a problem inside other callbacks,
+ * including dispatch callbacks, so we call io_remove_watch_poll
+ * to remove this source. At this point, iwp->src must
+ * be NULL, or we would leak it.
+ *
+ * This would be solved much more elegantly by child sources,
+ * but we support older glib versions that do not have them.
+ */
IOWatchPoll *iwp = io_watch_poll_from_source(source);
- QTAILQ_REMOVE(&io_watch_poll_list, iwp, node);
- g_io_watch_funcs.finalize(source);
+ assert(iwp->src == NULL);
}
static GSourceFuncs io_watch_poll_funcs = {
gpointer user_data)
{
IOWatchPoll *iwp;
- GSource *src;
- guint tag;
+ int tag;
- src = g_io_create_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP);
- g_source_set_funcs(src, &io_watch_poll_funcs);
- g_source_set_callback(src, (GSourceFunc)fd_read, user_data, NULL);
- tag = g_source_attach(src, NULL);
- g_source_unref(src);
-
- iwp = g_malloc0(sizeof(*iwp));
- iwp->src = src;
- iwp->max_size = 0;
+ iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll));
iwp->fd_can_read = fd_can_read;
iwp->opaque = user_data;
+ iwp->channel = channel;
+ iwp->fd_read = (GSourceFunc) fd_read;
+ iwp->src = NULL;
- QTAILQ_INSERT_HEAD(&io_watch_poll_list, iwp, node);
-
+ tag = g_source_attach(&iwp->parent, NULL);
+ g_source_unref(&iwp->parent);
return tag;
}
+static void io_remove_watch_poll(guint tag)
+{
+ GSource *source;
+ IOWatchPoll *iwp;
+
+ g_return_if_fail (tag > 0);
+
+ source = g_main_context_find_source_by_id(NULL, tag);
+ g_return_if_fail (source != NULL);
+
+ iwp = io_watch_poll_from_source(source);
+ if (iwp->src) {
+ g_source_destroy(iwp->src);
+ g_source_unref(iwp->src);
+ iwp->src = NULL;
+ }
+ g_source_destroy(&iwp->parent);
+}
+
#ifndef _WIN32
static GIOChannel *io_channel_from_fd(int fd)
{
len = s->max_size;
}
if (len == 0) {
- return FALSE;
+ return TRUE;
}
status = g_io_channel_read_chars(chan, (gchar *)buf,
len, &bytes_read, NULL);
if (status == G_IO_STATUS_EOF) {
+ if (s->fd_in_tag) {
+ io_remove_watch_poll(s->fd_in_tag);
+ s->fd_in_tag = 0;
+ }
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
return FALSE;
}
FDCharDriver *s = chr->opaque;
if (s->fd_in_tag) {
- g_source_remove(s->fd_in_tag);
+ io_remove_watch_poll(s->fd_in_tag);
+ s->fd_in_tag = 0;
}
if (s->fd_in) {
FDCharDriver *s = chr->opaque;
if (s->fd_in_tag) {
- g_source_remove(s->fd_in_tag);
+ io_remove_watch_poll(s->fd_in_tag);
s->fd_in_tag = 0;
}
GIOChannel *fd;
guint fd_tag;
int connected;
- int polling;
int read_bytes;
guint timer_tag;
} PtyCharDriver;
if (s->connected) {
goto out;
}
- if (s->polling) {
- /* If we arrive here without polling being cleared due
- * read returning -EIO, then we are (re-)connected */
- pty_chr_state(chr, 1);
- goto out;
- }
/* Next poll ... */
pty_chr_update_read_handler(chr);
out:
+ s->timer_tag = 0;
return FALSE;
}
len = sizeof(buf);
if (len > s->read_bytes)
len = s->read_bytes;
- if (len == 0)
- return FALSE;
+ if (len == 0) {
+ return TRUE;
+ }
status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL);
if (status != G_IO_STATUS_NORMAL) {
pty_chr_state(chr, 0);
static void pty_chr_update_read_handler(CharDriverState *chr)
{
PtyCharDriver *s = chr->opaque;
+ GPollFD pfd;
- if (s->fd_tag) {
- g_source_remove(s->fd_tag);
- }
-
- s->fd_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr);
- s->polling = 1;
- /*
- * Short timeout here: just need wait long enougth that qemu makes
- * it through the poll loop once. When reconnected we want a
- * short timeout so we notice it almost instantly. Otherwise
- * read() gives us -EIO instantly, making pty_chr_state() reset the
- * timeout to the normal (much longer) poll interval before the
- * timer triggers.
- */
- pty_chr_rearm_timer(chr, 10);
+ pfd.fd = g_io_channel_unix_get_fd(s->fd);
+ pfd.events = G_IO_OUT;
+ pfd.revents = 0;
+ g_poll(&pfd, 1, 0);
+ if (pfd.revents & G_IO_HUP) {
+ pty_chr_state(chr, 0);
+ } else {
+ pty_chr_state(chr, 1);
+ }
}
static void pty_chr_state(CharDriverState *chr, int connected)
PtyCharDriver *s = chr->opaque;
if (!connected) {
- g_source_remove(s->fd_tag);
- s->fd_tag = 0;
+ if (s->fd_tag) {
+ io_remove_watch_poll(s->fd_tag);
+ s->fd_tag = 0;
+ }
s->connected = 0;
- s->polling = 0;
/* (re-)connect poll interval for idle guests: once per second.
* We check more frequently in case the guests sends data to
* the virtual device linked to our pty. */
pty_chr_rearm_timer(chr, 1000);
} else {
- if (!s->connected)
+ 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->connected = 1;
+ s->fd_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr);
+ }
}
}
int fd;
if (s->fd_tag) {
- g_source_remove(s->fd_tag);
+ io_remove_watch_poll(s->fd_tag);
+ s->fd_tag = 0;
}
fd = g_io_channel_unix_get_fd(s->fd);
g_io_channel_unref(s->fd);
close(fd);
if (s->timer_tag) {
g_source_remove(s->timer_tag);
+ s->timer_tag = 0;
}
g_free(s);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
gsize bytes_read = 0;
GIOStatus status;
- if (s->max_size == 0)
- return FALSE;
+ if (s->max_size == 0) {
+ return TRUE;
+ }
status = g_io_channel_read_chars(s->chan, (gchar *)s->buf, sizeof(s->buf),
&bytes_read, NULL);
s->bufcnt = bytes_read;
s->bufptr = s->bufcnt;
if (status != G_IO_STATUS_NORMAL) {
+ if (s->tag) {
+ io_remove_watch_poll(s->tag);
+ s->tag = 0;
+ }
return FALSE;
}
NetCharDriver *s = chr->opaque;
if (s->tag) {
- g_source_remove(s->tag);
+ io_remove_watch_poll(s->tag);
s->tag = 0;
}
{
NetCharDriver *s = chr->opaque;
if (s->tag) {
- g_source_remove(s->tag);
+ io_remove_watch_poll(s->tag);
+ s->tag = 0;
}
if (s->chan) {
g_io_channel_unref(s->chan);
int len, size;
if (!s->connected || s->max_size <= 0) {
- return FALSE;
+ return TRUE;
}
len = sizeof(buf);
if (len > s->max_size)
if (s->listen_chan) {
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
}
- g_source_remove(s->tag);
- s->tag = 0;
+ if (s->tag) {
+ io_remove_watch_poll(s->tag);
+ s->tag = 0;
+ }
g_io_channel_unref(s->chan);
s->chan = NULL;
closesocket(s->fd);
socket_set_nodelay(fd);
s->fd = fd;
s->chan = io_channel_from_socket(fd);
- g_source_remove(s->listen_tag);
- s->listen_tag = 0;
+ if (s->listen_tag) {
+ g_source_remove(s->listen_tag);
+ s->listen_tag = 0;
+ }
tcp_chr_connect(chr);
return 0;
}
fd = qemu_accept(s->listen_fd, addr, &len);
if (fd < 0 && errno != EINTR) {
+ s->listen_tag = 0;
return FALSE;
} else if (fd >= 0) {
if (s->do_telnetopt)
TCPCharDriver *s = chr->opaque;
if (s->fd >= 0) {
if (s->tag) {
- g_source_remove(s->tag);
+ io_remove_watch_poll(s->tag);
+ s->tag = 0;
}
if (s->chan) {
g_io_channel_unref(s->chan);
if (s->listen_fd >= 0) {
if (s->listen_tag) {
g_source_remove(s->listen_tag);
+ s->listen_tag = 0;
}
if (s->listen_chan) {
g_io_channel_unref(s->listen_chan);
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 */
chr->opaque = NULL;
}
-static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts,
- Error **errp)
+static CharDriverState *qemu_chr_open_memory(ChardevMemory *opts,
+ Error **errp)
{
CharDriverState *chr;
RingBufCharDriver *d;
/* The size must be power of 2 */
if (d->size & (d->size - 1)) {
- error_setg(errp, "size of ringbuf chardev must be power of two");
+ error_setg(errp, "size of memory chardev must be power of two");
goto fail;
}
CharDriverState *chr;
const uint8_t *write_data;
int ret;
- size_t write_count;
+ gsize write_count;
chr = qemu_chr_find(device);
if (!chr) {
backend->pipe->device = g_strdup(device);
}
-static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend,
- Error **errp)
+static void qemu_chr_parse_memory(QemuOpts *opts, ChardevBackend *backend,
+ Error **errp)
{
int val;
- backend->memory = g_new0(ChardevRingbuf, 1);
+ backend->memory = g_new0(ChardevMemory, 1);
val = qemu_opt_get_number(opts, "size", 0);
if (val != 0) {
is_telnet, is_waitconnect, errp);
}
-static CharDriverState *qmp_chardev_open_dgram(ChardevDgram *dgram,
- Error **errp)
+static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
+ Error **errp)
{
int fd;
- fd = socket_dgram(dgram->remote, dgram->local, errp);
+ fd = socket_dgram(udp->remote, udp->local, errp);
if (error_is_set(errp)) {
return NULL;
}
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);
+ case CHARDEV_BACKEND_KIND_UDP:
+ chr = qmp_chardev_open_udp(backend->udp, errp);
break;
#ifdef HAVE_CHARDEV_TTY
case CHARDEV_BACKEND_KIND_PTY:
chr = vc_init(backend->vc);
break;
case CHARDEV_BACKEND_KIND_MEMORY:
- chr = qemu_chr_open_ringbuf(backend->memory, errp);
+ chr = qemu_chr_open_memory(backend->memory, errp);
break;
default:
error_setg(errp, "unknown chardev backend (%d)", backend->kind);
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]);
+ }
QTAILQ_INSERT_TAIL(&chardevs, chr, next);
return ret;
} else {
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);
+ qemu_chr_parse_memory);
register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE,
qemu_chr_parse_file_out);
register_char_driver_qapi("stdio", CHARDEV_BACKEND_KIND_STDIO,