summaryrefslogtreecommitdiffstats
path: root/qemu/io
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/io')
-rw-r--r--qemu/io/Makefile.objs10
-rw-r--r--qemu/io/channel-buffer.c249
-rw-r--r--qemu/io/channel-command.c368
-rw-r--r--qemu/io/channel-file.c225
-rw-r--r--qemu/io/channel-socket.c772
-rw-r--r--qemu/io/channel-tls.c395
-rw-r--r--qemu/io/channel-util.c51
-rw-r--r--qemu/io/channel-watch.c347
-rw-r--r--qemu/io/channel-websock.c964
-rw-r--r--qemu/io/channel.c307
-rw-r--r--qemu/io/task.c161
11 files changed, 3849 insertions, 0 deletions
diff --git a/qemu/io/Makefile.objs b/qemu/io/Makefile.objs
new file mode 100644
index 000000000..9d8337d89
--- /dev/null
+++ b/qemu/io/Makefile.objs
@@ -0,0 +1,10 @@
+io-obj-y = channel.o
+io-obj-y += channel-buffer.o
+io-obj-y += channel-command.o
+io-obj-y += channel-file.o
+io-obj-y += channel-socket.o
+io-obj-y += channel-tls.o
+io-obj-y += channel-watch.o
+io-obj-y += channel-websock.o
+io-obj-y += channel-util.o
+io-obj-y += task.o
diff --git a/qemu/io/channel-buffer.c b/qemu/io/channel-buffer.c
new file mode 100644
index 000000000..3e5117bf2
--- /dev/null
+++ b/qemu/io/channel-buffer.c
@@ -0,0 +1,249 @@
+/*
+ * QEMU I/O channels memory buffer driver
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "io/channel-buffer.h"
+#include "io/channel-watch.h"
+#include "qemu/sockets.h"
+#include "trace.h"
+
+QIOChannelBuffer *
+qio_channel_buffer_new(size_t capacity)
+{
+ QIOChannelBuffer *ioc;
+
+ ioc = QIO_CHANNEL_BUFFER(object_new(TYPE_QIO_CHANNEL_BUFFER));
+
+ if (capacity) {
+ ioc->data = g_new0(uint8_t, capacity);
+ ioc->capacity = capacity;
+ }
+
+ return ioc;
+}
+
+
+static void qio_channel_buffer_finalize(Object *obj)
+{
+ QIOChannelBuffer *ioc = QIO_CHANNEL_BUFFER(obj);
+ g_free(ioc->data);
+ ioc->capacity = ioc->usage = ioc->offset = 0;
+}
+
+
+static ssize_t qio_channel_buffer_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
+ ssize_t ret = 0;
+ size_t i;
+
+ for (i = 0; i < niov; i++) {
+ size_t want = iov[i].iov_len;
+ if (bioc->offset >= bioc->usage) {
+ break;
+ }
+ if ((bioc->offset + want) > bioc->usage) {
+ want = bioc->usage - bioc->offset;
+ }
+ memcpy(iov[i].iov_base, bioc->data + bioc->offset, want);
+ ret += want;
+ bioc->offset += want;
+ }
+
+ return ret;
+}
+
+static ssize_t qio_channel_buffer_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
+ ssize_t ret = 0;
+ size_t i;
+ size_t towrite = 0;
+
+ for (i = 0; i < niov; i++) {
+ towrite += iov[i].iov_len;
+ }
+
+ if ((bioc->offset + towrite) > bioc->capacity) {
+ bioc->capacity = bioc->offset + towrite;
+ bioc->data = g_realloc(bioc->data, bioc->capacity);
+ }
+
+ if (bioc->offset > bioc->usage) {
+ memset(bioc->data, 0, bioc->offset - bioc->usage);
+ bioc->usage = bioc->offset;
+ }
+
+ for (i = 0; i < niov; i++) {
+ memcpy(bioc->data + bioc->usage,
+ iov[i].iov_base,
+ iov[i].iov_len);
+ bioc->usage += iov[i].iov_len;
+ bioc->offset += iov[i].iov_len;
+ ret += iov[i].iov_len;
+ }
+
+ return ret;
+}
+
+static int qio_channel_buffer_set_blocking(QIOChannel *ioc G_GNUC_UNUSED,
+ bool enabled G_GNUC_UNUSED,
+ Error **errp G_GNUC_UNUSED)
+{
+ return 0;
+}
+
+
+static off_t qio_channel_buffer_seek(QIOChannel *ioc,
+ off_t offset,
+ int whence,
+ Error **errp)
+{
+ QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
+
+ bioc->offset = offset;
+
+ return offset;
+}
+
+
+static int qio_channel_buffer_close(QIOChannel *ioc,
+ Error **errp)
+{
+ QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
+
+ g_free(bioc->data);
+ bioc->capacity = bioc->usage = bioc->offset = 0;
+
+ return 0;
+}
+
+
+typedef struct QIOChannelBufferSource QIOChannelBufferSource;
+struct QIOChannelBufferSource {
+ GSource parent;
+ QIOChannelBuffer *bioc;
+ GIOCondition condition;
+};
+
+static gboolean
+qio_channel_buffer_source_prepare(GSource *source,
+ gint *timeout)
+{
+ QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
+
+ *timeout = -1;
+
+ return (G_IO_IN | G_IO_OUT) & bsource->condition;
+}
+
+static gboolean
+qio_channel_buffer_source_check(GSource *source)
+{
+ QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
+
+ return (G_IO_IN | G_IO_OUT) & bsource->condition;
+}
+
+static gboolean
+qio_channel_buffer_source_dispatch(GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ QIOChannelFunc func = (QIOChannelFunc)callback;
+ QIOChannelBufferSource *bsource = (QIOChannelBufferSource *)source;
+
+ return (*func)(QIO_CHANNEL(bsource->bioc),
+ ((G_IO_IN | G_IO_OUT) & bsource->condition),
+ user_data);
+}
+
+static void
+qio_channel_buffer_source_finalize(GSource *source)
+{
+ QIOChannelBufferSource *ssource = (QIOChannelBufferSource *)source;
+
+ object_unref(OBJECT(ssource->bioc));
+}
+
+GSourceFuncs qio_channel_buffer_source_funcs = {
+ qio_channel_buffer_source_prepare,
+ qio_channel_buffer_source_check,
+ qio_channel_buffer_source_dispatch,
+ qio_channel_buffer_source_finalize
+};
+
+static GSource *qio_channel_buffer_create_watch(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
+ QIOChannelBufferSource *ssource;
+ GSource *source;
+
+ source = g_source_new(&qio_channel_buffer_source_funcs,
+ sizeof(QIOChannelBufferSource));
+ ssource = (QIOChannelBufferSource *)source;
+
+ ssource->bioc = bioc;
+ object_ref(OBJECT(bioc));
+
+ ssource->condition = condition;
+
+ return source;
+}
+
+
+static void qio_channel_buffer_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
+
+ ioc_klass->io_writev = qio_channel_buffer_writev;
+ ioc_klass->io_readv = qio_channel_buffer_readv;
+ ioc_klass->io_set_blocking = qio_channel_buffer_set_blocking;
+ ioc_klass->io_seek = qio_channel_buffer_seek;
+ ioc_klass->io_close = qio_channel_buffer_close;
+ ioc_klass->io_create_watch = qio_channel_buffer_create_watch;
+}
+
+static const TypeInfo qio_channel_buffer_info = {
+ .parent = TYPE_QIO_CHANNEL,
+ .name = TYPE_QIO_CHANNEL_BUFFER,
+ .instance_size = sizeof(QIOChannelBuffer),
+ .instance_finalize = qio_channel_buffer_finalize,
+ .class_init = qio_channel_buffer_class_init,
+};
+
+static void qio_channel_buffer_register_types(void)
+{
+ type_register_static(&qio_channel_buffer_info);
+}
+
+type_init(qio_channel_buffer_register_types);
diff --git a/qemu/io/channel-command.c b/qemu/io/channel-command.c
new file mode 100644
index 000000000..ad25313be
--- /dev/null
+++ b/qemu/io/channel-command.c
@@ -0,0 +1,368 @@
+/*
+ * QEMU I/O channels external command driver
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "io/channel-command.h"
+#include "io/channel-watch.h"
+#include "qapi/error.h"
+#include "qemu/sockets.h"
+#include "trace.h"
+
+
+QIOChannelCommand *
+qio_channel_command_new_pid(int writefd,
+ int readfd,
+ pid_t pid)
+{
+ QIOChannelCommand *ioc;
+
+ ioc = QIO_CHANNEL_COMMAND(object_new(TYPE_QIO_CHANNEL_COMMAND));
+
+ ioc->readfd = readfd;
+ ioc->writefd = writefd;
+ ioc->pid = pid;
+
+ trace_qio_channel_command_new_pid(ioc, writefd, readfd, pid);
+ return ioc;
+}
+
+
+#ifndef WIN32
+QIOChannelCommand *
+qio_channel_command_new_spawn(const char *const argv[],
+ int flags,
+ Error **errp)
+{
+ pid_t pid = -1;
+ int stdinfd[2] = { -1, -1 };
+ int stdoutfd[2] = { -1, -1 };
+ int devnull = -1;
+ bool stdinnull = false, stdoutnull = false;
+ QIOChannelCommand *ioc;
+
+ flags = flags & O_ACCMODE;
+
+ if (flags == O_RDONLY) {
+ stdinnull = true;
+ }
+ if (flags == O_WRONLY) {
+ stdoutnull = true;
+ }
+
+ if (stdinnull || stdoutnull) {
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to open /dev/null");
+ goto error;
+ }
+ }
+
+ if ((!stdinnull && pipe(stdinfd) < 0) ||
+ (!stdoutnull && pipe(stdoutfd) < 0)) {
+ error_setg_errno(errp, errno,
+ "Unable to open pipe");
+ goto error;
+ }
+
+ pid = qemu_fork(errp);
+ if (pid < 0) {
+ goto error;
+ }
+
+ if (pid == 0) { /* child */
+ dup2(stdinnull ? devnull : stdinfd[0], STDIN_FILENO);
+ dup2(stdoutnull ? devnull : stdoutfd[1], STDOUT_FILENO);
+ /* Leave stderr connected to qemu's stderr */
+
+ if (!stdinnull) {
+ close(stdinfd[0]);
+ close(stdinfd[1]);
+ }
+ if (!stdoutnull) {
+ close(stdoutfd[0]);
+ close(stdoutfd[1]);
+ }
+ if (devnull != -1) {
+ close(devnull);
+ }
+
+ execv(argv[0], (char * const *)argv);
+ _exit(1);
+ }
+
+ if (!stdinnull) {
+ close(stdinfd[0]);
+ }
+ if (!stdoutnull) {
+ close(stdoutfd[1]);
+ }
+
+ ioc = qio_channel_command_new_pid(stdinnull ? devnull : stdinfd[1],
+ stdoutnull ? devnull : stdoutfd[0],
+ pid);
+ trace_qio_channel_command_new_spawn(ioc, argv[0], flags);
+ return ioc;
+
+ error:
+ if (devnull != -1) {
+ close(devnull);
+ }
+ if (stdinfd[0] != -1) {
+ close(stdinfd[0]);
+ }
+ if (stdinfd[1] != -1) {
+ close(stdinfd[1]);
+ }
+ if (stdoutfd[0] != -1) {
+ close(stdoutfd[0]);
+ }
+ if (stdoutfd[1] != -1) {
+ close(stdoutfd[1]);
+ }
+ return NULL;
+}
+
+#else /* WIN32 */
+QIOChannelCommand *
+qio_channel_command_new_spawn(const char *const argv[],
+ int flags,
+ Error **errp)
+{
+ error_setg_errno(errp, ENOSYS,
+ "Command spawn not supported on this platform");
+ return NULL;
+}
+#endif /* WIN32 */
+
+#ifndef WIN32
+static int qio_channel_command_abort(QIOChannelCommand *ioc,
+ Error **errp)
+{
+ pid_t ret;
+ int status;
+ int step = 0;
+
+ /* See if intermediate process has exited; if not, try a nice
+ * SIGTERM followed by a more severe SIGKILL.
+ */
+ rewait:
+ trace_qio_channel_command_abort(ioc, ioc->pid);
+ ret = waitpid(ioc->pid, &status, WNOHANG);
+ trace_qio_channel_command_wait(ioc, ioc->pid, ret, status);
+ if (ret == (pid_t)-1) {
+ if (errno == EINTR) {
+ goto rewait;
+ } else {
+ error_setg_errno(errp, errno,
+ "Cannot wait on pid %llu",
+ (unsigned long long)ioc->pid);
+ return -1;
+ }
+ } else if (ret == 0) {
+ if (step == 0) {
+ kill(ioc->pid, SIGTERM);
+ } else if (step == 1) {
+ kill(ioc->pid, SIGKILL);
+ } else {
+ error_setg(errp,
+ "Process %llu refused to die",
+ (unsigned long long)ioc->pid);
+ return -1;
+ }
+ step++;
+ usleep(10 * 1000);
+ goto rewait;
+ }
+
+ return 0;
+}
+#endif /* ! WIN32 */
+
+
+static void qio_channel_command_init(Object *obj)
+{
+ QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj);
+ ioc->readfd = -1;
+ ioc->writefd = -1;
+ ioc->pid = -1;
+}
+
+static void qio_channel_command_finalize(Object *obj)
+{
+ QIOChannelCommand *ioc = QIO_CHANNEL_COMMAND(obj);
+ if (ioc->readfd != -1) {
+ close(ioc->readfd);
+ }
+ if (ioc->writefd != -1 &&
+ ioc->writefd != ioc->readfd) {
+ close(ioc->writefd);
+ }
+ ioc->writefd = ioc->readfd = -1;
+ if (ioc->pid > 0) {
+#ifndef WIN32
+ qio_channel_command_abort(ioc, NULL);
+#endif
+ }
+}
+
+
+static ssize_t qio_channel_command_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
+ ssize_t ret;
+
+ retry:
+ ret = readv(cioc->readfd, iov, niov);
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ if (errno == EINTR) {
+ goto retry;
+ }
+
+ error_setg_errno(errp, errno,
+ "Unable to read from command");
+ return -1;
+ }
+
+ return ret;
+}
+
+static ssize_t qio_channel_command_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
+ ssize_t ret;
+
+ retry:
+ ret = writev(cioc->writefd, iov, niov);
+ if (ret <= 0) {
+ if (errno == EAGAIN) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ if (errno == EINTR) {
+ goto retry;
+ }
+ error_setg_errno(errp, errno, "%s",
+ "Unable to write to command");
+ return -1;
+ }
+ return ret;
+}
+
+static int qio_channel_command_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp)
+{
+ QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
+
+ if (enabled) {
+ qemu_set_block(cioc->writefd);
+ qemu_set_block(cioc->readfd);
+ } else {
+ qemu_set_nonblock(cioc->writefd);
+ qemu_set_nonblock(cioc->readfd);
+ }
+
+ return 0;
+}
+
+
+static int qio_channel_command_close(QIOChannel *ioc,
+ Error **errp)
+{
+ QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
+ int rv = 0;
+
+ /* We close FDs before killing, because that
+ * gives a better chance of clean shutdown
+ */
+ if (cioc->readfd != -1 &&
+ close(cioc->readfd) < 0) {
+ rv = -1;
+ }
+ if (cioc->writefd != -1 &&
+ cioc->writefd != cioc->readfd &&
+ close(cioc->writefd) < 0) {
+ rv = -1;
+ }
+ cioc->writefd = cioc->readfd = -1;
+#ifndef WIN32
+ if (qio_channel_command_abort(cioc, errp) < 0) {
+ return -1;
+ }
+#endif
+ if (rv < 0) {
+ error_setg_errno(errp, errno, "%s",
+ "Unable to close command");
+ }
+ return rv;
+}
+
+
+static GSource *qio_channel_command_create_watch(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
+ return qio_channel_create_fd_pair_watch(ioc,
+ cioc->readfd,
+ cioc->writefd,
+ condition);
+}
+
+
+static void qio_channel_command_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
+
+ ioc_klass->io_writev = qio_channel_command_writev;
+ ioc_klass->io_readv = qio_channel_command_readv;
+ ioc_klass->io_set_blocking = qio_channel_command_set_blocking;
+ ioc_klass->io_close = qio_channel_command_close;
+ ioc_klass->io_create_watch = qio_channel_command_create_watch;
+}
+
+static const TypeInfo qio_channel_command_info = {
+ .parent = TYPE_QIO_CHANNEL,
+ .name = TYPE_QIO_CHANNEL_COMMAND,
+ .instance_size = sizeof(QIOChannelCommand),
+ .instance_init = qio_channel_command_init,
+ .instance_finalize = qio_channel_command_finalize,
+ .class_init = qio_channel_command_class_init,
+};
+
+static void qio_channel_command_register_types(void)
+{
+ type_register_static(&qio_channel_command_info);
+}
+
+type_init(qio_channel_command_register_types);
diff --git a/qemu/io/channel-file.c b/qemu/io/channel-file.c
new file mode 100644
index 000000000..e1da2435e
--- /dev/null
+++ b/qemu/io/channel-file.c
@@ -0,0 +1,225 @@
+/*
+ * QEMU I/O channels files driver
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "io/channel-file.h"
+#include "io/channel-watch.h"
+#include "qapi/error.h"
+#include "qemu/sockets.h"
+#include "trace.h"
+
+QIOChannelFile *
+qio_channel_file_new_fd(int fd)
+{
+ QIOChannelFile *ioc;
+
+ ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE));
+
+ ioc->fd = fd;
+
+ trace_qio_channel_file_new_fd(ioc, fd);
+
+ return ioc;
+}
+
+
+QIOChannelFile *
+qio_channel_file_new_path(const char *path,
+ int flags,
+ mode_t mode,
+ Error **errp)
+{
+ QIOChannelFile *ioc;
+
+ ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE));
+
+ if (flags & O_WRONLY) {
+ ioc->fd = open(path, flags, mode);
+ } else {
+ ioc->fd = open(path, flags);
+ }
+ if (ioc->fd < 0) {
+ object_unref(OBJECT(ioc));
+ error_setg_errno(errp, errno,
+ "Unable to open %s", path);
+ return NULL;
+ }
+
+ trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd);
+
+ return ioc;
+}
+
+
+static void qio_channel_file_init(Object *obj)
+{
+ QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj);
+ ioc->fd = -1;
+}
+
+static void qio_channel_file_finalize(Object *obj)
+{
+ QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj);
+ if (ioc->fd != -1) {
+ close(ioc->fd);
+ ioc->fd = -1;
+ }
+}
+
+
+static ssize_t qio_channel_file_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+ ssize_t ret;
+
+ retry:
+ ret = readv(fioc->fd, iov, niov);
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ if (errno == EINTR) {
+ goto retry;
+ }
+
+ error_setg_errno(errp, errno,
+ "Unable to read from file");
+ return -1;
+ }
+
+ return ret;
+}
+
+static ssize_t qio_channel_file_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+ ssize_t ret;
+
+ retry:
+ ret = writev(fioc->fd, iov, niov);
+ if (ret <= 0) {
+ if (errno == EAGAIN) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ if (errno == EINTR) {
+ goto retry;
+ }
+ error_setg_errno(errp, errno,
+ "Unable to write to file");
+ return -1;
+ }
+ return ret;
+}
+
+static int qio_channel_file_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+
+ if (enabled) {
+ qemu_set_block(fioc->fd);
+ } else {
+ qemu_set_nonblock(fioc->fd);
+ }
+ return 0;
+}
+
+
+static off_t qio_channel_file_seek(QIOChannel *ioc,
+ off_t offset,
+ int whence,
+ Error **errp)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+ off_t ret;
+
+ ret = lseek(fioc->fd, offset, whence);
+ if (ret == (off_t)-1) {
+ error_setg_errno(errp, errno,
+ "Unable to seek to offset %lld whence %d in file",
+ (long long int)offset, whence);
+ return -1;
+ }
+ return ret;
+}
+
+
+static int qio_channel_file_close(QIOChannel *ioc,
+ Error **errp)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+
+ if (close(fioc->fd) < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to close file");
+ return -1;
+ }
+ return 0;
+}
+
+
+static GSource *qio_channel_file_create_watch(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+ return qio_channel_create_fd_watch(ioc,
+ fioc->fd,
+ condition);
+}
+
+static void qio_channel_file_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
+
+ ioc_klass->io_writev = qio_channel_file_writev;
+ ioc_klass->io_readv = qio_channel_file_readv;
+ ioc_klass->io_set_blocking = qio_channel_file_set_blocking;
+ ioc_klass->io_seek = qio_channel_file_seek;
+ ioc_klass->io_close = qio_channel_file_close;
+ ioc_klass->io_create_watch = qio_channel_file_create_watch;
+}
+
+static const TypeInfo qio_channel_file_info = {
+ .parent = TYPE_QIO_CHANNEL,
+ .name = TYPE_QIO_CHANNEL_FILE,
+ .instance_size = sizeof(QIOChannelFile),
+ .instance_init = qio_channel_file_init,
+ .instance_finalize = qio_channel_file_finalize,
+ .class_init = qio_channel_file_class_init,
+};
+
+static void qio_channel_file_register_types(void)
+{
+ type_register_static(&qio_channel_file_info);
+}
+
+type_init(qio_channel_file_register_types);
diff --git a/qemu/io/channel-socket.c b/qemu/io/channel-socket.c
new file mode 100644
index 000000000..ca8bc20b1
--- /dev/null
+++ b/qemu/io/channel-socket.c
@@ -0,0 +1,772 @@
+/*
+ * QEMU I/O channels sockets driver
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "io/channel-socket.h"
+#include "io/channel-watch.h"
+#include "trace.h"
+
+#define SOCKET_MAX_FDS 16
+
+SocketAddress *
+qio_channel_socket_get_local_address(QIOChannelSocket *ioc,
+ Error **errp)
+{
+ return socket_sockaddr_to_address(&ioc->localAddr,
+ ioc->localAddrLen,
+ errp);
+}
+
+SocketAddress *
+qio_channel_socket_get_remote_address(QIOChannelSocket *ioc,
+ Error **errp)
+{
+ return socket_sockaddr_to_address(&ioc->remoteAddr,
+ ioc->remoteAddrLen,
+ errp);
+}
+
+QIOChannelSocket *
+qio_channel_socket_new(void)
+{
+ QIOChannelSocket *sioc;
+ QIOChannel *ioc;
+
+ sioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET));
+ sioc->fd = -1;
+
+ ioc = QIO_CHANNEL(sioc);
+ ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN);
+
+#ifdef WIN32
+ ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+
+ trace_qio_channel_socket_new(sioc);
+
+ return sioc;
+}
+
+
+static int
+qio_channel_socket_set_fd(QIOChannelSocket *sioc,
+ int fd,
+ Error **errp)
+{
+ if (sioc->fd != -1) {
+ error_setg(errp, "Socket is already open");
+ return -1;
+ }
+
+ sioc->fd = fd;
+ sioc->remoteAddrLen = sizeof(sioc->remoteAddr);
+ sioc->localAddrLen = sizeof(sioc->localAddr);
+
+
+ if (getpeername(fd, (struct sockaddr *)&sioc->remoteAddr,
+ &sioc->remoteAddrLen) < 0) {
+ if (errno == ENOTCONN) {
+ memset(&sioc->remoteAddr, 0, sizeof(sioc->remoteAddr));
+ sioc->remoteAddrLen = sizeof(sioc->remoteAddr);
+ } else {
+ error_setg_errno(errp, errno,
+ "Unable to query remote socket address");
+ goto error;
+ }
+ }
+
+ if (getsockname(fd, (struct sockaddr *)&sioc->localAddr,
+ &sioc->localAddrLen) < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to query local socket address");
+ goto error;
+ }
+
+#ifndef WIN32
+ if (sioc->localAddr.ss_family == AF_UNIX) {
+ QIOChannel *ioc = QIO_CHANNEL(sioc);
+ ioc->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS);
+ }
+#endif /* WIN32 */
+
+ return 0;
+
+ error:
+ sioc->fd = -1; /* Let the caller close FD on failure */
+ return -1;
+}
+
+QIOChannelSocket *
+qio_channel_socket_new_fd(int fd,
+ Error **errp)
+{
+ QIOChannelSocket *ioc;
+
+ ioc = qio_channel_socket_new();
+ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) {
+ object_unref(OBJECT(ioc));
+ return NULL;
+ }
+
+ trace_qio_channel_socket_new_fd(ioc, fd);
+
+ return ioc;
+}
+
+
+int qio_channel_socket_connect_sync(QIOChannelSocket *ioc,
+ SocketAddress *addr,
+ Error **errp)
+{
+ int fd;
+
+ trace_qio_channel_socket_connect_sync(ioc, addr);
+ fd = socket_connect(addr, errp, NULL, NULL);
+ if (fd < 0) {
+ trace_qio_channel_socket_connect_fail(ioc);
+ return -1;
+ }
+
+ trace_qio_channel_socket_connect_complete(ioc, fd);
+ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int qio_channel_socket_connect_worker(QIOTask *task,
+ Error **errp,
+ gpointer opaque)
+{
+ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
+ SocketAddress *addr = opaque;
+ int ret;
+
+ ret = qio_channel_socket_connect_sync(ioc,
+ addr,
+ errp);
+
+ object_unref(OBJECT(ioc));
+ return ret;
+}
+
+
+void qio_channel_socket_connect_async(QIOChannelSocket *ioc,
+ SocketAddress *addr,
+ QIOTaskFunc callback,
+ gpointer opaque,
+ GDestroyNotify destroy)
+{
+ QIOTask *task = qio_task_new(
+ OBJECT(ioc), callback, opaque, destroy);
+ SocketAddress *addrCopy;
+
+ qapi_copy_SocketAddress(&addrCopy, addr);
+
+ /* socket_connect() does a non-blocking connect(), but it
+ * still blocks in DNS lookups, so we must use a thread */
+ trace_qio_channel_socket_connect_async(ioc, addr);
+ qio_task_run_in_thread(task,
+ qio_channel_socket_connect_worker,
+ addrCopy,
+ (GDestroyNotify)qapi_free_SocketAddress);
+}
+
+
+int qio_channel_socket_listen_sync(QIOChannelSocket *ioc,
+ SocketAddress *addr,
+ Error **errp)
+{
+ int fd;
+
+ trace_qio_channel_socket_listen_sync(ioc, addr);
+ fd = socket_listen(addr, errp);
+ if (fd < 0) {
+ trace_qio_channel_socket_listen_fail(ioc);
+ return -1;
+ }
+
+ trace_qio_channel_socket_listen_complete(ioc, fd);
+ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int qio_channel_socket_listen_worker(QIOTask *task,
+ Error **errp,
+ gpointer opaque)
+{
+ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
+ SocketAddress *addr = opaque;
+ int ret;
+
+ ret = qio_channel_socket_listen_sync(ioc,
+ addr,
+ errp);
+
+ object_unref(OBJECT(ioc));
+ return ret;
+}
+
+
+void qio_channel_socket_listen_async(QIOChannelSocket *ioc,
+ SocketAddress *addr,
+ QIOTaskFunc callback,
+ gpointer opaque,
+ GDestroyNotify destroy)
+{
+ QIOTask *task = qio_task_new(
+ OBJECT(ioc), callback, opaque, destroy);
+ SocketAddress *addrCopy;
+
+ qapi_copy_SocketAddress(&addrCopy, addr);
+
+ /* socket_listen() blocks in DNS lookups, so we must use a thread */
+ trace_qio_channel_socket_listen_async(ioc, addr);
+ qio_task_run_in_thread(task,
+ qio_channel_socket_listen_worker,
+ addrCopy,
+ (GDestroyNotify)qapi_free_SocketAddress);
+}
+
+
+int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc,
+ SocketAddress *localAddr,
+ SocketAddress *remoteAddr,
+ Error **errp)
+{
+ int fd;
+
+ trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr);
+ fd = socket_dgram(remoteAddr, localAddr, errp);
+ if (fd < 0) {
+ trace_qio_channel_socket_dgram_fail(ioc);
+ return -1;
+ }
+
+ trace_qio_channel_socket_dgram_complete(ioc, fd);
+ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct QIOChannelSocketDGramWorkerData {
+ SocketAddress *localAddr;
+ SocketAddress *remoteAddr;
+};
+
+
+static void qio_channel_socket_dgram_worker_free(gpointer opaque)
+{
+ struct QIOChannelSocketDGramWorkerData *data = opaque;
+ qapi_free_SocketAddress(data->localAddr);
+ qapi_free_SocketAddress(data->remoteAddr);
+ g_free(data);
+}
+
+static int qio_channel_socket_dgram_worker(QIOTask *task,
+ Error **errp,
+ gpointer opaque)
+{
+ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task));
+ struct QIOChannelSocketDGramWorkerData *data = opaque;
+ int ret;
+
+ /* socket_dgram() blocks in DNS lookups, so we must use a thread */
+ ret = qio_channel_socket_dgram_sync(ioc,
+ data->localAddr,
+ data->remoteAddr,
+ errp);
+
+ object_unref(OBJECT(ioc));
+ return ret;
+}
+
+
+void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,
+ SocketAddress *localAddr,
+ SocketAddress *remoteAddr,
+ QIOTaskFunc callback,
+ gpointer opaque,
+ GDestroyNotify destroy)
+{
+ QIOTask *task = qio_task_new(
+ OBJECT(ioc), callback, opaque, destroy);
+ struct QIOChannelSocketDGramWorkerData *data = g_new0(
+ struct QIOChannelSocketDGramWorkerData, 1);
+
+ qapi_copy_SocketAddress(&data->localAddr, localAddr);
+ qapi_copy_SocketAddress(&data->remoteAddr, remoteAddr);
+
+ trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr);
+ qio_task_run_in_thread(task,
+ qio_channel_socket_dgram_worker,
+ data,
+ qio_channel_socket_dgram_worker_free);
+}
+
+
+QIOChannelSocket *
+qio_channel_socket_accept(QIOChannelSocket *ioc,
+ Error **errp)
+{
+ QIOChannelSocket *cioc;
+
+ cioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET));
+ cioc->fd = -1;
+ cioc->remoteAddrLen = sizeof(ioc->remoteAddr);
+ cioc->localAddrLen = sizeof(ioc->localAddr);
+
+#ifdef WIN32
+ QIO_CHANNEL(cioc)->event = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+
+
+ retry:
+ trace_qio_channel_socket_accept(ioc);
+ cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr,
+ &cioc->remoteAddrLen);
+ if (cioc->fd < 0) {
+ trace_qio_channel_socket_accept_fail(ioc);
+ if (errno == EINTR) {
+ goto retry;
+ }
+ goto error;
+ }
+
+ if (getsockname(cioc->fd, (struct sockaddr *)&cioc->localAddr,
+ &cioc->localAddrLen) < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to query local socket address");
+ goto error;
+ }
+
+#ifndef WIN32
+ if (cioc->localAddr.ss_family == AF_UNIX) {
+ QIO_CHANNEL(cioc)->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS);
+ }
+#endif /* WIN32 */
+
+ trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd);
+ return cioc;
+
+ error:
+ object_unref(OBJECT(cioc));
+ return NULL;
+}
+
+static void qio_channel_socket_init(Object *obj)
+{
+ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj);
+ ioc->fd = -1;
+}
+
+static void qio_channel_socket_finalize(Object *obj)
+{
+ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj);
+ if (ioc->fd != -1) {
+#ifdef WIN32
+ WSAEventSelect(ioc->fd, NULL, 0);
+#endif
+ closesocket(ioc->fd);
+ ioc->fd = -1;
+ }
+}
+
+
+#ifndef WIN32
+static void qio_channel_socket_copy_fds(struct msghdr *msg,
+ int **fds, size_t *nfds)
+{
+ struct cmsghdr *cmsg;
+
+ *nfds = 0;
+ *fds = NULL;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ int fd_size, i;
+ int gotfds;
+
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) ||
+ cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS) {
+ continue;
+ }
+
+ fd_size = cmsg->cmsg_len - CMSG_LEN(0);
+
+ if (!fd_size) {
+ continue;
+ }
+
+ gotfds = fd_size / sizeof(int);
+ *fds = g_renew(int, *fds, *nfds + gotfds);
+ memcpy(*fds + *nfds, CMSG_DATA(cmsg), fd_size);
+
+ for (i = 0; i < gotfds; i++) {
+ int fd = (*fds)[*nfds + i];
+ 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
+ }
+ *nfds += gotfds;
+ }
+}
+
+
+static ssize_t qio_channel_socket_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+ ssize_t ret;
+ struct msghdr msg = { NULL, };
+ char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)];
+ int sflags = 0;
+
+ memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS));
+
+#ifdef MSG_CMSG_CLOEXEC
+ sflags |= MSG_CMSG_CLOEXEC;
+#endif
+
+ msg.msg_iov = (struct iovec *)iov;
+ msg.msg_iovlen = niov;
+ if (fds && nfds) {
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ }
+
+ retry:
+ ret = recvmsg(sioc->fd, &msg, sflags);
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ if (errno == EINTR) {
+ goto retry;
+ }
+
+ error_setg_errno(errp, errno,
+ "Unable to read from socket");
+ return -1;
+ }
+
+ if (fds && nfds) {
+ qio_channel_socket_copy_fds(&msg, fds, nfds);
+ }
+
+ return ret;
+}
+
+static ssize_t qio_channel_socket_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+ ssize_t ret;
+ struct msghdr msg = { NULL, };
+ char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)];
+ size_t fdsize = sizeof(int) * nfds;
+ struct cmsghdr *cmsg;
+
+ memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS));
+
+ msg.msg_iov = (struct iovec *)iov;
+ msg.msg_iovlen = niov;
+
+ if (nfds) {
+ if (nfds > SOCKET_MAX_FDS) {
+ error_setg_errno(errp, EINVAL,
+ "Only %d FDs can be sent, got %zu",
+ SOCKET_MAX_FDS, nfds);
+ return -1;
+ }
+
+ msg.msg_control = control;
+ msg.msg_controllen = CMSG_SPACE(sizeof(int) * nfds);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(fdsize);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ memcpy(CMSG_DATA(cmsg), fds, fdsize);
+ }
+
+ retry:
+ ret = sendmsg(sioc->fd, &msg, 0);
+ if (ret <= 0) {
+ if (errno == EAGAIN) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ if (errno == EINTR) {
+ goto retry;
+ }
+ error_setg_errno(errp, errno,
+ "Unable to write to socket");
+ return -1;
+ }
+ return ret;
+}
+#else /* WIN32 */
+static ssize_t qio_channel_socket_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+ ssize_t done = 0;
+ ssize_t i;
+
+ for (i = 0; i < niov; i++) {
+ ssize_t ret;
+ retry:
+ ret = recv(sioc->fd,
+ iov[i].iov_base,
+ iov[i].iov_len,
+ 0);
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ if (done) {
+ return done;
+ } else {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ } else if (errno == EINTR) {
+ goto retry;
+ } else {
+ error_setg_errno(errp, errno,
+ "Unable to read from socket");
+ return -1;
+ }
+ }
+ done += ret;
+ if (ret < iov[i].iov_len) {
+ return done;
+ }
+ }
+
+ return done;
+}
+
+static ssize_t qio_channel_socket_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+ ssize_t done = 0;
+ ssize_t i;
+
+ for (i = 0; i < niov; i++) {
+ ssize_t ret;
+ retry:
+ ret = send(sioc->fd,
+ iov[i].iov_base,
+ iov[i].iov_len,
+ 0);
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ if (done) {
+ return done;
+ } else {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ } else if (errno == EINTR) {
+ goto retry;
+ } else {
+ error_setg_errno(errp, errno,
+ "Unable to write to socket");
+ return -1;
+ }
+ }
+ done += ret;
+ if (ret < iov[i].iov_len) {
+ return done;
+ }
+ }
+
+ return done;
+}
+#endif /* WIN32 */
+
+static int
+qio_channel_socket_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+
+ if (enabled) {
+ qemu_set_block(sioc->fd);
+ } else {
+ qemu_set_nonblock(sioc->fd);
+#ifdef WIN32
+ WSAEventSelect(sioc->fd, ioc->event,
+ FD_READ | FD_ACCEPT | FD_CLOSE |
+ FD_CONNECT | FD_WRITE | FD_OOB);
+#endif
+ }
+ return 0;
+}
+
+
+static void
+qio_channel_socket_set_delay(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+ int v = enabled ? 0 : 1;
+
+ qemu_setsockopt(sioc->fd,
+ IPPROTO_TCP, TCP_NODELAY,
+ &v, sizeof(v));
+}
+
+
+static void
+qio_channel_socket_set_cork(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+ int v = enabled ? 1 : 0;
+
+ socket_set_cork(sioc->fd, v);
+}
+
+
+static int
+qio_channel_socket_close(QIOChannel *ioc,
+ Error **errp)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+
+ if (sioc->fd != -1) {
+#ifdef WIN32
+ WSAEventSelect(sioc->fd, NULL, 0);
+#endif
+ if (closesocket(sioc->fd) < 0) {
+ sioc->fd = -1;
+ error_setg_errno(errp, errno,
+ "Unable to close socket");
+ return -1;
+ }
+ sioc->fd = -1;
+ }
+ return 0;
+}
+
+static int
+qio_channel_socket_shutdown(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+ int sockhow;
+
+ switch (how) {
+ case QIO_CHANNEL_SHUTDOWN_READ:
+ sockhow = SHUT_RD;
+ break;
+ case QIO_CHANNEL_SHUTDOWN_WRITE:
+ sockhow = SHUT_WR;
+ break;
+ case QIO_CHANNEL_SHUTDOWN_BOTH:
+ default:
+ sockhow = SHUT_RDWR;
+ break;
+ }
+
+ if (shutdown(sioc->fd, sockhow) < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to shutdown socket");
+ return -1;
+ }
+ return 0;
+}
+
+static GSource *qio_channel_socket_create_watch(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+ return qio_channel_create_socket_watch(ioc,
+ sioc->fd,
+ condition);
+}
+
+static void qio_channel_socket_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
+
+ ioc_klass->io_writev = qio_channel_socket_writev;
+ ioc_klass->io_readv = qio_channel_socket_readv;
+ ioc_klass->io_set_blocking = qio_channel_socket_set_blocking;
+ ioc_klass->io_close = qio_channel_socket_close;
+ ioc_klass->io_shutdown = qio_channel_socket_shutdown;
+ ioc_klass->io_set_cork = qio_channel_socket_set_cork;
+ ioc_klass->io_set_delay = qio_channel_socket_set_delay;
+ ioc_klass->io_create_watch = qio_channel_socket_create_watch;
+}
+
+static const TypeInfo qio_channel_socket_info = {
+ .parent = TYPE_QIO_CHANNEL,
+ .name = TYPE_QIO_CHANNEL_SOCKET,
+ .instance_size = sizeof(QIOChannelSocket),
+ .instance_init = qio_channel_socket_init,
+ .instance_finalize = qio_channel_socket_finalize,
+ .class_init = qio_channel_socket_class_init,
+};
+
+static void qio_channel_socket_register_types(void)
+{
+ type_register_static(&qio_channel_socket_info);
+}
+
+type_init(qio_channel_socket_register_types);
diff --git a/qemu/io/channel-tls.c b/qemu/io/channel-tls.c
new file mode 100644
index 000000000..9a8525c81
--- /dev/null
+++ b/qemu/io/channel-tls.c
@@ -0,0 +1,395 @@
+/*
+ * QEMU I/O channels TLS driver
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "io/channel-tls.h"
+#include "trace.h"
+
+
+static ssize_t qio_channel_tls_write_handler(const char *buf,
+ size_t len,
+ void *opaque)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
+ ssize_t ret;
+
+ ret = qio_channel_write(tioc->master, buf, len, NULL);
+ if (ret == QIO_CHANNEL_ERR_BLOCK) {
+ errno = EAGAIN;
+ return -1;
+ } else if (ret < 0) {
+ errno = EIO;
+ return -1;
+ }
+ return ret;
+}
+
+static ssize_t qio_channel_tls_read_handler(char *buf,
+ size_t len,
+ void *opaque)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
+ ssize_t ret;
+
+ ret = qio_channel_read(tioc->master, buf, len, NULL);
+ if (ret == QIO_CHANNEL_ERR_BLOCK) {
+ errno = EAGAIN;
+ return -1;
+ } else if (ret < 0) {
+ errno = EIO;
+ return -1;
+ }
+ return ret;
+}
+
+
+QIOChannelTLS *
+qio_channel_tls_new_server(QIOChannel *master,
+ QCryptoTLSCreds *creds,
+ const char *aclname,
+ Error **errp)
+{
+ QIOChannelTLS *ioc;
+
+ ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS));
+
+ ioc->master = master;
+ object_ref(OBJECT(master));
+
+ ioc->session = qcrypto_tls_session_new(
+ creds,
+ NULL,
+ aclname,
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ errp);
+ if (!ioc->session) {
+ goto error;
+ }
+
+ qcrypto_tls_session_set_callbacks(
+ ioc->session,
+ qio_channel_tls_write_handler,
+ qio_channel_tls_read_handler,
+ ioc);
+
+ trace_qio_channel_tls_new_server(ioc, master, creds, aclname);
+ return ioc;
+
+ error:
+ object_unref(OBJECT(ioc));
+ return NULL;
+}
+
+QIOChannelTLS *
+qio_channel_tls_new_client(QIOChannel *master,
+ QCryptoTLSCreds *creds,
+ const char *hostname,
+ Error **errp)
+{
+ QIOChannelTLS *tioc;
+ QIOChannel *ioc;
+
+ tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS));
+ ioc = QIO_CHANNEL(tioc);
+
+ tioc->master = master;
+ if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) {
+ ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN);
+ }
+ object_ref(OBJECT(master));
+
+ tioc->session = qcrypto_tls_session_new(
+ creds,
+ hostname,
+ NULL,
+ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
+ errp);
+ if (!tioc->session) {
+ goto error;
+ }
+
+ qcrypto_tls_session_set_callbacks(
+ tioc->session,
+ qio_channel_tls_write_handler,
+ qio_channel_tls_read_handler,
+ tioc);
+
+ trace_qio_channel_tls_new_client(tioc, master, creds, hostname);
+ return tioc;
+
+ error:
+ object_unref(OBJECT(tioc));
+ return NULL;
+}
+
+
+static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer user_data);
+
+static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
+ QIOTask *task)
+{
+ Error *err = NULL;
+ QCryptoTLSSessionHandshakeStatus status;
+
+ if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) {
+ trace_qio_channel_tls_handshake_fail(ioc);
+ qio_task_abort(task, err);
+ goto cleanup;
+ }
+
+ status = qcrypto_tls_session_get_handshake_status(ioc->session);
+ if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
+ trace_qio_channel_tls_handshake_complete(ioc);
+ if (qcrypto_tls_session_check_credentials(ioc->session,
+ &err) < 0) {
+ trace_qio_channel_tls_credentials_deny(ioc);
+ qio_task_abort(task, err);
+ goto cleanup;
+ }
+ trace_qio_channel_tls_credentials_allow(ioc);
+ qio_task_complete(task);
+ } else {
+ GIOCondition condition;
+ if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) {
+ condition = G_IO_OUT;
+ } else {
+ condition = G_IO_IN;
+ }
+
+ trace_qio_channel_tls_handshake_pending(ioc, status);
+ qio_channel_add_watch(ioc->master,
+ condition,
+ qio_channel_tls_handshake_io,
+ task,
+ NULL);
+ }
+
+ cleanup:
+ error_free(err);
+}
+
+
+static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ QIOTask *task = user_data;
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(
+ qio_task_get_source(task));
+
+ qio_channel_tls_handshake_task(
+ tioc, task);
+
+ object_unref(OBJECT(tioc));
+
+ return FALSE;
+}
+
+void qio_channel_tls_handshake(QIOChannelTLS *ioc,
+ QIOTaskFunc func,
+ gpointer opaque,
+ GDestroyNotify destroy)
+{
+ QIOTask *task;
+
+ task = qio_task_new(OBJECT(ioc),
+ func, opaque, destroy);
+
+ trace_qio_channel_tls_handshake_start(ioc);
+ qio_channel_tls_handshake_task(ioc, task);
+}
+
+
+static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED)
+{
+}
+
+
+static void qio_channel_tls_finalize(Object *obj)
+{
+ QIOChannelTLS *ioc = QIO_CHANNEL_TLS(obj);
+
+ object_unref(OBJECT(ioc->master));
+ qcrypto_tls_session_free(ioc->session);
+}
+
+
+static ssize_t qio_channel_tls_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+ size_t i;
+ ssize_t got = 0;
+
+ for (i = 0 ; i < niov ; i++) {
+ ssize_t ret = qcrypto_tls_session_read(tioc->session,
+ iov[i].iov_base,
+ iov[i].iov_len);
+ if (ret < 0) {
+ if (errno == EAGAIN) {
+ if (got) {
+ return got;
+ } else {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ }
+
+ error_setg_errno(errp, errno,
+ "Cannot read from TLS channel");
+ return -1;
+ }
+ got += ret;
+ if (ret < iov[i].iov_len) {
+ break;
+ }
+ }
+ return got;
+}
+
+
+static ssize_t qio_channel_tls_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+ size_t i;
+ ssize_t done = 0;
+
+ for (i = 0 ; i < niov ; i++) {
+ ssize_t ret = qcrypto_tls_session_write(tioc->session,
+ iov[i].iov_base,
+ iov[i].iov_len);
+ if (ret <= 0) {
+ if (errno == EAGAIN) {
+ if (done) {
+ return done;
+ } else {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ }
+
+ error_setg_errno(errp, errno,
+ "Cannot write to TLS channel");
+ return -1;
+ }
+ done += ret;
+ if (ret < iov[i].iov_len) {
+ break;
+ }
+ }
+ return done;
+}
+
+static int qio_channel_tls_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+
+ return qio_channel_set_blocking(tioc->master, enabled, errp);
+}
+
+static void qio_channel_tls_set_delay(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+
+ qio_channel_set_delay(tioc->master, enabled);
+}
+
+static void qio_channel_tls_set_cork(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+
+ qio_channel_set_cork(tioc->master, enabled);
+}
+
+static int qio_channel_tls_shutdown(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+
+ return qio_channel_shutdown(tioc->master, how, errp);
+}
+
+static int qio_channel_tls_close(QIOChannel *ioc,
+ Error **errp)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+
+ return qio_channel_close(tioc->master, errp);
+}
+
+static GSource *qio_channel_tls_create_watch(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+
+ return qio_channel_create_watch(tioc->master, condition);
+}
+
+QCryptoTLSSession *
+qio_channel_tls_get_session(QIOChannelTLS *ioc)
+{
+ return ioc->session;
+}
+
+static void qio_channel_tls_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
+
+ ioc_klass->io_writev = qio_channel_tls_writev;
+ ioc_klass->io_readv = qio_channel_tls_readv;
+ ioc_klass->io_set_blocking = qio_channel_tls_set_blocking;
+ ioc_klass->io_set_delay = qio_channel_tls_set_delay;
+ ioc_klass->io_set_cork = qio_channel_tls_set_cork;
+ ioc_klass->io_close = qio_channel_tls_close;
+ ioc_klass->io_shutdown = qio_channel_tls_shutdown;
+ ioc_klass->io_create_watch = qio_channel_tls_create_watch;
+}
+
+static const TypeInfo qio_channel_tls_info = {
+ .parent = TYPE_QIO_CHANNEL,
+ .name = TYPE_QIO_CHANNEL_TLS,
+ .instance_size = sizeof(QIOChannelTLS),
+ .instance_init = qio_channel_tls_init,
+ .instance_finalize = qio_channel_tls_finalize,
+ .class_init = qio_channel_tls_class_init,
+};
+
+static void qio_channel_tls_register_types(void)
+{
+ type_register_static(&qio_channel_tls_info);
+}
+
+type_init(qio_channel_tls_register_types);
diff --git a/qemu/io/channel-util.c b/qemu/io/channel-util.c
new file mode 100644
index 000000000..0fb4bd083
--- /dev/null
+++ b/qemu/io/channel-util.c
@@ -0,0 +1,51 @@
+/*
+ * QEMU I/O channels utility APIs
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "io/channel-util.h"
+#include "io/channel-file.h"
+#include "io/channel-socket.h"
+
+
+static bool fd_is_socket(int fd)
+{
+ int optval;
+ socklen_t optlen;
+ optlen = sizeof(optval);
+ return qemu_getsockopt(fd,
+ SOL_SOCKET,
+ SO_TYPE,
+ (char *)&optval,
+ &optlen) == 0;
+}
+
+
+QIOChannel *qio_channel_new_fd(int fd,
+ Error **errp)
+{
+ QIOChannel *ioc;
+
+ if (fd_is_socket(fd)) {
+ ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, errp));
+ } else {
+ ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd));
+ }
+ return ioc;
+}
diff --git a/qemu/io/channel-watch.c b/qemu/io/channel-watch.c
new file mode 100644
index 000000000..cf1cdff89
--- /dev/null
+++ b/qemu/io/channel-watch.c
@@ -0,0 +1,347 @@
+/*
+ * QEMU I/O channels watch helper APIs
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "io/channel-watch.h"
+
+typedef struct QIOChannelFDSource QIOChannelFDSource;
+struct QIOChannelFDSource {
+ GSource parent;
+ GPollFD fd;
+ QIOChannel *ioc;
+ GIOCondition condition;
+};
+
+
+#ifdef CONFIG_WIN32
+typedef struct QIOChannelSocketSource QIOChannelSocketSource;
+struct QIOChannelSocketSource {
+ GSource parent;
+ GPollFD fd;
+ QIOChannel *ioc;
+ SOCKET socket;
+ int revents;
+ GIOCondition condition;
+};
+
+#endif
+
+
+typedef struct QIOChannelFDPairSource QIOChannelFDPairSource;
+struct QIOChannelFDPairSource {
+ GSource parent;
+ GPollFD fdread;
+ GPollFD fdwrite;
+ QIOChannel *ioc;
+ GIOCondition condition;
+};
+
+
+static gboolean
+qio_channel_fd_source_prepare(GSource *source G_GNUC_UNUSED,
+ gint *timeout)
+{
+ *timeout = -1;
+
+ return FALSE;
+}
+
+
+static gboolean
+qio_channel_fd_source_check(GSource *source)
+{
+ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
+
+ return ssource->fd.revents & ssource->condition;
+}
+
+
+static gboolean
+qio_channel_fd_source_dispatch(GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ QIOChannelFunc func = (QIOChannelFunc)callback;
+ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
+
+ return (*func)(ssource->ioc,
+ ssource->fd.revents & ssource->condition,
+ user_data);
+}
+
+
+static void
+qio_channel_fd_source_finalize(GSource *source)
+{
+ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source;
+
+ object_unref(OBJECT(ssource->ioc));
+}
+
+
+#ifdef CONFIG_WIN32
+static gboolean
+qio_channel_socket_source_prepare(GSource *source G_GNUC_UNUSED,
+ gint *timeout)
+{
+ *timeout = -1;
+
+ return FALSE;
+}
+
+
+/*
+ * NB, this impl only works when the socket is in non-blocking
+ * mode on Win32
+ */
+static gboolean
+qio_channel_socket_source_check(GSource *source)
+{
+ static struct timeval tv0;
+
+ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
+ WSANETWORKEVENTS ev;
+ fd_set rfds, wfds, xfds;
+
+ if (!ssource->condition) {
+ return 0;
+ }
+
+ WSAEnumNetworkEvents(ssource->socket, ssource->ioc->event, &ev);
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+ if (ssource->condition & G_IO_IN) {
+ FD_SET((SOCKET)ssource->socket, &rfds);
+ }
+ if (ssource->condition & G_IO_OUT) {
+ FD_SET((SOCKET)ssource->socket, &wfds);
+ }
+ if (ssource->condition & G_IO_PRI) {
+ FD_SET((SOCKET)ssource->socket, &xfds);
+ }
+ ssource->revents = 0;
+ if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) {
+ return 0;
+ }
+
+ if (FD_ISSET(ssource->socket, &rfds)) {
+ ssource->revents |= G_IO_IN;
+ }
+ if (FD_ISSET(ssource->socket, &wfds)) {
+ ssource->revents |= G_IO_OUT;
+ }
+ if (FD_ISSET(ssource->socket, &xfds)) {
+ ssource->revents |= G_IO_PRI;
+ }
+
+ return ssource->revents;
+}
+
+
+static gboolean
+qio_channel_socket_source_dispatch(GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ QIOChannelFunc func = (QIOChannelFunc)callback;
+ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
+
+ return (*func)(ssource->ioc, ssource->revents, user_data);
+}
+
+
+static void
+qio_channel_socket_source_finalize(GSource *source)
+{
+ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source;
+
+ object_unref(OBJECT(ssource->ioc));
+}
+
+
+GSourceFuncs qio_channel_socket_source_funcs = {
+ qio_channel_socket_source_prepare,
+ qio_channel_socket_source_check,
+ qio_channel_socket_source_dispatch,
+ qio_channel_socket_source_finalize
+};
+#endif
+
+
+static gboolean
+qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED,
+ gint *timeout)
+{
+ *timeout = -1;
+
+ return FALSE;
+}
+
+
+static gboolean
+qio_channel_fd_pair_source_check(GSource *source)
+{
+ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
+ GIOCondition poll_condition = ssource->fdread.revents |
+ ssource->fdwrite.revents;
+
+ return poll_condition & ssource->condition;
+}
+
+
+static gboolean
+qio_channel_fd_pair_source_dispatch(GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ QIOChannelFunc func = (QIOChannelFunc)callback;
+ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
+ GIOCondition poll_condition = ssource->fdread.revents |
+ ssource->fdwrite.revents;
+
+ return (*func)(ssource->ioc,
+ poll_condition & ssource->condition,
+ user_data);
+}
+
+
+static void
+qio_channel_fd_pair_source_finalize(GSource *source)
+{
+ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source;
+
+ object_unref(OBJECT(ssource->ioc));
+}
+
+
+GSourceFuncs qio_channel_fd_source_funcs = {
+ qio_channel_fd_source_prepare,
+ qio_channel_fd_source_check,
+ qio_channel_fd_source_dispatch,
+ qio_channel_fd_source_finalize
+};
+
+
+GSourceFuncs qio_channel_fd_pair_source_funcs = {
+ qio_channel_fd_pair_source_prepare,
+ qio_channel_fd_pair_source_check,
+ qio_channel_fd_pair_source_dispatch,
+ qio_channel_fd_pair_source_finalize
+};
+
+
+GSource *qio_channel_create_fd_watch(QIOChannel *ioc,
+ int fd,
+ GIOCondition condition)
+{
+ GSource *source;
+ QIOChannelFDSource *ssource;
+
+ source = g_source_new(&qio_channel_fd_source_funcs,
+ sizeof(QIOChannelFDSource));
+ ssource = (QIOChannelFDSource *)source;
+
+ ssource->ioc = ioc;
+ object_ref(OBJECT(ioc));
+
+ ssource->condition = condition;
+
+#ifdef CONFIG_WIN32
+ ssource->fd.fd = (gint64)_get_osfhandle(fd);
+#else
+ ssource->fd.fd = fd;
+#endif
+ ssource->fd.events = condition;
+
+ g_source_add_poll(source, &ssource->fd);
+
+ return source;
+}
+
+#ifdef CONFIG_WIN32
+GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
+ int socket,
+ GIOCondition condition)
+{
+ GSource *source;
+ QIOChannelSocketSource *ssource;
+
+ source = g_source_new(&qio_channel_socket_source_funcs,
+ sizeof(QIOChannelSocketSource));
+ ssource = (QIOChannelSocketSource *)source;
+
+ ssource->ioc = ioc;
+ object_ref(OBJECT(ioc));
+
+ ssource->condition = condition;
+ ssource->socket = socket;
+ ssource->revents = 0;
+
+ ssource->fd.fd = (gintptr)ioc->event;
+ ssource->fd.events = G_IO_IN;
+
+ g_source_add_poll(source, &ssource->fd);
+
+ return source;
+}
+#else
+GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
+ int socket,
+ GIOCondition condition)
+{
+ return qio_channel_create_fd_watch(ioc, socket, condition);
+}
+#endif
+
+GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc,
+ int fdread,
+ int fdwrite,
+ GIOCondition condition)
+{
+ GSource *source;
+ QIOChannelFDPairSource *ssource;
+
+ source = g_source_new(&qio_channel_fd_pair_source_funcs,
+ sizeof(QIOChannelFDPairSource));
+ ssource = (QIOChannelFDPairSource *)source;
+
+ ssource->ioc = ioc;
+ object_ref(OBJECT(ioc));
+
+ ssource->condition = condition;
+
+#ifdef CONFIG_WIN32
+ ssource->fdread.fd = (gint64)_get_osfhandle(fdread);
+ ssource->fdwrite.fd = (gint64)_get_osfhandle(fdwrite);
+#else
+ ssource->fdread.fd = fdread;
+ ssource->fdwrite.fd = fdwrite;
+#endif
+
+ ssource->fdread.events = condition & G_IO_IN;
+ ssource->fdwrite.events = condition & G_IO_OUT;
+
+ g_source_add_poll(source, &ssource->fdread);
+ g_source_add_poll(source, &ssource->fdwrite);
+
+ return source;
+}
diff --git a/qemu/io/channel-websock.c b/qemu/io/channel-websock.c
new file mode 100644
index 000000000..708178779
--- /dev/null
+++ b/qemu/io/channel-websock.c
@@ -0,0 +1,964 @@
+/*
+ * QEMU I/O channels driver websockets
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "io/channel-websock.h"
+#include "crypto/hash.h"
+#include "trace.h"
+
+
+/* Max amount to allow in rawinput/rawoutput buffers */
+#define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192
+
+#define QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN 24
+#define QIO_CHANNEL_WEBSOCK_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+#define QIO_CHANNEL_WEBSOCK_GUID_LEN strlen(QIO_CHANNEL_WEBSOCK_GUID)
+
+#define QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL "Sec-WebSocket-Protocol"
+#define QIO_CHANNEL_WEBSOCK_HEADER_VERSION "Sec-WebSocket-Version"
+#define QIO_CHANNEL_WEBSOCK_HEADER_KEY "Sec-WebSocket-Key"
+
+#define QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY "binary"
+
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE \
+ "HTTP/1.1 101 Switching Protocols\r\n" \
+ "Upgrade: websocket\r\n" \
+ "Connection: Upgrade\r\n" \
+ "Sec-WebSocket-Accept: %s\r\n" \
+ "Sec-WebSocket-Protocol: binary\r\n" \
+ "\r\n"
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM "\r\n"
+#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_END "\r\n\r\n"
+#define QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION "13"
+
+/* The websockets packet header is variable length
+ * depending on the size of the payload... */
+
+/* ...length when using 7-bit payload length */
+#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT 6
+/* ...length when using 16-bit payload length */
+#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT 8
+/* ...length when using 64-bit payload length */
+#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT 14
+
+/* Length of the optional data mask field in header */
+#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_MASK 4
+
+/* Maximum length that can fit in 7-bit payload size */
+#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT 126
+/* Maximum length that can fit in 16-bit payload size */
+#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_16_BIT 65536
+
+/* Magic 7-bit length to indicate use of 16-bit payload length */
+#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT 126
+/* Magic 7-bit length to indicate use of 64-bit payload length */
+#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT 127
+
+/* Bitmasks & shifts for accessing header fields */
+#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN 0x80
+#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE 0x0f
+#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK 0x80
+#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_PAYLOAD_LEN 0x7f
+#define QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN 7
+#define QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_HAS_MASK 7
+
+typedef struct QIOChannelWebsockHeader QIOChannelWebsockHeader;
+
+struct QEMU_PACKED QIOChannelWebsockHeader {
+ unsigned char b0;
+ unsigned char b1;
+ union {
+ struct QEMU_PACKED {
+ uint16_t l16;
+ QIOChannelWebsockMask m16;
+ } s16;
+ struct QEMU_PACKED {
+ uint64_t l64;
+ QIOChannelWebsockMask m64;
+ } s64;
+ QIOChannelWebsockMask m;
+ } u;
+};
+
+enum {
+ QIO_CHANNEL_WEBSOCK_OPCODE_CONTINUATION = 0x0,
+ QIO_CHANNEL_WEBSOCK_OPCODE_TEXT_FRAME = 0x1,
+ QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME = 0x2,
+ QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE = 0x8,
+ QIO_CHANNEL_WEBSOCK_OPCODE_PING = 0x9,
+ QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA
+};
+
+static char *qio_channel_websock_handshake_entry(const char *handshake,
+ size_t handshake_len,
+ const char *name)
+{
+ char *begin, *end, *ret = NULL;
+ char *line = g_strdup_printf("%s%s: ",
+ QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM,
+ name);
+ begin = g_strstr_len(handshake, handshake_len, line);
+ if (begin != NULL) {
+ begin += strlen(line);
+ end = g_strstr_len(begin, handshake_len - (begin - handshake),
+ QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
+ if (end != NULL) {
+ ret = g_strndup(begin, end - begin);
+ }
+ }
+ g_free(line);
+ return ret;
+}
+
+
+static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc,
+ const char *key,
+ Error **errp)
+{
+ char combined_key[QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
+ QIO_CHANNEL_WEBSOCK_GUID_LEN + 1];
+ char *accept = NULL, *response = NULL;
+ size_t responselen;
+
+ g_strlcpy(combined_key, key, QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + 1);
+ g_strlcat(combined_key, QIO_CHANNEL_WEBSOCK_GUID,
+ QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
+ QIO_CHANNEL_WEBSOCK_GUID_LEN + 1);
+
+ /* hash and encode it */
+ if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
+ combined_key,
+ QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
+ QIO_CHANNEL_WEBSOCK_GUID_LEN,
+ &accept,
+ errp) < 0) {
+ return -1;
+ }
+
+ response = g_strdup_printf(QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE, accept);
+ responselen = strlen(response);
+ buffer_reserve(&ioc->encoutput, responselen);
+ buffer_append(&ioc->encoutput, response, responselen);
+
+ g_free(accept);
+ g_free(response);
+
+ return 0;
+}
+
+static int qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,
+ const char *line,
+ size_t size,
+ Error **errp)
+{
+ int ret = -1;
+ char *protocols = qio_channel_websock_handshake_entry(
+ line, size, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL);
+ char *version = qio_channel_websock_handshake_entry(
+ line, size, QIO_CHANNEL_WEBSOCK_HEADER_VERSION);
+ char *key = qio_channel_websock_handshake_entry(
+ line, size, QIO_CHANNEL_WEBSOCK_HEADER_KEY);
+
+ if (!protocols) {
+ error_setg(errp, "Missing websocket protocol header data");
+ goto cleanup;
+ }
+
+ if (!version) {
+ error_setg(errp, "Missing websocket version header data");
+ goto cleanup;
+ }
+
+ if (!key) {
+ error_setg(errp, "Missing websocket key header data");
+ goto cleanup;
+ }
+
+ if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) {
+ error_setg(errp, "No '%s' protocol is supported by client '%s'",
+ QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols);
+ goto cleanup;
+ }
+
+ if (!g_str_equal(version, QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION)) {
+ error_setg(errp, "Version '%s' is not supported by client '%s'",
+ QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION, version);
+ goto cleanup;
+ }
+
+ if (strlen(key) != QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN) {
+ error_setg(errp, "Key length '%zu' was not as expected '%d'",
+ strlen(key), QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN);
+ goto cleanup;
+ }
+
+ ret = qio_channel_websock_handshake_send_response(ioc, key, errp);
+
+ cleanup:
+ g_free(protocols);
+ g_free(version);
+ g_free(key);
+ return ret;
+}
+
+static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
+ Error **errp)
+{
+ char *handshake_end;
+ ssize_t ret;
+ /* Typical HTTP headers from novnc are 512 bytes, so limiting
+ * total header size to 4096 is easily enough. */
+ size_t want = 4096 - ioc->encinput.offset;
+ buffer_reserve(&ioc->encinput, want);
+ ret = qio_channel_read(ioc->master,
+ (char *)buffer_end(&ioc->encinput), want, errp);
+ if (ret < 0) {
+ return -1;
+ }
+ ioc->encinput.offset += ret;
+
+ handshake_end = g_strstr_len((char *)ioc->encinput.buffer,
+ ioc->encinput.offset,
+ QIO_CHANNEL_WEBSOCK_HANDSHAKE_END);
+ if (!handshake_end) {
+ if (ioc->encinput.offset >= 4096) {
+ error_setg(errp,
+ "End of headers not found in first 4096 bytes");
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if (qio_channel_websock_handshake_process(ioc,
+ (char *)ioc->encinput.buffer,
+ ioc->encinput.offset,
+ errp) < 0) {
+ return -1;
+ }
+
+ buffer_advance(&ioc->encinput,
+ handshake_end - (char *)ioc->encinput.buffer +
+ strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_END));
+ return 1;
+}
+
+static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ QIOTask *task = user_data;
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(
+ qio_task_get_source(task));
+ Error *err = NULL;
+ ssize_t ret;
+
+ ret = qio_channel_write(wioc->master,
+ (char *)wioc->encoutput.buffer,
+ wioc->encoutput.offset,
+ &err);
+
+ if (ret < 0) {
+ trace_qio_channel_websock_handshake_fail(ioc);
+ qio_task_abort(task, err);
+ error_free(err);
+ return FALSE;
+ }
+
+ buffer_advance(&wioc->encoutput, ret);
+ if (wioc->encoutput.offset == 0) {
+ trace_qio_channel_websock_handshake_complete(ioc);
+ qio_task_complete(task);
+ return FALSE;
+ }
+ trace_qio_channel_websock_handshake_pending(ioc, G_IO_OUT);
+ return TRUE;
+}
+
+static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ QIOTask *task = user_data;
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(
+ qio_task_get_source(task));
+ Error *err = NULL;
+ int ret;
+
+ ret = qio_channel_websock_handshake_read(wioc, &err);
+ if (ret < 0) {
+ trace_qio_channel_websock_handshake_fail(ioc);
+ qio_task_abort(task, err);
+ error_free(err);
+ return FALSE;
+ }
+ if (ret == 0) {
+ trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN);
+ /* need more data still */
+ return TRUE;
+ }
+
+ object_ref(OBJECT(task));
+ trace_qio_channel_websock_handshake_reply(ioc);
+ qio_channel_add_watch(
+ wioc->master,
+ G_IO_OUT,
+ qio_channel_websock_handshake_send,
+ task,
+ (GDestroyNotify)object_unref);
+ return FALSE;
+}
+
+
+static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
+{
+ size_t header_size;
+ union {
+ char buf[QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT];
+ QIOChannelWebsockHeader ws;
+ } header;
+
+ if (!ioc->rawoutput.offset) {
+ return;
+ }
+
+ header.ws.b0 = (1 << QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN) |
+ (QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME &
+ QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE);
+ if (ioc->rawoutput.offset <
+ QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT) {
+ header.ws.b1 = (uint8_t)ioc->rawoutput.offset;
+ header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT;
+ } else if (ioc->rawoutput.offset <
+ QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_16_BIT) {
+ header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT;
+ header.ws.u.s16.l16 = cpu_to_be16((uint16_t)ioc->rawoutput.offset);
+ header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT;
+ } else {
+ header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT;
+ header.ws.u.s64.l64 = cpu_to_be64(ioc->rawoutput.offset);
+ header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT;
+ }
+ header_size -= QIO_CHANNEL_WEBSOCK_HEADER_LEN_MASK;
+
+ buffer_reserve(&ioc->encoutput, header_size + ioc->rawoutput.offset);
+ buffer_append(&ioc->encoutput, header.buf, header_size);
+ buffer_append(&ioc->encoutput, ioc->rawoutput.buffer,
+ ioc->rawoutput.offset);
+ buffer_reset(&ioc->rawoutput);
+}
+
+
+static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
+ Error **errp)
+{
+ unsigned char opcode, fin, has_mask;
+ size_t header_size;
+ size_t payload_len;
+ QIOChannelWebsockHeader *header =
+ (QIOChannelWebsockHeader *)ioc->encinput.buffer;
+
+ if (ioc->payload_remain) {
+ error_setg(errp,
+ "Decoding header but %zu bytes of payload remain",
+ ioc->payload_remain);
+ return -1;
+ }
+ if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT) {
+ /* header not complete */
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+
+ fin = (header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN) >>
+ QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN;
+ opcode = header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE;
+ has_mask = (header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK) >>
+ QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_HAS_MASK;
+ payload_len = header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_PAYLOAD_LEN;
+
+ if (opcode == QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE) {
+ /* disconnect */
+ return 0;
+ }
+
+ /* Websocket frame sanity check:
+ * * Websocket fragmentation is not supported.
+ * * All websockets frames sent by a client have to be masked.
+ * * Only binary encoding is supported.
+ */
+ if (!fin) {
+ error_setg(errp, "websocket fragmentation is not supported");
+ return -1;
+ }
+ if (!has_mask) {
+ error_setg(errp, "websocket frames must be masked");
+ return -1;
+ }
+ if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
+ error_setg(errp, "only binary websocket frames are supported");
+ return -1;
+ }
+
+ if (payload_len < QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT) {
+ ioc->payload_remain = payload_len;
+ header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT;
+ ioc->mask = header->u.m;
+ } else if (payload_len == QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT &&
+ ioc->encinput.offset >= QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT) {
+ ioc->payload_remain = be16_to_cpu(header->u.s16.l16);
+ header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT;
+ ioc->mask = header->u.s16.m16;
+ } else if (payload_len == QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT &&
+ ioc->encinput.offset >= QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT) {
+ ioc->payload_remain = be64_to_cpu(header->u.s64.l64);
+ header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT;
+ ioc->mask = header->u.s64.m64;
+ } else {
+ /* header not complete */
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+
+ buffer_advance(&ioc->encinput, header_size);
+ return 1;
+}
+
+
+static ssize_t qio_channel_websock_decode_payload(QIOChannelWebsock *ioc,
+ Error **errp)
+{
+ size_t i;
+ size_t payload_len;
+ uint32_t *payload32;
+
+ if (!ioc->payload_remain) {
+ error_setg(errp,
+ "Decoding payload but no bytes of payload remain");
+ return -1;
+ }
+
+ /* If we aren't at the end of the payload, then drop
+ * off the last bytes, so we're always multiple of 4
+ * for purpose of unmasking, except at end of payload
+ */
+ if (ioc->encinput.offset < ioc->payload_remain) {
+ payload_len = ioc->encinput.offset - (ioc->encinput.offset % 4);
+ } else {
+ payload_len = ioc->payload_remain;
+ }
+ if (payload_len == 0) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+
+ ioc->payload_remain -= payload_len;
+
+ /* unmask frame */
+ /* process 1 frame (32 bit op) */
+ payload32 = (uint32_t *)ioc->encinput.buffer;
+ for (i = 0; i < payload_len / 4; i++) {
+ payload32[i] ^= ioc->mask.u;
+ }
+ /* process the remaining bytes (if any) */
+ for (i *= 4; i < payload_len; i++) {
+ ioc->encinput.buffer[i] ^= ioc->mask.c[i % 4];
+ }
+
+ buffer_reserve(&ioc->rawinput, payload_len);
+ buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len);
+ buffer_advance(&ioc->encinput, payload_len);
+ return payload_len;
+}
+
+
+QIOChannelWebsock *
+qio_channel_websock_new_server(QIOChannel *master)
+{
+ QIOChannelWebsock *wioc;
+ QIOChannel *ioc;
+
+ wioc = QIO_CHANNEL_WEBSOCK(object_new(TYPE_QIO_CHANNEL_WEBSOCK));
+ ioc = QIO_CHANNEL(wioc);
+
+ wioc->master = master;
+ if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) {
+ ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN);
+ }
+ object_ref(OBJECT(master));
+
+ trace_qio_channel_websock_new_server(wioc, master);
+ return wioc;
+}
+
+void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
+ QIOTaskFunc func,
+ gpointer opaque,
+ GDestroyNotify destroy)
+{
+ QIOTask *task;
+
+ task = qio_task_new(OBJECT(ioc),
+ func,
+ opaque,
+ destroy);
+
+ trace_qio_channel_websock_handshake_start(ioc);
+ trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN);
+ qio_channel_add_watch(ioc->master,
+ G_IO_IN,
+ qio_channel_websock_handshake_io,
+ task,
+ NULL);
+}
+
+
+static void qio_channel_websock_finalize(Object *obj)
+{
+ QIOChannelWebsock *ioc = QIO_CHANNEL_WEBSOCK(obj);
+
+ buffer_free(&ioc->encinput);
+ buffer_free(&ioc->encoutput);
+ buffer_free(&ioc->rawinput);
+ buffer_free(&ioc->rawoutput);
+ object_unref(OBJECT(ioc->master));
+ if (ioc->io_tag) {
+ g_source_remove(ioc->io_tag);
+ }
+ if (ioc->io_err) {
+ error_free(ioc->io_err);
+ }
+}
+
+
+static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc,
+ Error **errp)
+{
+ ssize_t ret;
+
+ if (ioc->encinput.offset < 4096) {
+ size_t want = 4096 - ioc->encinput.offset;
+
+ buffer_reserve(&ioc->encinput, want);
+ ret = qio_channel_read(ioc->master,
+ (char *)ioc->encinput.buffer +
+ ioc->encinput.offset,
+ want,
+ errp);
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret == 0 &&
+ ioc->encinput.offset == 0) {
+ return 0;
+ }
+ ioc->encinput.offset += ret;
+ }
+
+ if (ioc->payload_remain == 0) {
+ ret = qio_channel_websock_decode_header(ioc, errp);
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret == 0) {
+ return 0;
+ }
+ }
+
+ ret = qio_channel_websock_decode_payload(ioc, errp);
+ if (ret < 0) {
+ return ret;
+ }
+ return ret;
+}
+
+
+static ssize_t qio_channel_websock_write_wire(QIOChannelWebsock *ioc,
+ Error **errp)
+{
+ ssize_t ret;
+ ssize_t done = 0;
+ qio_channel_websock_encode(ioc);
+
+ while (ioc->encoutput.offset > 0) {
+ ret = qio_channel_write(ioc->master,
+ (char *)ioc->encoutput.buffer,
+ ioc->encoutput.offset,
+ errp);
+ if (ret < 0) {
+ if (ret == QIO_CHANNEL_ERR_BLOCK &&
+ done > 0) {
+ return done;
+ } else {
+ return ret;
+ }
+ }
+ buffer_advance(&ioc->encoutput, ret);
+ done += ret;
+ }
+ return done;
+}
+
+
+static void qio_channel_websock_flush_free(gpointer user_data)
+{
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(user_data);
+ object_unref(OBJECT(wioc));
+}
+
+static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc);
+
+static gboolean qio_channel_websock_flush(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(user_data);
+ ssize_t ret;
+
+ if (condition & G_IO_OUT) {
+ ret = qio_channel_websock_write_wire(wioc, &wioc->io_err);
+ if (ret < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (condition & G_IO_IN) {
+ ret = qio_channel_websock_read_wire(wioc, &wioc->io_err);
+ if (ret < 0) {
+ goto cleanup;
+ }
+ if (ret == 0) {
+ wioc->io_eof = TRUE;
+ }
+ }
+
+ cleanup:
+ qio_channel_websock_set_watch(wioc);
+ return FALSE;
+}
+
+
+static void qio_channel_websock_unset_watch(QIOChannelWebsock *ioc)
+{
+ if (ioc->io_tag) {
+ g_source_remove(ioc->io_tag);
+ ioc->io_tag = 0;
+ }
+}
+
+static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc)
+{
+ GIOCondition cond = 0;
+
+ qio_channel_websock_unset_watch(ioc);
+
+ if (ioc->io_err) {
+ return;
+ }
+
+ if (ioc->encoutput.offset) {
+ cond |= G_IO_OUT;
+ }
+ if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER &&
+ !ioc->io_eof) {
+ cond |= G_IO_IN;
+ }
+
+ if (cond) {
+ object_ref(OBJECT(ioc));
+ ioc->io_tag =
+ qio_channel_add_watch(ioc->master,
+ cond,
+ qio_channel_websock_flush,
+ ioc,
+ qio_channel_websock_flush_free);
+ }
+}
+
+
+static ssize_t qio_channel_websock_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
+ size_t i;
+ ssize_t got = 0;
+ ssize_t ret;
+
+ if (wioc->io_err) {
+ *errp = error_copy(wioc->io_err);
+ return -1;
+ }
+
+ if (!wioc->rawinput.offset) {
+ ret = qio_channel_websock_read_wire(QIO_CHANNEL_WEBSOCK(ioc), errp);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ for (i = 0 ; i < niov ; i++) {
+ size_t want = iov[i].iov_len;
+ if (want > (wioc->rawinput.offset - got)) {
+ want = (wioc->rawinput.offset - got);
+ }
+
+ memcpy(iov[i].iov_base,
+ wioc->rawinput.buffer + got,
+ want);
+ got += want;
+
+ if (want < iov[i].iov_len) {
+ break;
+ }
+ }
+
+ buffer_advance(&wioc->rawinput, got);
+ qio_channel_websock_set_watch(wioc);
+ return got;
+}
+
+
+static ssize_t qio_channel_websock_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
+ size_t i;
+ ssize_t done = 0;
+ ssize_t ret;
+
+ if (wioc->io_err) {
+ *errp = error_copy(wioc->io_err);
+ return -1;
+ }
+
+ if (wioc->io_eof) {
+ error_setg(errp, "%s", "Broken pipe");
+ return -1;
+ }
+
+ for (i = 0; i < niov; i++) {
+ size_t want = iov[i].iov_len;
+ if ((want + wioc->rawoutput.offset) > QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
+ want = (QIO_CHANNEL_WEBSOCK_MAX_BUFFER - wioc->rawoutput.offset);
+ }
+ if (want == 0) {
+ goto done;
+ }
+
+ buffer_reserve(&wioc->rawoutput, want);
+ buffer_append(&wioc->rawoutput, iov[i].iov_base, want);
+ done += want;
+ if (want < iov[i].iov_len) {
+ break;
+ }
+ }
+
+ done:
+ ret = qio_channel_websock_write_wire(wioc, errp);
+ if (ret < 0 &&
+ ret != QIO_CHANNEL_ERR_BLOCK) {
+ qio_channel_websock_unset_watch(wioc);
+ return -1;
+ }
+
+ qio_channel_websock_set_watch(wioc);
+
+ if (done == 0) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+
+ return done;
+}
+
+static int qio_channel_websock_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp)
+{
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
+
+ qio_channel_set_blocking(wioc->master, enabled, errp);
+ return 0;
+}
+
+static void qio_channel_websock_set_delay(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc);
+
+ qio_channel_set_delay(tioc->master, enabled);
+}
+
+static void qio_channel_websock_set_cork(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc);
+
+ qio_channel_set_cork(tioc->master, enabled);
+}
+
+static int qio_channel_websock_shutdown(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp)
+{
+ QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc);
+
+ return qio_channel_shutdown(tioc->master, how, errp);
+}
+
+static int qio_channel_websock_close(QIOChannel *ioc,
+ Error **errp)
+{
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
+
+ return qio_channel_close(wioc->master, errp);
+}
+
+typedef struct QIOChannelWebsockSource QIOChannelWebsockSource;
+struct QIOChannelWebsockSource {
+ GSource parent;
+ QIOChannelWebsock *wioc;
+ GIOCondition condition;
+};
+
+static gboolean
+qio_channel_websock_source_prepare(GSource *source,
+ gint *timeout)
+{
+ QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source;
+ GIOCondition cond = 0;
+ *timeout = -1;
+
+ if (wsource->wioc->rawinput.offset) {
+ cond |= G_IO_IN;
+ }
+ if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
+ cond |= G_IO_OUT;
+ }
+
+ return cond & wsource->condition;
+}
+
+static gboolean
+qio_channel_websock_source_check(GSource *source)
+{
+ QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source;
+ GIOCondition cond = 0;
+
+ if (wsource->wioc->rawinput.offset) {
+ cond |= G_IO_IN;
+ }
+ if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
+ cond |= G_IO_OUT;
+ }
+
+ return cond & wsource->condition;
+}
+
+static gboolean
+qio_channel_websock_source_dispatch(GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ QIOChannelFunc func = (QIOChannelFunc)callback;
+ QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source;
+ GIOCondition cond = 0;
+
+ if (wsource->wioc->rawinput.offset) {
+ cond |= G_IO_IN;
+ }
+ if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
+ cond |= G_IO_OUT;
+ }
+
+ return (*func)(QIO_CHANNEL(wsource->wioc),
+ (cond & wsource->condition),
+ user_data);
+}
+
+static void
+qio_channel_websock_source_finalize(GSource *source)
+{
+ QIOChannelWebsockSource *ssource = (QIOChannelWebsockSource *)source;
+
+ object_unref(OBJECT(ssource->wioc));
+}
+
+GSourceFuncs qio_channel_websock_source_funcs = {
+ qio_channel_websock_source_prepare,
+ qio_channel_websock_source_check,
+ qio_channel_websock_source_dispatch,
+ qio_channel_websock_source_finalize
+};
+
+static GSource *qio_channel_websock_create_watch(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
+ QIOChannelWebsockSource *ssource;
+ GSource *source;
+
+ source = g_source_new(&qio_channel_websock_source_funcs,
+ sizeof(QIOChannelWebsockSource));
+ ssource = (QIOChannelWebsockSource *)source;
+
+ ssource->wioc = wioc;
+ object_ref(OBJECT(wioc));
+
+ ssource->condition = condition;
+
+ qio_channel_websock_set_watch(wioc);
+ return source;
+}
+
+static void qio_channel_websock_class_init(ObjectClass *klass,
+ void *class_data G_GNUC_UNUSED)
+{
+ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
+
+ ioc_klass->io_writev = qio_channel_websock_writev;
+ ioc_klass->io_readv = qio_channel_websock_readv;
+ ioc_klass->io_set_blocking = qio_channel_websock_set_blocking;
+ ioc_klass->io_set_cork = qio_channel_websock_set_cork;
+ ioc_klass->io_set_delay = qio_channel_websock_set_delay;
+ ioc_klass->io_close = qio_channel_websock_close;
+ ioc_klass->io_shutdown = qio_channel_websock_shutdown;
+ ioc_klass->io_create_watch = qio_channel_websock_create_watch;
+}
+
+static const TypeInfo qio_channel_websock_info = {
+ .parent = TYPE_QIO_CHANNEL,
+ .name = TYPE_QIO_CHANNEL_WEBSOCK,
+ .instance_size = sizeof(QIOChannelWebsock),
+ .instance_finalize = qio_channel_websock_finalize,
+ .class_init = qio_channel_websock_class_init,
+};
+
+static void qio_channel_websock_register_types(void)
+{
+ type_register_static(&qio_channel_websock_info);
+}
+
+type_init(qio_channel_websock_register_types);
diff --git a/qemu/io/channel.c b/qemu/io/channel.c
new file mode 100644
index 000000000..692eb179b
--- /dev/null
+++ b/qemu/io/channel.c
@@ -0,0 +1,307 @@
+/*
+ * QEMU I/O channels
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "io/channel.h"
+#include "qapi/error.h"
+#include "qemu/coroutine.h"
+
+bool qio_channel_has_feature(QIOChannel *ioc,
+ QIOChannelFeature feature)
+{
+ return ioc->features & (1 << feature);
+}
+
+
+ssize_t qio_channel_readv_full(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if ((fds || nfds) &&
+ !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) {
+ error_setg_errno(errp, EINVAL,
+ "Channel does not support file descriptor passing");
+ return -1;
+ }
+
+ return klass->io_readv(ioc, iov, niov, fds, nfds, errp);
+}
+
+
+ssize_t qio_channel_writev_full(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if ((fds || nfds) &&
+ !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) {
+ error_setg_errno(errp, EINVAL,
+ "Channel does not support file descriptor passing");
+ return -1;
+ }
+
+ return klass->io_writev(ioc, iov, niov, fds, nfds, errp);
+}
+
+
+ssize_t qio_channel_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ Error **errp)
+{
+ return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, errp);
+}
+
+
+ssize_t qio_channel_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ Error **errp)
+{
+ return qio_channel_writev_full(ioc, iov, niov, NULL, 0, errp);
+}
+
+
+ssize_t qio_channel_read(QIOChannel *ioc,
+ char *buf,
+ size_t buflen,
+ Error **errp)
+{
+ struct iovec iov = { .iov_base = buf, .iov_len = buflen };
+ return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, errp);
+}
+
+
+ssize_t qio_channel_write(QIOChannel *ioc,
+ const char *buf,
+ size_t buflen,
+ Error **errp)
+{
+ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
+ return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, errp);
+}
+
+
+int qio_channel_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+ return klass->io_set_blocking(ioc, enabled, errp);
+}
+
+
+int qio_channel_close(QIOChannel *ioc,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+ return klass->io_close(ioc, errp);
+}
+
+
+GSource *qio_channel_create_watch(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+ return klass->io_create_watch(ioc, condition);
+}
+
+
+guint qio_channel_add_watch(QIOChannel *ioc,
+ GIOCondition condition,
+ QIOChannelFunc func,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ GSource *source;
+ guint id;
+
+ source = qio_channel_create_watch(ioc, condition);
+
+ g_source_set_callback(source, (GSourceFunc)func, user_data, notify);
+
+ id = g_source_attach(source, NULL);
+ g_source_unref(source);
+
+ return id;
+}
+
+
+int qio_channel_shutdown(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if (!klass->io_shutdown) {
+ error_setg(errp, "Data path shutdown not supported");
+ return -1;
+ }
+
+ return klass->io_shutdown(ioc, how, errp);
+}
+
+
+void qio_channel_set_delay(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if (klass->io_set_delay) {
+ klass->io_set_delay(ioc, enabled);
+ }
+}
+
+
+void qio_channel_set_cork(QIOChannel *ioc,
+ bool enabled)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if (klass->io_set_cork) {
+ klass->io_set_cork(ioc, enabled);
+ }
+}
+
+
+off_t qio_channel_io_seek(QIOChannel *ioc,
+ off_t offset,
+ int whence,
+ Error **errp)
+{
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+ if (!klass->io_seek) {
+ error_setg(errp, "Channel does not support random access");
+ return -1;
+ }
+
+ return klass->io_seek(ioc, offset, whence, errp);
+}
+
+
+typedef struct QIOChannelYieldData QIOChannelYieldData;
+struct QIOChannelYieldData {
+ QIOChannel *ioc;
+ Coroutine *co;
+};
+
+
+static gboolean qio_channel_yield_enter(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer opaque)
+{
+ QIOChannelYieldData *data = opaque;
+ qemu_coroutine_enter(data->co, NULL);
+ return FALSE;
+}
+
+
+void coroutine_fn qio_channel_yield(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ QIOChannelYieldData data;
+
+ assert(qemu_in_coroutine());
+ data.ioc = ioc;
+ data.co = qemu_coroutine_self();
+ qio_channel_add_watch(ioc,
+ condition,
+ qio_channel_yield_enter,
+ &data,
+ NULL);
+ qemu_coroutine_yield();
+}
+
+
+static gboolean qio_channel_wait_complete(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer opaque)
+{
+ GMainLoop *loop = opaque;
+
+ g_main_loop_quit(loop);
+ return FALSE;
+}
+
+
+void qio_channel_wait(QIOChannel *ioc,
+ GIOCondition condition)
+{
+ GMainContext *ctxt = g_main_context_new();
+ GMainLoop *loop = g_main_loop_new(ctxt, TRUE);
+ GSource *source;
+
+ source = qio_channel_create_watch(ioc, condition);
+
+ g_source_set_callback(source,
+ (GSourceFunc)qio_channel_wait_complete,
+ loop,
+ NULL);
+
+ g_source_attach(source, ctxt);
+
+ g_main_loop_run(loop);
+
+ g_source_unref(source);
+ g_main_loop_unref(loop);
+ g_main_context_unref(ctxt);
+}
+
+
+#ifdef _WIN32
+static void qio_channel_finalize(Object *obj)
+{
+ QIOChannel *ioc = QIO_CHANNEL(obj);
+
+ if (ioc->event) {
+ CloseHandle(ioc->event);
+ }
+}
+#endif
+
+static const TypeInfo qio_channel_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QIO_CHANNEL,
+ .instance_size = sizeof(QIOChannel),
+#ifdef _WIN32
+ .instance_finalize = qio_channel_finalize,
+#endif
+ .abstract = true,
+ .class_size = sizeof(QIOChannelClass),
+};
+
+
+static void qio_channel_register_types(void)
+{
+ type_register_static(&qio_channel_info);
+}
+
+
+type_init(qio_channel_register_types);
diff --git a/qemu/io/task.c b/qemu/io/task.c
new file mode 100644
index 000000000..c7f97a9b1
--- /dev/null
+++ b/qemu/io/task.c
@@ -0,0 +1,161 @@
+/*
+ * QEMU I/O task
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "io/task.h"
+#include "qapi/error.h"
+#include "qemu/thread.h"
+#include "trace.h"
+
+struct QIOTask {
+ Object *source;
+ QIOTaskFunc func;
+ gpointer opaque;
+ GDestroyNotify destroy;
+};
+
+
+QIOTask *qio_task_new(Object *source,
+ QIOTaskFunc func,
+ gpointer opaque,
+ GDestroyNotify destroy)
+{
+ QIOTask *task;
+
+ task = g_new0(QIOTask, 1);
+
+ task->source = source;
+ object_ref(source);
+ task->func = func;
+ task->opaque = opaque;
+ task->destroy = destroy;
+
+ trace_qio_task_new(task, source, func, opaque);
+
+ return task;
+}
+
+static void qio_task_free(QIOTask *task)
+{
+ if (task->destroy) {
+ task->destroy(task->opaque);
+ }
+ object_unref(task->source);
+
+ g_free(task);
+}
+
+
+struct QIOTaskThreadData {
+ QIOTask *task;
+ QIOTaskWorker worker;
+ gpointer opaque;
+ GDestroyNotify destroy;
+ Error *err;
+ int ret;
+};
+
+
+static gboolean gio_task_thread_result(gpointer opaque)
+{
+ struct QIOTaskThreadData *data = opaque;
+
+ trace_qio_task_thread_result(data->task);
+ if (data->ret == 0) {
+ qio_task_complete(data->task);
+ } else {
+ qio_task_abort(data->task, data->err);
+ }
+
+ error_free(data->err);
+ if (data->destroy) {
+ data->destroy(data->opaque);
+ }
+
+ g_free(data);
+
+ return FALSE;
+}
+
+
+static gpointer qio_task_thread_worker(gpointer opaque)
+{
+ struct QIOTaskThreadData *data = opaque;
+
+ trace_qio_task_thread_run(data->task);
+ data->ret = data->worker(data->task, &data->err, data->opaque);
+ if (data->ret < 0 && data->err == NULL) {
+ error_setg(&data->err, "Task worker failed but did not set an error");
+ }
+
+ /* We're running in the background thread, and must only
+ * ever report the task results in the main event loop
+ * thread. So we schedule an idle callback to report
+ * the worker results
+ */
+ trace_qio_task_thread_exit(data->task);
+ g_idle_add(gio_task_thread_result, data);
+ return NULL;
+}
+
+
+void qio_task_run_in_thread(QIOTask *task,
+ QIOTaskWorker worker,
+ gpointer opaque,
+ GDestroyNotify destroy)
+{
+ struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1);
+ QemuThread thread;
+
+ data->task = task;
+ data->worker = worker;
+ data->opaque = opaque;
+ data->destroy = destroy;
+
+ trace_qio_task_thread_start(task, worker, opaque);
+ qemu_thread_create(&thread,
+ "io-task-worker",
+ qio_task_thread_worker,
+ data,
+ QEMU_THREAD_DETACHED);
+}
+
+
+void qio_task_complete(QIOTask *task)
+{
+ task->func(task->source, NULL, task->opaque);
+ trace_qio_task_complete(task);
+ qio_task_free(task);
+}
+
+void qio_task_abort(QIOTask *task,
+ Error *err)
+{
+ task->func(task->source, err, task->opaque);
+ trace_qio_task_abort(task);
+ qio_task_free(task);
+}
+
+
+Object *qio_task_get_source(QIOTask *task)
+{
+ object_ref(task->source);
+ return task->source;
+}