aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-12-18 12:42:10 +0000
committerPeter Maydell <peter.maydell@linaro.org>2015-12-18 12:42:10 +0000
commit67a708406221f476c0f8fa60c192c186150c5185 (patch)
tree3f9d266d76b202a93b1bf7fd8a47affa4d521308
parent6a6533213d78dea4407fe6933ad489796b582599 (diff)
parentd98e4eb7de93290f7921b0dbe869c7dd3c567945 (diff)
Merge remote-tracking branch 'remotes/berrange/tags/pull-io-channel-base-2015-12-18-1' into staging
Merge I/O channels base classes # gpg: Signature made Fri 18 Dec 2015 12:18:38 GMT using RSA key ID 15104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" * remotes/berrange/tags/pull-io-channel-base-2015-12-18-1: io: add QIOChannelBuffer class io: add QIOChannelCommand class io: add QIOChannelWebsock class io: add QIOChannelTLS class io: add QIOChannelFile class io: add QIOChannelSocket class io: add QIOTask class for async operations io: add helper module for creating watches on FDs io: add abstract QIOChannel classes Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS7
-rw-r--r--Makefile2
-rw-r--r--Makefile.objs5
-rw-r--r--Makefile.target2
-rwxr-xr-xconfigure11
-rw-r--r--include/io/channel-buffer.h60
-rw-r--r--include/io/channel-command.h91
-rw-r--r--include/io/channel-file.h93
-rw-r--r--include/io/channel-socket.h244
-rw-r--r--include/io/channel-tls.h142
-rw-r--r--include/io/channel-watch.h72
-rw-r--r--include/io/channel-websock.h108
-rw-r--r--include/io/channel.h502
-rw-r--r--include/io/task.h256
-rw-r--r--include/qemu/sockets.h19
-rw-r--r--io/Makefile.objs9
-rw-r--r--io/channel-buffer.c248
-rw-r--r--io/channel-command.c357
-rw-r--r--io/channel-file.c225
-rw-r--r--io/channel-socket.c741
-rw-r--r--io/channel-tls.c393
-rw-r--r--io/channel-watch.c198
-rw-r--r--io/channel-websock.c962
-rw-r--r--io/channel.c291
-rw-r--r--io/task.c159
-rwxr-xr-xscripts/create_config9
-rw-r--r--tests/.gitignore8
-rw-r--r--tests/Makefile19
-rw-r--r--tests/io-channel-helpers.c246
-rw-r--r--tests/io-channel-helpers.h42
-rw-r--r--tests/test-io-channel-buffer.c50
-rw-r--r--tests/test-io-channel-command.c129
-rw-r--r--tests/test-io-channel-file.c100
-rw-r--r--tests/test-io-channel-socket.c399
-rw-r--r--tests/test-io-channel-tls.c342
-rw-r--r--tests/test-io-task.c268
-rw-r--r--trace-events56
-rw-r--r--util/qemu-sockets.c2
38 files changed, 6866 insertions, 1 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index e8cee1e266..55a0fd8ae5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1243,6 +1243,13 @@ S: Odd fixes
F: util/buffer.c
F: include/qemu/buffer.h
+I/O Channels
+M: Daniel P. Berrange <berrange@redhat.com>
+S: Maintained
+F: io/
+F: include/io/
+F: tests/test-io-*
+
Usermode Emulation
------------------
Overall
diff --git a/Makefile b/Makefile
index 930ac2796b..af3e5f104d 100644
--- a/Makefile
+++ b/Makefile
@@ -159,6 +159,7 @@ dummy := $(call unnest-vars,, \
crypto-obj-y \
crypto-aes-obj-y \
qom-obj-y \
+ io-obj-y \
common-obj-y \
common-obj-m)
@@ -178,6 +179,7 @@ SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
+$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
subdir-%:
diff --git a/Makefile.objs b/Makefile.objs
index 77be052dde..dac2c02d9f 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -28,6 +28,11 @@ crypto-aes-obj-y = crypto/
qom-obj-y = qom/
+#######################################################################
+# io-obj-y is code used by both qemu system emulation and qemu-img
+
+io-obj-y = io/
+
######################################################################
# Target independent part of system emulation. The long term path is to
# suppress *all* target specific code in case of system emulation, i.e. a
diff --git a/Makefile.target b/Makefile.target
index 962d0045ff..34ddb7e762 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -176,6 +176,7 @@ dummy := $(call unnest-vars,.., \
crypto-obj-y \
crypto-aes-obj-y \
qom-obj-y \
+ io-obj-y \
common-obj-y \
common-obj-m)
target-obj-y := $(target-obj-y-save)
@@ -185,6 +186,7 @@ all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
+all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
$(QEMU_PROG_BUILD): config-devices.mak
diff --git a/configure b/configure
index b9552fda6f..375f103a2c 100755
--- a/configure
+++ b/configure
@@ -2427,6 +2427,14 @@ fi
##########################################
+# getifaddrs (for tests/test-io-channel-socket )
+
+have_ifaddrs_h=yes
+if ! check_include "ifaddrs.h" ; then
+ have_ifaddrs_h=no
+fi
+
+##########################################
# VTE probe
if test "$vte" != "no"; then
@@ -5137,6 +5145,9 @@ fi
if test "$tasn1" = "yes" ; then
echo "CONFIG_TASN1=y" >> $config_host_mak
fi
+if test "$have_ifaddrs_h" = "yes" ; then
+ echo "HAVE_IFADDRS_H=y" >> $config_host_mak
+fi
if test "$vte" = "yes" ; then
echo "CONFIG_VTE=y" >> $config_host_mak
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
diff --git a/include/io/channel-buffer.h b/include/io/channel-buffer.h
new file mode 100644
index 0000000000..91a52b3373
--- /dev/null
+++ b/include/io/channel-buffer.h
@@ -0,0 +1,60 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_BUFFER_H__
+#define QIO_CHANNEL_BUFFER_H__
+
+#include "io/channel.h"
+
+#define TYPE_QIO_CHANNEL_BUFFER "qio-channel-buffer"
+#define QIO_CHANNEL_BUFFER(obj) \
+ OBJECT_CHECK(QIOChannelBuffer, (obj), TYPE_QIO_CHANNEL_BUFFER)
+
+typedef struct QIOChannelBuffer QIOChannelBuffer;
+
+/**
+ * QIOChannelBuffer:
+ *
+ * The QIOChannelBuffer object provides a channel implementation
+ * that is able to perform I/O to/from a memory buffer.
+ *
+ */
+
+struct QIOChannelBuffer {
+ QIOChannel parent;
+ size_t capacity; /* Total allocated memory */
+ size_t usage; /* Current size of data */
+ size_t offset; /* Offset for future I/O ops */
+ char *data;
+};
+
+
+/**
+ * qio_channel_buffer_new:
+ * @capacity: the initial buffer capacity to allocate
+ *
+ * Allocate a new buffer which is initially empty
+ *
+ * Returns: the new channel object
+ */
+QIOChannelBuffer *
+qio_channel_buffer_new(size_t capacity);
+
+#endif /* QIO_CHANNEL_BUFFER_H__ */
diff --git a/include/io/channel-command.h b/include/io/channel-command.h
new file mode 100644
index 0000000000..bd3c59946f
--- /dev/null
+++ b/include/io/channel-command.h
@@ -0,0 +1,91 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_COMMAND_H__
+#define QIO_CHANNEL_COMMAND_H__
+
+#include "io/channel.h"
+
+#define TYPE_QIO_CHANNEL_COMMAND "qio-channel-command"
+#define QIO_CHANNEL_COMMAND(obj) \
+ OBJECT_CHECK(QIOChannelCommand, (obj), TYPE_QIO_CHANNEL_COMMAND)
+
+typedef struct QIOChannelCommand QIOChannelCommand;
+
+
+/**
+ * QIOChannelCommand:
+ *
+ * The QIOChannelCommand class provides a channel implementation
+ * that can transport data with an externally running command
+ * via its stdio streams.
+ */
+
+struct QIOChannelCommand {
+ QIOChannel parent;
+ int writefd;
+ int readfd;
+ pid_t pid;
+};
+
+
+/**
+ * qio_channel_command_new_pid:
+ * @writefd: the FD connected to the command's stdin
+ * @readfd: the FD connected to the command's stdout
+ * @pid: the PID of the running child command
+ * @errp: pointer to an uninitialized error object
+ *
+ * Create a channel for performing I/O with the
+ * previously spawned command identified by @pid.
+ * The two file descriptors provide the connection
+ * to command's stdio streams, either one or which
+ * may be -1 to indicate that stream is not open.
+ *
+ * The channel will take ownership of the process
+ * @pid and will kill it when closing the channel.
+ * Similarly it will take responsibility for
+ * closing the file descriptors @writefd and @readfd.
+ *
+ * Returns: the command channel object, or NULL on error
+ */
+QIOChannelCommand *
+qio_channel_command_new_pid(int writefd,
+ int readfd,
+ pid_t pid);
+
+/**
+ * qio_channel_command_new_spawn:
+ * @argv: the NULL terminated list of command arguments
+ * @flags: the I/O mode, one of O_RDONLY, O_WRONLY, O_RDWR
+ * @errp: pointer to an uninitialized error object
+ *
+ * Create a channel for performing I/O with the
+ * command to be spawned with arguments @argv.
+ *
+ * Returns: the command channel object, or NULL on error
+ */
+QIOChannelCommand *
+qio_channel_command_new_spawn(const char *const argv[],
+ int flags,
+ Error **errp);
+
+
+#endif /* QIO_CHANNEL_COMMAND_H__ */
diff --git a/include/io/channel-file.h b/include/io/channel-file.h
new file mode 100644
index 0000000000..308e6d44d6
--- /dev/null
+++ b/include/io/channel-file.h
@@ -0,0 +1,93 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_FILE_H__
+#define QIO_CHANNEL_FILE_H__
+
+#include "io/channel.h"
+
+#define TYPE_QIO_CHANNEL_FILE "qio-channel-file"
+#define QIO_CHANNEL_FILE(obj) \
+ OBJECT_CHECK(QIOChannelFile, (obj), TYPE_QIO_CHANNEL_FILE)
+
+typedef struct QIOChannelFile QIOChannelFile;
+
+/**
+ * QIOChannelFile:
+ *
+ * The QIOChannelFile object provides a channel implementation
+ * that is able to perform I/O on block devices, character
+ * devices, FIFOs, pipes and plain files. While it is technically
+ * able to work on sockets too on the UNIX platform, this is not
+ * portable to Windows and lacks some extra sockets specific
+ * functionality. So the QIOChannelSocket object is recommended
+ * for that use case.
+ *
+ */
+
+struct QIOChannelFile {
+ QIOChannel parent;
+ int fd;
+};
+
+
+/**
+ * qio_channel_file_new_fd:
+ * @fd: the file descriptor
+ *
+ * Create a new IO channel object for a file represented
+ * by the @fd parameter. @fd can be associated with a
+ * block device, character device, fifo, pipe, or a
+ * regular file. For sockets, the QIOChannelSocket class
+ * should be used instead, as this provides greater
+ * functionality and cross platform portability.
+ *
+ * The channel will own the passed in file descriptor
+ * and will take responsibility for closing it, so the
+ * caller must not close it. If appropriate the caller
+ * should dup() its FD before opening the channel.
+ *
+ * Returns: the new channel object
+ */
+QIOChannelFile *
+qio_channel_file_new_fd(int fd);
+
+/**
+ * qio_channel_file_new_path:
+ * @fd: the file descriptor
+ * @flags: the open flags (O_RDONLY|O_WRONLY|O_RDWR, etc)
+ * @mode: the file creation mode if O_WRONLY is set in @flags
+ * @errp: pointer to initialized error object
+ *
+ * Create a new IO channel object for a file represented
+ * by the @path parameter. @path can point to any
+ * type of file on which sequential I/O can be
+ * performed, whether it be a plain file, character
+ * device or block device.
+ *
+ * Returns: the new channel object
+ */
+QIOChannelFile *
+qio_channel_file_new_path(const char *path,
+ int flags,
+ mode_t mode,
+ Error **errp);
+
+#endif /* QIO_CHANNEL_FILE_H__ */
diff --git a/include/io/channel-socket.h b/include/io/channel-socket.h
new file mode 100644
index 0000000000..0719757b0f
--- /dev/null
+++ b/include/io/channel-socket.h
@@ -0,0 +1,244 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_SOCKET_H__
+#define QIO_CHANNEL_SOCKET_H__
+
+#include "io/channel.h"
+#include "io/task.h"
+#include "qemu/sockets.h"
+
+#define TYPE_QIO_CHANNEL_SOCKET "qio-channel-socket"
+#define QIO_CHANNEL_SOCKET(obj) \
+ OBJECT_CHECK(QIOChannelSocket, (obj), TYPE_QIO_CHANNEL_SOCKET)
+
+typedef struct QIOChannelSocket QIOChannelSocket;
+
+/**
+ * QIOChannelSocket:
+ *
+ * The QIOChannelSocket class provides a channel implementation
+ * that can transport data over a UNIX socket or TCP socket.
+ * Beyond the core channel API, it also provides functionality
+ * for accepting client connections, tuning some socket
+ * parameters and getting socket address strings.
+ */
+
+struct QIOChannelSocket {
+ QIOChannel parent;
+ int fd;
+ struct sockaddr_storage localAddr;
+ socklen_t localAddrLen;
+ struct sockaddr_storage remoteAddr;
+ socklen_t remoteAddrLen;
+};
+
+
+/**
+ * qio_channel_socket_new:
+ *
+ * Create a channel for performing I/O on a socket
+ * connection, that is initially closed. After
+ * creating the socket, it must be setup as a client
+ * connection or server.
+ *
+ * Returns: the socket channel object
+ */
+QIOChannelSocket *
+qio_channel_socket_new(void);
+
+/**
+ * qio_channel_socket_new_fd:
+ * @fd: the socket file descriptor
+ * @errp: pointer to an uninitialized error object
+ *
+ * Create a channel for performing I/O on the socket
+ * connection represented by the file descriptor @fd.
+ *
+ * Returns: the socket channel object, or NULL on error
+ */
+QIOChannelSocket *
+qio_channel_socket_new_fd(int fd,
+ Error **errp);
+
+
+/**
+ * qio_channel_socket_connect_sync:
+ * @ioc: the socket channel object
+ * @addr: the address to connect to
+ * @errp: pointer to an uninitialized error object
+ *
+ * Attempt to connect to the address @addr. This method
+ * will run in the foreground so the caller will not regain
+ * execution control until the connection is established or
+ * an error occurs.
+ */
+int qio_channel_socket_connect_sync(QIOChannelSocket *ioc,
+ SocketAddress *addr,
+ Error **errp);
+
+/**
+ * qio_channel_socket_connect_async:
+ * @ioc: the socket channel object
+ * @addr: the address to connect to
+ * @callback: the function to invoke on completion
+ * @opaque: user data to pass to @callback
+ * @destroy: the function to free @opaque
+ *
+ * Attempt to connect to the address @addr. This method
+ * will run in the background so the caller will regain
+ * execution control immediately. The function @callback
+ * will be invoked on completion or failure.
+ */
+void qio_channel_socket_connect_async(QIOChannelSocket *ioc,
+ SocketAddress *addr,
+ QIOTaskFunc callback,
+ gpointer opaque,
+ GDestroyNotify destroy);
+
+
+/**
+ * qio_channel_socket_listen_sync:
+ * @ioc: the socket channel object
+ * @addr: the address to listen to
+ * @errp: pointer to an uninitialized error object
+ *
+ * Attempt to listen to the address @addr. This method
+ * will run in the foreground so the caller will not regain
+ * execution control until the connection is established or
+ * an error occurs.
+ */
+int qio_channel_socket_listen_sync(QIOChannelSocket *ioc,
+ SocketAddress *addr,
+ Error **errp);
+
+/**
+ * qio_channel_socket_listen_async:
+ * @ioc: the socket channel object
+ * @addr: the address to listen to
+ * @callback: the function to invoke on completion
+ * @opaque: user data to pass to @callback
+ * @destroy: the function to free @opaque
+ *
+ * Attempt to listen to the address @addr. This method
+ * will run in the background so the caller will regain
+ * execution control immediately. The function @callback
+ * will be invoked on completion or failure.
+ */
+void qio_channel_socket_listen_async(QIOChannelSocket *ioc,
+ SocketAddress *addr,
+ QIOTaskFunc callback,
+ gpointer opaque,
+ GDestroyNotify destroy);
+
+
+/**
+ * qio_channel_socket_dgram_sync:
+ * @ioc: the socket channel object
+ * @localAddr: the address to local bind address
+ * @remoteAddr: the address to remote peer address
+ * @errp: pointer to an uninitialized error object
+ *
+ * Attempt to initialize a datagram socket bound to
+ * @localAddr and communicating with peer @remoteAddr.
+ * This method will run in the foreground so the caller
+ * will not regain execution control until the socket
+ * is established or an error occurs.
+ */
+int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc,
+ SocketAddress *localAddr,
+ SocketAddress *remoteAddr,
+ Error **errp);
+
+/**
+ * qio_channel_socket_dgram_async:
+ * @ioc: the socket channel object
+ * @localAddr: the address to local bind address
+ * @remoteAddr: the address to remote peer address
+ * @callback: the function to invoke on completion
+ * @opaque: user data to pass to @callback
+ * @destroy: the function to free @opaque
+ *
+ * Attempt to initialize a datagram socket bound to
+ * @localAddr and communicating with peer @remoteAddr.
+ * This method will run in the background so the caller
+ * will regain execution control immediately. The function
+ * @callback will be invoked on completion or failure.
+ */
+void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,
+ SocketAddress *localAddr,
+ SocketAddress *remoteAddr,
+ QIOTaskFunc callback,
+ gpointer opaque,
+ GDestroyNotify destroy);
+
+
+/**
+ * qio_channel_socket_get_local_address:
+ * @ioc: the socket channel object
+ * @errp: pointer to an uninitialized error object
+ *
+ * Get the string representation of the local socket
+ * address. A pointer to the allocated address information
+ * struct will be returned, which the caller is required to
+ * release with a call qapi_free_SocketAddress when no
+ * longer required.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+SocketAddress *
+qio_channel_socket_get_local_address(QIOChannelSocket *ioc,
+ Error **errp);
+
+/**
+ * qio_channel_socket_get_remote_address:
+ * @ioc: the socket channel object
+ * @errp: pointer to an uninitialized error object
+ *
+ * Get the string representation of the local socket
+ * address. A pointer to the allocated address information
+ * struct will be returned, which the caller is required to
+ * release with a call qapi_free_SocketAddress when no
+ * longer required.
+ *
+ * Returns: the socket address struct, or NULL on error
+ */
+SocketAddress *
+qio_channel_socket_get_remote_address(QIOChannelSocket *ioc,
+ Error **errp);
+
+
+/**
+ * qio_channel_socket_accept:
+ * @ioc: the socket channel object
+ * @errp: pointer to an uninitialized error object
+ *
+ * If the socket represents a server, then this accepts
+ * a new client connection. The returned channel will
+ * represent the connected client socket.
+ *
+ * Returns: the new client channel, or NULL on error
+ */
+QIOChannelSocket *
+qio_channel_socket_accept(QIOChannelSocket *ioc,
+ Error **errp);
+
+
+#endif /* QIO_CHANNEL_SOCKET_H__ */
diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h
new file mode 100644
index 0000000000..0298b1770e
--- /dev/null
+++ b/include/io/channel-tls.h
@@ -0,0 +1,142 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_TLS_H__
+#define QIO_CHANNEL_TLS_H__
+
+#include "io/channel.h"
+#include "io/task.h"
+#include "crypto/tlssession.h"
+
+#define TYPE_QIO_CHANNEL_TLS "qio-channel-tls"
+#define QIO_CHANNEL_TLS(obj) \
+ OBJECT_CHECK(QIOChannelTLS, (obj), TYPE_QIO_CHANNEL_TLS)
+
+typedef struct QIOChannelTLS QIOChannelTLS;
+
+/**
+ * QIOChannelTLS
+ *
+ * The QIOChannelTLS class provides a channel wrapper which
+ * can transparently run the TLS encryption protocol. It is
+ * usually used over a TCP socket, but there is actually no
+ * technical restriction on which type of master channel is
+ * used as the transport.
+ *
+ * This channel object is capable of running as either a
+ * TLS server or TLS client.
+ */
+
+struct QIOChannelTLS {
+ QIOChannel parent;
+ QIOChannel *master;
+ QCryptoTLSSession *session;
+};
+
+/**
+ * qio_channel_tls_new_server:
+ * @master: the underlying channel object
+ * @creds: the credentials to use for TLS handshake
+ * @aclname: the access control list for validating clients
+ * @errp: pointer to an uninitialized error object
+ *
+ * Create a new TLS channel that runs the server side of
+ * a TLS session. The TLS session handshake will use the
+ * credentials provided in @creds. If the @aclname parameter
+ * is non-NULL, then the client will have to provide
+ * credentials (ie a x509 client certificate) which will
+ * then be validated against the ACL.
+ *
+ * After creating the channel, it is mandatory to call
+ * the qio_channel_tls_handshake() method before attempting
+ * todo any I/O on the channel.
+ *
+ * Once the handshake has completed, all I/O should be done
+ * via the new TLS channel object and not the original
+ * master channel
+ *
+ * Returns: the new TLS channel object, or NULL
+ */
+QIOChannelTLS *
+qio_channel_tls_new_server(QIOChannel *master,
+ QCryptoTLSCreds *creds,
+ const char *aclname,
+ Error **errp);
+
+/**
+ * qio_channel_tls_new_client:
+ * @master: the underlying channel object
+ * @creds: the credentials to use for TLS handshake
+ * @hostname: the user specified server hostname
+ * @errp: pointer to an uninitialized error object
+ *
+ * Create a new TLS channel that runs the client side of
+ * a TLS session. The TLS session handshake will use the
+ * credentials provided in @creds. The @hostname parameter
+ * should provide the user specified hostname of the server
+ * and will be validated against the server's credentials
+ * (ie CommonName of the x509 certificate)
+ *
+ * After creating the channel, it is mandatory to call
+ * the qio_channel_tls_handshake() method before attempting
+ * todo any I/O on the channel.
+ *
+ * Once the handshake has completed, all I/O should be done
+ * via the new TLS channel object and not the original
+ * master channel
+ *
+ * Returns: the new TLS channel object, or NULL
+ */
+QIOChannelTLS *
+qio_channel_tls_new_client(QIOChannel *master,
+ QCryptoTLSCreds *creds,
+ const char *hostname,
+ Error **errp);
+
+/**
+ * qio_channel_tls_handshake:
+ * @ioc: the TLS channel object
+ * @func: the callback to invoke when completed
+ * @opaque: opaque data to pass to @func
+ * @destroy: optional callback to free @opaque
+ *
+ * Perform the TLS session handshake. This method
+ * will return immediately and the handshake will
+ * continue in the background, provided the main
+ * loop is running. When the handshake is complete,
+ * or fails, the @func callback will be invoked.
+ */
+void qio_channel_tls_handshake(QIOChannelTLS *ioc,
+ QIOTaskFunc func,
+ gpointer opaque,
+ GDestroyNotify destroy);
+
+/**
+ * qio_channel_tls_get_session:
+ * @ioc: the TLS channel object
+ *
+ * Get the TLS session used by the channel.
+ *
+ * Returns: the TLS session
+ */
+QCryptoTLSSession *
+qio_channel_tls_get_session(QIOChannelTLS *ioc);
+
+#endif /* QIO_CHANNEL_TLS_H__ */
diff --git a/include/io/channel-watch.h b/include/io/channel-watch.h
new file mode 100644
index 0000000000..656358ad64
--- /dev/null
+++ b/include/io/channel-watch.h
@@ -0,0 +1,72 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_WATCH_H__
+#define QIO_CHANNEL_WATCH_H__
+
+#include "io/channel.h"
+
+/*
+ * This module provides helper functions that will be needed by
+ * the various QIOChannel implementations, for creating watches
+ * on file descriptors / sockets
+ */
+
+/**
+ * qio_channel_create_fd_watch:
+ * @ioc: the channel object
+ * @fd: the file descriptor
+ * @condition: the I/O condition
+ *
+ * Create a new main loop source that is able to
+ * monitor the file descriptor @fd for the
+ * I/O conditions in @condition. This is able
+ * monitor block devices, character devices,
+ * sockets, pipes but not plain files.
+ *
+ * Returns: the new main loop source
+ */
+GSource *qio_channel_create_fd_watch(QIOChannel *ioc,
+ int fd,
+ GIOCondition condition);
+
+/**
+ * qio_channel_create_fd_pair_watch:
+ * @ioc: the channel object
+ * @fdread: the file descriptor for reading
+ * @fdwrite: the file descriptor for writing
+ * @condition: the I/O condition
+ *
+ * Create a new main loop source that is able to
+ * monitor the pair of file descriptors @fdread
+ * and @fdwrite for the I/O conditions in @condition.
+ * This is intended for monitoring unidirectional
+ * file descriptors such as pipes, where a pair
+ * of descriptors is required for bidirectional
+ * I/O
+ *
+ * Returns: the new main loop source
+ */
+GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc,
+ int fdread,
+ int fdwrite,
+ GIOCondition condition);
+
+#endif /* QIO_CHANNEL_WATCH_H__ */
diff --git a/include/io/channel-websock.h b/include/io/channel-websock.h
new file mode 100644
index 0000000000..0dc21cc56d
--- /dev/null
+++ b/include/io/channel-websock.h
@@ -0,0 +1,108 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_WEBSOCK_H__
+#define QIO_CHANNEL_WEBSOCK_H__
+
+#include "io/channel.h"
+#include "qemu/buffer.h"
+#include "io/task.h"
+
+#define TYPE_QIO_CHANNEL_WEBSOCK "qio-channel-websock"
+#define QIO_CHANNEL_WEBSOCK(obj) \
+ OBJECT_CHECK(QIOChannelWebsock, (obj), TYPE_QIO_CHANNEL_WEBSOCK)
+
+typedef struct QIOChannelWebsock QIOChannelWebsock;
+typedef union QIOChannelWebsockMask QIOChannelWebsockMask;
+
+union QIOChannelWebsockMask {
+ char c[4];
+ uint32_t u;
+};
+
+/**
+ * QIOChannelWebsock
+ *
+ * The QIOChannelWebsock class provides a channel wrapper which
+ * can transparently run the HTTP websockets protocol. This is
+ * usually used over a TCP socket, but there is actually no
+ * technical restriction on which type of master channel is
+ * used as the transport.
+ *
+ * This channel object is currently only capable of running as
+ * a websocket server and is a pretty crude implementation
+ * of it, not supporting the full websockets protocol feature
+ * set. It is sufficient to use with a simple websockets
+ * client for encapsulating VNC for noVNC in-browser client.
+ */
+
+struct QIOChannelWebsock {
+ QIOChannel parent;
+ QIOChannel *master;
+ Buffer encinput;
+ Buffer encoutput;
+ Buffer rawinput;
+ Buffer rawoutput;
+ size_t payload_remain;
+ QIOChannelWebsockMask mask;
+ guint io_tag;
+ Error *io_err;
+ gboolean io_eof;
+};
+
+/**
+ * qio_channel_websock_new_server:
+ * @master: the underlying channel object
+ *
+ * Create a new websockets channel that runs the server
+ * side of the protocol.
+ *
+ * After creating the channel, it is mandatory to call
+ * the qio_channel_websock_handshake() method before attempting
+ * todo any I/O on the channel.
+ *
+ * Once the handshake has completed, all I/O should be done
+ * via the new websocket channel object and not the original
+ * master channel
+ *
+ * Returns: the new websockets channel object
+ */
+QIOChannelWebsock *
+qio_channel_websock_new_server(QIOChannel *master);
+
+/**
+ * qio_channel_websock_handshake:
+ * @ioc: the websocket channel object
+ * @func: the callback to invoke when completed
+ * @opaque: opaque data to pass to @func
+ * @destroy: optional callback to free @opaque
+ *
+ * Perform the websocket handshake. This method
+ * will return immediately and the handshake will
+ * continue in the background, provided the main
+ * loop is running. When the handshake is complete,
+ * or fails, the @func callback will be invoked.
+ */
+void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
+ QIOTaskFunc func,
+ gpointer opaque,
+ GDestroyNotify destroy);
+
+#endif /* QIO_CHANNEL_WEBSOCK_H__ */
diff --git a/include/io/channel.h b/include/io/channel.h
new file mode 100644
index 0000000000..4ecec98bf4
--- /dev/null
+++ b/include/io/channel.h
@@ -0,0 +1,502 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_CHANNEL_H__
+#define QIO_CHANNEL_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+#define TYPE_QIO_CHANNEL "qio-channel"
+#define QIO_CHANNEL(obj) \
+ OBJECT_CHECK(QIOChannel, (obj), TYPE_QIO_CHANNEL)
+#define QIO_CHANNEL_CLASS(klass) \
+ OBJECT_CLASS_CHECK(QIOChannelClass, klass, TYPE_QIO_CHANNEL)
+#define QIO_CHANNEL_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(QIOChannelClass, obj, TYPE_QIO_CHANNEL)
+
+typedef struct QIOChannel QIOChannel;
+typedef struct QIOChannelClass QIOChannelClass;
+
+#define QIO_CHANNEL_ERR_BLOCK -2
+
+typedef enum QIOChannelFeature QIOChannelFeature;
+
+enum QIOChannelFeature {
+ QIO_CHANNEL_FEATURE_FD_PASS = (1 << 0),
+ QIO_CHANNEL_FEATURE_SHUTDOWN = (1 << 1),
+};
+
+
+typedef enum QIOChannelShutdown QIOChannelShutdown;
+
+enum QIOChannelShutdown {
+ QIO_CHANNEL_SHUTDOWN_BOTH,
+ QIO_CHANNEL_SHUTDOWN_READ,
+ QIO_CHANNEL_SHUTDOWN_WRITE,
+};
+
+typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc,
+ GIOCondition condition,
+ gpointer data);
+
+/**
+ * QIOChannel:
+ *
+ * The QIOChannel defines the core API for a generic I/O channel
+ * class hierarchy. It is inspired by GIOChannel, but has the
+ * following differences
+ *
+ * - Use QOM to properly support arbitrary subclassing
+ * - Support use of iovecs for efficient I/O with multiple blocks
+ * - None of the character set translation, binary data exclusively
+ * - Direct support for QEMU Error object reporting
+ * - File descriptor passing
+ *
+ * This base class is abstract so cannot be instantiated. There
+ * will be subclasses for dealing with sockets, files, and higher
+ * level protocols such as TLS, WebSocket, etc.
+ */
+
+struct QIOChannel {
+ Object parent;
+ unsigned int features; /* bitmask of QIOChannelFeatures */
+};
+
+/**
+ * QIOChannelClass:
+ *
+ * This class defines the contract that all subclasses
+ * must follow to provide specific channel implementations.
+ * The first five callbacks are mandatory to support, others
+ * provide additional optional features.
+ *
+ * Consult the corresponding public API docs for a description
+ * of the semantics of each callback
+ */
+struct QIOChannelClass {
+ ObjectClass parent;
+
+ /* Mandatory callbacks */
+ ssize_t (*io_writev)(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp);
+ ssize_t (*io_readv)(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp);
+ int (*io_close)(QIOChannel *ioc,
+ Error **errp);
+ GSource * (*io_create_watch)(QIOChannel *ioc,
+ GIOCondition condition);
+ int (*io_set_blocking)(QIOChannel *ioc,
+ bool enabled,
+ Error **errp);
+
+ /* Optional callbacks */
+ int (*io_shutdown)(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp);
+ void (*io_set_cork)(QIOChannel *ioc,
+ bool enabled);
+ void (*io_set_delay)(QIOChannel *ioc,
+ bool enabled);
+ off_t (*io_seek)(QIOChannel *ioc,
+ off_t offset,
+ int whence,
+ Error **errp);
+};
+
+/* General I/O handling functions */
+
+/**
+ * qio_channel_has_feature:
+ * @ioc: the channel object
+ * @feature: the feature to check support of
+ *
+ * Determine whether the channel implementation supports
+ * the optional feature named in @feature.
+ *
+ * Returns: true if supported, false otherwise.
+ */
+bool qio_channel_has_feature(QIOChannel *ioc,
+ QIOChannelFeature feature);
+
+/**
+ * qio_channel_readv_full:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to read data into
+ * @niov: the length of the @iov array
+ * @fds: pointer to an array that will received file handles
+ * @nfds: pointer filled with number of elements in @fds on return
+ * @errp: pointer to an uninitialized error object
+ *
+ * Read data from the IO channel, storing it in the
+ * memory regions referenced by @iov. Each element
+ * in the @iov will be fully populated with data
+ * before the next one is used. The @niov parameter
+ * specifies the total number of elements in @iov.
+ *
+ * It is not required for all @iov to be filled with
+ * data. If the channel is in blocking mode, at least
+ * one byte of data will be read, but no more is
+ * guaranteed. If the channel is non-blocking and no
+ * data is available, it will return QIO_CHANNEL_ERR_BLOCK
+ *
+ * If the channel has passed any file descriptors,
+ * the @fds array pointer will be allocated and
+ * the elements filled with the received file
+ * descriptors. The @nfds pointer will be updated
+ * to indicate the size of the @fds array that
+ * was allocated. It is the callers responsibility
+ * to call close() on each file descriptor and to
+ * call g_free() on the array pointer in @fds.
+ *
+ * It is an error to pass a non-NULL @fds parameter
+ * unless qio_channel_has_feature() returns a true
+ * value for the QIO_CHANNEL_FEATURE_FD_PASS constant.
+ *
+ * Returns: the number of bytes read, or -1 on error,
+ * or QIO_CHANNEL_ERR_BLOCK if no data is available
+ * and the channel is non-blocking
+ */
+ssize_t qio_channel_readv_full(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int **fds,
+ size_t *nfds,
+ Error **errp);
+
+
+/**
+ * qio_channel_writev_full:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to write data from
+ * @niov: the length of the @iov array
+ * @fds: an array of file handles to send
+ * @nfds: number of file handles in @fds
+ * @errp: pointer to an uninitialized error object
+ *
+ * Write data to the IO channel, reading it from the
+ * memory regions referenced by @iov. Each element
+ * in the @iov will be fully sent, before the next
+ * one is used. The @niov parameter specifies the
+ * total number of elements in @iov.
+ *
+ * It is not required for all @iov data to be fully
+ * sent. If the channel is in blocking mode, at least
+ * one byte of data will be sent, but no more is
+ * guaranteed. If the channel is non-blocking and no
+ * data can be sent, it will return QIO_CHANNEL_ERR_BLOCK
+ *
+ * If there are file descriptors to send, the @fds
+ * array should be non-NULL and provide the handles.
+ * All file descriptors will be sent if at least one
+ * byte of data was sent.
+ *
+ * It is an error to pass a non-NULL @fds parameter
+ * unless qio_channel_has_feature() returns a true
+ * value for the QIO_CHANNEL_FEATURE_FD_PASS constant.
+ *
+ * Returns: the number of bytes sent, or -1 on error,
+ * or QIO_CHANNEL_ERR_BLOCK if no data is can be sent
+ * and the channel is non-blocking
+ */
+ssize_t qio_channel_writev_full(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ int *fds,
+ size_t nfds,
+ Error **errp);
+
+/**
+ * qio_channel_readv:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to read data into
+ * @niov: the length of the @iov array
+ * @errp: pointer to an uninitialized error object
+ *
+ * Behaves as qio_channel_readv_full() but does not support
+ * receiving of file handles.
+ */
+ssize_t qio_channel_readv(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ Error **errp);
+
+/**
+ * qio_channel_writev:
+ * @ioc: the channel object
+ * @iov: the array of memory regions to write data from
+ * @niov: the length of the @iov array
+ * @errp: pointer to an uninitialized error object
+ *
+ * Behaves as qio_channel_writev_full() but does not support
+ * sending of file handles.
+ */
+ssize_t qio_channel_writev(QIOChannel *ioc,
+ const struct iovec *iov,
+ size_t niov,
+ Error **errp);
+
+/**
+ * qio_channel_readv:
+ * @ioc: the channel object
+ * @buf: the memory region to read data into
+ * @buflen: the length of @buf
+ * @errp: pointer to an uninitialized error object
+ *
+ * Behaves as qio_channel_readv_full() but does not support
+ * receiving of file handles, and only supports reading into
+ * a single memory region.
+ */
+ssize_t qio_channel_read(QIOChannel *ioc,
+ char *buf,
+ size_t buflen,
+ Error **errp);
+
+/**
+ * qio_channel_writev:
+ * @ioc: the channel object
+ * @buf: the memory regions to send data from
+ * @buflen: the length of @buf
+ * @errp: pointer to an uninitialized error object
+ *
+ * Behaves as qio_channel_writev_full() but does not support
+ * sending of file handles, and only supports writing from a
+ * single memory region.
+ */
+ssize_t qio_channel_write(QIOChannel *ioc,
+ const char *buf,
+ size_t buflen,
+ Error **errp);
+
+/**
+ * qio_channel_set_blocking:
+ * @ioc: the channel object
+ * @enabled: the blocking flag state
+ * @errp: pointer to an uninitialized error object
+ *
+ * If @enabled is true, then the channel is put into
+ * blocking mode, otherwise it will be non-blocking.
+ *
+ * In non-blocking mode, read/write operations may
+ * return QIO_CHANNEL_ERR_BLOCK if they would otherwise
+ * block on I/O
+ */
+int qio_channel_set_blocking(QIOChannel *ioc,
+ bool enabled,
+ Error **errp);
+
+/**
+ * qio_channel_close:
+ * @ioc: the channel object
+ * @errp: pointer to an uninitialized error object
+ *
+ * Close the channel, flushing any pending I/O
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qio_channel_close(QIOChannel *ioc,
+ Error **errp);
+
+/**
+ * qio_channel_shutdown:
+ * @ioc: the channel object
+ * @how: the direction to shutdown
+ * @errp: pointer to an uninitialized error object
+ *
+ * Shutdowns transmission and/or receiving of data
+ * without closing the underlying transport.
+ *
+ * Not all implementations will support this facility,
+ * so may report an error. To avoid errors, the
+ * caller may check for the feature flag
+ * QIO_CHANNEL_FEATURE_SHUTDOWN prior to calling
+ * this method.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int qio_channel_shutdown(QIOChannel *ioc,
+ QIOChannelShutdown how,
+ Error **errp);
+
+/**
+ * qio_channel_set_delay:
+ * @ioc: the channel object
+ * @enabled: the new flag state
+ *
+ * Controls whether the underlying transport is
+ * permitted to delay writes in order to merge
+ * small packets. If @enabled is true, then the
+ * writes may be delayed in order to opportunistically
+ * merge small packets into larger ones. If @enabled
+ * is false, writes are dispatched immediately with
+ * no delay.
+ *
+ * When @enabled is false, applications may wish to
+ * use the qio_channel_set_cork() method to explicitly
+ * control write merging.
+ *
+ * On channels which are backed by a socket, this
+ * API corresponds to the inverse of TCP_NODELAY flag,
+ * controlling whether the Nagle algorithm is active.
+ *
+ * This setting is merely a hint, so implementations are
+ * free to ignore this without it being considered an
+ * error.
+ */
+void qio_channel_set_delay(QIOChannel *ioc,
+ bool enabled);
+
+/**
+ * qio_channel_set_cork:
+ * @ioc: the channel object
+ * @enabled: the new flag state
+ *
+ * Controls whether the underlying transport is
+ * permitted to dispatch data that is written.
+ * If @enabled is true, then any data written will
+ * be queued in local buffers until @enabled is
+ * set to false once again.
+ *
+ * This feature is typically used when the automatic
+ * write coalescing facility is disabled via the
+ * qio_channel_set_delay() method.
+ *
+ * On channels which are backed by a socket, this
+ * API corresponds to the TCP_CORK flag.
+ *
+ * This setting is merely a hint, so implementations are
+ * free to ignore this without it being considered an
+ * error.
+ */
+void qio_channel_set_cork(QIOChannel *ioc,
+ bool enabled);
+
+
+/**
+ * qio_channel_seek:
+ * @ioc: the channel object
+ * @offset: the position to seek to, relative to @whence
+ * @whence: one of the (POSIX) SEEK_* constants listed below
+ * @errp: pointer to an uninitialized error object
+ *
+ * Moves the current I/O position within the channel
+ * @ioc, to be @offset. The value of @offset is
+ * interpreted relative to @whence:
+ *
+ * SEEK_SET - the position is set to @offset bytes
+ * SEEK_CUR - the position is moved by @offset bytes
+ * SEEK_END - the position is set to end of the file plus @offset bytes
+ *
+ * Not all implementations will support this facility,
+ * so may report an error.
+ *
+ * Returns: the new position on success, (off_t)-1 on failure
+ */
+off_t qio_channel_io_seek(QIOChannel *ioc,
+ off_t offset,
+ int whence,
+ Error **errp);
+
+
+/**
+ * qio_channel_create_watch:
+ * @ioc: the channel object
+ * @condition: the I/O condition to monitor
+ *
+ * Create a new main loop source that is used to watch
+ * for the I/O condition @condition. Typically the
+ * qio_channel_add_watch() method would be used instead
+ * of this, since it directly attaches a callback to
+ * the source
+ *
+ * Returns: the new main loop source.
+ */
+GSource *qio_channel_create_watch(QIOChannel *ioc,
+ GIOCondition condition);
+
+/**
+ * qio_channel_add_watch:
+ * @ioc: the channel object
+ * @condition: the I/O condition to monitor
+ * @func: callback to invoke when the source becomes ready
+ * @user_data: opaque data to pass to @func
+ * @notify: callback to free @user_data
+ *
+ * Create a new main loop source that is used to watch
+ * for the I/O condition @condition. The callback @func
+ * will be registered against the source, to be invoked
+ * when the source becomes ready. The optional @user_data
+ * will be passed to @func when it is invoked. The @notify
+ * callback will be used to free @user_data when the
+ * watch is deleted
+ *
+ * The returned source ID can be used with g_source_remove()
+ * to remove and free the source when no longer required.
+ * Alternatively the @func callback can return a FALSE
+ * value.
+ *
+ * Returns: the source ID
+ */
+guint qio_channel_add_watch(QIOChannel *ioc,
+ GIOCondition condition,
+ QIOChannelFunc func,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+
+/**
+ * qio_channel_yield:
+ * @ioc: the channel object
+ * @condition: the I/O condition to wait for
+ *
+ * Yields execution from the current coroutine until
+ * the condition indicated by @condition becomes
+ * available.
+ *
+ * This must only be called from coroutine context
+ */
+void qio_channel_yield(QIOChannel *ioc,
+ GIOCondition condition);
+
+/**
+ * qio_channel_wait:
+ * @ioc: the channel object
+ * @condition: the I/O condition to wait for
+ *
+ * Block execution from the current thread until
+ * the condition indicated by @condition becomes
+ * available.
+ *
+ * This will enter a nested event loop to perform
+ * the wait.
+ */
+void qio_channel_wait(QIOChannel *ioc,
+ GIOCondition condition);
+
+#endif /* QIO_CHANNEL_H__ */
diff --git a/include/io/task.h b/include/io/task.h
new file mode 100644
index 0000000000..2418714156
--- /dev/null
+++ b/include/io/task.h
@@ -0,0 +1,256 @@
+/*
+ * 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/>.
+ *
+ */
+
+#ifndef QIO_TASK_H__
+#define QIO_TASK_H__
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qom/object.h"
+
+typedef struct QIOTask QIOTask;
+
+typedef void (*QIOTaskFunc)(Object *source,
+ Error *err,
+ gpointer opaque);
+
+typedef int (*QIOTaskWorker)(QIOTask *task,
+ Error **errp,
+ gpointer opaque);
+
+/**
+ * QIOTask:
+ *
+ * The QIOTask object provides a simple mechanism for reporting
+ * success / failure of long running background operations.
+ *
+ * A object on which the operation is to be performed could have
+ * a public API which accepts a task callback:
+ *
+ * <example>
+ * <title>Task callback function signature</title>
+ * <programlisting>
+ * void myobject_operation(QMyObject *obj,
+ * QIOTaskFunc *func,
+ * gpointer opaque,
+ * GDestroyNotify *notify);
+ * </programlisting>
+ * </example>
+ *
+ * The 'func' parameter is the callback to be invoked, and 'opaque'
+ * is data to pass to it. The optional 'notify' function is used
+ * to free 'opaque' when no longer needed.
+ *
+ * Now, lets say the implementation of this method wants to set
+ * a timer to run once a second checking for completion of some
+ * activity. It would do something like
+ *
+ * <example>
+ * <title>Task callback function implementation</title>
+ * <programlisting>
+ * void myobject_operation(QMyObject *obj,
+ * QIOTaskFunc *func,
+ * gpointer opaque,
+ * GDestroyNotify *notify)
+ * {
+ * QIOTask *task;
+ *
+ * task = qio_task_new(OBJECT(obj), func, opaque, notify);
+ *
+ * g_timeout_add_full(G_PRIORITY_DEFAULT,
+ * 1000,
+ * myobject_operation_timer,
+ * task,
+ * NULL);
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * It could equally have setup a watch on a file descriptor or
+ * created a background thread, or something else entirely.
+ * Notice that the source object is passed to the task, and
+ * QIOTask will hold a reference on that. This ensure that
+ * the QMyObject instance cannot be garbage collected while
+ * the async task is still in progress.
+ *
+ * In this case, myobject_operation_timer will fire after
+ * 3 secs and do
+ *
+ * <example>
+ * <title>Task timer function</title>
+ * <programlisting>
+ * gboolean myobject_operation_timer(gpointer opaque)
+ * {
+ * QIOTask *task = QIO_TASK(opaque);
+ * Error *err;*
+ *
+ * ...check something important...
+ * if (err) {
+ * qio_task_abort(task, err);
+ * error_free(task);
+ * return FALSE;
+ * } else if (...work is completed ...) {
+ * qio_task_complete(task);
+ * return FALSE;
+ * }
+ * ...carry on polling ...
+ * return TRUE;
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * Once this function returns false, object_unref will be called
+ * automatically on the task causing it to be released and the
+ * ref on QMyObject dropped too.
+ *
+ * The QIOTask module can also be used to perform operations
+ * in a background thread context, while still reporting the
+ * results in the main event thread. This allows code which
+ * cannot easily be rewritten to be asychronous (such as DNS
+ * lookups) to be easily run non-blocking. Reporting the
+ * results in the main thread context means that the caller
+ * typically does not need to be concerned about thread
+ * safety wrt the QEMU global mutex.
+ *
+ * For example, the socket_listen() method will block the caller
+ * while DNS lookups take place if given a name, instead of IP
+ * address. The C library often do not provide a practical async
+ * DNS API, so the to get non-blocking DNS lookups in a portable
+ * manner requires use of a thread. So achieve a non-blocking
+ * socket listen using QIOTask would require:
+ *
+ * <example>
+ * static int myobject_listen_worker(QIOTask *task,
+ * Error **errp,
+ * gpointer opaque)
+ * {
+ * QMyObject obj = QMY_OBJECT(qio_task_get_source(task));
+ * SocketAddress *addr = opaque;
+ *
+ * obj->fd = socket_listen(addr, errp);
+ * if (obj->fd < 0) {
+ * return -1;
+ * }
+ * return 0;
+ * }
+ *
+ * void myobject_listen_async(QMyObject *obj,
+ * SocketAddress *addr,
+ * QIOTaskFunc *func,
+ * gpointer opaque,
+ * GDestroyNotify *notify)
+ * {
+ * QIOTask *task;
+ * SocketAddress *addrCopy;
+ *
+ * qapi_copy_SocketAddress(&addrCopy, addr);
+ * task = qio_task_new(OBJECT(obj), func, opaque, notify);
+ *
+ * qio_task_run_in_thread(task, myobject_listen_worker,
+ * addrCopy,
+ * qapi_free_SocketAddress);
+ * }
+ * </example>
+ *
+ * NB, The 'func' callback passed into myobject_listen_async
+ * will be invoked from the main event thread, despite the
+ * actual operation being performed in a different thread.
+ */
+
+/**
+ * qio_task_new:
+ * @source: the object on which the operation is invoked
+ * @func: the callback to invoke when the task completes
+ * @opaque: opaque data to pass to @func when invoked
+ * @destroy: optional callback to free @opaque
+ *
+ * Creates a new task struct to track completion of a
+ * background operation running on the object @source.
+ * When the operation completes or fails, the callback
+ * @func will be invoked. The callback can access the
+ * 'err' attribute in the task object to determine if
+ * the operation was successful or not.
+ *
+ * The returned task will be released when one of
+ * qio_task_abort() or qio_task_complete() are invoked.
+ *
+ * Returns: the task struct
+ */
+QIOTask *qio_task_new(Object *source,
+ QIOTaskFunc func,
+ gpointer opaque,
+ GDestroyNotify destroy);
+
+/**
+ * qio_task_run_in_thread:
+ * @task: the task struct
+ * @worker: the function to invoke in a thread
+ * @opaque: opaque data to pass to @worker
+ * @destroy: function to free @opaque
+ *
+ * Run a task in a background thread. If @worker
+ * returns 0 it will call qio_task_complete() in
+ * the main event thread context. If @worker
+ * returns -1 it will call qio_task_abort() in
+ * the main event thread context.
+ */
+void qio_task_run_in_thread(QIOTask *task,
+ QIOTaskWorker worker,
+ gpointer opaque,
+ GDestroyNotify destroy);
+
+/**
+ * qio_task_complete:
+ * @task: the task struct
+ *
+ * Mark the operation as succesfully completed
+ * and free the memory for @task.
+ */
+void qio_task_complete(QIOTask *task);
+
+/**
+ * qio_task_abort:
+ * @task: the task struct
+ * @err: the error to record for the operation
+ *
+ * Mark the operation as failed, with @err providing
+ * details about the failure. The @err may be freed
+ * afer the function returns, as the notification
+ * callback is invoked synchronously. The @task will
+ * be freed when this call completes.
+ */
+void qio_task_abort(QIOTask *task,
+ Error *err);
+
+
+/**
+ * qio_task_get_source:
+ * @task: the task struct
+ *
+ * Get the source object associated with the background
+ * task. This returns a new reference to the object,
+ * which the caller must released with object_unref()
+ * when no longer required.
+ *
+ * Returns: the source object
+ */
+Object *qio_task_get_source(QIOTask *task);
+
+#endif /* QIO_TASK_H__ */
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 5a183c570d..74c692d432 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -89,6 +89,25 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str);
int socket_init(void);
/**
+ * socket_sockaddr_to_address:
+ * @sa: socket address struct
+ * @salen: size of @sa struct
+ * @errp: pointer to uninitialized error object
+ *
+ * Get the string representation of the socket
+ * address. A pointer to the allocated address information
+ * struct will be returned, which the caller is required to
+ * release with a call qapi_free_SocketAddress when no
+ * longer required.
+ *
+ * Returns: the socket address struct, or NULL on error
+ */
+SocketAddress *
+socket_sockaddr_to_address(struct sockaddr_storage *sa,
+ socklen_t salen,
+ Error **errp);
+
+/**
* socket_local_address:
* @fd: the socket file handle
* @errp: pointer to uninitialized error object
diff --git a/io/Makefile.objs b/io/Makefile.objs
new file mode 100644
index 0000000000..0e3de31a5a
--- /dev/null
+++ b/io/Makefile.objs
@@ -0,0 +1,9 @@
+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 += task.o
diff --git a/io/channel-buffer.c b/io/channel-buffer.c
new file mode 100644
index 0000000000..daebc92bd8
--- /dev/null
+++ b/io/channel-buffer.c
@@ -0,0 +1,248 @@
+/*
+ * 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 "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(char, 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/io/channel-command.c b/io/channel-command.c
new file mode 100644
index 0000000000..598fdab5a3
--- /dev/null
+++ b/io/channel-command.c
@@ -0,0 +1,357 @@
+/*
+ * 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 "io/channel-command.h"
+#include "io/channel-watch.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) {
+ 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]);
+ }
+
+ 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 (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;
+ }
+ 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);
+ ioc->readfd = -1;
+ }
+ if (ioc->writefd != -1) {
+ close(ioc->writefd);
+ ioc->writefd = -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 ||
+ errno == EWOULDBLOCK) {
+ 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 ||
+ errno == EWOULDBLOCK) {
+ 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 (close(cioc->writefd) < 0) {
+ rv = -1;
+ }
+ if (close(cioc->readfd) < 0) {
+ rv = -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/io/channel-file.c b/io/channel-file.c
new file mode 100644
index 0000000000..13609005f9
--- /dev/null
+++ b/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 "io/channel-file.h"
+#include "io/channel-watch.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 ||
+ errno == EWOULDBLOCK) {
+ 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 ||
+ errno == EWOULDBLOCK) {
+ 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/io/channel-socket.c b/io/channel-socket.c
new file mode 100644
index 0000000000..90b3c73358
--- /dev/null
+++ b/io/channel-socket.c
@@ -0,0 +1,741 @@
+/*
+ * 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 "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);
+
+ 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 (socket_error() == ENOTCONN) {
+ memset(&sioc->remoteAddr, 0, sizeof(sioc->remoteAddr));
+ sioc->remoteAddrLen = sizeof(sioc->remoteAddr);
+ } else {
+ error_setg_errno(errp, socket_error(),
+ "Unable to query remote socket address");
+ goto error;
+ }
+ }
+
+ if (getsockname(fd, (struct sockaddr *)&sioc->localAddr,
+ &sioc->localAddrLen) < 0) {
+ error_setg_errno(errp, socket_error(),
+ "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(localAddr, remoteAddr, 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);
+
+ retry:
+ trace_qio_channel_socket_accept(ioc);
+ cioc->fd = accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr,
+ &cioc->remoteAddrLen);
+ if (cioc->fd < 0) {
+ trace_qio_channel_socket_accept_fail(ioc);
+ if (socket_error() == EINTR) {
+ goto retry;
+ }
+ goto error;
+ }
+
+ if (getsockname(cioc->fd, (struct sockaddr *)&ioc->localAddr,
+ &ioc->localAddrLen) < 0) {
+ error_setg_errno(errp, socket_error(),
+ "Unable to query local socket address");
+ goto error;
+ }
+
+ 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) {
+ close(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;
+
+#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 (socket_error() == EAGAIN ||
+ socket_error() == EWOULDBLOCK) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ if (socket_error() == EINTR) {
+ goto retry;
+ }
+
+ error_setg_errno(errp, socket_error(),
+ "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, };
+
+ msg.msg_iov = (struct iovec *)iov;
+ msg.msg_iovlen = niov;
+
+ if (nfds) {
+ char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)];
+ size_t fdsize = sizeof(int) * nfds;
+ struct cmsghdr *cmsg;
+
+ 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 (socket_error() == EAGAIN ||
+ socket_error() == EWOULDBLOCK) {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ if (socket_error() == EINTR) {
+ goto retry;
+ }
+ error_setg_errno(errp, socket_error(),
+ "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 (socket_error() == EAGAIN) {
+ if (done) {
+ return done;
+ } else {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ } else if (socket_error() == EINTR) {
+ goto retry;
+ } else {
+ error_setg_errno(errp, socket_error(),
+ "Unable to write to 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 (socket_error() == EAGAIN) {
+ if (done) {
+ return done;
+ } else {
+ return QIO_CHANNEL_ERR_BLOCK;
+ }
+ } else if (socket_error() == EINTR) {
+ goto retry;
+ } else {
+ error_setg_errno(errp, socket_error(),
+ "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);
+ }
+ 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 (closesocket(sioc->fd) < 0) {
+ sioc->fd = -1;
+ error_setg_errno(errp, socket_error(),
+ "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, socket_error(),
+ "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_fd_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/io/channel-tls.c b/io/channel-tls.c
new file mode 100644
index 0000000000..8ac4f76f78
--- /dev/null
+++ b/io/channel-tls.c
@@ -0,0 +1,393 @@
+/*
+ * 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 "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/io/channel-watch.c b/io/channel-watch.c
new file mode 100644
index 0000000000..2f745f16c4
--- /dev/null
+++ b/io/channel-watch.c
@@ -0,0 +1,198 @@
+/*
+ * 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 "io/channel-watch.h"
+
+typedef struct QIOChannelFDSource QIOChannelFDSource;
+struct QIOChannelFDSource {
+ GSource parent;
+ GPollFD fd;
+ QIOChannel *ioc;
+ GIOCondition condition;
+};
+
+
+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));
+}
+
+
+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;
+
+ ssource->fd.fd = fd;
+ ssource->fd.events = condition;
+
+ g_source_add_poll(source, &ssource->fd);
+
+ return source;
+}
+
+
+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;
+
+ ssource->fdread.fd = fdread;
+ ssource->fdread.events = condition & G_IO_IN;
+
+ ssource->fdwrite.fd = fdwrite;
+ 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/io/channel-websock.c b/io/channel-websock.c
new file mode 100644
index 0000000000..9273a8b31e
--- /dev/null
+++ b/io/channel-websock.c
@@ -0,0 +1,962 @@
+/*
+ * 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 "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/io/channel.c b/io/channel.c
new file mode 100644
index 0000000000..5e94469de2
--- /dev/null
+++ b/io/channel.c
@@ -0,0 +1,291 @@
+/*
+ * 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 "io/channel.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);
+}
+
+
+static const TypeInfo qio_channel_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QIO_CHANNEL,
+ .instance_size = sizeof(QIOChannel),
+ .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/io/task.c b/io/task.c
new file mode 100644
index 0000000000..3127fca771
--- /dev/null
+++ b/io/task.c
@@ -0,0 +1,159 @@
+/*
+ * 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 "io/task.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;
+}
diff --git a/scripts/create_config b/scripts/create_config
index 546f889144..9cb176f1ba 100755
--- a/scripts/create_config
+++ b/scripts/create_config
@@ -61,6 +61,15 @@ case $line in
value=${line#*=}
echo "#define $name $value"
;;
+ HAVE_*=y) # configuration
+ name=${line%=*}
+ echo "#define $name 1"
+ ;;
+ HAVE_*=*) # configuration
+ name=${line%=*}
+ value=${line#*=}
+ echo "#define $name $value"
+ ;;
ARCH=*) # configuration
arch=${line#*=}
arch_name=`echo $arch | LC_ALL=C tr '[a-z]' '[A-Z]'`
diff --git a/tests/.gitignore b/tests/.gitignore
index 1e55722b6a..77aaba6c2f 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -24,6 +24,14 @@ test-cutils
test-hbitmap
test-int128
test-iov
+test-io-channel-buffer
+test-io-channel-command
+test-io-channel-command.fifo
+test-io-channel-file
+test-io-channel-file.txt
+test-io-channel-socket
+test-io-channel-tls
+test-io-task
test-mul64
test-opts-visitor
test-qapi-event.[ch]
diff --git a/tests/Makefile b/tests/Makefile
index 053c1ae481..6ff4627d0c 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -84,6 +84,12 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF)
check-unit-y += tests/test-timed-average$(EXESUF)
+check-unit-y += tests/test-io-task$(EXESUF)
+check-unit-y += tests/test-io-channel-socket$(EXESUF)
+check-unit-y += tests/test-io-channel-file$(EXESUF)
+check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF)
+check-unit-y += tests/test-io-channel-command$(EXESUF)
+check-unit-y += tests/test-io-channel-buffer$(EXESUF)
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -381,6 +387,7 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
$(test-qom-obj-y)
test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y)
+test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y)
tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
@@ -469,6 +476,18 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \
tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS)
tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y)
+tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
+tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
+ tests/io-channel-helpers.o $(test-io-obj-y)
+tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
+ tests/io-channel-helpers.o $(test-io-obj-y)
+tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \
+ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \
+ tests/io-channel-helpers.o $(test-io-obj-y)
+tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \
+ tests/io-channel-helpers.o $(test-io-obj-y)
+tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \
+ tests/io-channel-helpers.o $(test-io-obj-y)
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
diff --git a/tests/io-channel-helpers.c b/tests/io-channel-helpers.c
new file mode 100644
index 0000000000..78d36dd703
--- /dev/null
+++ b/tests/io-channel-helpers.c
@@ -0,0 +1,246 @@
+/*
+ * QEMU I/O channel test helpers
+ *
+ * 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 "io-channel-helpers.h"
+
+struct QIOChannelTest {
+ QIOChannel *src;
+ QIOChannel *dst;
+ bool blocking;
+ size_t len;
+ size_t niov;
+ char *input;
+ struct iovec *inputv;
+ char *output;
+ struct iovec *outputv;
+ Error *writeerr;
+ Error *readerr;
+};
+
+
+static void test_skip_iovec(struct iovec **iov,
+ size_t *niov,
+ size_t skip,
+ struct iovec *old)
+{
+ size_t offset = 0;
+ size_t i;
+
+ for (i = 0; i < *niov; i++) {
+ if (skip < (*iov)[i].iov_len) {
+ old->iov_len = (*iov)[i].iov_len;
+ old->iov_base = (*iov)[i].iov_base;
+
+ (*iov)[i].iov_len -= skip;
+ (*iov)[i].iov_base += skip;
+ break;
+ } else {
+ skip -= (*iov)[i].iov_len;
+
+ if (i == 0 && old->iov_base) {
+ (*iov)[i].iov_len = old->iov_len;
+ (*iov)[i].iov_base = old->iov_base;
+ old->iov_len = 0;
+ old->iov_base = NULL;
+ }
+
+ offset++;
+ }
+ }
+
+ *iov = *iov + offset;
+ *niov -= offset;
+}
+
+
+/* This thread sends all data using iovecs */
+static gpointer test_io_thread_writer(gpointer opaque)
+{
+ QIOChannelTest *data = opaque;
+ struct iovec *iov = data->inputv;
+ size_t niov = data->niov;
+ struct iovec old = { 0 };
+
+ qio_channel_set_blocking(data->src, data->blocking, NULL);
+
+ while (niov) {
+ ssize_t ret;
+ ret = qio_channel_writev(data->src,
+ iov,
+ niov,
+ &data->writeerr);
+ if (ret == QIO_CHANNEL_ERR_BLOCK) {
+ if (data->blocking) {
+ error_setg(&data->writeerr,
+ "Unexpected I/O blocking");
+ break;
+ } else {
+ qio_channel_wait(data->src,
+ G_IO_OUT);
+ continue;
+ }
+ } else if (ret < 0) {
+ break;
+ } else if (ret == 0) {
+ error_setg(&data->writeerr,
+ "Unexpected zero length write");
+ break;
+ }
+
+ test_skip_iovec(&iov, &niov, ret, &old);
+ }
+
+ return NULL;
+}
+
+
+/* This thread receives all data using iovecs */
+static gpointer test_io_thread_reader(gpointer opaque)
+{
+ QIOChannelTest *data = opaque;
+ struct iovec *iov = data->outputv;
+ size_t niov = data->niov;
+ struct iovec old = { 0 };
+
+ qio_channel_set_blocking(data->dst, data->blocking, NULL);
+
+ while (niov) {
+ ssize_t ret;
+
+ ret = qio_channel_readv(data->dst,
+ iov,
+ niov,
+ &data->readerr);
+
+ if (ret == QIO_CHANNEL_ERR_BLOCK) {
+ if (data->blocking) {
+ error_setg(&data->writeerr,
+ "Unexpected I/O blocking");
+ break;
+ } else {
+ qio_channel_wait(data->dst,
+ G_IO_IN);
+ continue;
+ }
+ } else if (ret < 0) {
+ break;
+ } else if (ret == 0) {
+ break;
+ }
+
+ test_skip_iovec(&iov, &niov, ret, &old);
+ }
+
+ return NULL;
+}
+
+
+QIOChannelTest *qio_channel_test_new(void)
+{
+ QIOChannelTest *data = g_new0(QIOChannelTest, 1);
+ size_t i;
+ size_t offset;
+
+
+ /* We'll send 1 MB of data */
+#define CHUNK_COUNT 250
+#define CHUNK_LEN 4194
+
+ data->len = CHUNK_COUNT * CHUNK_LEN;
+ data->input = g_new0(char, data->len);
+ data->output = g_new0(gchar, data->len);
+
+ /* Fill input with a pattern */
+ for (i = 0; i < data->len; i += CHUNK_LEN) {
+ memset(data->input + i, (i / CHUNK_LEN), CHUNK_LEN);
+ }
+
+ /* We'll split the data across a bunch of IO vecs */
+ data->niov = CHUNK_COUNT;
+ data->inputv = g_new0(struct iovec, data->niov);
+ data->outputv = g_new0(struct iovec, data->niov);
+
+ for (i = 0, offset = 0; i < data->niov; i++, offset += CHUNK_LEN) {
+ data->inputv[i].iov_base = data->input + offset;
+ data->outputv[i].iov_base = data->output + offset;
+ data->inputv[i].iov_len = CHUNK_LEN;
+ data->outputv[i].iov_len = CHUNK_LEN;
+ }
+
+ return data;
+}
+
+void qio_channel_test_run_threads(QIOChannelTest *test,
+ bool blocking,
+ QIOChannel *src,
+ QIOChannel *dst)
+{
+ GThread *reader, *writer;
+
+ test->src = src;
+ test->dst = dst;
+ test->blocking = blocking;
+
+ reader = g_thread_new("reader",
+ test_io_thread_reader,
+ test);
+ writer = g_thread_new("writer",
+ test_io_thread_writer,
+ test);
+
+ g_thread_join(reader);
+ g_thread_join(writer);
+
+ test->dst = test->src = NULL;
+}
+
+
+void qio_channel_test_run_writer(QIOChannelTest *test,
+ QIOChannel *src)
+{
+ test->src = src;
+ test_io_thread_writer(test);
+ test->src = NULL;
+}
+
+
+void qio_channel_test_run_reader(QIOChannelTest *test,
+ QIOChannel *dst)
+{
+ test->dst = dst;
+ test_io_thread_reader(test);
+ test->dst = NULL;
+}
+
+
+void qio_channel_test_validate(QIOChannelTest *test)
+{
+ g_assert_cmpint(memcmp(test->input,
+ test->output,
+ test->len), ==, 0);
+ g_assert(test->readerr == NULL);
+ g_assert(test->writeerr == NULL);
+
+ g_free(test->inputv);
+ g_free(test->outputv);
+ g_free(test->input);
+ g_free(test->output);
+ g_free(test);
+}
diff --git a/tests/io-channel-helpers.h b/tests/io-channel-helpers.h
new file mode 100644
index 0000000000..fedc64fd5a
--- /dev/null
+++ b/tests/io-channel-helpers.h
@@ -0,0 +1,42 @@
+/*
+ * QEMU I/O channel test helpers
+ *
+ * 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 "io/channel.h"
+
+#ifndef TEST_IO_CHANNEL_HELPERS
+#define TEST_IO_CHANNEL_HELPERS
+
+typedef struct QIOChannelTest QIOChannelTest;
+
+QIOChannelTest *qio_channel_test_new(void);
+
+void qio_channel_test_run_threads(QIOChannelTest *test,
+ bool blocking,
+ QIOChannel *src,
+ QIOChannel *dst);
+
+void qio_channel_test_run_writer(QIOChannelTest *test,
+ QIOChannel *src);
+void qio_channel_test_run_reader(QIOChannelTest *test,
+ QIOChannel *dst);
+
+void qio_channel_test_validate(QIOChannelTest *test);
+
+#endif /* TEST_IO_CHANNEL_HELPERS */
diff --git a/tests/test-io-channel-buffer.c b/tests/test-io-channel-buffer.c
new file mode 100644
index 0000000000..6637501b41
--- /dev/null
+++ b/tests/test-io-channel-buffer.c
@@ -0,0 +1,50 @@
+/*
+ * QEMU I/O channel buffer test
+ *
+ * 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 "io/channel-buffer.h"
+#include "io-channel-helpers.h"
+
+
+static void test_io_channel_buf(void)
+{
+ QIOChannelBuffer *buf;
+ QIOChannelTest *test;
+
+ buf = qio_channel_buffer_new(0);
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_writer(test, QIO_CHANNEL(buf));
+ buf->offset = 0;
+ qio_channel_test_run_reader(test, QIO_CHANNEL(buf));
+ qio_channel_test_validate(test);
+
+ object_unref(OBJECT(buf));
+}
+
+
+int main(int argc, char **argv)
+{
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/io/channel/buf", test_io_channel_buf);
+ return g_test_run();
+}
diff --git a/tests/test-io-channel-command.c b/tests/test-io-channel-command.c
new file mode 100644
index 0000000000..03cac36a3f
--- /dev/null
+++ b/tests/test-io-channel-command.c
@@ -0,0 +1,129 @@
+/*
+ * QEMU I/O channel command test
+ *
+ * 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 "io/channel-command.h"
+#include "io-channel-helpers.h"
+
+#ifndef WIN32
+static void test_io_channel_command_fifo(bool async)
+{
+#define TEST_FIFO "tests/test-io-channel-command.fifo"
+ QIOChannel *src, *dst;
+ QIOChannelTest *test;
+ char *srcfifo = g_strdup_printf("PIPE:%s,wronly", TEST_FIFO);
+ char *dstfifo = g_strdup_printf("PIPE:%s,rdonly", TEST_FIFO);
+ const char *srcargv[] = {
+ "/bin/socat", "-", srcfifo, NULL,
+ };
+ const char *dstargv[] = {
+ "/bin/socat", dstfifo, "-", NULL,
+ };
+
+ unlink(TEST_FIFO);
+ if (access("/bin/socat", X_OK) < 0) {
+ return; /* Pretend success if socat is not present */
+ }
+ if (mkfifo(TEST_FIFO, 0600) < 0) {
+ abort();
+ }
+ src = QIO_CHANNEL(qio_channel_command_new_spawn(srcargv,
+ O_WRONLY,
+ &error_abort));
+ dst = QIO_CHANNEL(qio_channel_command_new_spawn(dstargv,
+ O_RDONLY,
+ &error_abort));
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, async, src, dst);
+ qio_channel_test_validate(test);
+
+ object_unref(OBJECT(src));
+ object_unref(OBJECT(dst));
+
+ g_free(srcfifo);
+ g_free(dstfifo);
+ unlink(TEST_FIFO);
+}
+
+
+static void test_io_channel_command_fifo_async(void)
+{
+ test_io_channel_command_fifo(true);
+}
+
+static void test_io_channel_command_fifo_sync(void)
+{
+ test_io_channel_command_fifo(false);
+}
+
+
+static void test_io_channel_command_echo(bool async)
+{
+ QIOChannel *ioc;
+ QIOChannelTest *test;
+ const char *socatargv[] = {
+ "/bin/socat", "-", "-", NULL,
+ };
+
+ if (access("/bin/socat", X_OK) < 0) {
+ return; /* Pretend success if socat is not present */
+ }
+
+ ioc = QIO_CHANNEL(qio_channel_command_new_spawn(socatargv,
+ O_RDWR,
+ &error_abort));
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, async, ioc, ioc);
+ qio_channel_test_validate(test);
+
+ object_unref(OBJECT(ioc));
+}
+
+
+static void test_io_channel_command_echo_async(void)
+{
+ test_io_channel_command_echo(true);
+}
+
+static void test_io_channel_command_echo_sync(void)
+{
+ test_io_channel_command_echo(false);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_init(&argc, &argv, NULL);
+
+#ifndef WIN32
+ g_test_add_func("/io/channel/command/fifo/sync",
+ test_io_channel_command_fifo_sync);
+ g_test_add_func("/io/channel/command/fifo/async",
+ test_io_channel_command_fifo_async);
+ g_test_add_func("/io/channel/command/echo/sync",
+ test_io_channel_command_echo_sync);
+ g_test_add_func("/io/channel/command/echo/async",
+ test_io_channel_command_echo_async);
+#endif
+
+ return g_test_run();
+}
diff --git a/tests/test-io-channel-file.c b/tests/test-io-channel-file.c
new file mode 100644
index 0000000000..f276a32de0
--- /dev/null
+++ b/tests/test-io-channel-file.c
@@ -0,0 +1,100 @@
+/*
+ * QEMU I/O channel file test
+ *
+ * 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 "io/channel-file.h"
+#include "io-channel-helpers.h"
+
+
+static void test_io_channel_file(void)
+{
+ QIOChannel *src, *dst;
+ QIOChannelTest *test;
+
+#define TEST_FILE "tests/test-io-channel-file.txt"
+ unlink(TEST_FILE);
+ src = QIO_CHANNEL(qio_channel_file_new_path(
+ TEST_FILE,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600,
+ &error_abort));
+ dst = QIO_CHANNEL(qio_channel_file_new_path(
+ TEST_FILE,
+ O_RDONLY | O_BINARY, 0,
+ &error_abort));
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_writer(test, src);
+ qio_channel_test_run_reader(test, dst);
+ qio_channel_test_validate(test);
+
+ unlink(TEST_FILE);
+ object_unref(OBJECT(src));
+ object_unref(OBJECT(dst));
+}
+
+
+#ifndef _WIN32
+static void test_io_channel_pipe(bool async)
+{
+ QIOChannel *src, *dst;
+ QIOChannelTest *test;
+ int fd[2];
+
+ if (pipe(fd) < 0) {
+ perror("pipe");
+ abort();
+ }
+
+ src = QIO_CHANNEL(qio_channel_file_new_fd(fd[1]));
+ dst = QIO_CHANNEL(qio_channel_file_new_fd(fd[0]));
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, async, src, dst);
+ qio_channel_test_validate(test);
+
+ object_unref(OBJECT(src));
+ object_unref(OBJECT(dst));
+}
+
+
+static void test_io_channel_pipe_async(void)
+{
+ test_io_channel_pipe(true);
+}
+
+static void test_io_channel_pipe_sync(void)
+{
+ test_io_channel_pipe(false);
+}
+#endif /* ! _WIN32 */
+
+
+int main(int argc, char **argv)
+{
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/io/channel/file", test_io_channel_file);
+#ifndef _WIN32
+ g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync);
+ g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async);
+#endif
+ return g_test_run();
+}
diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c
new file mode 100644
index 0000000000..194d043878
--- /dev/null
+++ b/tests/test-io-channel-socket.c
@@ -0,0 +1,399 @@
+/*
+ * QEMU I/O channel sockets test
+ *
+ * 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 "io/channel-socket.h"
+#include "io-channel-helpers.h"
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+
+static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
+{
+#ifdef HAVE_IFADDRS_H
+ struct ifaddrs *ifaddr = NULL, *ifa;
+ struct addrinfo hints = { 0 };
+ struct addrinfo *ai = NULL;
+ int gaierr;
+
+ *has_ipv4 = *has_ipv6 = false;
+
+ if (getifaddrs(&ifaddr) < 0) {
+ g_printerr("Failed to lookup interface addresses: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr) {
+ continue;
+ }
+
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ *has_ipv4 = true;
+ }
+ if (ifa->ifa_addr->sa_family == AF_INET6) {
+ *has_ipv6 = true;
+ }
+ }
+
+ freeifaddrs(ifaddr);
+
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+
+ gaierr = getaddrinfo("::1", NULL, &hints, &ai);
+ if (gaierr != 0) {
+ if (gaierr == EAI_ADDRFAMILY ||
+ gaierr == EAI_FAMILY ||
+ gaierr == EAI_NONAME) {
+ *has_ipv6 = false;
+ } else {
+ g_printerr("Failed to resolve ::1 address: %s\n",
+ gai_strerror(gaierr));
+ return -1;
+ }
+ }
+
+ freeaddrinfo(ai);
+
+ return 0;
+#else
+ *has_ipv4 = *has_ipv6 = false;
+
+ return -1;
+#endif
+}
+
+
+static void test_io_channel_set_socket_bufs(QIOChannel *src,
+ QIOChannel *dst)
+{
+ int buflen = 64 * 1024;
+
+ /*
+ * Make the socket buffers small so that we see
+ * the effects of partial reads/writes
+ */
+ setsockopt(((QIOChannelSocket *)src)->fd,
+ SOL_SOCKET, SO_SNDBUF,
+ (char *)&buflen,
+ sizeof(buflen));
+
+ setsockopt(((QIOChannelSocket *)dst)->fd,
+ SOL_SOCKET, SO_SNDBUF,
+ (char *)&buflen,
+ sizeof(buflen));
+}
+
+
+static void test_io_channel_setup_sync(SocketAddress *listen_addr,
+ SocketAddress *connect_addr,
+ QIOChannel **src,
+ QIOChannel **dst)
+{
+ QIOChannelSocket *lioc;
+
+ lioc = qio_channel_socket_new();
+ qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort);
+
+ if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
+ SocketAddress *laddr = qio_channel_socket_get_local_address(
+ lioc, &error_abort);
+
+ g_free(connect_addr->u.inet->port);
+ connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
+
+ qapi_free_SocketAddress(laddr);
+ }
+
+ *src = QIO_CHANNEL(qio_channel_socket_new());
+ qio_channel_socket_connect_sync(
+ QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort);
+ qio_channel_set_delay(*src, false);
+
+ *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
+ g_assert(*dst);
+
+ test_io_channel_set_socket_bufs(*src, *dst);
+
+ object_unref(OBJECT(lioc));
+}
+
+
+struct TestIOChannelData {
+ bool err;
+ GMainLoop *loop;
+};
+
+
+static void test_io_channel_complete(Object *src,
+ Error *err,
+ gpointer opaque)
+{
+ struct TestIOChannelData *data = opaque;
+ data->err = err != NULL;
+ g_main_loop_quit(data->loop);
+}
+
+
+static void test_io_channel_setup_async(SocketAddress *listen_addr,
+ SocketAddress *connect_addr,
+ QIOChannel **src,
+ QIOChannel **dst)
+{
+ QIOChannelSocket *lioc;
+ struct TestIOChannelData data;
+
+ data.loop = g_main_loop_new(g_main_context_default(),
+ TRUE);
+
+ lioc = qio_channel_socket_new();
+ qio_channel_socket_listen_async(
+ lioc, listen_addr,
+ test_io_channel_complete, &data, NULL);
+
+ g_main_loop_run(data.loop);
+ g_main_context_iteration(g_main_context_default(), FALSE);
+
+ g_assert(!data.err);
+
+ if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
+ SocketAddress *laddr = qio_channel_socket_get_local_address(
+ lioc, &error_abort);
+
+ g_free(connect_addr->u.inet->port);
+ connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
+
+ qapi_free_SocketAddress(laddr);
+ }
+
+ *src = QIO_CHANNEL(qio_channel_socket_new());
+
+ qio_channel_socket_connect_async(
+ QIO_CHANNEL_SOCKET(*src), connect_addr,
+ test_io_channel_complete, &data, NULL);
+
+ g_main_loop_run(data.loop);
+ g_main_context_iteration(g_main_context_default(), FALSE);
+
+ g_assert(!data.err);
+
+ *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
+ g_assert(*dst);
+
+ qio_channel_set_delay(*src, false);
+ test_io_channel_set_socket_bufs(*src, *dst);
+
+ object_unref(OBJECT(lioc));
+
+ g_main_loop_unref(data.loop);
+}
+
+
+static void test_io_channel(bool async,
+ SocketAddress *listen_addr,
+ SocketAddress *connect_addr)
+{
+ QIOChannel *src, *dst;
+ QIOChannelTest *test;
+ if (async) {
+ test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, true, src, dst);
+ qio_channel_test_validate(test);
+
+ object_unref(OBJECT(src));
+ object_unref(OBJECT(dst));
+
+ test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, false, src, dst);
+ qio_channel_test_validate(test);
+
+ object_unref(OBJECT(src));
+ object_unref(OBJECT(dst));
+ } else {
+ test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, true, src, dst);
+ qio_channel_test_validate(test);
+
+ object_unref(OBJECT(src));
+ object_unref(OBJECT(dst));
+
+ test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, false, src, dst);
+ qio_channel_test_validate(test);
+
+ object_unref(OBJECT(src));
+ object_unref(OBJECT(dst));
+ }
+}
+
+
+static void test_io_channel_ipv4(bool async)
+{
+ SocketAddress *listen_addr = g_new0(SocketAddress, 1);
+ SocketAddress *connect_addr = g_new0(SocketAddress, 1);
+
+ listen_addr->type = SOCKET_ADDRESS_KIND_INET;
+ listen_addr->u.inet = g_new0(InetSocketAddress, 1);
+ listen_addr->u.inet->host = g_strdup("0.0.0.0");
+ listen_addr->u.inet->port = NULL; /* Auto-select */
+
+ connect_addr->type = SOCKET_ADDRESS_KIND_INET;
+ connect_addr->u.inet = g_new0(InetSocketAddress, 1);
+ connect_addr->u.inet->host = g_strdup("127.0.0.1");
+ connect_addr->u.inet->port = NULL; /* Filled in later */
+
+ test_io_channel(async, listen_addr, connect_addr);
+
+ qapi_free_SocketAddress(listen_addr);
+ qapi_free_SocketAddress(connect_addr);
+}
+
+
+static void test_io_channel_ipv4_sync(void)
+{
+ return test_io_channel_ipv4(false);
+}
+
+
+static void test_io_channel_ipv4_async(void)
+{
+ return test_io_channel_ipv4(true);
+}
+
+
+static void test_io_channel_ipv6(bool async)
+{
+ SocketAddress *listen_addr = g_new0(SocketAddress, 1);
+ SocketAddress *connect_addr = g_new0(SocketAddress, 1);
+
+ listen_addr->type = SOCKET_ADDRESS_KIND_INET;
+ listen_addr->u.inet = g_new0(InetSocketAddress, 1);
+ listen_addr->u.inet->host = g_strdup("::");
+ listen_addr->u.inet->port = NULL; /* Auto-select */
+
+ connect_addr->type = SOCKET_ADDRESS_KIND_INET;
+ connect_addr->u.inet = g_new0(InetSocketAddress, 1);
+ connect_addr->u.inet->host = g_strdup("::1");
+ connect_addr->u.inet->port = NULL; /* Filled in later */
+
+ test_io_channel(async, listen_addr, connect_addr);
+
+ qapi_free_SocketAddress(listen_addr);
+ qapi_free_SocketAddress(connect_addr);
+}
+
+
+static void test_io_channel_ipv6_sync(void)
+{
+ return test_io_channel_ipv6(false);
+}
+
+
+static void test_io_channel_ipv6_async(void)
+{
+ return test_io_channel_ipv6(true);
+}
+
+
+#ifndef _WIN32
+static void test_io_channel_unix(bool async)
+{
+ SocketAddress *listen_addr = g_new0(SocketAddress, 1);
+ SocketAddress *connect_addr = g_new0(SocketAddress, 1);
+
+#define TEST_SOCKET "test-io-channel-socket.sock"
+ listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
+ listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
+ listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
+
+ connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
+ connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
+ connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
+
+ test_io_channel(async, listen_addr, connect_addr);
+
+ qapi_free_SocketAddress(listen_addr);
+ qapi_free_SocketAddress(connect_addr);
+ unlink(TEST_SOCKET);
+}
+
+
+static void test_io_channel_unix_sync(void)
+{
+ return test_io_channel_unix(false);
+}
+
+
+static void test_io_channel_unix_async(void)
+{
+ return test_io_channel_unix(true);
+}
+#endif /* _WIN32 */
+
+
+int main(int argc, char **argv)
+{
+ bool has_ipv4, has_ipv6;
+
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_init(&argc, &argv, NULL);
+
+ /* We're creating actual IPv4/6 sockets, so we should
+ * check if the host running tests actually supports
+ * each protocol to avoid breaking tests on machines
+ * with either IPv4 or IPv6 disabled.
+ */
+ if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
+ return 1;
+ }
+
+ if (has_ipv4) {
+ g_test_add_func("/io/channel/socket/ipv4-sync",
+ test_io_channel_ipv4_sync);
+ g_test_add_func("/io/channel/socket/ipv4-async",
+ test_io_channel_ipv4_async);
+ }
+ if (has_ipv6) {
+ g_test_add_func("/io/channel/socket/ipv6-sync",
+ test_io_channel_ipv6_sync);
+ g_test_add_func("/io/channel/socket/ipv6-async",
+ test_io_channel_ipv6_async);
+ }
+
+#ifndef _WIN32
+ g_test_add_func("/io/channel/socket/unix-sync",
+ test_io_channel_unix_sync);
+ g_test_add_func("/io/channel/socket/unix-async",
+ test_io_channel_unix_async);
+#endif /* _WIN32 */
+
+ return g_test_run();
+}
diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c
new file mode 100644
index 0000000000..3c11a5097c
--- /dev/null
+++ b/tests/test-io-channel-tls.c
@@ -0,0 +1,342 @@
+/*
+ * QEMU I/O channel TLS test
+ *
+ * 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.1 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/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "config-host.h"
+#include "crypto-tls-x509-helpers.h"
+#include "io/channel-tls.h"
+#include "io/channel-socket.h"
+#include "io-channel-helpers.h"
+#include "crypto/tlscredsx509.h"
+#include "qemu/acl.h"
+#include "qom/object_interfaces.h"
+
+#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT
+
+#define WORKDIR "tests/test-io-channel-tls-work/"
+#define KEYFILE WORKDIR "key-ctx.pem"
+
+struct QIOChannelTLSTestData {
+ const char *servercacrt;
+ const char *clientcacrt;
+ const char *servercrt;
+ const char *clientcrt;
+ bool expectServerFail;
+ bool expectClientFail;
+ const char *hostname;
+ const char *const *wildcards;
+};
+
+struct QIOChannelTLSHandshakeData {
+ bool finished;
+ bool failed;
+};
+
+static void test_tls_handshake_done(Object *source,
+ Error *err,
+ gpointer opaque)
+{
+ struct QIOChannelTLSHandshakeData *data = opaque;
+
+ data->finished = true;
+ data->failed = err != NULL;
+}
+
+
+static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint,
+ const char *certdir,
+ Error **errp)
+{
+ Object *parent = object_get_objects_root();
+ Object *creds = object_new_with_props(
+ TYPE_QCRYPTO_TLS_CREDS_X509,
+ parent,
+ (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
+ "testtlscredsserver" : "testtlscredsclient"),
+ errp,
+ "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ?
+ "server" : "client"),
+ "dir", certdir,
+ "verify-peer", "yes",
+ /* We skip initial sanity checks here because we
+ * want to make sure that problems are being
+ * detected at the TLS session validation stage,
+ * and the test-crypto-tlscreds test already
+ * validate the sanity check code.
+ */
+ "sanity-check", "no",
+ NULL
+ );
+
+ if (*errp) {
+ return NULL;
+ }
+ return QCRYPTO_TLS_CREDS(creds);
+}
+
+
+/*
+ * This tests validation checking of peer certificates
+ *
+ * This is replicating the checks that are done for an
+ * active TLS session after handshake completes. To
+ * simulate that we create our TLS contexts, skipping
+ * sanity checks. When then get a socketpair, and
+ * initiate a TLS session across them. Finally do
+ * do actual cert validation tests
+ */
+static void test_io_channel_tls(const void *opaque)
+{
+ struct QIOChannelTLSTestData *data =
+ (struct QIOChannelTLSTestData *)opaque;
+ QCryptoTLSCreds *clientCreds;
+ QCryptoTLSCreds *serverCreds;
+ QIOChannelTLS *clientChanTLS;
+ QIOChannelTLS *serverChanTLS;
+ QIOChannelSocket *clientChanSock;
+ QIOChannelSocket *serverChanSock;
+ qemu_acl *acl;
+ const char * const *wildcards;
+ int channel[2];
+ struct QIOChannelTLSHandshakeData clientHandshake = { false, false };
+ struct QIOChannelTLSHandshakeData serverHandshake = { false, false };
+ Error *err = NULL;
+ QIOChannelTest *test;
+ GMainContext *mainloop;
+
+ /* We'll use this for our fake client-server connection */
+ g_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == 0);
+
+#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/"
+#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/"
+ mkdir(CLIENT_CERT_DIR, 0700);
+ mkdir(SERVER_CERT_DIR, 0700);
+
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
+
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
+
+ g_assert(link(data->servercacrt,
+ SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
+ g_assert(link(data->servercrt,
+ SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0);
+ g_assert(link(KEYFILE,
+ SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0);
+
+ g_assert(link(data->clientcacrt,
+ CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0);
+ g_assert(link(data->clientcrt,
+ CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0);
+ g_assert(link(KEYFILE,
+ CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0);
+
+ clientCreds = test_tls_creds_create(
+ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
+ CLIENT_CERT_DIR,
+ &err);
+ g_assert(clientCreds != NULL);
+
+ serverCreds = test_tls_creds_create(
+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
+ SERVER_CERT_DIR,
+ &err);
+ g_assert(serverCreds != NULL);
+
+ acl = qemu_acl_init("channeltlsacl");
+ qemu_acl_reset(acl);
+ wildcards = data->wildcards;
+ while (wildcards && *wildcards) {
+ qemu_acl_append(acl, 0, *wildcards);
+ wildcards++;
+ }
+
+ clientChanSock = qio_channel_socket_new_fd(
+ channel[0], &err);
+ g_assert(clientChanSock != NULL);
+ serverChanSock = qio_channel_socket_new_fd(
+ channel[1], &err);
+ g_assert(serverChanSock != NULL);
+
+ /*
+ * We have an evil loop to do the handshake in a single
+ * thread, so we need these non-blocking to avoid deadlock
+ * of ourselves
+ */
+ qio_channel_set_blocking(QIO_CHANNEL(clientChanSock), false, NULL);
+ qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false, NULL);
+
+ /* Now the real part of the test, setup the sessions */
+ clientChanTLS = qio_channel_tls_new_client(
+ QIO_CHANNEL(clientChanSock), clientCreds,
+ data->hostname, &err);
+ g_assert(clientChanTLS != NULL);
+
+ serverChanTLS = qio_channel_tls_new_server(
+ QIO_CHANNEL(serverChanSock), serverCreds,
+ "channeltlsacl", &err);
+ g_assert(serverChanTLS != NULL);
+
+ qio_channel_tls_handshake(clientChanTLS,
+ test_tls_handshake_done,
+ &clientHandshake,
+ NULL);
+ qio_channel_tls_handshake(serverChanTLS,
+ test_tls_handshake_done,
+ &serverHandshake,
+ NULL);
+
+ /*
+ * Finally we loop around & around doing handshake on each
+ * session until we get an error, or the handshake completes.
+ * This relies on the socketpair being nonblocking to avoid
+ * deadlocking ourselves upon handshake
+ */
+ mainloop = g_main_context_default();
+ do {
+ g_main_context_iteration(mainloop, TRUE);
+ } while (!clientHandshake.finished &&
+ !serverHandshake.finished);
+
+ g_assert(clientHandshake.failed == data->expectClientFail);
+ g_assert(serverHandshake.failed == data->expectServerFail);
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, false,
+ QIO_CHANNEL(clientChanTLS),
+ QIO_CHANNEL(serverChanTLS));
+ qio_channel_test_validate(test);
+
+ test = qio_channel_test_new();
+ qio_channel_test_run_threads(test, true,
+ QIO_CHANNEL(clientChanTLS),
+ QIO_CHANNEL(serverChanTLS));
+ qio_channel_test_validate(test);
+
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT);
+ unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY);
+
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT);
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
+ unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
+
+ rmdir(CLIENT_CERT_DIR);
+ rmdir(SERVER_CERT_DIR);
+
+ object_unparent(OBJECT(serverCreds));
+ object_unparent(OBJECT(clientCreds));
+
+ object_unref(OBJECT(serverChanTLS));
+ object_unref(OBJECT(clientChanTLS));
+
+ object_unref(OBJECT(serverChanSock));
+ object_unref(OBJECT(clientChanSock));
+
+ close(channel[0]);
+ close(channel[1]);
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ module_call_init(MODULE_INIT_QOM);
+ g_test_init(&argc, &argv, NULL);
+ setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1);
+
+ mkdir(WORKDIR, 0700);
+
+ test_tls_init(KEYFILE);
+
+# define TEST_CHANNEL(name, caCrt, \
+ serverCrt, clientCrt, \
+ expectServerFail, expectClientFail, \
+ hostname, wildcards) \
+ struct QIOChannelTLSTestData name = { \
+ caCrt, caCrt, serverCrt, clientCrt, \
+ expectServerFail, expectClientFail, \
+ hostname, wildcards \
+ }; \
+ g_test_add_data_func("/qio/channel/tls/" # name, \
+ &name, test_io_channel_tls);
+
+ /* A perfect CA, perfect client & perfect server */
+
+ /* Basic:CA:critical */
+ TLS_ROOT_REQ(cacertreq,
+ "UK", "qemu CA", NULL, NULL, NULL, NULL,
+ true, true, true,
+ true, true, GNUTLS_KEY_KEY_CERT_SIGN,
+ false, false, NULL, NULL,
+ 0, 0);
+ TLS_CERT_REQ(servercertreq, cacertreq,
+ "UK", "qemu.org", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL,
+ 0, 0);
+ TLS_CERT_REQ(clientcertreq, cacertreq,
+ "UK", "qemu", NULL, NULL, NULL, NULL,
+ true, true, false,
+ true, true,
+ GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
+ true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL,
+ 0, 0);
+
+ const char *const wildcards[] = {
+ "C=UK,CN=qemu*",
+ NULL,
+ };
+ TEST_CHANNEL(basic, cacertreq.filename, servercertreq.filename,
+ clientcertreq.filename, false, false,
+ "qemu.org", wildcards);
+
+ ret = g_test_run();
+
+ test_tls_discard_cert(&clientcertreq);
+ test_tls_discard_cert(&servercertreq);
+ test_tls_discard_cert(&cacertreq);
+
+ test_tls_cleanup(KEYFILE);
+ rmdir(WORKDIR);
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
+
+int
+main(void)
+{
+ return EXIT_SUCCESS;
+}
+
+#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */
diff --git a/tests/test-io-task.c b/tests/test-io-task.c
new file mode 100644
index 0000000000..3344382c7f
--- /dev/null
+++ b/tests/test-io-task.c
@@ -0,0 +1,268 @@
+/*
+ * QEMU I/O task tests
+ *
+ * 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 <glib.h>
+
+#include "io/task.h"
+
+#define TYPE_DUMMY "qemu:dummy"
+
+typedef struct DummyObject DummyObject;
+typedef struct DummyObjectClass DummyObjectClass;
+
+struct DummyObject {
+ Object parent;
+};
+
+struct DummyObjectClass {
+ ObjectClass parent;
+};
+
+static const TypeInfo dummy_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_DUMMY,
+ .instance_size = sizeof(DummyObject),
+ .class_size = sizeof(DummyObjectClass),
+};
+
+struct TestTaskData {
+ Object *source;
+ Error *err;
+ bool freed;
+};
+
+
+static void task_callback(Object *source,
+ Error *err,
+ gpointer opaque)
+{
+ struct TestTaskData *data = opaque;
+
+ data->source = source;
+ data->err = err;
+}
+
+
+static void test_task_complete(void)
+{
+ QIOTask *task;
+ Object *obj = object_new(TYPE_DUMMY);
+ Object *src;
+ struct TestTaskData data = { NULL, NULL, false };
+
+ task = qio_task_new(obj, task_callback, &data, NULL);
+ src = qio_task_get_source(task);
+
+ qio_task_complete(task);
+
+ g_assert(obj == src);
+
+ object_unref(obj);
+ object_unref(src);
+
+ g_assert(data.source == obj);
+ g_assert(data.err == NULL);
+ g_assert(data.freed == false);
+}
+
+
+static void task_data_free(gpointer opaque)
+{
+ struct TestTaskData *data = opaque;
+
+ data->freed = true;
+}
+
+
+static void test_task_data_free(void)
+{
+ QIOTask *task;
+ Object *obj = object_new(TYPE_DUMMY);
+ struct TestTaskData data = { NULL, NULL, false };
+
+ task = qio_task_new(obj, task_callback, &data, task_data_free);
+
+ qio_task_complete(task);
+
+ object_unref(obj);
+
+ g_assert(data.source == obj);
+ g_assert(data.err == NULL);
+ g_assert(data.freed == true);
+}
+
+
+static void test_task_error(void)
+{
+ QIOTask *task;
+ Object *obj = object_new(TYPE_DUMMY);
+ struct TestTaskData data = { NULL, NULL, false };
+ Error *err = NULL;
+
+ task = qio_task_new(obj, task_callback, &data, NULL);
+
+ error_setg(&err, "Some error");
+
+ qio_task_abort(task, err);
+
+ error_free(err);
+ object_unref(obj);
+
+ g_assert(data.source == obj);
+ g_assert(data.err == err);
+ g_assert(data.freed == false);
+
+}
+
+
+struct TestThreadWorkerData {
+ Object *source;
+ Error *err;
+ bool fail;
+ GThread *worker;
+ GThread *complete;
+ GMainLoop *loop;
+};
+
+static int test_task_thread_worker(QIOTask *task,
+ Error **errp,
+ gpointer opaque)
+{
+ struct TestThreadWorkerData *data = opaque;
+
+ data->worker = g_thread_self();
+
+ if (data->fail) {
+ error_setg(errp, "Testing fail");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void test_task_thread_callback(Object *source,
+ Error *err,
+ gpointer opaque)
+{
+ struct TestThreadWorkerData *data = opaque;
+
+ data->source = source;
+ data->err = err;
+
+ data->complete = g_thread_self();
+
+ g_main_loop_quit(data->loop);
+}
+
+
+static void test_task_thread_complete(void)
+{
+ QIOTask *task;
+ Object *obj = object_new(TYPE_DUMMY);
+ struct TestThreadWorkerData data = { 0 };
+ GThread *self;
+
+ data.loop = g_main_loop_new(g_main_context_default(),
+ TRUE);
+
+ task = qio_task_new(obj,
+ test_task_thread_callback,
+ &data,
+ NULL);
+
+ qio_task_run_in_thread(task,
+ test_task_thread_worker,
+ &data,
+ NULL);
+
+ g_main_loop_run(data.loop);
+
+ g_main_loop_unref(data.loop);
+ object_unref(obj);
+
+ g_assert(data.source == obj);
+ g_assert(data.err == NULL);
+
+ self = g_thread_self();
+
+ /* Make sure the test_task_thread_worker actually got
+ * run in a different thread */
+ g_assert(data.worker != self);
+
+ /* And that the test_task_thread_callback got rnu in
+ * the main loop thread (ie this one) */
+ g_assert(data.complete == self);
+}
+
+
+static void test_task_thread_error(void)
+{
+ QIOTask *task;
+ Object *obj = object_new(TYPE_DUMMY);
+ struct TestThreadWorkerData data = { 0 };
+ GThread *self;
+
+ data.loop = g_main_loop_new(g_main_context_default(),
+ TRUE);
+ data.fail = true;
+
+ task = qio_task_new(obj,
+ test_task_thread_callback,
+ &data,
+ NULL);
+
+ qio_task_run_in_thread(task,
+ test_task_thread_worker,
+ &data,
+ NULL);
+
+ g_main_loop_run(data.loop);
+
+ g_main_loop_unref(data.loop);
+ object_unref(obj);
+
+ g_assert(data.source == obj);
+ g_assert(data.err != NULL);
+
+ self = g_thread_self();
+
+ /* Make sure the test_task_thread_worker actually got
+ * run in a different thread */
+ g_assert(data.worker != self);
+
+ /* And that the test_task_thread_callback got rnu in
+ * the main loop thread (ie this one) */
+ g_assert(data.complete == self);
+}
+
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ module_call_init(MODULE_INIT_QOM);
+ type_register_static(&dummy_info);
+ g_test_add_func("/crypto/task/complete", test_task_complete);
+ g_test_add_func("/crypto/task/datafree", test_task_data_free);
+ g_test_add_func("/crypto/task/error", test_task_error);
+ g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete);
+ g_test_add_func("/crypto/task/thread_error", test_task_thread_error);
+ return g_test_run();
+}
diff --git a/trace-events b/trace-events
index fa504cf494..6f036384a8 100644
--- a/trace-events
+++ b/trace-events
@@ -1808,3 +1808,59 @@ user_handle_signal(void *env, int target_sig) "env=%p signal %d"
user_host_signal(void *env, int host_sig, int target_sig) "env=%p signal %d (target %d("
user_queue_signal(void *env, int target_sig) "env=%p signal %d"
user_s390x_restore_sigregs(void *env, uint64_t sc_psw_addr, uint64_t env_psw_addr) "env=%p frame psw.addr "PRIx64 " current psw.addr "PRIx64""
+
+# io/task.c
+qio_task_new(void *task, void *source, void *func, void *opaque) "Task new task=%p source=%p func=%p opaque=%p"
+qio_task_complete(void *task) "Task complete task=%p"
+qio_task_abort(void *task) "Task abort task=%p"
+qio_task_thread_start(void *task, void *worker, void *opaque) "Task thread start task=%p worker=%p opaque=%p"
+qio_task_thread_run(void *task) "Task thread run task=%p"
+qio_task_thread_exit(void *task) "Task thread exit task=%p"
+qio_task_thread_result(void *task) "Task thread result task=%p"
+
+# io/channel-socket.c
+qio_channel_socket_new(void *ioc) "Socket new ioc=%p"
+qio_channel_socket_new_fd(void *ioc, int fd) "Socket new ioc=%p fd=%d"
+qio_channel_socket_connect_sync(void *ioc, void *addr) "Socket connect sync ioc=%p addr=%p"
+qio_channel_socket_connect_async(void *ioc, void *addr) "Socket connect async ioc=%p addr=%p"
+qio_channel_socket_connect_fail(void *ioc) "Socket connect fail ioc=%p"
+qio_channel_socket_connect_complete(void *ioc, int fd) "Socket connect complete ioc=%p fd=%d"
+qio_channel_socket_listen_sync(void *ioc, void *addr) "Socket listen sync ioc=%p addr=%p"
+qio_channel_socket_listen_async(void *ioc, void *addr) "Socket listen async ioc=%p addr=%p"
+qio_channel_socket_listen_fail(void *ioc) "Socket listen fail ioc=%p"
+qio_channel_socket_listen_complete(void *ioc, int fd) "Socket listen complete ioc=%p fd=%d"
+qio_channel_socket_dgram_sync(void *ioc, void *localAddr, void *remoteAddr) "Socket dgram sync ioc=%p localAddr=%p remoteAddr=%p"
+qio_channel_socket_dgram_async(void *ioc, void *localAddr, void *remoteAddr) "Socket dgram async ioc=%p localAddr=%p remoteAddr=%p"
+qio_channel_socket_dgram_fail(void *ioc) "Socket dgram fail ioc=%p"
+qio_channel_socket_dgram_complete(void *ioc, int fd) "Socket dgram complete ioc=%p fd=%d"
+qio_channel_socket_accept(void *ioc) "Socket accept start ioc=%p"
+qio_channel_socket_accept_fail(void *ioc) "Socket accept fail ioc=%p"
+qio_channel_socket_accept_complete(void *ioc, void *cioc, int fd) "Socket accept complete ioc=%p cioc=%p fd=%d"
+
+# io/channel-file.c
+qio_channel_file_new_fd(void *ioc, int fd) "File new fd ioc=%p fd=%d"
+qio_channel_file_new_path(void *ioc, const char *path, int flags, int mode, int fd) "File new fd ioc=%p path=%s flags=%d mode=%d fd=%d"
+
+# io/channel-tls.c
+qio_channel_tls_new_client(void *ioc, void *master, void *creds, const char *hostname) "TLS new client ioc=%p master=%p creds=%p hostname=%s"
+qio_channel_tls_new_server(void *ioc, void *master, void *creds, const char *aclname) "TLS new client ioc=%p master=%p creds=%p acltname=%s"
+qio_channel_tls_handshake_start(void *ioc) "TLS handshake start ioc=%p"
+qio_channel_tls_handshake_pending(void *ioc, int status) "TLS handshake pending ioc=%p status=%d"
+qio_channel_tls_handshake_fail(void *ioc) "TLS handshake fail ioc=%p"
+qio_channel_tls_handshake_complete(void *ioc) "TLS handshake complete ioc=%p"
+qio_channel_tls_credentials_allow(void *ioc) "TLS credentials allow ioc=%p"
+qio_channel_tls_credentials_deny(void *ioc) "TLS credentials deny ioc=%p"
+
+# io/channel-websock.c
+qio_channel_websock_new_server(void *ioc, void *master) "Websock new client ioc=%p master=%p"
+qio_channel_websock_handshake_start(void *ioc) "Websock handshake start ioc=%p"
+qio_channel_websock_handshake_pending(void *ioc, int status) "Websock handshake pending ioc=%p status=%d"
+qio_channel_websock_handshake_reply(void *ioc) "Websock handshake reply ioc=%p"
+qio_channel_websock_handshake_fail(void *ioc) "Websock handshake fail ioc=%p"
+qio_channel_websock_handshake_complete(void *ioc) "Websock handshake complete ioc=%p"
+
+# io/channel-command.c
+qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Command new pid ioc=%p writefd=%d readfd=%d pid=%d"
+qio_channel_command_new_spawn(void *ioc, const char *binary, int flags) "Command new spawn ioc=%p binary=%s flags=%d"
+qio_channel_command_abort(void *ioc, int pid) "Command abort ioc=%p pid=%d"
+qio_channel_command_wait(void *ioc, int pid, int ret, int status) "Command abort ioc=%p pid=%d ret=%d status=%d"
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 5a31d164d9..922efb3179 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1086,7 +1086,7 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa,
}
#endif /* WIN32 */
-static SocketAddress *
+SocketAddress *
socket_sockaddr_to_address(struct sockaddr_storage *sa,
socklen_t salen,
Error **errp)