summaryrefslogtreecommitdiffstats
path: root/qemu-char.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu-char.c')
-rw-r--r--qemu-char.c206
1 files changed, 78 insertions, 128 deletions
diff --git a/qemu-char.c b/qemu-char.c
index dee623e..dd410ce 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -594,65 +594,51 @@ int recv_all(int fd, void *_buf, int len1, bool single_read)
typedef struct IOWatchPoll
{
+ GSource parent;
+
GSource *src;
- int max_size;
IOCanReadHandler *fd_can_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 = g_source_get_context(iwp->src) != NULL;
+ if (was_active == now_active) {
return FALSE;
}
- return g_io_watch_funcs.prepare(source, timeout_);
+ if (now_active) {
+ g_source_attach(iwp->src, NULL);
+ } else {
+ g_source_remove(g_source_get_id(iwp->src));
+ }
+ 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)
{
IOWatchPoll *iwp = io_watch_poll_from_source(source);
- QTAILQ_REMOVE(&io_watch_poll_list, iwp, node);
- g_io_watch_funcs.finalize(source);
+ g_source_unref(iwp->src);
}
static GSourceFuncs io_watch_poll_funcs = {
@@ -669,24 +655,14 @@ static guint io_add_watch_poll(GIOChannel *channel,
gpointer user_data)
{
IOWatchPoll *iwp;
- GSource *src;
- guint 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->src = g_io_create_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP);
+ g_source_set_callback(iwp->src, (GSourceFunc)fd_read, user_data, NULL);
- QTAILQ_INSERT_HEAD(&io_watch_poll_list, iwp, node);
-
- return tag;
+ return g_source_attach(&iwp->parent, NULL);
}
#ifndef _WIN32
@@ -727,33 +703,37 @@ static GIOChannel *io_channel_from_socket(int fd)
return chan;
}
-static int io_channel_send_all(GIOChannel *fd, const void *_buf, int len1)
+static int io_channel_send(GIOChannel *fd, const void *buf, size_t len)
{
GIOStatus status;
- gsize bytes_written;
- int len;
- const uint8_t *buf = _buf;
+ size_t offset;
- len = len1;
- while (len > 0) {
- status = g_io_channel_write_chars(fd, (const gchar *)buf, len,
+ 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;
- return -1;
} else {
errno = EINVAL;
- return -1;
}
+
+ return -1;
} else if (status == G_IO_STATUS_EOF) {
break;
- } else {
- buf += bytes_written;
- len -= bytes_written;
}
+
+ offset += bytes_written;
}
- return len1 - len;
+
+ return offset;
}
#ifndef _WIN32
@@ -770,7 +750,7 @@ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
FDCharDriver *s = chr->opaque;
- return io_channel_send_all(s->fd_out, buf, len);
+ return io_channel_send(s->fd_out, buf, len);
}
static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
@@ -1088,7 +1068,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
pty_chr_update_read_handler(chr);
return 0;
}
- return io_channel_send_all(s->fd, buf, len);
+ return io_channel_send(s->fd, buf, len);
}
static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
@@ -2347,7 +2327,7 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
if (s->connected) {
- return io_channel_send_all(s->chan, buf, len);
+ return io_channel_send(s->chan, buf, len);
} else {
/* XXX: indicate an error ? */
return len;
@@ -2440,6 +2420,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
@@ -2570,7 +2553,7 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd)
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;
@@ -2722,7 +2705,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
printf("QEMU waiting for connection on: %s\n",
chr->filename);
tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
- socket_set_nonblock(s->listen_fd);
+ qemu_set_nonblock(s->listen_fd);
}
return chr;
}
@@ -2764,7 +2747,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);
@@ -2789,70 +2772,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 */
@@ -3404,6 +3323,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;
@@ -3445,6 +3365,29 @@ int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
return tag;
}
+int qemu_chr_fe_claim(CharDriverState *s)
+{
+ if (s->avail_connections < 1) {
+ return -1;
+ }
+ s->avail_connections--;
+ return 0;
+}
+
+void qemu_chr_fe_claim_no_fail(CharDriverState *s)
+{
+ 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);
@@ -3493,9 +3436,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 = {
@@ -3653,7 +3603,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");
OpenPOWER on IntegriCloud