All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration
@ 2015-09-03 15:38 Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 01/46] sockets: add helpers for creating SocketAddress from a socket Daniel P. Berrange
                   ` (45 more replies)
  0 siblings, 46 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Earlier in the year I posted an RFC series providing impl of
generic TLS support for VNC and chardevs.

  https://lists.gnu.org/archive/html/qemu-devel/2015-04/msg02038.html

Since that time the initial QOM enhancements and crypto API
consolidation patches from that RFC have been merged. I
currently have a set of patches extracting the VNC server
TLS support into a generic crypto API for TLS that is under
active review, hopefully getting close to merge (v6):

  https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00437.html

What I'm posting here is the 46 remaining patches I have that
introduce TLS support across chardevs and migration code, and in
the process standardize I/O channel handling across VNC, chardevs
and migration. I'm doing this so people can see the use context
of the TLS patches I have on review, as well as show the kind of
changes I'm making to the migration, chardev & sockets code to
facilitate conversations with other devs working on the same bits
of code. As compared to the original RFC patches, this series is
a more complete job - there is much more unit testing added and
win32 compatibility verified, and many todos/hacks cleaned up.

I still need to figure out NBD integration, which is harder than
I first thought, due to the need for me to understand the special
needs of the block AIO framework, which doesn't seem to use the
normal APIs for registering I/O callbacks with the main event loop.

I also realize that the ACL code needs to be QOM'ified to make
it possible to create/delete ACLs more generically and share then
across services.

I consider pretty much all of these patches as finished and ready
for review, but this series is a bit large to be reviewed in one
go, so I'm going to continue drip-feed it in reasonable sized chunks.

Some of these 46 patches are fairly generic and can be easily posted
in isolation for quicker merge, so I will be focusing on identifying
such patches and posting them individually to appropriate maintainers.

This series needs to be applied on top of the patch series here

  https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00437.html

For convience I have pushed this entire branch to github

  https://github.com/berrange/qemu/commits/qemu-io-channel-18

Daniel P. Berrange (46):
  sockets: add helpers for creating SocketAddress from a socket
  sockets: move qapi_copy_SocketAddress into qemu-sockets.c
  sockets: allow port to be NULL when listening on IP address
  osdep: add qemu_fork() wrapper for safely handling signals
  coroutine: move into libqemuutil.a library
  io: add abstract QIOChannel classes
  io: add helper module for creating watches on FDs
  io: pull Buffer code out of VNC module
  io: add QIOTask class for async operations
  io: add QIOChannelSocket class
  io: add QIOChannelFile class
  io: add QIOChannelTLS class
  io: add QIOChannelWebsock class
  io: add QIOChannelCommand class
  ui: convert VNC startup code to use SocketAddress
  ui: convert VNC server to use QIOChannelSocket
  ui: convert VNC server to use QIOChannelTLS
  ui: convert VNC server to use QIOChannelWebsock
  char: remove fixed length filename allocation
  char: convert from GIOChannel to QIOChannel
  char: don't assume telnet initialization will not block
  char: introduce support for TLS encrypted TCP chardev backend
  nbd: convert to use the QAPI SocketAddress object
  qemu-nbd: convert to use the QAPI SocketAddress object
  sockets: remove use of QemuOpts from header file
  sockets: remove use of QemuOpts from socket_listen
  sockets: remove use of QemuOpts from socket_connect
  sockets: remove use of QemuOpts from socket_dgram
  migration: remove use of qemu_bufopen from vmstate tests
  migration: remove memory buffer based QEMUFile backend
  migration: move definition of struct QEMUFile back into qemu-file.c
  migration: split migration hooks out of QEMUFileOps
  migration: ensure qemu_fflush() always writes full data amount
  migration: introduce qemu_fset_blocking function on QEMUFile
  migration: force QEMUFile to blocking mode for outgoing migration
  migration: introduce a new QEMUFile impl based on QIOChannel
  migration: convert unix socket protocol to use QIOChannel
  migration: convert tcp socket protocol to use QIOChannel
  migration: convert fd socket protocol to use QIOChannel
  migration: convert exec socket protocol to use QIOChannel
  migration: convert RDMA to use QIOChannel interface
  migration: convert savevm to use QIOChannel for writing to files
  migration: delete QEMUFile sockets implementation
  migration: delete QEMUFile stdio implementation
  migration: support TLS encryption with TCP migration backend
  migration: remove support for non-iovec based write handlers

 MAINTAINERS                             |   7 +
 Makefile                                |   2 +
 Makefile.objs                           |  12 +-
 Makefile.target                         |   2 +
 block.c                                 |   2 +-
 block/nbd.c                             |  66 +--
 block/qcow2.h                           |   2 +-
 block/vdi.c                             |   2 +-
 block/write-threshold.c                 |   2 +-
 blockjob.c                              |   2 +-
 configure                               |  11 +
 coroutine-gthread.c                     |   2 +-
 coroutine-sigaltstack.c                 |   2 +-
 coroutine-ucontext.c                    |   2 +-
 coroutine-win32.c                       |   2 +-
 hw/9pfs/codir.c                         |   2 +-
 hw/9pfs/cofile.c                        |   2 +-
 hw/9pfs/cofs.c                          |   2 +-
 hw/9pfs/coxattr.c                       |   2 +-
 hw/9pfs/virtio-9p-coth.c                |   2 +-
 hw/9pfs/virtio-9p-coth.h                |   2 +-
 hw/9pfs/virtio-9p.h                     |   2 +-
 include/block/block.h                   |   2 +-
 include/block/block_int.h               |   2 +-
 include/io/buffer.h                     | 118 ++++
 include/io/channel-command.h            |  91 +++
 include/io/channel-file.h               |  93 +++
 include/io/channel-socket.h             | 297 ++++++++++
 include/io/channel-tls.h                | 142 +++++
 include/io/channel-watch.h              |  72 +++
 include/io/channel-websock.h            | 108 ++++
 include/io/channel.h                    | 374 +++++++++++++
 include/io/task.h                       | 256 +++++++++
 include/migration/qemu-file.h           |  48 +-
 include/{block => qemu}/coroutine.h     |   0
 include/{block => qemu}/coroutine_int.h |   2 +-
 include/qemu/osdep.h                    |  16 +
 include/qemu/sockets.h                  |  44 +-
 io/Makefile.objs                        |   9 +
 io/buffer.c                             |  65 +++
 io/channel-command.c                    | 370 ++++++++++++
 io/channel-file.c                       | 209 +++++++
 io/channel-socket.c                     | 718 ++++++++++++++++++++++++
 io/channel-tls.c                        | 381 +++++++++++++
 io/channel-watch.c                      | 200 +++++++
 io/channel-websock.c                    | 965 ++++++++++++++++++++++++++++++++
 io/channel.c                            | 229 ++++++++
 io/task.c                               | 150 +++++
 migration/Makefile.objs                 |   6 +-
 migration/exec.c                        |  48 +-
 migration/fd.c                          |  57 +-
 migration/migration.c                   |   9 +-
 migration/qemu-file-buf.c               | 462 ---------------
 migration/qemu-file-channel.c           | 200 +++++++
 migration/qemu-file-internal.h          |  53 --
 migration/qemu-file-stdio.c             | 194 -------
 migration/qemu-file-unix.c              | 238 --------
 migration/qemu-file.c                   | 113 ++--
 migration/rdma.c                        | 252 ++++++---
 migration/savevm.c                      |  16 +-
 migration/tcp.c                         | 372 ++++++++++--
 migration/unix.c                        | 103 ++--
 nbd.c                                   |   2 +-
 qapi-schema.json                        |   2 +
 qemu-char.c                             | 938 +++++++++++++++----------------
 qemu-coroutine-io.c                     |   2 +-
 qemu-coroutine-lock.c                   |   4 +-
 qemu-coroutine-sleep.c                  |   2 +-
 qemu-coroutine.c                        |   4 +-
 qemu-nbd.c                              | 102 ++--
 qemu-options.hx                         |  16 +-
 scripts/create_config                   |   9 +
 tests/.gitignore                        |   7 +
 tests/Makefile                          |  22 +-
 tests/io-channel-helpers.c              | 247 ++++++++
 tests/io-channel-helpers.h              |  33 ++
 tests/test-coroutine.c                  |   4 +-
 tests/test-io-channel-command.c         | 121 ++++
 tests/test-io-channel-file.c            |  89 +++
 tests/test-io-channel-socket.c          | 349 ++++++++++++
 tests/test-io-channel-tls.c             | 335 +++++++++++
 tests/test-io-task.c                    | 276 +++++++++
 tests/test-vmstate.c                    |  57 +-
 thread-pool.c                           |   2 +-
 ui/vnc-auth-sasl.c                      |  87 ++-
 ui/vnc-auth-vencrypt.c                  |  93 ++-
 ui/vnc-enc-tight.c                      |  38 +-
 ui/vnc-enc-zlib.c                       |   6 +-
 ui/vnc-enc-zrle.c                       |  18 +-
 ui/vnc-jobs.c                           |  25 +-
 ui/vnc-ws.c                             | 400 +++----------
 ui/vnc-ws.h                             |  71 +--
 ui/vnc.c                                | 799 ++++++++++++--------------
 ui/vnc.h                                |  77 +--
 util/oslib-posix.c                      |  71 +++
 util/oslib-win32.c                      |   9 +
 util/qemu-sockets.c                     | 427 ++++++++------
 97 files changed, 8875 insertions(+), 3085 deletions(-)
 create mode 100644 include/io/buffer.h
 create mode 100644 include/io/channel-command.h
 create mode 100644 include/io/channel-file.h
 create mode 100644 include/io/channel-socket.h
 create mode 100644 include/io/channel-tls.h
 create mode 100644 include/io/channel-watch.h
 create mode 100644 include/io/channel-websock.h
 create mode 100644 include/io/channel.h
 create mode 100644 include/io/task.h
 rename include/{block => qemu}/coroutine.h (100%)
 rename include/{block => qemu}/coroutine_int.h (98%)
 create mode 100644 io/Makefile.objs
 create mode 100644 io/buffer.c
 create mode 100644 io/channel-command.c
 create mode 100644 io/channel-file.c
 create mode 100644 io/channel-socket.c
 create mode 100644 io/channel-tls.c
 create mode 100644 io/channel-watch.c
 create mode 100644 io/channel-websock.c
 create mode 100644 io/channel.c
 create mode 100644 io/task.c
 delete mode 100644 migration/qemu-file-buf.c
 create mode 100644 migration/qemu-file-channel.c
 delete mode 100644 migration/qemu-file-internal.h
 delete mode 100644 migration/qemu-file-stdio.c
 delete mode 100644 migration/qemu-file-unix.c
 create mode 100644 tests/io-channel-helpers.c
 create mode 100644 tests/io-channel-helpers.h
 create mode 100644 tests/test-io-channel-command.c
 create mode 100644 tests/test-io-channel-file.c
 create mode 100644 tests/test-io-channel-socket.c
 create mode 100644 tests/test-io-channel-tls.c
 create mode 100644 tests/test-io-task.c

-- 
2.4.3

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 01/46] sockets: add helpers for creating SocketAddress from a socket
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 02/46] sockets: move qapi_copy_SocketAddress into qemu-sockets.c Daniel P. Berrange
                   ` (44 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Add two helper methods that, given a socket file descriptor,
can return a populated SocketAddress struct containing either
the local or remote address information.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/qemu/sockets.h |  30 ++++++++++++++
 util/qemu-sockets.c    | 110 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 140 insertions(+)

diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index c174b5c..3ea7cc9 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -88,4 +88,34 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp);
 int parse_host_port(struct sockaddr_in *saddr, const char *str);
 int socket_init(void);
 
+/**
+ * socket_local_address:
+ * @fd: the socket file handle
+ * @errp: pointer to 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 *socket_local_address(int fd, Error **errp);
+
+/**
+ * socket_remote_address:
+ * @fd: the socket file handle
+ * @errp: pointer to uninitialized error object
+ *
+ * Get the string representation of the remote 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_remote_address(int fd, Error **errp);
+
 #endif /* QEMU_SOCKET_H */
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 2add83a..2bdfacf 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1015,3 +1015,113 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp)
     qemu_opts_del(opts);
     return fd;
 }
+
+
+static SocketAddress *
+socket_sockaddr_to_address_inet(struct sockaddr_storage *sa,
+                                socklen_t salen,
+                                Error **errp)
+{
+    char host[NI_MAXHOST];
+    char serv[NI_MAXSERV];
+    SocketAddress *addr;
+    int ret;
+
+    ret = getnameinfo((struct sockaddr *)sa, salen,
+                      host, sizeof(host),
+                      serv, sizeof(serv),
+                      NI_NUMERICHOST | NI_NUMERICSERV);
+    if (ret != 0) {
+        error_setg(errp, "Cannot format numeric socket address: %s\n",
+                   gai_strerror(ret));
+        return NULL;
+    }
+
+    addr = g_new0(SocketAddress, 1);
+    addr->kind = SOCKET_ADDRESS_KIND_INET;
+    addr->inet = g_new0(InetSocketAddress, 1);
+    addr->inet->host = g_strdup(host);
+    addr->inet->port = g_strdup(serv);
+    if (sa->ss_family == AF_INET) {
+        addr->inet->ipv4 = true;
+    } else {
+        addr->inet->ipv6 = true;
+    }
+
+    return addr;
+}
+
+
+#ifndef WIN32
+static SocketAddress *
+socket_sockaddr_to_address_unix(struct sockaddr_storage *sa,
+                                socklen_t salen,
+                                Error **errp)
+{
+    SocketAddress *addr;
+    struct sockaddr_un *su = (struct sockaddr_un *)sa;
+
+    addr = g_new0(SocketAddress, 1);
+    addr->kind = SOCKET_ADDRESS_KIND_UNIX;
+    addr->q_unix = g_new0(UnixSocketAddress, 1);
+    if (su->sun_path[0]) {
+        addr->q_unix->path = g_strndup(su->sun_path,
+                                       sizeof(su->sun_path));
+    }
+
+    return addr;
+}
+#endif /* WIN32 */
+
+static SocketAddress *
+socket_sockaddr_to_address(struct sockaddr_storage *sa,
+                           socklen_t salen,
+                           Error **errp)
+{
+    switch (sa->ss_family) {
+    case AF_INET:
+    case AF_INET6:
+        return socket_sockaddr_to_address_inet(sa, salen, errp);
+
+#ifndef WIN32
+    case AF_UNIX:
+        return socket_sockaddr_to_address_unix(sa, salen, errp);
+#endif /* WIN32 */
+
+    default:
+        error_setg(errp, "socket family %d unsupported",
+                   sa->ss_family);
+        return NULL;
+    }
+    return 0;
+}
+
+
+SocketAddress *socket_local_address(int fd, Error **errp)
+{
+    struct sockaddr_storage ss;
+    socklen_t sslen = sizeof(ss);
+
+    if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0) {
+        error_setg_errno(errp, socket_error(), "%s",
+                         "Unable to query local socket address");
+        return NULL;
+    }
+
+    return socket_sockaddr_to_address(&ss, sslen, errp);
+}
+
+
+SocketAddress *socket_remote_address(int fd, Error **errp)
+{
+    struct sockaddr_storage ss;
+    socklen_t sslen = sizeof(ss);
+
+    if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) {
+        error_setg_errno(errp, socket_error(), "%s",
+                         "Unable to query remote socket address");
+        return NULL;
+    }
+
+    return socket_sockaddr_to_address(&ss, sslen, errp);
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 02/46] sockets: move qapi_copy_SocketAddress into qemu-sockets.c
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 01/46] sockets: add helpers for creating SocketAddress from a socket Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 03/46] sockets: allow port to be NULL when listening on IP address Daniel P. Berrange
                   ` (43 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The qapi_copy_SocketAddress method is going to be useful
in more places than just qemu-char.c, so move it into
the qemu-sockets.c file to allow its reuse.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/qemu/sockets.h |  4 ++++
 qemu-char.c            | 25 -------------------------
 util/qemu-sockets.c    | 30 ++++++++++++++++++++++++++++++
 3 files changed, 34 insertions(+), 25 deletions(-)

diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 3ea7cc9..5a183c5 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -118,4 +118,8 @@ SocketAddress *socket_local_address(int fd, Error **errp);
  */
 SocketAddress *socket_remote_address(int fd, Error **errp);
 
+
+void qapi_copy_SocketAddress(SocketAddress **p_dest,
+                             SocketAddress *src);
+
 #endif /* QEMU_SOCKET_H */
diff --git a/qemu-char.c b/qemu-char.c
index d956f8d..ed35f2a 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -93,31 +93,6 @@
 
 /***********************************************************/
 /* Socket address helpers */
-static void qapi_copy_SocketAddress(SocketAddress **p_dest,
-                                    SocketAddress *src)
-{
-    QmpOutputVisitor *qov;
-    QmpInputVisitor *qiv;
-    Visitor *ov, *iv;
-    QObject *obj;
-
-    *p_dest = NULL;
-
-    qov = qmp_output_visitor_new();
-    ov = qmp_output_get_visitor(qov);
-    visit_type_SocketAddress(ov, &src, NULL, &error_abort);
-    obj = qmp_output_get_qobject(qov);
-    qmp_output_visitor_cleanup(qov);
-    if (!obj) {
-        return;
-    }
-
-    qiv = qmp_input_visitor_new(obj);
-    iv = qmp_input_get_visitor(qiv);
-    visit_type_SocketAddress(iv, p_dest, NULL, &error_abort);
-    qmp_input_visitor_cleanup(qiv);
-    qobject_decref(obj);
-}
 
 static int SocketAddress_to_str(char *dest, int max_len,
                                 const char *prefix, SocketAddress *addr,
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 2bdfacf..9cc5bee 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -25,6 +25,9 @@
 #include "monitor/monitor.h"
 #include "qemu/sockets.h"
 #include "qemu/main-loop.h"
+#include "qapi/qmp-input-visitor.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi-visit.h"
 
 #ifndef AI_ADDRCONFIG
 # define AI_ADDRCONFIG 0
@@ -1125,3 +1128,30 @@ SocketAddress *socket_remote_address(int fd, Error **errp)
 
     return socket_sockaddr_to_address(&ss, sslen, errp);
 }
+
+
+void qapi_copy_SocketAddress(SocketAddress **p_dest,
+                             SocketAddress *src)
+{
+    QmpOutputVisitor *qov;
+    QmpInputVisitor *qiv;
+    Visitor *ov, *iv;
+    QObject *obj;
+
+    *p_dest = NULL;
+
+    qov = qmp_output_visitor_new();
+    ov = qmp_output_get_visitor(qov);
+    visit_type_SocketAddress(ov, &src, NULL, &error_abort);
+    obj = qmp_output_get_qobject(qov);
+    qmp_output_visitor_cleanup(qov);
+    if (!obj) {
+        return;
+    }
+
+    qiv = qmp_input_visitor_new(obj);
+    iv = qmp_input_get_visitor(qiv);
+    visit_type_SocketAddress(iv, p_dest, NULL, &error_abort);
+    qmp_input_visitor_cleanup(qiv);
+    qobject_decref(obj);
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 03/46] sockets: allow port to be NULL when listening on IP address
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 01/46] sockets: add helpers for creating SocketAddress from a socket Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 02/46] sockets: move qapi_copy_SocketAddress into qemu-sockets.c Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 04/46] osdep: add qemu_fork() wrapper for safely handling signals Daniel P. Berrange
                   ` (42 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

If the port in the SocketAddress struct is NULL, it can allow
the kernel to automatically select a free port. This is useful
in particular in unit tests to avoid a race trying to find a
free port to run a test case on.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 util/qemu-sockets.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 9cc5bee..f9d2f6e 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -128,12 +128,15 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
     ai.ai_family = PF_UNSPEC;
     ai.ai_socktype = SOCK_STREAM;
 
-    if ((qemu_opt_get(opts, "host") == NULL) ||
-        (qemu_opt_get(opts, "port") == NULL)) {
-        error_setg(errp, "host and/or port not specified");
+    if ((qemu_opt_get(opts, "host") == NULL)) {
+        error_setg(errp, "host not specified");
         return -1;
     }
-    pstrcpy(port, sizeof(port), qemu_opt_get(opts, "port"));
+    if (qemu_opt_get(opts, "port") != NULL) {
+        pstrcpy(port, sizeof(port), qemu_opt_get(opts, "port"));
+    } else {
+        port[0] = '\0';
+    }
     addr = qemu_opt_get(opts, "host");
 
     to = qemu_opt_get_number(opts, "to", 0);
@@ -145,6 +148,10 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
     /* lookup */
     if (port_offset) {
         unsigned long long baseport;
+        if (strlen(port) == 0) {
+            error_setg(errp, "port not specified");
+            return -1;
+        }
         if (parse_uint_full(port, &baseport, 10) < 0) {
             error_setg(errp, "can't convert to a number: %s", port);
             return -1;
@@ -156,7 +163,8 @@ int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
         }
         snprintf(port, sizeof(port), "%d", (int)baseport + port_offset);
     }
-    rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
+    rc = getaddrinfo(strlen(addr) ? addr : NULL,
+                     strlen(port) ? port : NULL, &ai, &res);
     if (rc != 0) {
         error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
                    gai_strerror(rc));
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 04/46] osdep: add qemu_fork() wrapper for safely handling signals
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (2 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 03/46] sockets: allow port to be NULL when listening on IP address Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 05/46] coroutine: move into libqemuutil.a library Daniel P. Berrange
                   ` (41 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

When using regular fork() the child process of course inherits
all the parents' signal handlers. If the child then proceeds
to close() any open file descriptors, it may break some of those
registered signal handlers. The child generally does not want to
ever run any of the signal handlers tha parent may have installed
in the short time before it exec's. The parent may also have blocked
various signals which the child process will want enabled.

This introduces a wrapper qemu_fork() that takes care to sanitize
signal handling across fork. Before forking it blocks all signals
in the parent thread. After fork returns, the parent unblocks the
signals and carries on as usual. The child, however, resets all the
signal handlers back to their defaults before it unblocks signals.
The child process can now exec the binary in a "clean" signal
environment.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/qemu/osdep.h | 16 ++++++++++++
 util/oslib-posix.c   | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 util/oslib-win32.c   |  9 +++++++
 3 files changed, 96 insertions(+)

diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index e490028..b1676c2 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -69,6 +69,8 @@
 #include "sysemu/os-posix.h"
 #endif
 
+#include "qapi/error.h"
+
 #if defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10
 /* [u]int_fast*_t not in <sys/int_types.h> */
 typedef unsigned char           uint_fast8_t;
@@ -286,4 +288,18 @@ void os_mem_prealloc(int fd, char *area, size_t sz);
 
 int qemu_read_password(char *buf, int buf_size);
 
+/**
+ * qemu_fork:
+ *
+ * A version of fork that avoids signal handler race
+ * conditions that can lead to child process getting
+ * signals that are otherwise only expected by the
+ * parent. It also resets all signal handlers to the
+ * default settings.
+ *
+ * Returns 0 to child process, pid number to parent
+ * or -1 on failure.
+ */
+pid_t qemu_fork(Error **errp);
+
 #endif
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 3ae4987..3e57e87 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -482,3 +482,74 @@ int qemu_read_password(char *buf, int buf_size)
     printf("\n");
     return ret;
 }
+
+
+pid_t qemu_fork(Error **errp)
+{
+    sigset_t oldmask, newmask;
+    struct sigaction sig_action;
+    int saved_errno;
+    pid_t pid;
+
+    /*
+     * Need to block signals now, so that child process can safely
+     * kill off caller's signal handlers without a race.
+     */
+    sigfillset(&newmask);
+    if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) {
+        error_setg_errno(errp, errno,
+                         "cannot block signals");
+        return -1;
+    }
+
+    pid = fork();
+    saved_errno = errno;
+
+    if (pid < 0) {
+        /* attempt to restore signal mask, but ignore failure, to
+         * avoid obscuring the fork failure */
+        (void)pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
+        error_setg_errno(errp, saved_errno,
+                         "cannot fork child process");
+        errno = saved_errno;
+        return -1;
+    } else if (pid) {
+        /* parent process */
+
+        /* Restore our original signal mask now that the child is
+         * safely running. Only documented failures are EFAULT (not
+         * possible, since we are using just-grabbed mask) or EINVAL
+         * (not possible, since we are using correct arguments).  */
+        (void)pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
+    } else {
+        /* child process */
+        size_t i;
+
+        /* Clear out all signal handlers from parent so nothing
+         * unexpected can happen in our child once we unblock
+         * signals */
+        sig_action.sa_handler = SIG_DFL;
+        sig_action.sa_flags = 0;
+        sigemptyset(&sig_action.sa_mask);
+
+        for (i = 1; i < NSIG; i++) {
+            /* Only possible errors are EFAULT or EINVAL The former
+             * won't happen, the latter we expect, so no need to check
+             * return value */
+            (void)sigaction(i, &sig_action, NULL);
+        }
+
+        /* Unmask all signals in child, since we've no idea what the
+         * caller's done with their signal mask and don't want to
+         * propagate that to children */
+        sigemptyset(&newmask);
+        if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) {
+            Error *local_err = NULL;
+            error_setg(&local_err, errno,
+                       "cannot unblock signals");
+            error_report_err(local_err);
+            _exit(1);
+        }
+    }
+    return pid;
+}
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index 08f5a9c..09f9e98 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -496,3 +496,12 @@ int qemu_read_password(char *buf, int buf_size)
     buf[i] = '\0';
     return 0;
 }
+
+
+pid_t qemu_fork(Error **errp)
+{
+    errno = ENOSYS;
+    error_setg_errno(errp, errno,
+                     "cannot fork child process");
+    return -1;
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 05/46] coroutine: move into libqemuutil.a library
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (3 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 04/46] osdep: add qemu_fork() wrapper for safely handling signals Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 06/46] io: add abstract QIOChannel classes Daniel P. Berrange
                   ` (40 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The coroutine files are currently referenced by the block-obj-y
variable. The coroutine functionality though is already used by
more than just the block code. eg migration code uses coroutine
yield. In the future the I/O channel code will also use the
coroutine yield functionality. Since the coroutine code is nicely
self-contained it can be easily built as part of the libqemuutil.a
library, making it widely available.

The headers are also moved into include/qemu, instead of the
include/block directory, since they are now part of the util
codebase, and the impl was never in the block/ directory
either.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 Makefile.objs                           | 7 +++----
 block.c                                 | 2 +-
 block/qcow2.h                           | 2 +-
 block/vdi.c                             | 2 +-
 block/write-threshold.c                 | 2 +-
 blockjob.c                              | 2 +-
 coroutine-gthread.c                     | 2 +-
 coroutine-sigaltstack.c                 | 2 +-
 coroutine-ucontext.c                    | 2 +-
 coroutine-win32.c                       | 2 +-
 hw/9pfs/codir.c                         | 2 +-
 hw/9pfs/cofile.c                        | 2 +-
 hw/9pfs/cofs.c                          | 2 +-
 hw/9pfs/coxattr.c                       | 2 +-
 hw/9pfs/virtio-9p-coth.c                | 2 +-
 hw/9pfs/virtio-9p-coth.h                | 2 +-
 hw/9pfs/virtio-9p.h                     | 2 +-
 include/block/block.h                   | 2 +-
 include/block/block_int.h               | 2 +-
 include/{block => qemu}/coroutine.h     | 0
 include/{block => qemu}/coroutine_int.h | 2 +-
 migration/qemu-file-buf.c               | 2 +-
 migration/qemu-file-stdio.c             | 2 +-
 migration/qemu-file-unix.c              | 2 +-
 migration/qemu-file.c                   | 2 +-
 migration/rdma.c                        | 2 +-
 nbd.c                                   | 2 +-
 qemu-coroutine-io.c                     | 2 +-
 qemu-coroutine-lock.c                   | 4 ++--
 qemu-coroutine-sleep.c                  | 2 +-
 qemu-coroutine.c                        | 4 ++--
 tests/test-coroutine.c                  | 4 ++--
 tests/test-vmstate.c                    | 2 +-
 thread-pool.c                           | 2 +-
 34 files changed, 38 insertions(+), 39 deletions(-)
 rename include/{block => qemu}/coroutine.h (100%)
 rename include/{block => qemu}/coroutine_int.h (98%)

diff --git a/Makefile.objs b/Makefile.objs
index 2ff4224..8ce5c2d 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,6 +2,9 @@
 # Common libraries for tools and emulators
 stub-obj-y = stubs/
 util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o
+util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
+util-obj-y += qemu-coroutine-sleep.o
+util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
 
 #######################################################################
 # block-obj-y is code used by both qemu system emulation and qemu-img
@@ -14,10 +17,6 @@ block-obj-$(CONFIG_WIN32) += aio-win32.o
 block-obj-y += block/
 block-obj-y += qemu-io-cmds.o
 
-block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
-block-obj-y += qemu-coroutine-sleep.o
-block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
-
 block-obj-m = block/
 
 #######################################################################
diff --git a/block.c b/block.c
index d088ee0..ea2f67e 100644
--- a/block.c
+++ b/block.c
@@ -33,7 +33,7 @@
 #include "sysemu/block-backend.h"
 #include "sysemu/sysemu.h"
 #include "qemu/notify.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "block/qapi.h"
 #include "qmp-commands.h"
 #include "qemu/timer.h"
diff --git a/block/qcow2.h b/block/qcow2.h
index 72e1328..9365350 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -26,7 +26,7 @@
 #define BLOCK_QCOW2_H
 
 #include "crypto/cipher.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 
 //#define DEBUG_ALLOC
 //#define DEBUG_ALLOC2
diff --git a/block/vdi.c b/block/vdi.c
index 7642ef3..6273c6c 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -53,7 +53,7 @@
 #include "block/block_int.h"
 #include "qemu/module.h"
 #include "migration/migration.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 
 #if defined(CONFIG_UUID)
 #include <uuid/uuid.h>
diff --git a/block/write-threshold.c b/block/write-threshold.c
index a53c1f5..0fe3891 100644
--- a/block/write-threshold.c
+++ b/block/write-threshold.c
@@ -11,7 +11,7 @@
  */
 
 #include "block/block_int.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "block/write-threshold.h"
 #include "qemu/notify.h"
 #include "qapi-event.h"
diff --git a/blockjob.c b/blockjob.c
index 62bb906..dc42d15 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -31,7 +31,7 @@
 #include "block/block_int.h"
 #include "qapi/qmp/qerror.h"
 #include "qapi/qmp/qjson.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "qmp-commands.h"
 #include "qemu/timer.h"
 #include "qapi-event.h"
diff --git a/coroutine-gthread.c b/coroutine-gthread.c
index 6bd6d6b..0bcd778 100644
--- a/coroutine-gthread.c
+++ b/coroutine-gthread.c
@@ -20,7 +20,7 @@
 
 #include <glib.h>
 #include "qemu-common.h"
-#include "block/coroutine_int.h"
+#include "qemu/coroutine_int.h"
 
 typedef struct {
     Coroutine base;
diff --git a/coroutine-sigaltstack.c b/coroutine-sigaltstack.c
index 63519ff..39842a4 100644
--- a/coroutine-sigaltstack.c
+++ b/coroutine-sigaltstack.c
@@ -31,7 +31,7 @@
 #include <pthread.h>
 #include <signal.h>
 #include "qemu-common.h"
-#include "block/coroutine_int.h"
+#include "qemu/coroutine_int.h"
 
 typedef struct {
     Coroutine base;
diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c
index 259fcb4..26cbebb 100644
--- a/coroutine-ucontext.c
+++ b/coroutine-ucontext.c
@@ -27,7 +27,7 @@
 #include <stdint.h>
 #include <ucontext.h>
 #include "qemu-common.h"
-#include "block/coroutine_int.h"
+#include "qemu/coroutine_int.h"
 
 #ifdef CONFIG_VALGRIND_H
 #include <valgrind/valgrind.h>
diff --git a/coroutine-win32.c b/coroutine-win32.c
index 17ace37..4f922c5 100644
--- a/coroutine-win32.c
+++ b/coroutine-win32.c
@@ -23,7 +23,7 @@
  */
 
 #include "qemu-common.h"
-#include "block/coroutine_int.h"
+#include "qemu/coroutine_int.h"
 
 typedef struct
 {
diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c
index 65ad329..ec9cc7f 100644
--- a/hw/9pfs/codir.c
+++ b/hw/9pfs/codir.c
@@ -14,7 +14,7 @@
 
 #include "fsdev/qemu-fsdev.h"
 #include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "virtio-9p-coth.h"
 
 int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent,
diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c
index 2efebf3..7cb55ee 100644
--- a/hw/9pfs/cofile.c
+++ b/hw/9pfs/cofile.c
@@ -14,7 +14,7 @@
 
 #include "fsdev/qemu-fsdev.h"
 #include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "virtio-9p-coth.h"
 
 int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c
index 42ee614..e1953a9 100644
--- a/hw/9pfs/cofs.c
+++ b/hw/9pfs/cofs.c
@@ -14,7 +14,7 @@
 
 #include "fsdev/qemu-fsdev.h"
 #include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "virtio-9p-coth.h"
 
 static ssize_t __readlink(V9fsState *s, V9fsPath *path, V9fsString *buf)
diff --git a/hw/9pfs/coxattr.c b/hw/9pfs/coxattr.c
index 18ee08d..55c0d23 100644
--- a/hw/9pfs/coxattr.c
+++ b/hw/9pfs/coxattr.c
@@ -14,7 +14,7 @@
 
 #include "fsdev/qemu-fsdev.h"
 #include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "virtio-9p-coth.h"
 
 int v9fs_co_llistxattr(V9fsPDU *pdu, V9fsPath *path, void *value, size_t size)
diff --git a/hw/9pfs/virtio-9p-coth.c b/hw/9pfs/virtio-9p-coth.c
index 8185c53..5057f8d 100644
--- a/hw/9pfs/virtio-9p-coth.c
+++ b/hw/9pfs/virtio-9p-coth.c
@@ -15,7 +15,7 @@
 #include "fsdev/qemu-fsdev.h"
 #include "qemu/thread.h"
 #include "qemu/event_notifier.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "virtio-9p-coth.h"
 
 /* v9fs glib thread pool */
diff --git a/hw/9pfs/virtio-9p-coth.h b/hw/9pfs/virtio-9p-coth.h
index 4f51b25..0fbe49a 100644
--- a/hw/9pfs/virtio-9p-coth.h
+++ b/hw/9pfs/virtio-9p-coth.h
@@ -16,7 +16,7 @@
 #define _QEMU_VIRTIO_9P_COTH_H
 
 #include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "virtio-9p.h"
 #include <glib.h>
 
diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
index 2e7d488..d7a4dc1 100644
--- a/hw/9pfs/virtio-9p.h
+++ b/hw/9pfs/virtio-9p.h
@@ -13,7 +13,7 @@
 #include "fsdev/file-op-9p.h"
 #include "fsdev/virtio-9p-marshal.h"
 #include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 
 enum {
     P9_TLERROR = 6,
diff --git a/include/block/block.h b/include/block/block.h
index 37916f7..069e5f8 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -4,7 +4,7 @@
 #include "block/aio.h"
 #include "qemu-common.h"
 #include "qemu/option.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "block/accounting.h"
 #include "qapi/qmp/qobject.h"
 #include "qapi-types.h"
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 14ad4c3..c37ed77 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -28,7 +28,7 @@
 #include "block/block.h"
 #include "qemu/option.h"
 #include "qemu/queue.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "qemu/timer.h"
 #include "qapi-types.h"
 #include "qemu/hbitmap.h"
diff --git a/include/block/coroutine.h b/include/qemu/coroutine.h
similarity index 100%
rename from include/block/coroutine.h
rename to include/qemu/coroutine.h
diff --git a/include/block/coroutine_int.h b/include/qemu/coroutine_int.h
similarity index 98%
rename from include/block/coroutine_int.h
rename to include/qemu/coroutine_int.h
index 9aa1aae..42d6838 100644
--- a/include/block/coroutine_int.h
+++ b/include/qemu/coroutine_int.h
@@ -26,7 +26,7 @@
 #define QEMU_COROUTINE_INT_H
 
 #include "qemu/queue.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 
 typedef enum {
     COROUTINE_YIELD = 1,
diff --git a/migration/qemu-file-buf.c b/migration/qemu-file-buf.c
index 2de9330..556f5dc 100644
--- a/migration/qemu-file-buf.c
+++ b/migration/qemu-file-buf.c
@@ -29,7 +29,7 @@
 #include "qemu/error-report.h"
 #include "qemu/iov.h"
 #include "qemu/sockets.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "migration/migration.h"
 #include "migration/qemu-file.h"
 #include "migration/qemu-file-internal.h"
diff --git a/migration/qemu-file-stdio.c b/migration/qemu-file-stdio.c
index 285068b..002dc5d 100644
--- a/migration/qemu-file-stdio.c
+++ b/migration/qemu-file-stdio.c
@@ -22,7 +22,7 @@
  * THE SOFTWARE.
  */
 #include "qemu-common.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "migration/qemu-file.h"
 
 typedef struct QEMUFileStdio {
diff --git a/migration/qemu-file-unix.c b/migration/qemu-file-unix.c
index bfbc086..e4f195a 100644
--- a/migration/qemu-file-unix.c
+++ b/migration/qemu-file-unix.c
@@ -24,7 +24,7 @@
 #include "qemu-common.h"
 #include "qemu/iov.h"
 #include "qemu/sockets.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "migration/qemu-file.h"
 #include "migration/qemu-file-internal.h"
 
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index 6bb3dc1..d2359c4 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -26,7 +26,7 @@
 #include "qemu/error-report.h"
 #include "qemu/iov.h"
 #include "qemu/sockets.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "migration/migration.h"
 #include "migration/qemu-file.h"
 #include "migration/qemu-file-internal.h"
diff --git a/migration/rdma.c b/migration/rdma.c
index 74876fd..9c64546 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -19,7 +19,7 @@
 #include "qemu/main-loop.h"
 #include "qemu/sockets.h"
 #include "qemu/bitmap.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/socket.h>
diff --git a/nbd.c b/nbd.c
index 06b501b..89ae66e 100644
--- a/nbd.c
+++ b/nbd.c
@@ -19,7 +19,7 @@
 #include "block/nbd.h"
 #include "sysemu/block-backend.h"
 
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 
 #include <errno.h>
 #include <string.h>
diff --git a/qemu-coroutine-io.c b/qemu-coroutine-io.c
index 28dc735..e1eae73 100644
--- a/qemu-coroutine-io.c
+++ b/qemu-coroutine-io.c
@@ -24,7 +24,7 @@
  */
 #include "qemu-common.h"
 #include "qemu/sockets.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "qemu/iov.h"
 #include "qemu/main-loop.h"
 
diff --git a/qemu-coroutine-lock.c b/qemu-coroutine-lock.c
index 6b49033..130ee19 100644
--- a/qemu-coroutine-lock.c
+++ b/qemu-coroutine-lock.c
@@ -23,8 +23,8 @@
  */
 
 #include "qemu-common.h"
-#include "block/coroutine.h"
-#include "block/coroutine_int.h"
+#include "qemu/coroutine.h"
+#include "qemu/coroutine_int.h"
 #include "qemu/queue.h"
 #include "trace.h"
 
diff --git a/qemu-coroutine-sleep.c b/qemu-coroutine-sleep.c
index 9abb7fd..b35db56 100644
--- a/qemu-coroutine-sleep.c
+++ b/qemu-coroutine-sleep.c
@@ -11,7 +11,7 @@
  *
  */
 
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "qemu/timer.h"
 #include "block/aio.h"
 
diff --git a/qemu-coroutine.c b/qemu-coroutine.c
index c17a92b..8953560 100644
--- a/qemu-coroutine.c
+++ b/qemu-coroutine.c
@@ -16,8 +16,8 @@
 #include "qemu-common.h"
 #include "qemu/thread.h"
 #include "qemu/atomic.h"
-#include "block/coroutine.h"
-#include "block/coroutine_int.h"
+#include "qemu/coroutine.h"
+#include "qemu/coroutine_int.h"
 
 enum {
     POOL_BATCH_SIZE = 64,
diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c
index b552d9f..f5951cb 100644
--- a/tests/test-coroutine.c
+++ b/tests/test-coroutine.c
@@ -12,8 +12,8 @@
  */
 
 #include <glib.h>
-#include "block/coroutine.h"
-#include "block/coroutine_int.h"
+#include "qemu/coroutine.h"
+#include "qemu/coroutine_int.h"
 
 /*
  * Check that qemu_in_coroutine() works
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
index 1d620e0..4d13bd0 100644
--- a/tests/test-vmstate.c
+++ b/tests/test-vmstate.c
@@ -27,7 +27,7 @@
 #include "qemu-common.h"
 #include "migration/migration.h"
 #include "migration/vmstate.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 
 static char temp_file[] = "/tmp/vmst.test.XXXXXX";
 static int temp_fd;
diff --git a/thread-pool.c b/thread-pool.c
index ac909f4..402c778 100644
--- a/thread-pool.c
+++ b/thread-pool.c
@@ -18,7 +18,7 @@
 #include "qemu/queue.h"
 #include "qemu/thread.h"
 #include "qemu/osdep.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
 #include "trace.h"
 #include "block/thread-pool.h"
 #include "qemu/main-loop.h"
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 06/46] io: add abstract QIOChannel classes
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (4 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 05/46] coroutine: move into libqemuutil.a library Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 07/46] io: add helper module for creating watches on FDs Daniel P. Berrange
                   ` (39 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Start the new generic I/O channel framework by defining a
QIOChannel abstract base class. This is designed to feel
similar to GLib's GIOChannel, but with the addition of
support for using iovecs, qemu error reporting, file
descriptor passing, coroutine integration and use of
the QOM framework for easier sub-classing.

The intention is that anywhere in QEMU that almost
anywhere that deals with sockets will use this new I/O
infrastructure, so that it becomes trivial to then layer
in support for TLS encryption. This will at least include
the VNC server, char device backend and migration code.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 MAINTAINERS          |   7 +
 Makefile             |   2 +
 Makefile.objs        |   5 +
 Makefile.target      |   2 +
 include/io/channel.h | 374 +++++++++++++++++++++++++++++++++++++++++++++++++++
 io/Makefile.objs     |   1 +
 io/channel.c         | 229 +++++++++++++++++++++++++++++++
 7 files changed, 620 insertions(+)
 create mode 100644 include/io/channel.h
 create mode 100644 io/Makefile.objs
 create mode 100644 io/channel.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 08f356a..3e48607 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1078,6 +1078,13 @@ F: crypto/
 F: include/crypto/
 F: tests/test-crypto-*
 
+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 ee78930..5723649 100644
--- a/Makefile
+++ b/Makefile
@@ -153,6 +153,7 @@ dummy := $(call unnest-vars,, \
                 block-obj-m \
                 crypto-obj-y \
                 qom-obj-y \
+                io-obj-y \
                 common-obj-y \
                 common-obj-m)
 
@@ -176,6 +177,7 @@ SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
 $(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
 $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
 $(SOFTMMU_SUBDIR_RULES): $(qom-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 8ce5c2d..f988313 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -29,6 +29,11 @@ crypto-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/
+
 ######################################################################
 # smartcard
 
diff --git a/Makefile.target b/Makefile.target
index 0d968d6..514e5ac 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -172,6 +172,7 @@ dummy := $(call unnest-vars,.., \
                block-obj-m \
                crypto-obj-y \
                qom-obj-y \
+               io-obj-y \
                common-obj-y \
                common-obj-m)
 target-obj-y := $(target-obj-y-save)
@@ -180,6 +181,7 @@ all-obj-y += $(target-obj-y)
 all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
 all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
 all-obj-$(CONFIG_SOFTMMU) += $(qom-obj-y)
+all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
 
 $(QEMU_PROG_BUILD): config-devices.mak
 
diff --git a/include/io/channel.h b/include/io/channel.h
new file mode 100644
index 0000000..314bcc3
--- /dev/null
+++ b/include/io/channel.h
@@ -0,0 +1,374 @@
+/*
+ * 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 {
+    QIO_CHANNEL_FEATURE_FD_PASS = (1 << 0),
+} QIOChannelFeature;
+
+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
+ *  - Peeking at incoming data without reading it
+ *
+ * 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;
+};
+
+/**
+ * QIOChannelClass:
+ *
+ * This class defines the contract that all subclasses
+ * must follow to provide specific channel implementations.
+ * All the callbacks are mandatory with the exception of
+ * io_has_feature, which defaults to returning false.
+ *
+ * Consult the corresponding public API docs for a description
+ * of the semantics of each callback
+ */
+struct QIOChannelClass {
+    ObjectClass parent;
+
+    bool (*io_has_feature)(QIOChannel *ioc,
+                           QIOChannelFeature feature);
+    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);
+    void (*io_set_blocking)(QIOChannel *ioc,
+                            bool enabled);
+};
+
+/* 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
+ *
+ * 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
+ */
+void qio_channel_set_blocking(QIOChannel *ioc,
+                              bool enabled);
+
+/**
+ * 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_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/io/Makefile.objs b/io/Makefile.objs
new file mode 100644
index 0000000..a6ed361
--- /dev/null
+++ b/io/Makefile.objs
@@ -0,0 +1 @@
+io-obj-y = channel.o
diff --git a/io/channel.c b/io/channel.c
new file mode 100644
index 0000000..d234b06
--- /dev/null
+++ b/io/channel.c
@@ -0,0 +1,229 @@
+/*
+ * 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)
+{
+    QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+
+    if (!klass->io_has_feature) {
+        return false;
+    }
+
+    return klass->io_has_feature(ioc, 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);
+
+    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);
+    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);
+}
+
+void qio_channel_set_blocking(QIOChannel *ioc,
+                              bool enabled)
+{
+    QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
+    klass->io_set_blocking(ioc, enabled);
+}
+
+
+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;
+}
+
+typedef struct {
+    QIOChannel *ioc;
+    Coroutine *co;
+} QIOChannelYieldData;
+
+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 void qio_channel_init(Object *obj G_GNUC_UNUSED)
+{
+    /* nada */
+}
+
+static void qio_channel_finalize(Object *obj G_GNUC_UNUSED)
+{
+    /* nada */
+}
+
+
+static const TypeInfo qio_channel_info = {
+    .parent = TYPE_OBJECT,
+    .name = TYPE_QIO_CHANNEL,
+    .instance_size = sizeof(QIOChannel),
+    .instance_init = qio_channel_init,
+    .instance_finalize = qio_channel_finalize,
+    .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);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 07/46] io: add helper module for creating watches on FDs
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (5 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 06/46] io: add abstract QIOChannel classes Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 08/46] io: pull Buffer code out of VNC module Daniel P. Berrange
                   ` (38 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

A number of the channel implementations will require the
ability to create watches on file descriptors / sockets.
To avoid duplicating this code in each channel, provide a
helper API for dealing with file descriptor watches.

There are two watch implementations provided. The first
is useful for bi-directional file descriptors such as
sockets, regular files, character devices, etc. The
second works with a pair of unidirectional file descriptors
such as pipes.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/io/channel-watch.h |  72 ++++++++++++++++
 io/Makefile.objs           |   1 +
 io/channel-watch.c         | 200 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 273 insertions(+)
 create mode 100644 include/io/channel-watch.h
 create mode 100644 io/channel-watch.c

diff --git a/include/io/channel-watch.h b/include/io/channel-watch.h
new file mode 100644
index 0000000..656358a
--- /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/io/Makefile.objs b/io/Makefile.objs
index a6ed361..b02ea90 100644
--- a/io/Makefile.objs
+++ b/io/Makefile.objs
@@ -1 +1,2 @@
 io-obj-y = channel.o
+io-obj-y += channel-watch.o
diff --git a/io/channel-watch.c b/io/channel-watch.c
new file mode 100644
index 0000000..9564605
--- /dev/null
+++ b/io/channel-watch.c
@@ -0,0 +1,200 @@
+/*
+ * 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));
+    g_source_set_name(source, "QIOChannelFD");
+    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));
+    g_source_set_name(source, "QIOChannelFDPair");
+    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;
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 08/46] io: pull Buffer code out of VNC module
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (6 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 07/46] io: add helper module for creating watches on FDs Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 09/46] io: add QIOTask class for async operations Daniel P. Berrange
                   ` (37 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The Buffer code in the VNC server is useful for the IO channel
code, so pull it out into a shared module, QIOBuffer.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/io/buffer.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 io/Makefile.objs    |   3 +-
 io/buffer.c         |  65 +++++++++++++++++++++++++++++
 ui/vnc-auth-sasl.c  |   4 +-
 ui/vnc-enc-tight.c  |  38 ++++++++---------
 ui/vnc-enc-zlib.c   |   6 +--
 ui/vnc-enc-zrle.c   |  18 ++++----
 ui/vnc-jobs.c       |  15 +++----
 ui/vnc-ws.c         |  36 ++++++++--------
 ui/vnc-ws.h         |   6 +--
 ui/vnc.c            |  67 ++++++-----------------------
 ui/vnc.h            |  50 ++++++++--------------
 12 files changed, 277 insertions(+), 149 deletions(-)
 create mode 100644 include/io/buffer.h
 create mode 100644 io/buffer.c

diff --git a/include/io/buffer.h b/include/io/buffer.h
new file mode 100644
index 0000000..2b1b261
--- /dev/null
+++ b/include/io/buffer.h
@@ -0,0 +1,118 @@
+/*
+ * QEMU I/O buffers
+ *
+ * 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_BUFFER_H__
+#define QIO_BUFFER_H__
+
+#include "qemu-common.h"
+
+typedef struct QIOBuffer QIOBuffer;
+
+/**
+ * QIOBuffer:
+ *
+ * The QIOBuffer object provides a simple dynamically resizing
+ * array, with separate tracking of capacity and usage. This
+ * is typically useful when buffering I/O data.
+ */
+
+struct QIOBuffer {
+    size_t capacity;
+    size_t offset;
+    uint8_t *buffer;
+};
+
+/**
+ * qio_buffer_reserve:
+ * @buffer: the buffer object
+ * @len: the minimum required free space
+ *
+ * Ensure that the buffer has space allocated for at least
+ * @len bytes. If the current buffer is too small, it will
+ * be reallocated, possibly to a larger size than requested.
+ */
+void qio_buffer_reserve(QIOBuffer *buffer, size_t len);
+
+/**
+ * qio_buffer_reset:
+ * @buffer: the buffer object
+ *
+ * Reset the length of the stored data to zero, but do
+ * not free / reallocate the memory buffer
+ */
+void qio_buffer_reset(QIOBuffer *buffer);
+
+/**
+ * qio_buffer_free:
+ * @buffer: the buffer object
+ *
+ * Reset the length of the stored data to zero and also
+ * free the internal memory buffer
+ */
+void qio_buffer_free(QIOBuffer *buffer);
+
+/**
+ * qio_buffer_append:
+ * @buffer: the buffer object
+ * @data: the data block to append
+ * @len: the length of @data in bytes
+ *
+ * Append the contents of @data to the end of the buffer.
+ * The caller must ensure that the buffer has sufficient
+ * free space for @len bytes, typically by calling the
+ * qio_buffer_reserve() method prior to appending.
+ */
+void qio_buffer_append(QIOBuffer *buffer, const void *data, size_t len);
+
+/**
+ * qio_buffer_advance:
+ * @buffer: the buffer object
+ * @len: the number of bytes to skip
+ *
+ * Remove @len bytes of data from the head of the buffer.
+ * The internal buffer will not be reallocated, so will
+ * have at least @len bytes of free space after this
+ * call completes
+ */
+void qio_buffer_advance(QIOBuffer *buffer, size_t len);
+
+/**
+ * qio_buffer_end:
+ * @buffer: the buffer object
+ *
+ * Get a pointer to the tail end of the internal buffer
+ * The returned pointer is only valid until the next
+ * call to qio_buffer_reserve().
+ *
+ * Returns: the tail of the buffer
+ */
+uint8_t *qio_buffer_end(QIOBuffer *buffer);
+
+/**
+ * qio_buffer_empty:
+ * @buffer: the buffer object
+ *
+ * Determine if the buffer contains any current data
+ *
+ * Returns: true if the buffer holds data, false otherwise
+ */
+gboolean qio_buffer_empty(QIOBuffer *buffer);
+
+#endif /* QIO_BUFFER_H__ */
diff --git a/io/Makefile.objs b/io/Makefile.objs
index b02ea90..593ed9e 100644
--- a/io/Makefile.objs
+++ b/io/Makefile.objs
@@ -1,2 +1,3 @@
-io-obj-y = channel.o
+io-obj-y = buffer.o
+io-obj-y += channel.o
 io-obj-y += channel-watch.o
diff --git a/io/buffer.c b/io/buffer.c
new file mode 100644
index 0000000..68ae68d
--- /dev/null
+++ b/io/buffer.c
@@ -0,0 +1,65 @@
+/*
+ * QEMU I/O buffers
+ *
+ * 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/buffer.h"
+
+void qio_buffer_reserve(QIOBuffer *buffer, size_t len)
+{
+    if ((buffer->capacity - buffer->offset) < len) {
+        buffer->capacity += (len + 1024);
+        buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
+    }
+}
+
+gboolean qio_buffer_empty(QIOBuffer *buffer)
+{
+    return buffer->offset == 0;
+}
+
+uint8_t *qio_buffer_end(QIOBuffer *buffer)
+{
+    return buffer->buffer + buffer->offset;
+}
+
+void qio_buffer_reset(QIOBuffer *buffer)
+{
+    buffer->offset = 0;
+}
+
+void qio_buffer_free(QIOBuffer *buffer)
+{
+    g_free(buffer->buffer);
+    buffer->offset = 0;
+    buffer->capacity = 0;
+    buffer->buffer = NULL;
+}
+
+void qio_buffer_append(QIOBuffer *buffer, const void *data, size_t len)
+{
+    memcpy(buffer->buffer + buffer->offset, data, len);
+    buffer->offset += len;
+}
+
+void qio_buffer_advance(QIOBuffer *buffer, size_t len)
+{
+    memmove(buffer->buffer, buffer->buffer + len,
+            (buffer->offset - len));
+    buffer->offset -= len;
+}
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index fc732bd..d118266 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -113,8 +113,8 @@ long vnc_client_read_sasl(VncState *vs)
         return vnc_client_io_error(vs, -1, -EIO);
     VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
               encoded, ret, decoded, decodedLen);
-    buffer_reserve(&vs->input, decodedLen);
-    buffer_append(&vs->input, decoded, decodedLen);
+    qio_buffer_reserve(&vs->input, decodedLen);
+    qio_buffer_append(&vs->input, decoded, decodedLen);
     return decodedLen;
 }
 
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
index 9a9ddf2..772ec79 100644
--- a/ui/vnc-enc-tight.c
+++ b/ui/vnc-enc-tight.c
@@ -856,7 +856,7 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
     }
 
     /* reserve memory in output buffer */
-    buffer_reserve(&vs->tight.zlib, bytes + 64);
+    qio_buffer_reserve(&vs->tight.zlib, bytes + 64);
 
     /* set pointers */
     zstream->next_in = vs->tight.tight.buffer;
@@ -879,7 +879,7 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
     tight_send_compact_size(vs, bytes);
     vnc_write(vs, vs->tight.zlib.buffer, bytes);
 
-    buffer_reset(&vs->tight.zlib);
+    qio_buffer_reset(&vs->tight.zlib);
 
     return bytes;
 }
@@ -1053,7 +1053,7 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)
     vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
     vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT);
 
-    buffer_reserve(&vs->tight.gradient, w * 3 * sizeof (int));
+    qio_buffer_reserve(&vs->tight.gradient, w * 3 * sizeof(int));
 
     if (vs->tight.pixel24) {
         tight_filter_gradient24(vs, vs->tight.tight.buffer, w, h);
@@ -1066,7 +1066,7 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)
         bytes = 2;
     }
 
-    buffer_reset(&vs->tight.gradient);
+    qio_buffer_reset(&vs->tight.gradient);
 
     bytes = w * h * bytes;
     vs->tight.tight.offset = bytes;
@@ -1149,7 +1149,7 @@ static int send_palette_rect(VncState *vs, int x, int y,
 static void jpeg_init_destination(j_compress_ptr cinfo)
 {
     VncState *vs = cinfo->client_data;
-    Buffer *buffer = &vs->tight.jpeg;
+    QIOBuffer *buffer = &vs->tight.jpeg;
 
     cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset;
     cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset);
@@ -1159,10 +1159,10 @@ static void jpeg_init_destination(j_compress_ptr cinfo)
 static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
 {
     VncState *vs = cinfo->client_data;
-    Buffer *buffer = &vs->tight.jpeg;
+    QIOBuffer *buffer = &vs->tight.jpeg;
 
     buffer->offset = buffer->capacity;
-    buffer_reserve(buffer, 2048);
+    qio_buffer_reserve(buffer, 2048);
     jpeg_init_destination(cinfo);
     return TRUE;
 }
@@ -1171,7 +1171,7 @@ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
 static void jpeg_term_destination(j_compress_ptr cinfo)
 {
     VncState *vs = cinfo->client_data;
-    Buffer *buffer = &vs->tight.jpeg;
+    QIOBuffer *buffer = &vs->tight.jpeg;
 
     buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer;
 }
@@ -1190,7 +1190,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
         return send_full_color_rect(vs, x, y, w, h);
     }
 
-    buffer_reserve(&vs->tight.jpeg, 2048);
+    qio_buffer_reserve(&vs->tight.jpeg, 2048);
 
     cinfo.err = jpeg_std_error(&jerr);
     jpeg_create_compress(&cinfo);
@@ -1227,7 +1227,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
 
     tight_send_compact_size(vs, vs->tight.jpeg.offset);
     vnc_write(vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset);
-    buffer_reset(&vs->tight.jpeg);
+    qio_buffer_reset(&vs->tight.jpeg);
 
     return 1;
 }
@@ -1270,7 +1270,7 @@ static void png_write_data(png_structp png_ptr, png_bytep data,
 {
     VncState *vs = png_get_io_ptr(png_ptr);
 
-    buffer_reserve(&vs->tight.png, vs->tight.png.offset + length);
+    qio_buffer_reserve(&vs->tight.png, vs->tight.png.offset + length);
     memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length);
 
     vs->tight.png.offset += length;
@@ -1351,7 +1351,7 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,
 
     png_write_info(png_ptr, info_ptr);
 
-    buffer_reserve(&vs->tight.png, 2048);
+    qio_buffer_reserve(&vs->tight.png, 2048);
     linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, w);
     buf = (uint8_t *)pixman_image_get_data(linebuf);
     for (dy = 0; dy < h; dy++)
@@ -1377,14 +1377,14 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h,
 
     tight_send_compact_size(vs, vs->tight.png.offset);
     vnc_write(vs, vs->tight.png.buffer, vs->tight.png.offset);
-    buffer_reset(&vs->tight.png);
+    qio_buffer_reset(&vs->tight.png);
     return 1;
 }
 #endif /* CONFIG_VNC_PNG */
 
 static void vnc_tight_start(VncState *vs)
 {
-    buffer_reset(&vs->tight.tight);
+    qio_buffer_reset(&vs->tight.tight);
 
     // make the output buffer be the zlib buffer, so we can compress it later
     vs->tight.tmp = vs->output;
@@ -1686,13 +1686,13 @@ void vnc_tight_clear(VncState *vs)
         }
     }
 
-    buffer_free(&vs->tight.tight);
-    buffer_free(&vs->tight.zlib);
-    buffer_free(&vs->tight.gradient);
+    qio_buffer_free(&vs->tight.tight);
+    qio_buffer_free(&vs->tight.zlib);
+    qio_buffer_free(&vs->tight.gradient);
 #ifdef CONFIG_VNC_JPEG
-    buffer_free(&vs->tight.jpeg);
+    qio_buffer_free(&vs->tight.jpeg);
 #endif
 #ifdef CONFIG_VNC_PNG
-    buffer_free(&vs->tight.png);
+    qio_buffer_free(&vs->tight.png);
 #endif
 }
diff --git a/ui/vnc-enc-zlib.c b/ui/vnc-enc-zlib.c
index d1b97f2..47ba146 100644
--- a/ui/vnc-enc-zlib.c
+++ b/ui/vnc-enc-zlib.c
@@ -47,7 +47,7 @@ void vnc_zlib_zfree(void *x, void *addr)
 
 static void vnc_zlib_start(VncState *vs)
 {
-    buffer_reset(&vs->zlib.zlib);
+    qio_buffer_reset(&vs->zlib.zlib);
 
     // make the output buffer be the zlib buffer, so we can compress it later
     vs->zlib.tmp = vs->output;
@@ -96,7 +96,7 @@ static int vnc_zlib_stop(VncState *vs)
     }
 
     // reserve memory in output buffer
-    buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64);
+    qio_buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64);
 
     // set pointers
     zstream->next_in = vs->zlib.zlib.buffer;
@@ -148,5 +148,5 @@ void vnc_zlib_clear(VncState *vs)
     if (vs->zlib.stream.opaque) {
         deflateEnd(&vs->zlib.stream);
     }
-    buffer_free(&vs->zlib.zlib);
+    qio_buffer_free(&vs->zlib.zlib);
 }
diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c
index ed3b484..bd1e320 100644
--- a/ui/vnc-enc-zrle.c
+++ b/ui/vnc-enc-zrle.c
@@ -36,7 +36,7 @@ static const int bits_per_packed_pixel[] = {
 
 static void vnc_zrle_start(VncState *vs)
 {
-    buffer_reset(&vs->zrle.zrle);
+    qio_buffer_reset(&vs->zrle.zrle);
 
     /* make the output buffer be the zlib buffer, so we can compress it later */
     vs->zrle.tmp = vs->output;
@@ -53,10 +53,10 @@ static void vnc_zrle_stop(VncState *vs)
 static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h,
                              int bpp)
 {
-    Buffer tmp;
+    QIOBuffer tmp;
 
-    buffer_reset(&vs->zrle.fb);
-    buffer_reserve(&vs->zrle.fb, w * h * bpp + bpp);
+    qio_buffer_reset(&vs->zrle.fb);
+    qio_buffer_reserve(&vs->zrle.fb, w * h * bpp + bpp);
 
     tmp = vs->output;
     vs->output = vs->zrle.fb;
@@ -72,7 +72,7 @@ static int zrle_compress_data(VncState *vs, int level)
 {
     z_streamp zstream = &vs->zrle.stream;
 
-    buffer_reset(&vs->zrle.zlib);
+    qio_buffer_reset(&vs->zrle.zlib);
 
     if (zstream->opaque != vs) {
         int err;
@@ -92,7 +92,7 @@ static int zrle_compress_data(VncState *vs, int level)
     }
 
     /* reserve memory in output buffer */
-    buffer_reserve(&vs->zrle.zlib, vs->zrle.zrle.offset + 64);
+    qio_buffer_reserve(&vs->zrle.zlib, vs->zrle.zrle.offset + 64);
 
     /* set pointers */
     zstream->next_in = vs->zrle.zrle.buffer;
@@ -360,7 +360,7 @@ void vnc_zrle_clear(VncState *vs)
     if (vs->zrle.stream.opaque) {
         deflateEnd(&vs->zrle.stream);
     }
-    buffer_free(&vs->zrle.zrle);
-    buffer_free(&vs->zrle.fb);
-    buffer_free(&vs->zrle.zlib);
+    qio_buffer_free(&vs->zrle.zrle);
+    qio_buffer_free(&vs->zrle.fb);
+    qio_buffer_free(&vs->zrle.zlib);
 }
diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 22c9abc..9824c34 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -54,7 +54,7 @@ struct VncJobQueue {
     QemuCond cond;
     QemuMutex mutex;
     QemuThread thread;
-    Buffer buffer;
+    QIOBuffer buffer;
     bool exit;
     QTAILQ_HEAD(, VncJob) jobs;
 };
@@ -167,7 +167,7 @@ void vnc_jobs_consume_buffer(VncState *vs)
     vnc_lock_output(vs);
     if (vs->jobs_buffer.offset) {
         vnc_write(vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset);
-        buffer_reset(&vs->jobs_buffer);
+        qio_buffer_reset(&vs->jobs_buffer);
     }
     flush = vs->csock != -1 && vs->abort != true;
     vnc_unlock_output(vs);
@@ -196,7 +196,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
     local->output =  queue->buffer;
     local->csock = -1; /* Don't do any network work on this thread */
 
-    buffer_reset(&local->output);
+    qio_buffer_reset(&local->output);
 }
 
 static void vnc_async_encoding_end(VncState *orig, VncState *local)
@@ -273,10 +273,11 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
     vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
 
     vnc_lock_output(job->vs);
+
     if (job->vs->csock != -1) {
-        buffer_reserve(&job->vs->jobs_buffer, vs.output.offset);
-        buffer_append(&job->vs->jobs_buffer, vs.output.buffer,
-                      vs.output.offset);
+        qio_buffer_reserve(&job->vs->jobs_buffer, vs.output.offset);
+        qio_buffer_append(&job->vs->jobs_buffer, vs.output.buffer,
+                          vs.output.offset);
         /* Copy persistent encoding data */
         vnc_async_encoding_end(job->vs, &vs);
 
@@ -310,7 +311,7 @@ static void vnc_queue_clear(VncJobQueue *q)
 {
     qemu_cond_destroy(&queue->cond);
     qemu_mutex_destroy(&queue->mutex);
-    buffer_free(&queue->buffer);
+    qio_buffer_free(&queue->buffer);
     g_free(q);
     queue = NULL; /* Unset global queue */
 }
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 175ea50..2fe4476 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -95,8 +95,8 @@ void vncws_handshake_read(void *opaque)
     /* Typical HTTP headers from novnc are 512 bytes, so limiting
      * total header size to 4096 is easily enough. */
     size_t want = 4096 - vs->ws_input.offset;
-    buffer_reserve(&vs->ws_input, want);
-    ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), want);
+    qio_buffer_reserve(&vs->ws_input, want);
+    ret = vnc_client_read_buf(vs, qio_buffer_end(&vs->ws_input), want);
 
     if (!ret) {
         if (vs->csock == -1) {
@@ -111,7 +111,7 @@ void vncws_handshake_read(void *opaque)
     if (handshake_end) {
         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
         vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset);
-        buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
+        qio_buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
                 strlen(WS_HANDSHAKE_END));
     } else if (vs->ws_input.offset >= 4096) {
         VNC_DEBUG("End of headers not found in first 4096 bytes\n");
@@ -127,8 +127,8 @@ long vnc_client_read_ws(VncState *vs)
     size_t payload_size, header_size;
     VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer,
             vs->ws_input.capacity, vs->ws_input.offset);
-    buffer_reserve(&vs->ws_input, 4096);
-    ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096);
+    qio_buffer_reserve(&vs->ws_input, 4096);
+    ret = vnc_client_read_buf(vs, qio_buffer_end(&vs->ws_input), 4096);
     if (!ret) {
         return 0;
     }
@@ -146,7 +146,7 @@ long vnc_client_read_ws(VncState *vs)
                 return err;
             }
 
-            buffer_advance(&vs->ws_input, header_size);
+            qio_buffer_advance(&vs->ws_input, header_size);
         }
         if (vs->ws_payload_remain != 0) {
             err = vncws_decode_frame_payload(&vs->ws_input,
@@ -162,10 +162,10 @@ long vnc_client_read_ws(VncState *vs)
             }
             ret += err;
 
-            buffer_reserve(&vs->input, payload_size);
-            buffer_append(&vs->input, payload, payload_size);
+            qio_buffer_reserve(&vs->input, payload_size);
+            qio_buffer_append(&vs->input, payload, payload_size);
 
-            buffer_advance(&vs->ws_input, payload_size);
+            qio_buffer_advance(&vs->ws_input, payload_size);
         }
     } while (vs->ws_input.offset > 0);
 
@@ -178,13 +178,13 @@ long vnc_client_write_ws(VncState *vs)
     VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n",
               vs->output.buffer, vs->output.capacity, vs->output.offset);
     vncws_encode_frame(&vs->ws_output, vs->output.buffer, vs->output.offset);
-    buffer_reset(&vs->output);
+    qio_buffer_reset(&vs->output);
     ret = vnc_client_write_buf(vs, vs->ws_output.buffer, vs->ws_output.offset);
     if (!ret) {
         return 0;
     }
 
-    buffer_advance(&vs->ws_output, ret);
+    qio_buffer_advance(&vs->ws_output, ret);
 
     if (vs->ws_output.offset == 0) {
         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
@@ -267,8 +267,8 @@ void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)
     g_free(key);
 }
 
-void vncws_encode_frame(Buffer *output, const void *payload,
-        const size_t payload_size)
+void vncws_encode_frame(QIOBuffer *output, const void *payload,
+                        const size_t payload_size)
 {
     size_t header_size = 0;
     unsigned char opcode = WS_OPCODE_BINARY_FRAME;
@@ -295,12 +295,12 @@ void vncws_encode_frame(Buffer *output, const void *payload,
         header_size = 10;
     }
 
-    buffer_reserve(output, header_size + payload_size);
-    buffer_append(output, header.buf, header_size);
-    buffer_append(output, payload, payload_size);
+    qio_buffer_reserve(output, header_size + payload_size);
+    qio_buffer_append(output, header.buf, header_size);
+    qio_buffer_append(output, payload, payload_size);
 }
 
-int vncws_decode_frame_header(Buffer *input,
+int vncws_decode_frame_header(QIOBuffer *input,
                               size_t *header_size,
                               size_t *payload_remain,
                               WsMask *payload_mask)
@@ -354,7 +354,7 @@ int vncws_decode_frame_header(Buffer *input,
     return 1;
 }
 
-int vncws_decode_frame_payload(Buffer *input,
+int vncws_decode_frame_payload(QIOBuffer *input,
                                size_t *payload_remain, WsMask *payload_mask,
                                uint8_t **payload, size_t *payload_size)
 {
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
index 4ab0a8c..2a222a8 100644
--- a/ui/vnc-ws.h
+++ b/ui/vnc-ws.h
@@ -77,13 +77,13 @@ void vncws_handshake_read(void *opaque);
 long vnc_client_write_ws(VncState *vs);
 long vnc_client_read_ws(VncState *vs);
 void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);
-void vncws_encode_frame(Buffer *output, const void *payload,
+void vncws_encode_frame(QIOBuffer *output, const void *payload,
             const size_t payload_size);
-int vncws_decode_frame_header(Buffer *input,
+int vncws_decode_frame_header(QIOBuffer *input,
                               size_t *header_size,
                               size_t *payload_remain,
                               WsMask *payload_mask);
-int vncws_decode_frame_payload(Buffer *input,
+int vncws_decode_frame_payload(QIOBuffer *input,
                                size_t *payload_remain, WsMask *payload_mask,
                                uint8_t **payload, size_t *payload_size);
 
diff --git a/ui/vnc.c b/ui/vnc.c
index d73966a..3303987 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -647,49 +647,6 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
     vnc_write_s32(vs, encoding);
 }
 
-void buffer_reserve(Buffer *buffer, size_t len)
-{
-    if ((buffer->capacity - buffer->offset) < len) {
-        buffer->capacity += (len + 1024);
-        buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
-    }
-}
-
-static int buffer_empty(Buffer *buffer)
-{
-    return buffer->offset == 0;
-}
-
-uint8_t *buffer_end(Buffer *buffer)
-{
-    return buffer->buffer + buffer->offset;
-}
-
-void buffer_reset(Buffer *buffer)
-{
-        buffer->offset = 0;
-}
-
-void buffer_free(Buffer *buffer)
-{
-    g_free(buffer->buffer);
-    buffer->offset = 0;
-    buffer->capacity = 0;
-    buffer->buffer = NULL;
-}
-
-void buffer_append(Buffer *buffer, const void *data, size_t len)
-{
-    memcpy(buffer->buffer + buffer->offset, data, len);
-    buffer->offset += len;
-}
-
-void buffer_advance(Buffer *buf, size_t len)
-{
-    memmove(buf->buffer, buf->buffer + len,
-            (buf->offset - len));
-    buf->offset -= len;
-}
 
 static void vnc_desktop_resize(VncState *vs)
 {
@@ -1220,10 +1177,10 @@ void vnc_disconnect_finish(VncState *vs)
     vnc_lock_output(vs);
     vnc_qmp_event(vs, QAPI_EVENT_VNC_DISCONNECTED);
 
-    buffer_free(&vs->input);
-    buffer_free(&vs->output);
-    buffer_free(&vs->ws_input);
-    buffer_free(&vs->ws_output);
+    qio_buffer_free(&vs->input);
+    qio_buffer_free(&vs->output);
+    qio_buffer_free(&vs->ws_input);
+    qio_buffer_free(&vs->ws_output);
 
     qapi_free_VncClientInfo(vs->info);
 
@@ -1251,7 +1208,7 @@ void vnc_disconnect_finish(VncState *vs)
     if (vs->bh != NULL) {
         qemu_bh_delete(vs->bh);
     }
-    buffer_free(&vs->jobs_buffer);
+    qio_buffer_free(&vs->jobs_buffer);
 
     for (i = 0; i < VNC_STAT_ROWS; ++i) {
         g_free(vs->lossy_rect[i]);
@@ -1393,7 +1350,7 @@ static ssize_t vnc_client_write_plain(VncState *vs)
     if (!ret)
         return 0;
 
-    buffer_advance(&vs->output, ret);
+    qio_buffer_advance(&vs->output, ret);
 
     if (vs->output.offset == 0) {
         qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
@@ -1496,8 +1453,8 @@ static ssize_t vnc_client_read_plain(VncState *vs)
     ssize_t ret;
     VNC_DEBUG("Read plain %p size %zd offset %zd\n",
               vs->input.buffer, vs->input.capacity, vs->input.offset);
-    buffer_reserve(&vs->input, 4096);
-    ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
+    qio_buffer_reserve(&vs->input, 4096);
+    ret = vnc_client_read_buf(vs, qio_buffer_end(&vs->input), 4096);
     if (!ret)
         return 0;
     vs->input.offset += ret;
@@ -1555,7 +1512,7 @@ void vnc_client_read(void *opaque)
         }
 
         if (!ret) {
-            buffer_advance(&vs->input, len);
+            qio_buffer_advance(&vs->input, len);
         } else {
             vs->read_handler_expect = ret;
         }
@@ -1564,13 +1521,13 @@ void vnc_client_read(void *opaque)
 
 void vnc_write(VncState *vs, const void *data, size_t len)
 {
-    buffer_reserve(&vs->output, len);
+    qio_buffer_reserve(&vs->output, len);
 
-    if (vs->csock != -1 && buffer_empty(&vs->output)) {
+    if (vs->csock != -1 && qio_buffer_empty(&vs->output)) {
         qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
     }
 
-    buffer_append(&vs->output, data, len);
+    qio_buffer_append(&vs->output, data, len);
 }
 
 void vnc_write_s32(VncState *vs, int32_t value)
diff --git a/ui/vnc.h b/ui/vnc.h
index 4dd769c..339a1bf 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -34,6 +34,7 @@
 #include "audio/audio.h"
 #include "qemu/bitmap.h"
 #include "crypto/tlssession.h"
+#include "io/buffer.h"
 #include <zlib.h>
 #include <stdbool.h>
 
@@ -56,13 +57,6 @@
  *
  *****************************************************************************/
 
-typedef struct Buffer
-{
-    size_t capacity;
-    size_t offset;
-    uint8_t *buffer;
-} Buffer;
-
 typedef struct VncState VncState;
 typedef struct VncJob VncJob;
 typedef struct VncRect VncRect;
@@ -191,15 +185,15 @@ typedef struct VncTight {
     uint8_t quality;
     uint8_t compression;
     uint8_t pixel24;
-    Buffer tight;
-    Buffer tmp;
-    Buffer zlib;
-    Buffer gradient;
+    QIOBuffer tight;
+    QIOBuffer tmp;
+    QIOBuffer zlib;
+    QIOBuffer gradient;
 #ifdef CONFIG_VNC_JPEG
-    Buffer jpeg;
+    QIOBuffer jpeg;
 #endif
 #ifdef CONFIG_VNC_PNG
-    Buffer png;
+    QIOBuffer png;
 #endif
     int levels[4];
     z_stream stream[4];
@@ -210,18 +204,18 @@ typedef struct VncHextile {
 } VncHextile;
 
 typedef struct VncZlib {
-    Buffer zlib;
-    Buffer tmp;
+    QIOBuffer zlib;
+    QIOBuffer tmp;
     z_stream stream;
     int level;
 } VncZlib;
 
 typedef struct VncZrle {
     int type;
-    Buffer fb;
-    Buffer zrle;
-    Buffer tmp;
-    Buffer zlib;
+    QIOBuffer fb;
+    QIOBuffer zrle;
+    QIOBuffer tmp;
+    QIOBuffer zlib;
     z_stream stream;
     VncPalette palette;
 } VncZrle;
@@ -290,10 +284,10 @@ struct VncState
 
     VncClientInfo *info;
 
-    Buffer output;
-    Buffer input;
-    Buffer ws_input;
-    Buffer ws_output;
+    QIOBuffer output;
+    QIOBuffer input;
+    QIOBuffer ws_input;
+    QIOBuffer ws_output;
     size_t ws_payload_remain;
     WsMask ws_payload_mask;
     /* current output mode information */
@@ -315,7 +309,7 @@ struct VncState
     bool initialized;
     QemuMutex output_mutex;
     QEMUBH *bh;
-    Buffer jobs_buffer;
+    QIOBuffer jobs_buffer;
 
     /* Encoding specific, if you add something here, don't forget to
      *  update vnc_async_encoding_start()
@@ -535,14 +529,6 @@ ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno);
 void start_client_init(VncState *vs);
 void start_auth_vnc(VncState *vs);
 
-/* Buffer management */
-void buffer_reserve(Buffer *buffer, size_t len);
-void buffer_reset(Buffer *buffer);
-void buffer_free(Buffer *buffer);
-void buffer_append(Buffer *buffer, const void *data, size_t len);
-void buffer_advance(Buffer *buf, size_t len);
-uint8_t *buffer_end(Buffer *buffer);
-
 
 /* Misc helpers */
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 09/46] io: add QIOTask class for async operations
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (7 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 08/46] io: pull Buffer code out of VNC module Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 10/46] io: add QIOChannelSocket class Daniel P. Berrange
                   ` (36 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

A number of I/O operations need to be performed asynchronously
to avoid blocking the main loop. The caller of such APIs need
to provide a callback to be invoked on completion/error and
need access to the error, if any. The small QIOTask provides
a simple framework for dealing with such probes. The API
docs inline provide an outline of how this is to be used.

Some functions don't have the ability to run asynchronously
(eg getaddrinfo always blocks), so to facilitate their use,
the task class provides a mechanism to run a blocking
function in a thread, while triggering the completion
callback in the main event loop thread. This easily allows
any synchronous function to be made asynchronous, albeit
at the cost of spawning a thread.

In this series, the QIOTask class will be used for things like
the TLS handshake, the websockets handshake and TCP connect()
progress.

The concept of QIOTask is inspired by the GAsyncResult
interface / GTask class in the GIO libraries. The min
version requirements on glib don't allow those to be
used from QEMU, so QIOTask provides a facsimilie which
can be easily switched to GTask in the future if the
min version is increased.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/io/task.h    | 256 +++++++++++++++++++++++++++++++++++++++++++++++
 io/Makefile.objs     |   1 +
 io/task.c            | 150 ++++++++++++++++++++++++++++
 tests/.gitignore     |   1 +
 tests/Makefile       |   3 +
 tests/test-io-task.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 687 insertions(+)
 create mode 100644 include/io/task.h
 create mode 100644 io/task.c
 create mode 100644 tests/test-io-task.c

diff --git a/include/io/task.h b/include/io/task.h
new file mode 100644
index 0000000..2418714
--- /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/io/Makefile.objs b/io/Makefile.objs
index 593ed9e..bef2ca1 100644
--- a/io/Makefile.objs
+++ b/io/Makefile.objs
@@ -1,3 +1,4 @@
 io-obj-y = buffer.o
+io-obj-y += task.o
 io-obj-y += channel.o
 io-obj-y += channel-watch.o
diff --git a/io/task.c b/io/task.c
new file mode 100644
index 0000000..a2ff297
--- /dev/null
+++ b/io/task.c
@@ -0,0 +1,150 @@
+/*
+ * 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"
+
+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;
+
+    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;
+
+    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 gio_task_thread_worker(gpointer opaque)
+{
+    struct QIOTaskThreadData *data = opaque;
+
+    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
+     */
+    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;
+
+    qemu_thread_create(&thread,
+                       "io-task-worker",
+                       gio_task_thread_worker,
+                       data,
+                       QEMU_THREAD_DETACHED);
+}
+
+
+void qio_task_complete(QIOTask *task)
+{
+    task->func(task->source, NULL, task->opaque);
+    qio_task_free(task);
+}
+
+void qio_task_abort(QIOTask *task,
+                    Error *err)
+{
+    task->func(task->source, err, task->opaque);
+    qio_task_free(task);
+}
+
+
+Object *qio_task_get_source(QIOTask *task)
+{
+    object_ref(task->source);
+    return task->source;
+}
diff --git a/tests/.gitignore b/tests/.gitignore
index 2c5e2c3..6269480 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -23,6 +23,7 @@ test-cutils
 test-hbitmap
 test-int128
 test-iov
+test-io-task
 test-mul64
 test-opts-visitor
 test-qapi-event.[ch]
diff --git a/tests/Makefile b/tests/Makefile
index e184424..3b7f5a6 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -78,6 +78,7 @@ check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF)
 check-unit-y += tests/test-crypto-cipher$(EXESUF)
 check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
 check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
+check-unit-y += tests/test-io-task$(EXESUF)
 
 check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -285,6 +286,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)
@@ -357,6 +359,7 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \
 	tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y)
 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)
 
 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/test-io-task.c b/tests/test-io-task.c
new file mode 100644
index 0000000..dc2f5c2
--- /dev/null
+++ b/tests/test-io-task.c
@@ -0,0 +1,276 @@
+/*
+ * QEMU Crypto hash algorithms
+ *
+ * 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();
+    g_thread_ref(data->worker);
+
+    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_thread_ref(data->complete);
+
+    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);
+
+    g_thread_unref(data.worker);
+    g_thread_unref(data.complete);
+}
+
+
+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);
+
+    g_thread_unref(data.worker);
+    g_thread_unref(data.complete);
+}
+
+
+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();
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 10/46] io: add QIOChannelSocket class
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (8 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 09/46] io: add QIOTask class for async operations Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 11/46] io: add QIOChannelFile class Daniel P. Berrange
                   ` (35 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Implement a QIOChannel subclass that supports sockets I/O.
The implementation is able to manage a single socket file
descriptor, whether a TCP/UNIX listener, TCP/UNIX connection,
or a UDP datagram. It provides APIs which can listen and
connect either asynchronously or synchronously. Since there
is no asynchronous DNS lookup API available, it uses the
QIOTask helper for spawning a background thread to ensure
non-blocking operation.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 configure                      |  11 +
 include/io/channel-socket.h    | 297 +++++++++++++++++
 io/Makefile.objs               |   1 +
 io/channel-socket.c            | 718 +++++++++++++++++++++++++++++++++++++++++
 scripts/create_config          |   9 +
 tests/.gitignore               |   1 +
 tests/Makefile                 |   3 +
 tests/io-channel-helpers.c     | 222 +++++++++++++
 tests/io-channel-helpers.h     |  30 ++
 tests/test-io-channel-socket.c | 349 ++++++++++++++++++++
 10 files changed, 1641 insertions(+)
 create mode 100644 include/io/channel-socket.h
 create mode 100644 io/channel-socket.c
 create mode 100644 tests/io-channel-helpers.c
 create mode 100644 tests/io-channel-helpers.h
 create mode 100644 tests/test-io-channel-socket.c

diff --git a/configure b/configure
index 3dbb18f..f53adcd 100755
--- a/configure
+++ b/configure
@@ -2259,6 +2259,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
@@ -4901,6 +4909,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-socket.h b/include/io/channel-socket.h
new file mode 100644
index 0000000..2d1e86a
--- /dev/null
+++ b/include/io/channel-socket.h
@@ -0,0 +1,297 @@
+/*
+ * 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_set_nodelay:
+ * @ioc: the socket channel object
+ * @enabled: the new flag state
+ *
+ * Set the state of the TCP_NODELAY socket flag. If the
+ * @enabled parameter is true, then TCP_NODELAY will be
+ * set and data will be transmitted immediately. If
+ * @enabled is false, then data may be temporarily
+ * held for transmission to enable writes to be
+ * coallesced.
+ */
+void
+qio_channel_socket_set_nodelay(QIOChannelSocket *ioc,
+                               bool enabled);
+
+/**
+ * qio_channel_socket_set_cork:
+ * @ioc: the socket channel object
+ * @enabled: the new flag state
+ *
+ * Set the state of the TCP_CORK socket flag. If the
+ * @enabled parameter is true, then TCP_CORK will be
+ * set and data will be not be transmitted immediately.
+ * If @enabled is false, then any previously queued
+ * data is released for transmission. This method is
+ * useful when the TCP_NODELAY flag is enabled to have
+ * direct control over transmission times.
+ */
+void
+qio_channel_socket_set_cork(QIOChannelSocket *ioc,
+                            bool enabled);
+
+/**
+ * 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);
+
+typedef enum {
+    QIO_CHANNEL_SOCKET_SHUTDOWN_BOTH,
+    QIO_CHANNEL_SOCKET_SHUTDOWN_READ,
+    QIO_CHANNEL_SOCKET_SHUTDOWN_WRITE,
+} QIOChannelSocketShutdown;
+
+/**
+ * qio_channel_socket_shutdown:
+ * @ioc: the socket channel object
+ * @how: the direction to shutdown
+ * @errp: pointer to an uninitialized error object
+ *
+ * Shutdowns transmission or receiving on a socket
+ * without closing the socket file descriptor.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int
+qio_channel_socket_shutdown(QIOChannelSocket *ioc,
+                            QIOChannelSocketShutdown how,
+                            Error **errp);
+
+#endif /* QIO_CHANNEL_SOCKET_H__ */
diff --git a/io/Makefile.objs b/io/Makefile.objs
index bef2ca1..f12ec26 100644
--- a/io/Makefile.objs
+++ b/io/Makefile.objs
@@ -2,3 +2,4 @@ io-obj-y = buffer.o
 io-obj-y += task.o
 io-obj-y += channel.o
 io-obj-y += channel-watch.o
+io-obj-y += channel-socket.o
diff --git a/io/channel-socket.c b/io/channel-socket.c
new file mode 100644
index 0000000..0b28a00
--- /dev/null
+++ b/io/channel-socket.c
@@ -0,0 +1,718 @@
+/*
+ * 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 <glib/gi18n.h>
+
+#include "io/channel-socket.h"
+#include "io/channel-watch.h"
+
+#define SOCKET_MAX_FDS 16
+
+SocketAddress *
+qio_channel_socket_get_local_address(QIOChannelSocket *ioc,
+                                     Error **errp)
+{
+    return socket_local_address(ioc->fd, errp);
+}
+
+SocketAddress *
+qio_channel_socket_get_remote_address(QIOChannelSocket *ioc,
+                                      Error **errp)
+{
+    return socket_remote_address(ioc->fd, errp);
+}
+
+QIOChannelSocket *
+qio_channel_socket_new(void)
+{
+    QIOChannelSocket *ioc;
+
+    ioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET));
+    ioc->fd = -1;
+
+    return ioc;
+}
+
+
+static int
+qio_channel_socket_set_fd(QIOChannelSocket *ioc,
+                          int fd,
+                          Error **errp)
+{
+    if (ioc->fd != -1) {
+        error_setg(errp, _("Socket is already open"));
+        return -1;
+    }
+
+    ioc->fd = fd;
+    ioc->remoteAddrLen = sizeof(ioc->remoteAddr);
+    ioc->localAddrLen = sizeof(ioc->localAddr);
+
+    if (getpeername(fd, (struct sockaddr *)&ioc->remoteAddr,
+                    &ioc->remoteAddrLen) < 0) {
+        if (socket_error() == ENOTCONN) {
+            memset(&ioc->remoteAddr, 0, sizeof(ioc->remoteAddr));
+            ioc->remoteAddrLen = sizeof(ioc->remoteAddr);
+        } else {
+            error_setg_errno(errp, socket_error(), "%s",
+                             _("Unable to query remote socket address"));
+            goto error;
+        }
+    }
+
+    if (getsockname(fd, (struct sockaddr *)&ioc->localAddr,
+                    &ioc->localAddrLen) < 0) {
+        error_setg_errno(errp, socket_error(), "%s",
+                         _("Unable to query local socket address"));
+        goto error;
+    }
+
+    return 0;
+
+ error:
+    ioc->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;
+    }
+
+    return ioc;
+}
+
+
+int qio_channel_socket_connect_sync(QIOChannelSocket *ioc,
+                                    SocketAddress *addr,
+                                    Error **errp)
+{
+    int fd;
+
+    fd = socket_connect(addr, errp, NULL, NULL);
+    if (fd < 0) {
+        return -1;
+    }
+
+    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;
+
+    return qio_channel_socket_connect_sync(ioc,
+                                           addr,
+                                           errp);
+}
+
+
+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 */
+    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;
+
+    fd = socket_listen(addr, errp);
+    if (fd < 0) {
+        return -1;
+    }
+
+    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;
+
+    return qio_channel_socket_listen_sync(ioc,
+                                          addr,
+                                          errp);
+}
+
+
+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 */
+    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;
+
+    fd = socket_dgram(localAddr, remoteAddr, errp);
+    if (fd < 0) {
+        return -1;
+    }
+
+    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;
+
+    /* socket_dgram() blocks in DNS lookups, so we must use a thread */
+    return qio_channel_socket_dgram_sync(ioc,
+                                         data->localAddr,
+                                         data->remoteAddr,
+                                         errp);
+}
+
+
+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);
+
+    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:
+    cioc->fd = accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr,
+                      &cioc->remoteAddrLen);
+    if (cioc->fd < 0) {
+        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(), "%s",
+                         _("Unable to query local socket address"));
+        goto error;
+    }
+
+    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;
+    }
+}
+
+static bool qio_channel_socket_has_feature(QIOChannel *ioc,
+                                           QIOChannelFeature feature)
+{
+#ifndef WIN32
+    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+
+    switch (feature) {
+    case QIO_CHANNEL_FEATURE_FD_PASS:
+        if (sioc->localAddr.ss_family == AF_UNIX) {
+            return true;
+        } else {
+            return false;
+        }
+    default:
+        return false;
+    }
+#else
+    return false;
+#endif
+}
+
+
+#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(), "%s",
+                         _("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(), "%s",
+                         _("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;
+
+    if (fds || nfds) {
+        error_setg_errno(errp, EINVAL, "%s",
+                         _("Channel does not support file descriptor passing"));
+        return -1;
+    }
+
+    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(), "%s",
+                                 _("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;
+
+    if (fds || nfds) {
+        error_setg_errno(errp, EINVAL, "%s",
+                         _("Channel does not support file descriptor passing"));
+        return -1;
+    }
+
+    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(), "%s",
+                                 _("Unable to write to socket"));
+                return -1;
+            }
+        }
+        done += ret;
+        if (ret < iov[i].iov_len) {
+            return done;
+        }
+    }
+
+    return done;
+}
+#endif /* WIN32 */
+
+static void qio_channel_socket_set_blocking(QIOChannel *ioc,
+                                            bool enabled)
+{
+    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+
+    if (enabled) {
+        qemu_set_block(sioc->fd);
+    } else {
+        qemu_set_nonblock(sioc->fd);
+    }
+}
+
+
+void
+qio_channel_socket_set_nodelay(QIOChannelSocket *ioc,
+                               bool enabled)
+{
+    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+    int v = enabled ? 1 : 0;
+
+    qemu_setsockopt(sioc->fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
+}
+
+
+void
+qio_channel_socket_set_cork(QIOChannelSocket *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(), "%s",
+                         _("Unable to close socket"));
+        return -1;
+    }
+    sioc->fd = -1;
+    return 0;
+}
+
+int
+qio_channel_socket_shutdown(QIOChannelSocket *ioc,
+                            QIOChannelSocketShutdown how,
+                            Error **errp)
+{
+    int sockhow;
+    switch (how) {
+    case QIO_CHANNEL_SOCKET_SHUTDOWN_READ:
+        sockhow = SHUT_RD;
+        break;
+    case QIO_CHANNEL_SOCKET_SHUTDOWN_WRITE:
+        sockhow = SHUT_WR;
+        break;
+    case QIO_CHANNEL_SOCKET_SHUTDOWN_BOTH:
+    default:
+        sockhow = SHUT_RDWR;
+        break;
+    }
+
+    if (shutdown(ioc->fd, sockhow) < 0) {
+        error_setg_errno(errp, socket_error(), "%s",
+                         _("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_has_feature = qio_channel_socket_has_feature;
+    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_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/scripts/create_config b/scripts/create_config
index 546f889..9cb176f 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 6269480..ac891c6 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -23,6 +23,7 @@ test-cutils
 test-hbitmap
 test-int128
 test-iov
+test-io-channel-socket
 test-io-task
 test-mul64
 test-opts-visitor
diff --git a/tests/Makefile b/tests/Makefile
index 3b7f5a6..37bde1a 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -79,6 +79,7 @@ check-unit-y += tests/test-crypto-cipher$(EXESUF)
 check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
 check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF)
 check-unit-y += tests/test-io-task$(EXESUF)
+check-unit-y += tests/test-io-channel-socket$(EXESUF)
 
 check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -360,6 +361,8 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \
 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)
 
 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 0000000..946c9b9
--- /dev/null
+++ b/tests/io-channel-helpers.c
@@ -0,0 +1,222 @@
+/*
+ * 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 TestIOData {
+    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)
+{
+    struct TestIOData *data = opaque;
+    struct iovec *iov = data->inputv;
+    size_t niov = data->niov;
+    struct iovec old = { 0 };
+
+    qio_channel_set_blocking(data->src, data->blocking);
+
+    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)
+{
+    struct TestIOData *data = opaque;
+    struct iovec *iov = data->outputv;
+    size_t niov = data->niov;
+    struct iovec old = { 0 };
+
+    qio_channel_set_blocking(data->dst, data->blocking);
+
+    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;
+}
+
+
+static struct TestIOData *test_load_io_data(const char *filename)
+{
+    struct TestIOData *data = g_new0(struct TestIOData, 1);
+    GError *err = NULL;
+    size_t offset;
+    size_t i;
+
+#define IOVEC_LEN (64 * 1024)
+
+    if (!g_file_get_contents(filename, &data->input, &data->len, &err)) {
+        g_printerr("Unable to load file: %s\n", err->message);
+        abort();
+    }
+    data->len++; /* So that we send the trailing \0 too */
+    data->output = g_new0(gchar, data->len);
+
+    data->niov = (data->len / IOVEC_LEN) + 1;
+    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 += IOVEC_LEN) {
+        data->inputv[i].iov_base = data->input + offset;
+        data->outputv[i].iov_base = data->output + offset;
+        if ((data->len - offset) > IOVEC_LEN) {
+            data->inputv[i].iov_len = IOVEC_LEN;
+            data->outputv[i].iov_len = IOVEC_LEN;
+        } else {
+            data->inputv[i].iov_len = data->len - offset;
+            data->outputv[i].iov_len = data->len - offset;
+        }
+    }
+
+    return data;
+}
+
+
+void test_io_channel_comms(bool blocking,
+                           QIOChannel *src,
+                           QIOChannel *dst)
+{
+    struct TestIOData *data;
+    GThread *reader, *writer;
+
+    data = test_load_io_data("libqemuutil.a");
+    data->src = src;
+    data->dst = dst;
+    data->blocking = blocking;
+
+    reader = g_thread_new("reader",
+                          test_io_thread_reader,
+                          data);
+    writer = g_thread_new("writer",
+                          test_io_thread_writer,
+                          data);
+
+    g_thread_join(reader);
+    g_thread_join(writer);
+
+    g_assert_cmpint(memcmp(data->input,
+                           data->output,
+                           data->len), ==, 0);
+    g_assert(data->readerr == NULL);
+    g_assert(data->writeerr == NULL);
+
+    g_free(data->input);
+    g_free(data->output);
+    g_free(data);
+}
diff --git a/tests/io-channel-helpers.h b/tests/io-channel-helpers.h
new file mode 100644
index 0000000..d0085a2
--- /dev/null
+++ b/tests/io-channel-helpers.h
@@ -0,0 +1,30 @@
+/*
+ * 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
+
+void test_io_channel_comms(bool blocking,
+                           QIOChannel *src,
+                           QIOChannel *dst);
+
+#endif /* TEST_IO_CHANNEL_HELPERS */
diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c
new file mode 100644
index 0000000..338f816
--- /dev/null
+++ b/tests/test-io-channel-socket.c
@@ -0,0 +1,349 @@
+/*
+ * 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;
+
+    if ((gaierr = getaddrinfo("::1", NULL, &hints, &ai)) != 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->kind == SOCKET_ADDRESS_KIND_INET) {
+        SocketAddress *laddr = qio_channel_socket_get_local_address(
+            lioc, &error_abort);
+
+        connect_addr->inet->port = g_strdup(laddr->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_socket_set_nodelay(QIO_CHANNEL_SOCKET(*src), true);
+
+    *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_assert(!data.err);
+
+    if (listen_addr->kind == SOCKET_ADDRESS_KIND_INET) {
+        SocketAddress *laddr = qio_channel_socket_get_local_address(
+            lioc, &error_abort);
+
+        connect_addr->inet->port = g_strdup(laddr->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_assert(!data.err);
+
+    *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
+    g_assert(*dst);
+
+    qio_channel_socket_set_nodelay(QIO_CHANNEL_SOCKET(*src), true);
+    test_io_channel_set_socket_bufs(*src, *dst);
+
+    object_unref(OBJECT(lioc));
+}
+
+
+static void test_io_channel(bool async,
+                            SocketAddress *listen_addr,
+                            SocketAddress *connect_addr)
+{
+    QIOChannel *src, *dst;
+    if (async) {
+        test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
+        test_io_channel_comms(true, src, dst);
+        object_unref(OBJECT(src));
+        object_unref(OBJECT(dst));
+
+        test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
+        test_io_channel_comms(false, src, dst);
+        object_unref(OBJECT(src));
+        object_unref(OBJECT(dst));
+    } else {
+        test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
+        test_io_channel_comms(true, src, dst);
+        object_unref(OBJECT(src));
+        object_unref(OBJECT(dst));
+
+        test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
+        test_io_channel_comms(false, src, dst);
+        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->kind = SOCKET_ADDRESS_KIND_INET;
+    listen_addr->inet = g_new0(InetSocketAddress, 1);
+    listen_addr->inet->host = g_strdup("0.0.0.0");
+    listen_addr->inet->port = NULL; /* Auto-select */
+
+    connect_addr->kind = SOCKET_ADDRESS_KIND_INET;
+    connect_addr->inet = g_new0(InetSocketAddress, 1);
+    connect_addr->inet->host = g_strdup("127.0.0.1");
+    connect_addr->inet->port = NULL; /* Filled in later */
+
+    test_io_channel(async, listen_addr, 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->kind = SOCKET_ADDRESS_KIND_INET;
+    listen_addr->inet = g_new0(InetSocketAddress, 1);
+    listen_addr->inet->host = g_strdup("::");
+    listen_addr->inet->port = NULL; /* Auto-select */
+
+    connect_addr->kind = SOCKET_ADDRESS_KIND_INET;
+    connect_addr->inet = g_new0(InetSocketAddress, 1);
+    connect_addr->inet->host = g_strdup("::1");
+    connect_addr->inet->port = NULL; /* Filled in later */
+
+    test_io_channel(async, listen_addr, 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);
+
+    listen_addr->kind = SOCKET_ADDRESS_KIND_UNIX;
+    listen_addr->q_unix = g_new0(UnixSocketAddress, 1);
+    listen_addr->q_unix->path = g_strdup("test-io-channel-socket.sock");
+
+    connect_addr->kind = SOCKET_ADDRESS_KIND_UNIX;
+    connect_addr->q_unix = g_new0(UnixSocketAddress, 1);
+    connect_addr->q_unix->path = g_strdup("test-io-channel-socket.sock");
+
+    test_io_channel(async, listen_addr, connect_addr);
+}
+
+
+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);
+
+    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();
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 11/46] io: add QIOChannelFile class
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (9 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 10/46] io: add QIOChannelSocket class Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class Daniel P. Berrange
                   ` (34 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Add a QIOChannel subclass that is capable of operating on things
that are files, such as plain files, pipes, character/block
devices, but notably not sockets.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/io/channel-file.h    |  93 +++++++++++++++++++
 io/Makefile.objs             |   1 +
 io/channel-file.c            | 209 +++++++++++++++++++++++++++++++++++++++++++
 tests/.gitignore             |   2 +
 tests/Makefile               |   3 +
 tests/io-channel-helpers.c   |  25 ++++++
 tests/io-channel-helpers.h   |   3 +
 tests/test-io-channel-file.c |  89 ++++++++++++++++++
 8 files changed, 425 insertions(+)
 create mode 100644 include/io/channel-file.h
 create mode 100644 io/channel-file.c
 create mode 100644 tests/test-io-channel-file.c

diff --git a/include/io/channel-file.h b/include/io/channel-file.h
new file mode 100644
index 0000000..308e6d4
--- /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/io/Makefile.objs b/io/Makefile.objs
index f12ec26..9eb0fd9 100644
--- a/io/Makefile.objs
+++ b/io/Makefile.objs
@@ -3,3 +3,4 @@ io-obj-y += task.o
 io-obj-y += channel.o
 io-obj-y += channel-watch.o
 io-obj-y += channel-socket.o
+io-obj-y += channel-file.o
diff --git a/io/channel-file.c b/io/channel-file.c
new file mode 100644
index 0000000..4695e28
--- /dev/null
+++ b/io/channel-file.c
@@ -0,0 +1,209 @@
+/*
+ * 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 <glib/gi18n.h>
+
+#include "io/channel-file.h"
+#include "io/channel-watch.h"
+#include "qemu/sockets.h"
+
+QIOChannelFile *
+qio_channel_file_new_fd(int fd)
+{
+    QIOChannelFile *ioc;
+
+    ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE));
+
+    ioc->fd = fd;
+
+    return ioc;
+}
+
+
+QIOChannelFile *
+qio_channel_file_new_path(const char *path,
+                          int flags,
+                          mode_t mode,
+                          Error **errp)
+{
+    int fd;
+
+    if (flags & O_WRONLY) {
+        fd = open(path, flags, mode);
+    } else {
+        fd = open(path, flags);
+    }
+    if (fd < 0) {
+        error_setg_errno(errp, errno,
+                         "Unable to open %s", path);
+        return NULL;
+    }
+
+    return qio_channel_file_new_fd(fd);
+}
+
+
+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;
+
+    if (fds || nfds) {
+        error_setg_errno(errp, EINVAL, "%s",
+                         _("Channel does not support file descriptor passing"));
+        return -1;
+    }
+
+ 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, "%s",
+                         _("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;
+
+    if (fds || nfds) {
+        error_setg_errno(errp, EINVAL, "%s",
+                         _("Channel does not support file descriptor passing"));
+        return -1;
+    }
+
+ 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, "%s",
+                         _("Unable to write to file"));
+        return -1;
+    }
+    return ret;
+}
+
+static void qio_channel_file_set_blocking(QIOChannel *ioc,
+                                          bool enabled)
+{
+    QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
+
+    if (enabled) {
+        qemu_set_block(fioc->fd);
+    } else {
+        qemu_set_nonblock(fioc->fd);
+    }
+}
+
+
+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, "%s",
+                         _("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_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/tests/.gitignore b/tests/.gitignore
index ac891c6..bb66d94 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -23,6 +23,8 @@ test-cutils
 test-hbitmap
 test-int128
 test-iov
+test-io-channel-file
+test-io-channel-file.txt
 test-io-channel-socket
 test-io-task
 test-mul64
diff --git a/tests/Makefile b/tests/Makefile
index 37bde1a..f896051 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -80,6 +80,7 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF)
 check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(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-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -363,6 +364,8 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
 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)
 
 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
index 946c9b9..9fc827f 100644
--- a/tests/io-channel-helpers.c
+++ b/tests/io-channel-helpers.c
@@ -220,3 +220,28 @@ void test_io_channel_comms(bool blocking,
     g_free(data->output);
     g_free(data);
 }
+
+
+void test_io_channel_comms_serial(QIOChannel *src,
+                                  QIOChannel *dst)
+{
+    struct TestIOData *data;
+
+    data = test_load_io_data("libqemuutil.a");
+    data->src = src;
+    data->dst = dst;
+    data->blocking = true;
+
+    test_io_thread_writer(data);
+    test_io_thread_reader(data);
+
+    g_assert_cmpint(memcmp(data->input,
+                           data->output,
+                           data->len), ==, 0);
+    g_assert(data->readerr == NULL);
+    g_assert(data->writeerr == NULL);
+
+    g_free(data->input);
+    g_free(data->output);
+    g_free(data);
+}
diff --git a/tests/io-channel-helpers.h b/tests/io-channel-helpers.h
index d0085a2..7edbaad 100644
--- a/tests/io-channel-helpers.h
+++ b/tests/io-channel-helpers.h
@@ -27,4 +27,7 @@ void test_io_channel_comms(bool blocking,
                            QIOChannel *src,
                            QIOChannel *dst);
 
+void test_io_channel_comms_serial(QIOChannel *src,
+                                  QIOChannel *dst);
+
 #endif /* TEST_IO_CHANNEL_HELPERS */
diff --git a/tests/test-io-channel-file.c b/tests/test-io-channel-file.c
new file mode 100644
index 0000000..c0a2009
--- /dev/null
+++ b/tests/test-io-channel-file.c
@@ -0,0 +1,89 @@
+/*
+ * 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-file.h"
+#include "io-channel-helpers.h"
+
+
+static void test_io_channel_file(void)
+{
+    QIOChannel *src, *dst;
+
+#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_io_channel_comms_serial(src, dst);
+
+    unlink(TEST_FILE);
+    object_unref(OBJECT(src));
+    object_unref(OBJECT(dst));
+}
+
+
+#ifndef _WIN32
+static void test_io_channel_pipe(bool async)
+{
+    QIOChannel *src, *dst;
+    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_io_channel_comms(async, src, dst);
+
+    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();
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (10 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 11/46] io: add QIOChannelFile class Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-07 15:31   ` Dr. David Alan Gilbert
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 13/46] io: add QIOChannelWebsock class Daniel P. Berrange
                   ` (33 subsequent siblings)
  45 siblings, 1 reply; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Add a QIOChannel subclass that can run the TLS protocol over
the top of another QIOChannel instance. The object provides a
simplified API to perform the handshake when starting the TLS
session. The layering of TLS over the underlying channel does
not have to be setup immediately. It is possible to take an
existing QIOChannel that has done some handshake and then swap
in the QIOChannelTLS layer. This allows for use with protocols
which start TLS right away, and those which start plain text
and then negotiate TLS.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/io/channel-tls.h    | 142 +++++++++++++++++
 io/Makefile.objs            |   1 +
 io/channel-tls.c            | 381 ++++++++++++++++++++++++++++++++++++++++++++
 tests/.gitignore            |   1 +
 tests/Makefile              |   4 +
 tests/test-io-channel-tls.c | 335 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 864 insertions(+)
 create mode 100644 include/io/channel-tls.h
 create mode 100644 io/channel-tls.c
 create mode 100644 tests/test-io-channel-tls.c

diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h
new file mode 100644
index 0000000..0298b17
--- /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/io/Makefile.objs b/io/Makefile.objs
index 9eb0fd9..2b33d3c 100644
--- a/io/Makefile.objs
+++ b/io/Makefile.objs
@@ -4,3 +4,4 @@ io-obj-y += channel.o
 io-obj-y += channel-watch.o
 io-obj-y += channel-socket.o
 io-obj-y += channel-file.o
+io-obj-y += channel-tls.o
diff --git a/io/channel-tls.c b/io/channel-tls.c
new file mode 100644
index 0000000..c8de596
--- /dev/null
+++ b/io/channel-tls.c
@@ -0,0 +1,381 @@
+/*
+ * 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 <glib/gi18n.h>
+
+#include "io/channel-tls.h"
+
+/* #define QIO_DEBUG */
+
+#ifdef QIO_DEBUG
+#define DPRINTF(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+
+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);
+
+    return ioc;
+
+ error:
+    DPRINTF("Session setup failed %s\n",
+            error_get_pretty(*errp));
+    object_unref(OBJECT(ioc));
+    return NULL;
+}
+
+QIOChannelTLS *
+qio_channel_tls_new_client(QIOChannel *master,
+                           QCryptoTLSCreds *creds,
+                           const char *hostname,
+                           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,
+        hostname,
+        NULL,
+        QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
+        errp);
+    if (!ioc->session) {
+        goto error;
+    }
+
+    qcrypto_tls_session_set_callbacks(
+        ioc->session,
+        qio_channel_tls_write_handler,
+        qio_channel_tls_read_handler,
+        ioc);
+
+    return ioc;
+
+ error:
+    DPRINTF("Session setup failed %s\n",
+            error_get_pretty(*errp));
+    object_unref(OBJECT(ioc));
+    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) {
+        qio_task_abort(task, err);
+        goto cleanup;
+    }
+
+    status = qcrypto_tls_session_get_handshake_status(ioc->session);
+    if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
+        if (qcrypto_tls_session_check_credentials(ioc->session,
+                                                  &err) < 0) {
+            DPRINTF("Check creds failed session=%p err=%s\n",
+                    ioc->session, error_get_pretty(err));
+            qio_task_abort(task, err);
+            goto cleanup;
+        }
+        DPRINTF("Handshake compelte session=%p\n",
+                ioc->session);
+        qio_task_complete(task);
+    } else {
+        GIOCondition condition;
+        DPRINTF("Handshake still running %d\n", status);
+        if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) {
+            condition = G_IO_OUT;
+        } else {
+            condition = G_IO_IN;
+        }
+
+        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);
+
+    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;
+
+    if (fds || nfds) {
+        error_setg(errp, "%s",
+                   _("Cannot receive file descriptors over TLS channel"));
+        return -1;
+    }
+
+    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, "%s",
+                             _("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;
+
+    if (fds || nfds) {
+        error_setg(errp, "%s",
+                   _("Cannot send file descriptors over TLS channel"));
+        return -1;
+    }
+
+    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, "%s",
+                             _("Cannot write to TLS channel"));
+            return -1;
+        }
+        done += ret;
+        if (ret < iov[i].iov_len) {
+            break;
+        }
+    }
+    return done;
+}
+
+static void qio_channel_tls_set_blocking(QIOChannel *ioc,
+                                         bool enabled)
+{
+    QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
+
+    qio_channel_set_blocking(tioc->master, enabled);
+}
+
+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_close = qio_channel_tls_close;
+    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/tests/.gitignore b/tests/.gitignore
index bb66d94..aa90bb2 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -26,6 +26,7 @@ test-iov
 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
diff --git a/tests/Makefile b/tests/Makefile
index f896051..8138362 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -81,6 +81,7 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(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-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -366,6 +367,9 @@ 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)
 
 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/test-io-channel-tls.c b/tests/test-io-channel-tls.c
new file mode 100644
index 0000000..75a1396
--- /dev/null
+++ b/tests/test-io-channel-tls.c
@@ -0,0 +1,335 @@
+/*
+ * 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;
+    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);
+    qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false);
+
+    /* 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_io_channel_comms(false,
+                          QIO_CHANNEL(clientChanTLS),
+                          QIO_CHANNEL(serverChanTLS));
+
+    test_io_channel_comms(true,
+                          QIO_CHANNEL(clientChanTLS),
+                          QIO_CHANNEL(serverChanTLS));
+
+    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 */
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 13/46] io: add QIOChannelWebsock class
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (11 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-07 15:44   ` Dr. David Alan Gilbert
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 14/46] io: add QIOChannelCommand class Daniel P. Berrange
                   ` (32 subsequent siblings)
  45 siblings, 1 reply; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Add a QIOChannel subclass that can run the websocket protocol over
the top of another QIOChannel instance. This initial implementation
is only capable of acting as a websockets server. There is no support
for acting as a websockets client yet.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/io/channel-websock.h | 108 +++++
 io/Makefile.objs             |   1 +
 io/channel-websock.c         | 965 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1074 insertions(+)
 create mode 100644 include/io/channel-websock.h
 create mode 100644 io/channel-websock.c

diff --git a/include/io/channel-websock.h b/include/io/channel-websock.h
new file mode 100644
index 0000000..8e69d86
--- /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 "io/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;
+    QIOBuffer encinput;
+    QIOBuffer encoutput;
+    QIOBuffer rawinput;
+    QIOBuffer 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/io/Makefile.objs b/io/Makefile.objs
index 2b33d3c..9f93087 100644
--- a/io/Makefile.objs
+++ b/io/Makefile.objs
@@ -5,3 +5,4 @@ io-obj-y += channel-watch.o
 io-obj-y += channel-socket.o
 io-obj-y += channel-file.o
 io-obj-y += channel-tls.o
+io-obj-y += channel-websock.o
diff --git a/io/channel-websock.c b/io/channel-websock.c
new file mode 100644
index 0000000..0345b90
--- /dev/null
+++ b/io/channel-websock.c
@@ -0,0 +1,965 @@
+/*
+ * 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 <glib/gi18n.h>
+
+/* #define DEBUG_IOC */
+
+#ifdef DEBUG_IOC
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, "channel-websock: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+/* Max amount to allow in rawinput/rawoutput buffers */
+#define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192
+
+#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
+#define SHA1_DIGEST_LEN 20
+
+#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_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"
+
+#define QIO_CHANNEL_WEBSOCK_HEAD_MIN_LEN sizeof(uint16_t)
+#define QIO_CHANNEL_WEBSOCK_HEAD_MAX_LEN \
+    (QIO_CHANNEL_WEBSOCK_HEAD_MIN_LEN + sizeof(uint64_t) + sizeof(uint32_t))
+
+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);
+    qio_buffer_reserve(&ioc->encoutput, responselen);
+    qio_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,
+            "Sec-WebSocket-Protocol");
+    char *version = qio_channel_websock_handshake_entry(line, size,
+            "Sec-WebSocket-Version");
+    char *key = qio_channel_websock_handshake_entry(line, size,
+            "Sec-WebSocket-Key");
+
+    if (!protocols) {
+        error_setg(errp, "%s", _("Missing websocket protocol header data"));
+        goto cleanup;
+    }
+
+    if (!version) {
+        error_setg(errp, "%s", _("Missing websocket version header data"));
+        goto cleanup;
+    }
+
+    if (!key) {
+        error_setg(errp, "%s", _("Missing websocket key header data"));
+        goto cleanup;
+    }
+
+    if (!g_strrstr(protocols, "binary")) {
+        error_setg(errp, _("No 'binary' protocol is supported by client '%s'"),
+                   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;
+    qio_buffer_reserve(&ioc->encinput, want);
+    ret = qio_channel_read(ioc->master,
+                           (char *)qio_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, "%s",
+                       _("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;
+    }
+
+    qio_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;
+
+    DPRINTF("Sending websock handshake reply %p\n", wioc);
+    ret = qio_channel_write(wioc->master,
+                            (char *)wioc->encoutput.buffer,
+                            wioc->encoutput.offset,
+                            &err);
+
+    if (ret < 0) {
+        qio_task_abort(task, err);
+        DPRINTF("Error sending websock handshake reply %p: %s\n",
+                wioc, error_get_pretty(err));
+        error_free(err);
+        return FALSE;
+    }
+
+    qio_buffer_advance(&wioc->encoutput, ret);
+    if (wioc->encoutput.offset == 0) {
+        DPRINTF("Finished sending websock handshake %p\n",
+                wioc);
+        qio_task_complete(task);
+        return FALSE;
+    }
+    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;
+
+    DPRINTF("Reading websock handshake request %p\n", wioc);
+    ret = qio_channel_websock_handshake_read(wioc, &err);
+    if (ret < 0) {
+        DPRINTF("Error reading websock handshake %s\n",
+                error_get_pretty(err));
+        qio_task_abort(task, err);
+        error_free(err);
+        return FALSE;
+    }
+    if (ret == 0) {
+        DPRINTF("Blocking on more request data\n");
+        /* need more data still */
+        return TRUE;
+    }
+
+    DPRINTF("Websock request complete, adding watch for reply %p\n",
+            wioc);
+
+    object_ref(OBJECT(task));
+    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 = 0;
+    unsigned char opcode = QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME;
+    union {
+        char buf[QIO_CHANNEL_WEBSOCK_HEAD_MAX_LEN];
+        QIOChannelWebsockHeader ws;
+    } header;
+
+    DPRINTF("Encoding pending raw output %zu %p\n",
+            ioc->rawoutput.offset, ioc);
+    if (!ioc->rawoutput.offset) {
+        return;
+    }
+
+    header.ws.b0 = 0x80 | (opcode & 0x0f);
+    if (ioc->rawoutput.offset <= 125) {
+        header.ws.b1 = (uint8_t)ioc->rawoutput.offset;
+        header_size = 2;
+    } else if (ioc->rawoutput.offset < 65536) {
+        header.ws.b1 = 0x7e;
+        header.ws.u.s16.l16 = cpu_to_be16((uint16_t)ioc->rawoutput.offset);
+        header_size = 4;
+    } else {
+        header.ws.b1 = 0x7f;
+        header.ws.u.s64.l64 = cpu_to_be64(ioc->rawoutput.offset);
+        header_size = 10;
+    }
+
+    qio_buffer_reserve(&ioc->encoutput, header_size + ioc->rawoutput.offset);
+    qio_buffer_append(&ioc->encoutput, header.buf, header_size);
+    qio_buffer_append(&ioc->encoutput, ioc->rawoutput.buffer,
+                      ioc->rawoutput.offset);
+    qio_buffer_reset(&ioc->rawoutput);
+    DPRINTF("Have %zu bytes encoded output %p\n",
+            ioc->encoutput.offset, ioc);
+}
+
+
+static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
+                                                 Error **errp)
+{
+    unsigned char opcode = 0, fin = 0, has_mask = 0;
+    size_t header_size = 0;
+    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_HEAD_MIN_LEN + 4) {
+        /* header not complete */
+        return QIO_CHANNEL_ERR_BLOCK;
+    }
+
+    fin = (header->b0 & 0x80) >> 7;
+    opcode = header->b0 & 0x0f;
+    has_mask = (header->b1 & 0x80) >> 7;
+    payload_len = header->b1 & 0x7f;
+
+    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, "%s", _("websocket fragmentation is not supported"));
+        return -1;
+    }
+    if (!has_mask) {
+        error_setg(errp, "%s", _("websocket frames must be masked"));
+        return -1;
+    }
+    if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
+        error_setg(errp, "%s", _("only binary websocket frames are supported"));
+        return -1;
+    }
+
+    if (payload_len < 126) {
+        ioc->payload_remain = payload_len;
+        header_size = 6;
+        ioc->mask = header->u.m;
+    } else if (payload_len == 126 && ioc->encinput.offset >= 8) {
+        ioc->payload_remain = be16_to_cpu(header->u.s16.l16);
+        header_size = 8;
+        ioc->mask = header->u.s16.m16;
+    } else if (payload_len == 127 && ioc->encinput.offset >= 14) {
+        ioc->payload_remain = be64_to_cpu(header->u.s64.l64);
+        header_size = 14;
+        ioc->mask = header->u.s64.m64;
+    } else {
+        /* header not complete */
+        return QIO_CHANNEL_ERR_BLOCK;
+    }
+
+    qio_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, "%s",
+                   _("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];
+    }
+
+    qio_buffer_reserve(&ioc->rawinput, payload_len);
+    qio_buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len);
+    qio_buffer_advance(&ioc->encinput, payload_len);
+    return payload_len;
+}
+
+
+QIOChannelWebsock *
+qio_channel_websock_new_server(QIOChannel *master)
+{
+    QIOChannelWebsock *wioc;
+
+    wioc = QIO_CHANNEL_WEBSOCK(object_new(TYPE_QIO_CHANNEL_WEBSOCK));
+
+    wioc->master = master;
+    object_ref(OBJECT(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);
+
+    DPRINTF("Adding watch on master %p for websocket %p handshake\n",
+            ioc->master, ioc);
+    qio_channel_add_watch(ioc->master,
+                          G_IO_IN,
+                          qio_channel_websock_handshake_io,
+                          task,
+                          NULL);
+}
+
+static void qio_channel_websock_init(Object *obj G_GNUC_UNUSED)
+{
+}
+
+
+static void qio_channel_websock_finalize(Object *obj)
+{
+    QIOChannelWebsock *ioc = QIO_CHANNEL_WEBSOCK(obj);
+
+    qio_buffer_free(&ioc->encinput);
+    qio_buffer_free(&ioc->encoutput);
+    qio_buffer_free(&ioc->rawinput);
+    qio_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;
+
+    DPRINTF("Have %zu bytes %p\n", ioc->encoutput.offset, ioc);
+    if (ioc->encinput.offset < 4096) {
+        size_t want = 4096 - ioc->encinput.offset;
+
+        qio_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) {
+            DPRINTF("EOF on wire & no more enc data availabl\n");
+            return 0;
+        }
+        ioc->encinput.offset += ret;
+        DPRINTF("Now have %zu bytes enc input\n", ioc->encinput.offset);
+    }
+
+    if (ioc->payload_remain == 0) {
+        DPRINTF("Looking to decode header\n");
+        ret = qio_channel_websock_decode_header(ioc, errp);
+        if (ret < 0) {
+            return ret;
+        }
+        if (ret == 0) {
+            DPRINTF("EOF when decoding header\n");
+            return 0;
+        }
+    }
+    DPRINTF("Looking to decode payload %zu\n", ioc->payload_remain);
+
+    ret = qio_channel_websock_decode_payload(ioc, errp);
+    if (ret < 0) {
+        return ret;
+    }
+    DPRINTF("Now have %zu bytes raw input\n", ioc->rawinput.offset);
+    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);
+
+    DPRINTF("Writing %zu bytes %p\n", ioc->encoutput.offset, 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) {
+                DPRINTF("Blocking but wrote %zu\n", done);
+                return done;
+            } else {
+                DPRINTF("Error while writing %s\n",
+                        error_get_pretty(*errp));
+                return ret;
+            }
+        }
+        qio_buffer_advance(&ioc->encoutput, ret);
+        done += ret;
+    }
+    DPRINTF("Wrote %zu total\n", done);
+    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;
+
+    DPRINTF("Websock master flush %p %d\n", ioc, condition);
+    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) {
+            DPRINTF("Got EOF when reading %p\n", wioc);
+            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) {
+        DPRINTF("Removing old master watch %u %p\n", ioc->io_tag, ioc);
+        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) {
+        DPRINTF("Not adding master watch due to error %p %s\n",
+                ioc, error_get_pretty(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;
+    }
+
+    DPRINTF("Cond %d output=%zu input=%zu eof=%d\n",
+            cond, ioc->encoutput.offset, ioc->encinput.offset, ioc->io_eof);
+    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;
+    DPRINTF("Read ioc %p %zu %p\n", iov, niov, ioc);
+    if (fds || nfds) {
+        error_setg(errp, "%s",
+                   _("Cannot receive file descriptors over websocket channel"));
+        return -1;
+    }
+
+    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;
+        }
+    }
+
+    qio_buffer_advance(&wioc->rawinput, got);
+    qio_channel_websock_set_watch(wioc);
+    DPRINTF("Returning %zu\n", got);
+    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 (fds || nfds) {
+        error_setg(errp, "%s",
+                   _("Cannot send file descriptors over websocket channel"));
+        return -1;
+    }
+
+    DPRINTF("Writev %p %zu %p err=%p\n", iov, niov, ioc, wioc->io_err);
+    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;
+        }
+
+        qio_buffer_reserve(&wioc->rawoutput, want);
+        qio_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 void qio_channel_websock_set_blocking(QIOChannel *ioc,
+                                             bool enabled)
+{
+    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
+
+    qio_channel_set_blocking(wioc->master, enabled);
+}
+
+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;
+    }
+
+#if 0
+    DPRINTF("Prep source %d cond %d input=%zu output=%zu\n",
+            wsource->condition, cond,
+            wsource->wioc->rawinput.offset,
+            wsource->wioc->rawoutput.offset);
+#endif
+
+    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;
+    }
+
+    if (cond & wsource->condition) {
+        DPRINTF("Check source %d cond %d input=%zu output=%zu\n",
+                wsource->condition, cond,
+                wsource->wioc->rawinput.offset,
+                wsource->wioc->rawoutput.offset);
+    }
+    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;
+    }
+
+    DPRINTF("Disp source %d cond %d input=%zu output=%zu\n",
+            wsource->condition, cond,
+            wsource->wioc->rawinput.offset,
+            wsource->wioc->rawoutput.offset);
+    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;
+
+    DPRINTF("Creating websock watch %p cond=%d\n", wioc, condition);
+    source = g_source_new(&qio_channel_websock_source_funcs,
+                          sizeof(QIOChannelWebsockSource));
+    g_source_set_name(source, "QIOChannelWebsock");
+    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_close = qio_channel_websock_close;
+    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_init = qio_channel_websock_init,
+    .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);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 14/46] io: add QIOChannelCommand class
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (12 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 13/46] io: add QIOChannelWebsock class Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 15/46] ui: convert VNC startup code to use SocketAddress Daniel P. Berrange
                   ` (31 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Add a QIOChannel subclass that is capable of performing I/O
to/from a separate process, via a pair of pipes. The command
can be used for unidirectional or bi-directional I/O.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/io/channel-command.h    |  91 ++++++++++
 io/Makefile.objs                |   1 +
 io/channel-command.c            | 370 ++++++++++++++++++++++++++++++++++++++++
 tests/.gitignore                |   2 +
 tests/Makefile                  |   3 +
 tests/test-io-channel-command.c | 121 +++++++++++++
 6 files changed, 588 insertions(+)
 create mode 100644 include/io/channel-command.h
 create mode 100644 io/channel-command.c
 create mode 100644 tests/test-io-channel-command.c

diff --git a/include/io/channel-command.h b/include/io/channel-command.h
new file mode 100644
index 0000000..bd3c599
--- /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/io/Makefile.objs b/io/Makefile.objs
index 9f93087..9e28ee5 100644
--- a/io/Makefile.objs
+++ b/io/Makefile.objs
@@ -6,3 +6,4 @@ io-obj-y += channel-socket.o
 io-obj-y += channel-file.o
 io-obj-y += channel-tls.o
 io-obj-y += channel-websock.o
+io-obj-y += channel-command.o
diff --git a/io/channel-command.c b/io/channel-command.c
new file mode 100644
index 0000000..3f684c6
--- /dev/null
+++ b/io/channel-command.c
@@ -0,0 +1,370 @@
+/*
+ * 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 <glib/gi18n.h>
+
+#include "io/channel-command.h"
+#include "io/channel-watch.h"
+#include "qemu/sockets.h"
+
+/* #define QIO_DEBUG */
+
+#ifdef QIO_DEBUG
+#define DPRINTF(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+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;
+
+    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;
+
+    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]);
+    }
+
+    return qio_channel_command_new_pid(stdinnull ? devnull : stdinfd[1],
+                                       stdoutnull ? devnull : stdoutfd[0],
+                                       pid);
+
+ 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.
+     */
+    DPRINTF("aborting child process %llu", (unsigned long long)ioc->pid);
+ rewait:
+    ret = waitpid(ioc->pid, &status, WNOHANG);
+    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;
+    }
+
+    DPRINTF("process has ended: %d", status);
+    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;
+
+    if (fds || nfds) {
+        error_setg_errno(errp, EINVAL, "%s",
+                         _("Channel does not support command descriptor passing"));
+        return -1;
+    }
+
+ 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, "%s",
+                         _("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;
+
+    if (fds || nfds) {
+        error_setg_errno(errp, EINVAL, "%s",
+                         _("Channel does not support command descriptor passing"));
+        return -1;
+    }
+
+ 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 void qio_channel_command_set_blocking(QIOChannel *ioc,
+                                             bool enabled)
+{
+    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);
+    }
+}
+
+
+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/tests/.gitignore b/tests/.gitignore
index aa90bb2..a02272d 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -23,6 +23,8 @@ test-cutils
 test-hbitmap
 test-int128
 test-iov
+test-io-channel-command
+test-io-channel-command.fifo
 test-io-channel-file
 test-io-channel-file.txt
 test-io-channel-socket
diff --git a/tests/Makefile b/tests/Makefile
index 8138362..cbcec26 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -82,6 +82,7 @@ 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-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -370,6 +371,8 @@ tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
 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)
 
 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/test-io-channel-command.c b/tests/test-io-channel-command.c
new file mode 100644
index 0000000..2dc020d
--- /dev/null
+++ b/tests/test-io-channel-command.c
@@ -0,0 +1,121 @@
+/*
+ * 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)
+{
+    QIOChannel *src, *dst;
+    char *cwd = g_get_current_dir();
+    char *fifo = g_strdup_printf("%s/test-io-channel-command.fifo", cwd);
+    char *srcfifo = g_strdup_printf("PIPE:%s,wronly", fifo);
+    char *dstfifo = g_strdup_printf("PIPE:%s,rdonly", fifo);
+    const char * srcargv[] = {
+        "/bin/socat", "-", srcfifo, NULL,
+    };
+    const char * dstargv[] = {
+        "/bin/socat", dstfifo, "-", NULL,
+    };
+
+    unlink(fifo);
+    if (access("/bin/socat", X_OK) < 0) {
+        return; /* Pretend success if socat is not present */
+    }
+    if (mkfifo(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_io_channel_comms(async, src, dst);
+
+    object_unref(OBJECT(src));
+    object_unref(OBJECT(dst));
+
+    g_free(cwd);
+    g_free(fifo);
+    g_free(srcfifo);
+    g_free(dstfifo);
+    unlink(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;
+    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_io_channel_comms(async, ioc, ioc);
+
+    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();
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 15/46] ui: convert VNC startup code to use SocketAddress
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (13 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 14/46] io: add QIOChannelCommand class Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 16/46] ui: convert VNC server to use QIOChannelSocket Daniel P. Berrange
                   ` (30 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The VNC code is currently using QemuOpts to configure the
sockets connections / listeners it needs. Convert it to
use SocketAddress to bring it in line with modern QAPI
based code elsewhere in QEMU.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 ui/vnc.c | 161 ++++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 91 insertions(+), 70 deletions(-)

diff --git a/ui/vnc.c b/ui/vnc.c
index 3303987..8896fef 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3463,18 +3463,14 @@ void vnc_display_open(const char *id, Error **errp)
 {
     VncDisplay *vs = vnc_display_find(id);
     QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
-    QemuOpts *sopts, *wsopts;
+    SocketAddress *saddr = NULL, *wsaddr = NULL;
     const char *share, *device_id;
     QemuConsole *con;
     bool password = false;
     bool reverse = false;
     const char *vnc;
-    const char *has_to;
     char *h;
-    bool has_ipv4 = false;
-    bool has_ipv6 = false;
     const char *credid;
-    const char *websocket;
     bool sasl = false;
 #ifdef CONFIG_VNC_SASL
     int saslErr;
@@ -3496,44 +3492,83 @@ void vnc_display_open(const char *id, Error **errp)
         return;
     }
 
-    sopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-    wsopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-
     h = strrchr(vnc, ':');
     if (h) {
-        char *host;
         size_t hlen = h - vnc;
 
-        if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
-            host = g_strndup(vnc + 1, hlen - 2);
+        const char *websocket = qemu_opt_get(opts, "websocket");
+        const char *has_to = qemu_opt_get(opts, "to");
+        bool has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
+        bool has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
+
+        saddr = g_new0(SocketAddress, 1);
+        if (websocket) {
+            if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
+                error_setg(errp,
+                           "SHA1 hash support is required for websockets");
+                goto fail;
+            }
+
+            wsaddr = g_new0(SocketAddress, 1);
+            vs->ws_enabled = true;
+        }
+
+        if (strncmp(vnc, "unix:", 5) == 0) {
+            saddr->kind = SOCKET_ADDRESS_KIND_UNIX;
+            saddr->q_unix = g_new0(UnixSocketAddress, 1);
+            saddr->q_unix->path = g_strdup(vnc + 5);
+
+            if (vs->ws_enabled) {
+                error_setg(errp, "UNIX sockets not supported with websock");
+                goto fail;
+            }
         } else {
-            host = g_strndup(vnc, hlen);
+            unsigned long long baseport;
+            saddr->kind = SOCKET_ADDRESS_KIND_INET;
+            saddr->inet = g_new0(InetSocketAddress, 1);
+            if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
+                saddr->inet->host = g_strndup(vnc + 1, hlen - 2);
+            } else {
+                saddr->inet->host = g_strndup(vnc, hlen);
+            }
+            if (parse_uint_full(h + 1, &baseport, 10) < 0) {
+                error_setg(errp, "can't convert to a number: %s", h + 1);
+                goto fail;
+            }
+            if (baseport > 65535 ||
+                baseport + 5900 > 65535) {
+                error_setg(errp, "port %s out of range", h + 1);
+                goto fail;
+            }
+            saddr->inet->port = g_strdup_printf(
+                "%d", (int)baseport + 5900);
+
+            if (has_to) {
+                saddr->inet->has_to = true;
+                saddr->inet->to = strtol(has_to, NULL, 10);
+            }
+            saddr->inet->ipv4 = saddr->inet->has_ipv4 = has_ipv4;
+            saddr->inet->ipv6 = saddr->inet->has_ipv6 = has_ipv6;
+
+            if (vs->ws_enabled) {
+                wsaddr->kind = SOCKET_ADDRESS_KIND_INET;
+                wsaddr->inet = g_new0(InetSocketAddress, 1);
+                wsaddr->inet->host = g_strdup(saddr->inet->host);
+                wsaddr->inet->port = g_strdup(websocket);
+
+                if (has_to) {
+                    wsaddr->inet->has_to = true;
+                    wsaddr->inet->to = strtol(has_to, NULL, 10);
+                }
+                wsaddr->inet->ipv4 = wsaddr->inet->has_ipv4 = has_ipv4;
+                wsaddr->inet->ipv6 = wsaddr->inet->has_ipv6 = has_ipv6;
+            }
         }
-        qemu_opt_set(sopts, "host", host, &error_abort);
-        qemu_opt_set(wsopts, "host", host, &error_abort);
-        qemu_opt_set(sopts, "port", h+1, &error_abort);
-        g_free(host);
     } else {
         error_setg(errp, "no vnc port specified");
         goto fail;
     }
 
-    has_to = qemu_opt_get(opts, "to");
-    has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
-    has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
-    if (has_to) {
-        qemu_opt_set(sopts, "to", has_to, &error_abort);
-        qemu_opt_set(wsopts, "to", has_to, &error_abort);
-    }
-    if (has_ipv4) {
-        qemu_opt_set(sopts, "ipv4", "on", &error_abort);
-        qemu_opt_set(wsopts, "ipv4", "on", &error_abort);
-    }
-    if (has_ipv6) {
-        qemu_opt_set(sopts, "ipv6", "on", &error_abort);
-        qemu_opt_set(wsopts, "ipv6", "on", &error_abort);
-    }
-
     password = qemu_opt_get_bool(opts, "password", false);
     if (password) {
         if (fips_get_state()) {
@@ -3639,16 +3674,6 @@ void vnc_display_open(const char *id, Error **errp)
     }
     vs->connections_limit = qemu_opt_get_number(opts, "connections", 32);
 
-    websocket = qemu_opt_get(opts, "websocket");
-    if (websocket) {
-        vs->ws_enabled = true;
-        qemu_opt_set(wsopts, "port", websocket, &error_abort);
-        if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
-            error_setg(errp, "SHA1 hash support is required for websockets");
-            goto fail;
-        }
-    }
-
 #ifdef CONFIG_VNC_JPEG
     vs->lossy = qemu_opt_get_bool(opts, "lossy", false);
 #endif
@@ -3682,7 +3707,7 @@ void vnc_display_open(const char *id, Error **errp)
     }
 #endif
 
-    if (vnc_display_setup_auth(vs, password, sasl, websocket, errp) < 0) {
+    if (vnc_display_setup_auth(vs, password, sasl, vs->ws_enabled, errp) < 0) {
         goto fail;
     }
 
@@ -3727,37 +3752,32 @@ void vnc_display_open(const char *id, Error **errp)
         int csock;
         vs->lsock = -1;
         vs->lwebsock = -1;
-        if (strncmp(vnc, "unix:", 5) == 0) {
-            csock = unix_connect(vnc+5, errp);
-        } else {
-            csock = inet_connect(vnc, errp);
+        if (vs->ws_enabled) {
+            error_setg(errp, "Cannot use websockets in reverse mode");
+            goto fail;
         }
+        csock = socket_connect(saddr, errp,
+                               NULL, NULL);
         if (csock < 0) {
             goto fail;
         }
+        vs->is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX;
         vnc_connect(vs, csock, false, false);
     } else {
         /* listen for connects */
-        if (strncmp(vnc, "unix:", 5) == 0) {
-            vs->lsock = unix_listen(vnc+5, NULL, 0, errp);
-            if (vs->lsock < 0) {
-                goto fail;
-            }
-            vs->is_unix = true;
-        } else {
-            vs->lsock = inet_listen_opts(sopts, 5900, errp);
-            if (vs->lsock < 0) {
-                goto fail;
-            }
-            if (vs->ws_enabled) {
-                vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
-                if (vs->lwebsock < 0) {
-                    if (vs->lsock != -1) {
-                        close(vs->lsock);
-                        vs->lsock = -1;
-                    }
-                    goto fail;
+        vs->lsock = socket_listen(saddr, errp);
+        if (vs->lsock < 0) {
+            goto fail;
+        }
+        vs->is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX;
+        if (vs->ws_enabled) {
+            vs->lwebsock = socket_listen(wsaddr, errp);
+            if (vs->lwebsock < 0) {
+                if (vs->lsock != -1) {
+                    close(vs->lsock);
+                    vs->lsock = -1;
                 }
+                goto fail;
             }
         }
         vs->enabled = true;
@@ -3767,13 +3787,14 @@ void vnc_display_open(const char *id, Error **errp)
                                 NULL, vs);
         }
     }
-    qemu_opts_del(sopts);
-    qemu_opts_del(wsopts);
+
+    qapi_free_SocketAddress(saddr);
+    qapi_free_SocketAddress(wsaddr);
     return;
 
 fail:
-    qemu_opts_del(sopts);
-    qemu_opts_del(wsopts);
+    qapi_free_SocketAddress(saddr);
+    qapi_free_SocketAddress(wsaddr);
     vs->enabled = false;
     vs->ws_enabled = false;
 }
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 16/46] ui: convert VNC server to use QIOChannelSocket
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (14 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 15/46] ui: convert VNC startup code to use SocketAddress Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 17/46] ui: convert VNC server to use QIOChannelTLS Daniel P. Berrange
                   ` (29 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The minimal first step conversion to use QIOChannelSocket
classes instead of directly using POSIX sockets API. This
will later be extended to also cover the TLS, SASL and
websockets code.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 ui/vnc-auth-sasl.c     |  57 +++--
 ui/vnc-auth-vencrypt.c |  27 ++-
 ui/vnc-jobs.c          |  12 +-
 ui/vnc-ws.c            |  51 ++++-
 ui/vnc-ws.h            |   8 +-
 ui/vnc.c               | 559 ++++++++++++++++++++++++++-----------------------
 ui/vnc.h               |  22 +-
 7 files changed, 426 insertions(+), 310 deletions(-)

diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index d118266..f5bfdd1 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -62,7 +62,7 @@ long vnc_client_write_sasl(VncState *vs)
                           (const char **)&vs->sasl.encoded,
                           &vs->sasl.encodedLength);
         if (err != SASL_OK)
-            return vnc_client_io_error(vs, -1, EIO);
+            return vnc_client_io_error(vs, -1, NULL);
 
         vs->sasl.encodedOffset = 0;
     }
@@ -86,7 +86,11 @@ long vnc_client_write_sasl(VncState *vs)
      * SASL encoded output
      */
     if (vs->output.offset == 0) {
-        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
     }
 
     return ret;
@@ -110,7 +114,7 @@ long vnc_client_read_sasl(VncState *vs)
                       &decoded, &decodedLen);
 
     if (err != SASL_OK)
-        return vnc_client_io_error(vs, -1, -EIO);
+        return vnc_client_io_error(vs, -1, NULL);
     VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
               encoded, ret, decoded, decodedLen);
     qio_buffer_reserve(&vs->input, decodedLen);
@@ -255,17 +259,17 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le
         vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
     } else {
         if (!vnc_auth_sasl_check_ssf(vs)) {
-            VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
+            VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc);
             goto authreject;
         }
 
         /* Check username whitelist ACL */
         if (vnc_auth_sasl_check_access(vs) < 0) {
-            VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
+            VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc);
             goto authreject;
         }
 
-        VNC_DEBUG("Authentication successful %d\n", vs->csock);
+        VNC_DEBUG("Authentication successful %p\n", vs->ioc);
         vnc_write_u32(vs, 0); /* Accept auth */
         /*
          * Delay writing in SSF encoded mode until pending output
@@ -383,17 +387,17 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l
         vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
     } else {
         if (!vnc_auth_sasl_check_ssf(vs)) {
-            VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
+            VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc);
             goto authreject;
         }
 
         /* Check username whitelist ACL */
         if (vnc_auth_sasl_check_access(vs) < 0) {
-            VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
+            VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc);
             goto authreject;
         }
 
-        VNC_DEBUG("Authentication successful %d\n", vs->csock);
+        VNC_DEBUG("Authentication successful %p\n", vs->ioc);
         vnc_write_u32(vs, 0); /* Accept auth */
         start_client_init(vs);
     }
@@ -487,6 +491,32 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s
     return 0;
 }
 
+static char *
+vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
+                          bool local,
+                          Error **errp)
+{
+    SocketAddress *addr;
+    char *ret;
+
+    if (local) {
+        addr = qio_channel_socket_get_local_address(ioc, errp);
+    } else {
+        addr = qio_channel_socket_get_remote_address(ioc, errp);
+    }
+    if (!addr) {
+        return NULL;
+    }
+
+    if (addr->kind != SOCKET_ADDRESS_KIND_INET) {
+        error_setg(errp, "Not an inet socket type");
+        return NULL;
+    }
+    ret = g_strdup_printf("%s;%s", addr->inet->host, addr->inet->port);
+    qapi_free_SocketAddress(addr);
+    return ret;
+}
+
 void start_auth_sasl(VncState *vs)
 {
     const char *mechlist = NULL;
@@ -495,13 +525,16 @@ void start_auth_sasl(VncState *vs)
     char *localAddr, *remoteAddr;
     int mechlistlen;
 
-    VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
+    VNC_DEBUG("Initialize SASL auth %p\n", vs->ioc);
 
     /* Get local & remote client addresses in form  IPADDR;PORT */
-    if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
+    localAddr = vnc_socket_ip_addr_string(vs->sioc, true, NULL);
+    if (!localAddr) {
         goto authabort;
+    }
 
-    if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
+    remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, NULL);
+    if (!remoteAddr) {
         g_free(localAddr);
         goto authabort;
     }
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 44ac2fa..95a6823 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -63,7 +63,9 @@ static void start_auth_vencrypt_subauth(VncState *vs)
     }
 }
 
-static void vnc_tls_handshake_io(void *opaque);
+static gboolean vnc_tls_handshake_io(QIOChannel *ioc,
+                                     GIOCondition condition,
+                                     void *opaque);
 
 static int vnc_start_vencrypt_handshake(VncState *vs)
 {
@@ -80,19 +82,31 @@ static int vnc_start_vencrypt_handshake(VncState *vs)
             goto error;
         }
         VNC_DEBUG("Client verification passed, starting TLS I/O\n");
-        qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
 
         start_auth_vencrypt_subauth(vs);
         break;
 
     case QCRYPTO_TLS_HANDSHAKE_RECVING:
         VNC_DEBUG("Handshake interrupted (blocking read)\n");
-        qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vnc_tls_handshake_io, vs, NULL);
         break;
 
     case QCRYPTO_TLS_HANDSHAKE_SENDING:
         VNC_DEBUG("Handshake interrupted (blocking write)\n");
-        qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_OUT, vnc_tls_handshake_io, vs, NULL);
         break;
     }
 
@@ -105,12 +119,15 @@ static int vnc_start_vencrypt_handshake(VncState *vs)
     return -1;
 }
 
-static void vnc_tls_handshake_io(void *opaque)
+static gboolean vnc_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
+                                     GIOCondition condition G_GNUC_UNUSED,
+                                     void *opaque)
 {
     VncState *vs = (VncState *)opaque;
 
     VNC_DEBUG("Handshake IO continue\n");
     vnc_start_vencrypt_handshake(vs);
+    return TRUE;
 }
 
 
diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 9824c34..7c44e5b 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -169,7 +169,7 @@ void vnc_jobs_consume_buffer(VncState *vs)
         vnc_write(vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset);
         qio_buffer_reset(&vs->jobs_buffer);
     }
-    flush = vs->csock != -1 && vs->abort != true;
+    flush = vs->ioc != NULL && vs->abort != true;
     vnc_unlock_output(vs);
 
     if (flush) {
@@ -194,7 +194,8 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
     local->hextile = orig->hextile;
     local->zrle = orig->zrle;
     local->output =  queue->buffer;
-    local->csock = -1; /* Don't do any network work on this thread */
+    local->sioc = NULL; /* Don't do any network work on this thread */
+    local->ioc = NULL; /* Don't do any network work on this thread */
 
     qio_buffer_reset(&local->output);
 }
@@ -231,7 +232,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
     }
 
     vnc_lock_output(job->vs);
-    if (job->vs->csock == -1 || job->vs->abort == true) {
+    if (job->vs->ioc == NULL || job->vs->abort == true) {
         vnc_unlock_output(job->vs);
         goto disconnected;
     }
@@ -251,7 +252,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
     QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
         int n;
 
-        if (job->vs->csock == -1) {
+        if (job->vs->ioc == NULL) {
             vnc_unlock_display(job->vs->vd);
             /* Copy persistent encoding data */
             vnc_async_encoding_end(job->vs, &vs);
@@ -273,8 +274,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
     vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
 
     vnc_lock_output(job->vs);
-
-    if (job->vs->csock != -1) {
+    if (job->vs->ioc != NULL) {
         qio_buffer_reserve(&job->vs->jobs_buffer, vs.output.offset);
         qio_buffer_append(&job->vs->jobs_buffer, vs.output.buffer,
                           vs.output.offset);
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 2fe4476..7b2cc68 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -37,17 +37,29 @@ static int vncws_start_tls_handshake(VncState *vs)
             goto error;
         }
         VNC_DEBUG("Client verification passed, starting TLS I/O\n");
-        qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL);
         break;
 
     case QCRYPTO_TLS_HANDSHAKE_RECVING:
         VNC_DEBUG("Handshake interrupted (blocking read)\n");
-        qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL);
         break;
 
     case QCRYPTO_TLS_HANDSHAKE_SENDING:
         VNC_DEBUG("Handshake interrupted (blocking write)\n");
-        qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_OUT, vncws_tls_handshake_io, vs, NULL);
         break;
     }
 
@@ -60,7 +72,9 @@ static int vncws_start_tls_handshake(VncState *vs)
     return -1;
 }
 
-void vncws_tls_handshake_io(void *opaque)
+gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
+                                GIOCondition condition G_GNUC_UNUSED,
+                                void *opaque)
 {
     VncState *vs = (VncState *)opaque;
     Error *err = NULL;
@@ -75,7 +89,7 @@ void vncws_tls_handshake_io(void *opaque)
                   error_get_pretty(err));
         error_free(err);
         vnc_client_error(vs);
-        return;
+        return TRUE;
     }
 
     qcrypto_tls_session_set_callbacks(vs->tls,
@@ -85,11 +99,11 @@ void vncws_tls_handshake_io(void *opaque)
 
     VNC_DEBUG("Start TLS WS handshake process\n");
     vncws_start_tls_handshake(vs);
+    return TRUE;
 }
 
-void vncws_handshake_read(void *opaque)
+static void vncws_handshake_read(VncState *vs)
 {
-    VncState *vs = opaque;
     uint8_t *handshake_end;
     long ret;
     /* Typical HTTP headers from novnc are 512 bytes, so limiting
@@ -99,7 +113,7 @@ void vncws_handshake_read(void *opaque)
     ret = vnc_client_read_buf(vs, qio_buffer_end(&vs->ws_input), want);
 
     if (!ret) {
-        if (vs->csock == -1) {
+        if (vs->disconnecting) {
             vnc_disconnect_finish(vs);
         }
         return;
@@ -109,7 +123,11 @@ void vncws_handshake_read(void *opaque)
     handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer,
             vs->ws_input.offset, WS_HANDSHAKE_END);
     if (handshake_end) {
-        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
         vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset);
         qio_buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
                 strlen(WS_HANDSHAKE_END));
@@ -120,6 +138,15 @@ void vncws_handshake_read(void *opaque)
 }
 
 
+gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
+                            GIOCondition condition G_GNUC_UNUSED,
+                            void *opaque)
+{
+    VncState *vs = opaque;
+    vncws_handshake_read(vs);
+    return TRUE;
+}
+
 long vnc_client_read_ws(VncState *vs)
 {
     int ret, err;
@@ -187,7 +214,11 @@ long vnc_client_write_ws(VncState *vs)
     qio_buffer_advance(&vs->ws_output, ret);
 
     if (vs->ws_output.offset == 0) {
-        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
     }
 
     return ret;
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
index 2a222a8..21caaf3 100644
--- a/ui/vnc-ws.h
+++ b/ui/vnc-ws.h
@@ -72,8 +72,12 @@ enum {
     WS_OPCODE_PONG = 0xA
 };
 
-void vncws_tls_handshake_io(void *opaque);
-void vncws_handshake_read(void *opaque);
+gboolean vncws_tls_handshake_io(QIOChannel *ioc,
+                                GIOCondition condition,
+                                void *opaque);
+gboolean vncws_handshake_io(QIOChannel *ioc,
+                            GIOCondition condition,
+                            void *opaque);
 long vnc_client_write_ws(VncState *vs);
 long vnc_client_read_ws(VncState *vs);
 void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);
diff --git a/ui/vnc.c b/ui/vnc.c
index 8896fef..2f8c15e 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -70,8 +70,8 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
         [VNC_SHARE_MODE_EXCLUSIVE]    = "exclusive",
         [VNC_SHARE_MODE_DISCONNECTED] = "disconnected",
     };
-    fprintf(stderr, "%s/%d: %s -> %s\n", __func__,
-            vs->csock, mn[vs->share_mode], mn[mode]);
+    fprintf(stderr, "%s/%p: %s -> %s\n", __func__,
+            vs->ioc, mn[vs->share_mode], mn[mode]);
 #endif
 
     switch (vs->share_mode) {
@@ -105,105 +105,74 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
     }
 }
 
-static char *addr_to_string(const char *format,
-                            struct sockaddr_storage *sa,
-                            socklen_t salen) {
-    char *addr;
-    char host[NI_MAXHOST];
-    char serv[NI_MAXSERV];
-    int err;
-    size_t addrlen;
 
-    if ((err = getnameinfo((struct sockaddr *)sa, salen,
-                           host, sizeof(host),
-                           serv, sizeof(serv),
-                           NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
-        VNC_DEBUG("Cannot resolve address %d: %s\n",
-                  err, gai_strerror(err));
-        return NULL;
-    }
-
-    /* Enough for the existing format + the 2 vars we're
-     * substituting in. */
-    addrlen = strlen(format) + strlen(host) + strlen(serv);
-    addr = g_malloc(addrlen + 1);
-    snprintf(addr, addrlen, format, host, serv);
-    addr[addrlen] = '\0';
-
-    return addr;
-}
-
-
-char *vnc_socket_local_addr(const char *format, int fd) {
-    struct sockaddr_storage sa;
-    socklen_t salen;
-
-    salen = sizeof(sa);
-    if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0)
-        return NULL;
-
-    return addr_to_string(format, &sa, salen);
-}
+static VncBasicInfo *
+vnc_basic_info_from_socket_address(SocketAddress *addr,
+                                   Error **errp)
+{
+    VncBasicInfo *info;
 
-char *vnc_socket_remote_addr(const char *format, int fd) {
-    struct sockaddr_storage sa;
-    socklen_t salen;
+    info = g_new0(VncBasicInfo, 1);
 
-    salen = sizeof(sa);
-    if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0)
-        return NULL;
+    switch (addr->kind) {
+    case SOCKET_ADDRESS_KIND_INET:
+        info->host = g_strdup(addr->inet->host);
+        info->service = g_strdup(addr->inet->port);
+        if (addr->inet->ipv6) {
+            info->family = NETWORK_ADDRESS_FAMILY_IPV6;
+        } else {
+            info->family = NETWORK_ADDRESS_FAMILY_IPV4;
+        }
+        break;
 
-    return addr_to_string(format, &sa, salen);
-}
+    case SOCKET_ADDRESS_KIND_UNIX:
+        info->host = g_strdup("");
+        info->service = g_strdup(addr->q_unix->path);
+        info->family = NETWORK_ADDRESS_FAMILY_UNIX;
+        break;
 
-static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
-                                        socklen_t salen)
-{
-    VncBasicInfo *info;
-    char host[NI_MAXHOST];
-    char serv[NI_MAXSERV];
-    int err;
-
-    if ((err = getnameinfo((struct sockaddr *)sa, salen,
-                           host, sizeof(host),
-                           serv, sizeof(serv),
-                           NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
-        VNC_DEBUG("Cannot resolve address %d: %s\n",
-                  err, gai_strerror(err));
+    default:
+        error_setg(errp, "Unsupported socket kind %d",
+                   addr->kind);
+        g_free(info);
         return NULL;
     }
 
-    info = g_malloc0(sizeof(VncBasicInfo));
-    info->host = g_strdup(host);
-    info->service = g_strdup(serv);
-    info->family = inet_netfamily(sa->ss_family);
     return info;
 }
 
-static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd)
+static VncBasicInfo *
+vnc_basic_info_get_from_server_addr(QIOChannelSocket *ioc,
+                                    Error **errp)
 {
-    struct sockaddr_storage sa;
-    socklen_t salen;
+    VncBasicInfo *info;
+    SocketAddress *addr;
 
-    salen = sizeof(sa);
-    if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
+    addr = qio_channel_socket_get_local_address(ioc, errp);
+    if (!addr) {
         return NULL;
     }
 
-    return vnc_basic_info_get(&sa, salen);
+    info = vnc_basic_info_from_socket_address(addr, errp);
+    qapi_free_SocketAddress(addr);
+    return info;
 }
 
-static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd)
+static VncBasicInfo *
+vnc_basic_info_get_from_remote_addr(QIOChannelSocket *ioc,
+                                    Error **errp)
 {
-    struct sockaddr_storage sa;
-    socklen_t salen;
+    VncBasicInfo *info;
+    SocketAddress *addr;
 
-    salen = sizeof(sa);
-    if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
+    addr = qio_channel_socket_get_remote_address(ioc, errp);
+    if (!addr) {
         return NULL;
     }
 
-    return vnc_basic_info_get(&sa, salen);
+    info = vnc_basic_info_from_socket_address(addr, errp);
+    qapi_free_SocketAddress(addr);
+    return info;
 }
 
 static const char *vnc_auth_name(VncDisplay *vd) {
@@ -256,8 +225,11 @@ static const char *vnc_auth_name(VncDisplay *vd) {
 static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
 {
     VncServerInfo *info;
-    VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock);
+    Error *err = NULL;
+    VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(
+        vd->lsock, &err);
     if (!bi) {
+        error_free(err);
         return NULL;
     }
 
@@ -291,11 +263,15 @@ static void vnc_client_cache_auth(VncState *client)
 
 static void vnc_client_cache_addr(VncState *client)
 {
-    VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock);
+    Error *err = NULL;
+    VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(
+        client->sioc, &err);
 
     if (bi) {
         client->info = g_malloc0(sizeof(*client->info));
         client->info->base = bi;
+    } else {
+        error_free(err);
     }
 }
 
@@ -332,28 +308,19 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
 
 static VncClientInfo *qmp_query_vnc_client(const VncState *client)
 {
-    struct sockaddr_storage sa;
-    socklen_t salen = sizeof(sa);
-    char host[NI_MAXHOST];
-    char serv[NI_MAXSERV];
     VncClientInfo *info;
+    VncBasicInfo *binfo;
+    Error *err = NULL;
 
-    if (getpeername(client->csock, (struct sockaddr *)&sa, &salen) < 0) {
-        return NULL;
-    }
-
-    if (getnameinfo((struct sockaddr *)&sa, salen,
-                    host, sizeof(host),
-                    serv, sizeof(serv),
-                    NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+    binfo = vnc_basic_info_get_from_remote_addr(
+        client->sioc, &err);
+    if (!binfo) {
+        error_free(err);
         return NULL;
     }
 
     info = g_malloc0(sizeof(*info));
-    info->base = g_malloc0(sizeof(*info->base));
-    info->base->host = g_strdup(host);
-    info->base->service = g_strdup(serv);
-    info->base->family = inet_netfamily(sa.ss_family);
+    info->base = binfo;
     info->base->websocket = client->websocket;
 
     if (client->tls) {
@@ -403,81 +370,86 @@ VncInfo *qmp_query_vnc(Error **errp)
 {
     VncInfo *info = g_malloc0(sizeof(*info));
     VncDisplay *vd = vnc_display_find(NULL);
+    SocketAddress *addr = NULL;
 
     if (vd == NULL || !vd->enabled) {
         info->enabled = false;
     } else {
-        struct sockaddr_storage sa;
-        socklen_t salen = sizeof(sa);
-        char host[NI_MAXHOST];
-        char serv[NI_MAXSERV];
-
         info->enabled = true;
 
         /* for compatibility with the original command */
         info->has_clients = true;
         info->clients = qmp_query_client_list(vd);
 
-        if (vd->lsock == -1) {
+        if (vd->lsock == NULL) {
             return info;
         }
 
-        if (getsockname(vd->lsock, (struct sockaddr *)&sa,
-                        &salen) == -1) {
-            error_setg(errp, QERR_UNDEFINED_ERROR);
+        addr = qio_channel_socket_get_local_address(vd->lsock, errp);
+        if (!addr) {
             goto out_error;
         }
 
-        if (getnameinfo((struct sockaddr *)&sa, salen,
-                        host, sizeof(host),
-                        serv, sizeof(serv),
-                        NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
-            error_setg(errp, QERR_UNDEFINED_ERROR);
+        switch (addr->kind) {
+        case SOCKET_ADDRESS_KIND_INET:
+            info->host = g_strdup(addr->inet->host);
+            info->service = g_strdup(addr->inet->port);
+            if (addr->inet->ipv6) {
+                info->family = NETWORK_ADDRESS_FAMILY_IPV6;
+            } else {
+                info->family = NETWORK_ADDRESS_FAMILY_IPV4;
+            }
+            break;
+
+        case SOCKET_ADDRESS_KIND_UNIX:
+            info->host = g_strdup("");
+            info->service = g_strdup(addr->q_unix->path);
+            info->family = NETWORK_ADDRESS_FAMILY_UNIX;
+            break;
+
+        default:
+            error_setg(errp, "Unsupported socket kind %d",
+                       addr->kind);
             goto out_error;
         }
 
         info->has_host = true;
-        info->host = g_strdup(host);
-
         info->has_service = true;
-        info->service = g_strdup(serv);
-
         info->has_family = true;
-        info->family = inet_netfamily(sa.ss_family);
 
         info->has_auth = true;
         info->auth = g_strdup(vnc_auth_name(vd));
     }
 
+    qapi_free_SocketAddress(addr);
     return info;
 
 out_error:
+    qapi_free_SocketAddress(addr);
     qapi_free_VncInfo(info);
     return NULL;
 }
 
-static VncBasicInfoList *qmp_query_server_entry(int socket,
+static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc,
                                                 bool websocket,
                                                 VncBasicInfoList *prev)
 {
     VncBasicInfoList *list;
     VncBasicInfo *info;
-    struct sockaddr_storage sa;
-    socklen_t salen = sizeof(sa);
-    char host[NI_MAXHOST];
-    char serv[NI_MAXSERV];
-
-    if (getsockname(socket, (struct sockaddr *)&sa, &salen) < 0 ||
-        getnameinfo((struct sockaddr *)&sa, salen,
-                    host, sizeof(host), serv, sizeof(serv),
-                    NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+    Error *err = NULL;
+    SocketAddress *addr;
+
+    addr = qio_channel_socket_get_local_address(ioc, &err);
+    if (!addr) {
+        error_free(err);
+        return prev;
+    }
+    info = vnc_basic_info_from_socket_address(addr, &err);
+    qapi_free_SocketAddress(addr);
+    if (!info) {
+        error_free(err);
         return prev;
     }
-
-    info = g_new0(VncBasicInfo, 1);
-    info->host = g_strdup(host);
-    info->service = g_strdup(serv);
-    info->family = inet_netfamily(sa.ss_family);
     info->websocket = websocket;
 
     list = g_new0(VncBasicInfoList, 1);
@@ -571,13 +543,13 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
             info->has_display = true;
             info->display = g_strdup(dev->id);
         }
-        if (vd->lsock != -1) {
-            info->server = qmp_query_server_entry(vd->lsock, false,
-                                                  info->server);
+        if (vd->lsock != NULL) {
+            info->server = qmp_query_server_entry(
+                vd->lsock, false, info->server);
         }
-        if (vd->lwebsock != -1) {
-            info->server = qmp_query_server_entry(vd->lwebsock, true,
-                                                  info->server);
+        if (vd->lwebsock != NULL) {
+            info->server = qmp_query_server_entry(
+                vd->lwebsock, true, info->server);
         }
 
         item = g_new0(VncInfo2List, 1);
@@ -650,7 +622,7 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
 
 static void vnc_desktop_resize(VncState *vs)
 {
-    if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
+    if (vs->ioc == NULL || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
         return;
     }
     if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
@@ -1014,7 +986,7 @@ static int find_and_clear_dirty_height(VncState *vs,
 static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
 {
     vs->has_dirty += has_dirty;
-    if (vs->need_update && vs->csock != -1) {
+    if (vs->need_update && vs->ioc != NULL) {
         VncDisplay *vd = vs->vd;
         VncJob *job;
         int y;
@@ -1078,7 +1050,7 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
         return n;
     }
 
-    if (vs->csock == -1) {
+    if (vs->disconnecting) {
         vnc_disconnect_finish(vs);
     } else if (sync) {
         vnc_jobs_join(vs);
@@ -1160,12 +1132,15 @@ static void audio_del(VncState *vs)
 
 static void vnc_disconnect_start(VncState *vs)
 {
-    if (vs->csock == -1)
+    if (vs->disconnecting) {
         return;
+    }
     vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
-    qemu_set_fd_handler(vs->csock, NULL, NULL, NULL);
-    closesocket(vs->csock);
-    vs->csock = -1;
+    if (vs->ioc_tag) {
+        g_source_remove(vs->ioc_tag);
+    }
+    qio_channel_close(vs->ioc, NULL);
+    vs->disconnecting = TRUE;
 }
 
 void vnc_disconnect_finish(VncState *vs)
@@ -1214,29 +1189,29 @@ void vnc_disconnect_finish(VncState *vs)
         g_free(vs->lossy_rect[i]);
     }
     g_free(vs->lossy_rect);
+
+    object_unref(OBJECT(vs->ioc));
+    vs->ioc = NULL;
+    object_unref(OBJECT(vs->sioc));
+    vs->sioc = NULL;
     g_free(vs);
 }
 
-ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno)
+ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp)
 {
-    if (ret == 0 || ret == -1) {
-        if (ret == -1) {
-            switch (last_errno) {
-                case EINTR:
-                case EAGAIN:
-#ifdef _WIN32
-                case WSAEWOULDBLOCK:
-#endif
-                    return 0;
-                default:
-                    break;
-            }
+    if (ret <= 0) {
+        if (ret == 0) {
+            VNC_DEBUG("Closing down client sock: EOF\n");
+        } else if (ret != QIO_CHANNEL_ERR_BLOCK) {
+            VNC_DEBUG("Closing down client sock: ret %d (%s)\n",
+                      ret, errp ? error_get_pretty(*errp) : "Unknown");
         }
 
-        VNC_DEBUG("Closing down client sock: ret %zd, errno %d\n",
-                  ret, ret < 0 ? last_errno : 0);
         vnc_disconnect_start(vs);
-
+        if (errp) {
+            error_free(*errp);
+            *errp = NULL;
+        }
         return 0;
     }
     return ret;
@@ -1253,13 +1228,12 @@ void vnc_client_error(VncState *vs)
 ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque)
 {
     VncState *vs = opaque;
-    ssize_t ret;
-
- retry:
-    ret = qemu_recv(vs->csock, buf, len, 0);
+    ssize_t ret = qio_channel_read(vs->ioc, buf, len, NULL);
     if (ret < 0) {
-        if (errno == EINTR) {
-            goto retry;
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            errno = EAGAIN;
+        } else {
+            errno = EIO;
         }
         return -1;
     }
@@ -1270,13 +1244,12 @@ ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque)
 ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque)
 {
     VncState *vs = opaque;
-    ssize_t ret;
-
- retry:
-    ret = send(vs->csock, buf, len, 0);
+    ssize_t ret = qio_channel_write(vs->ioc, buf, len, NULL);
     if (ret < 0) {
-        if (errno == EINTR) {
-            goto retry;
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            errno = EAGAIN;
+        } else {
+            errno = EIO;
         }
         return -1;
     }
@@ -1301,21 +1274,25 @@ ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque)
  */
 ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
 {
+    Error *err = NULL;
     ssize_t ret;
-    int err = 0;
     if (vs->tls) {
         ret = qcrypto_tls_session_write(vs->tls, (const char *)data, datalen);
         if (ret < 0) {
-            err = errno;
+            if (errno == EAGAIN) {
+                ret = QIO_CHANNEL_ERR_BLOCK;
+            } else {
+                ret = -1;
+                error_setg_errno(&err, errno, "%s",
+                                 "Cannot write to TLS socket");
+            }
         }
     } else {
-        ret = send(vs->csock, (const void *)data, datalen, 0);
-        if (ret < 0) {
-            err = socket_error();
-        }
+        ret = qio_channel_write(
+            vs->ioc, (const char *)data, datalen, &err);
     }
     VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
-    return vnc_client_io_error(vs, ret, err);
+    return vnc_client_io_error(vs, ret, &err);
 }
 
 
@@ -1353,7 +1330,11 @@ static ssize_t vnc_client_write_plain(VncState *vs)
     qio_buffer_advance(&vs->output, ret);
 
     if (vs->output.offset == 0) {
-        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
     }
 
     return ret;
@@ -1365,10 +1346,8 @@ static ssize_t vnc_client_write_plain(VncState *vs)
  * the client socket. Will delegate actual work according to whether
  * SASL SSF layers are enabled (thus requiring encryption calls)
  */
-static void vnc_client_write_locked(void *opaque)
+static void vnc_client_write_locked(VncState *vs)
 {
-    VncState *vs = opaque;
-
 #ifdef CONFIG_VNC_SASL
     if (vs->sasl.conn &&
         vs->sasl.runSSF &&
@@ -1385,15 +1364,18 @@ static void vnc_client_write_locked(void *opaque)
     }
 }
 
-void vnc_client_write(void *opaque)
+static void vnc_client_write(VncState *vs)
 {
-    VncState *vs = opaque;
 
     vnc_lock_output(vs);
     if (vs->output.offset || vs->ws_output.offset) {
-        vnc_client_write_locked(opaque);
-    } else if (vs->csock != -1) {
-        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+        vnc_client_write_locked(vs);
+    } else if (vs->ioc != NULL) {
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
     }
     vnc_unlock_output(vs);
 }
@@ -1423,20 +1405,24 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
 ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
 {
     ssize_t ret;
-    int err = -1;
+    Error *err = NULL;
     if (vs->tls) {
         ret = qcrypto_tls_session_read(vs->tls, (char *)data, datalen);
         if (ret < 0) {
-            err = errno;
+            if (errno == EAGAIN) {
+                ret = QIO_CHANNEL_ERR_BLOCK;
+            } else {
+                ret = -1;
+                error_setg_errno(&err, errno, "%s",
+                                 "Cannot read from TLS socket");
+            }
         }
     } else {
-        ret = qemu_recv(vs->csock, data, datalen, 0);
-        if (ret < 0) {
-            err = socket_error();
-        }
+        ret = qio_channel_read(
+            vs->ioc, (char *)data, datalen, &err);
     }
     VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
-    return vnc_client_io_error(vs, ret, err);
+    return vnc_client_io_error(vs, ret, &err);
 }
 
 
@@ -1473,9 +1459,8 @@ static void vnc_jobs_bh(void *opaque)
  * the client socket. Will delegate actual work according to whether
  * SASL SSF layers are enabled (thus requiring decryption calls)
  */
-void vnc_client_read(void *opaque)
+static void vnc_client_read(VncState *vs)
 {
-    VncState *vs = opaque;
     ssize_t ret;
 
 #ifdef CONFIG_VNC_SASL
@@ -1496,8 +1481,9 @@ void vnc_client_read(void *opaque)
             ret = vnc_client_read_plain(vs);
         }
     if (!ret) {
-        if (vs->csock == -1)
+        if (vs->disconnecting) {
             vnc_disconnect_finish(vs);
+        }
         return;
     }
 
@@ -1506,7 +1492,7 @@ void vnc_client_read(void *opaque)
         int ret;
 
         ret = vs->read_handler(vs, vs->input.buffer, len);
-        if (vs->csock == -1) {
+        if (vs->disconnecting) {
             vnc_disconnect_finish(vs);
             return;
         }
@@ -1519,12 +1505,30 @@ void vnc_client_read(void *opaque)
     }
 }
 
+gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
+                       GIOCondition condition, void *opaque)
+{
+    VncState *vs = opaque;
+    if (condition & G_IO_IN) {
+        vnc_client_read(vs);
+    }
+    if (condition & G_IO_OUT) {
+        vnc_client_write(vs);
+    }
+    return TRUE;
+}
+
+
 void vnc_write(VncState *vs, const void *data, size_t len)
 {
     qio_buffer_reserve(&vs->output, len);
 
-    if (vs->csock != -1 && qio_buffer_empty(&vs->output)) {
-        qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
+    if (vs->ioc != NULL && qio_buffer_empty(&vs->output)) {
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+        }
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
     }
 
     qio_buffer_append(&vs->output, data, len);
@@ -1565,8 +1569,7 @@ void vnc_write_u8(VncState *vs, uint8_t value)
 void vnc_flush(VncState *vs)
 {
     vnc_lock_output(vs);
-    if (vs->csock != -1 && (vs->output.offset ||
-                            vs->ws_output.offset)) {
+    if (vs->ioc != NULL && (vs->output.offset || vs->ws_output.offset)) {
         vnc_client_write_locked(vs);
     }
     vnc_unlock_output(vs);
@@ -2953,13 +2956,16 @@ static void vnc_refresh(DisplayChangeListener *dcl)
     }
 }
 
-static void vnc_connect(VncDisplay *vd, int csock,
+static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
                         bool skipauth, bool websocket)
 {
     VncState *vs = g_malloc0(sizeof(VncState));
     int i;
 
-    vs->csock = csock;
+    vs->sioc = sioc;
+    object_ref(OBJECT(vs->sioc));
+    vs->ioc = QIO_CHANNEL(sioc);
+    object_ref(OBJECT(vs->ioc));
     vs->vd = vd;
 
     if (skipauth) {
@@ -2982,19 +2988,21 @@ static void vnc_connect(VncDisplay *vd, int csock,
         vs->lossy_rect[i] = g_malloc0(VNC_STAT_COLS * sizeof (uint8_t));
     }
 
-    VNC_DEBUG("New client on socket %d\n", csock);
+    VNC_DEBUG("New client on socket %p\n", vs->sioc);
     update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
-    qemu_set_nonblock(vs->csock);
+    qio_channel_set_blocking(vs->ioc, false);
     if (websocket) {
         vs->websocket = 1;
         if (vd->ws_tls) {
-            qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
+            vs->ioc_tag = qio_channel_add_watch(
+                vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL);
         } else {
-            qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
+            vs->ioc_tag = qio_channel_add_watch(
+                vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL);
         }
-    } else
-    {
-        qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+    } else {
+        vs->ioc_tag = qio_channel_add_watch(
+            vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
     }
 
     vnc_client_cache_addr(vs);
@@ -3048,35 +3056,28 @@ void vnc_init_state(VncState *vs)
     /* vs might be free()ed here */
 }
 
-static void vnc_listen_read(void *opaque, bool websocket)
+static gboolean vnc_listen_io(QIOChannel *ioc,
+                              GIOCondition condition,
+                              void *opaque)
 {
     VncDisplay *vs = opaque;
-    struct sockaddr_in addr;
-    socklen_t addrlen = sizeof(addr);
-    int csock;
+    QIOChannelSocket *sioc = NULL;
+    Error *err = NULL;
 
     /* Catch-up */
     graphic_hw_update(vs->dcl.con);
-    if (websocket) {
-        csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
+    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err);
+    if (sioc != NULL) {
+        qio_channel_socket_set_nodelay(sioc, true);
+        vnc_connect(vs, sioc, false,
+                    ioc != QIO_CHANNEL(vs->lsock));
+        object_unref(OBJECT(sioc));
     } else {
-        csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
-    }
-
-    if (csock != -1) {
-        socket_set_nodelay(csock);
-        vnc_connect(vs, csock, false, websocket);
+        /* client probably closed connection before we got there */
+        error_free(err);
     }
-}
-
-static void vnc_listen_regular_read(void *opaque)
-{
-    vnc_listen_read(opaque, false);
-}
 
-static void vnc_listen_websocket_read(void *opaque)
-{
-    vnc_listen_read(opaque, true);
+    return TRUE;
 }
 
 static const DisplayChangeListenerOps dcl_ops = {
@@ -3102,9 +3103,6 @@ void vnc_display_init(const char *id)
     vs->id = strdup(id);
     QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
 
-    vs->lsock = -1;
-    vs->lwebsock = -1;
-
     QTAILQ_INIT(&vs->clients);
     vs->expires = TIME_MAX;
 
@@ -3132,16 +3130,20 @@ static void vnc_display_close(VncDisplay *vs)
         return;
     vs->enabled = false;
     vs->is_unix = false;
-    if (vs->lsock != -1) {
-        qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL);
-        close(vs->lsock);
-        vs->lsock = -1;
+    if (vs->lsock != NULL) {
+        if (vs->lsock_tag) {
+            g_source_remove(vs->lsock_tag);
+        }
+        object_unref(OBJECT(vs->lsock));
+        vs->lsock = NULL;
     }
     vs->ws_enabled = false;
-    if (vs->lwebsock != -1) {
-        qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
-        close(vs->lwebsock);
-        vs->lwebsock = -1;
+    if (vs->lwebsock != NULL) {
+        if (vs->lwebsock_tag) {
+            g_source_remove(vs->lwebsock_tag);
+        }
+        object_unref(OBJECT(vs->lwebsock));
+        vs->lwebsock = NULL;
     }
     vs->auth = VNC_AUTH_INVALID;
     vs->subauth = VNC_AUTH_INVALID;
@@ -3186,9 +3188,25 @@ int vnc_display_pw_expire(const char *id, time_t expires)
 char *vnc_display_local_addr(const char *id)
 {
     VncDisplay *vs = vnc_display_find(id);
+    SocketAddress *addr;
+    char *ret;
+    Error *err = NULL;
 
     assert(vs);
-    return vnc_socket_local_addr("%s:%s", vs->lsock);
+
+    addr = qio_channel_socket_get_local_address(vs->lsock, &err);
+    if (!addr) {
+        return NULL;
+    }
+
+    if (addr->kind != SOCKET_ADDRESS_KIND_INET) {
+        qapi_free_SocketAddress(addr);
+        return NULL;
+    }
+    ret = g_strdup_printf("%s;%s", addr->inet->host, addr->inet->port);
+    qapi_free_SocketAddress(addr);
+
+    return ret;
 }
 
 static QemuOptsList qemu_vnc_opts = {
@@ -3749,42 +3767,45 @@ void vnc_display_open(const char *id, Error **errp)
 
     if (reverse) {
         /* connect to viewer */
-        int csock;
-        vs->lsock = -1;
-        vs->lwebsock = -1;
+        QIOChannelSocket *sioc = NULL;
+        vs->lsock = NULL;
+        vs->lwebsock = NULL;
         if (vs->ws_enabled) {
             error_setg(errp, "Cannot use websockets in reverse mode");
             goto fail;
         }
-        csock = socket_connect(saddr, errp,
-                               NULL, NULL);
-        if (csock < 0) {
+        vs->is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX;
+        sioc = qio_channel_socket_new();
+        if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) {
             goto fail;
         }
-        vs->is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX;
-        vnc_connect(vs, csock, false, false);
+        vnc_connect(vs, sioc, false, false);
+        object_unref(OBJECT(sioc));
     } else {
-        /* listen for connects */
-        vs->lsock = socket_listen(saddr, errp);
-        if (vs->lsock < 0) {
+        vs->lsock = qio_channel_socket_new();
+        if (qio_channel_socket_listen_sync(vs->lsock, saddr, errp) < 0) {
             goto fail;
         }
         vs->is_unix = saddr->kind == SOCKET_ADDRESS_KIND_UNIX;
+        vs->enabled = true;
+
         if (vs->ws_enabled) {
-            vs->lwebsock = socket_listen(wsaddr, errp);
-            if (vs->lwebsock < 0) {
-                if (vs->lsock != -1) {
-                    close(vs->lsock);
-                    vs->lsock = -1;
-                }
+            vs->lwebsock = qio_channel_socket_new();
+            if (qio_channel_socket_listen_sync(vs->lwebsock,
+                                               wsaddr, errp) < 0) {
+                object_unref(OBJECT(vs->lsock));
+                vs->lsock = NULL;
                 goto fail;
             }
         }
-        vs->enabled = true;
-        qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
+
+        vs->lsock_tag = qio_channel_add_watch(
+            QIO_CHANNEL(vs->lsock),
+            G_IO_IN, vnc_listen_io, vs, NULL);
         if (vs->ws_enabled) {
-            qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
-                                NULL, vs);
+            vs->lwebsock_tag = qio_channel_add_watch(
+                QIO_CHANNEL(vs->lwebsock),
+                G_IO_IN, vnc_listen_io, vs, NULL);
         }
     }
 
@@ -3802,11 +3823,17 @@ fail:
 void vnc_display_add_client(const char *id, int csock, bool skipauth)
 {
     VncDisplay *vs = vnc_display_find(id);
+    QIOChannelSocket *sioc;
 
     if (!vs) {
         return;
     }
-    vnc_connect(vs, csock, skipauth, false);
+
+    sioc = qio_channel_socket_new_fd(csock, NULL);
+    if (sioc) {
+        vnc_connect(vs, sioc, skipauth, false);
+        object_unref(OBJECT(sioc));
+    }
 }
 
 static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts)
diff --git a/ui/vnc.h b/ui/vnc.h
index 339a1bf..e9b4a00 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -35,6 +35,7 @@
 #include "qemu/bitmap.h"
 #include "crypto/tlssession.h"
 #include "io/buffer.h"
+#include "io/channel-socket.h"
 #include <zlib.h>
 #include <stdbool.h>
 
@@ -145,8 +146,10 @@ struct VncDisplay
     int num_exclusive;
     int connections_limit;
     VncSharePolicy share_policy;
-    int lsock;
-    int lwebsock;
+    QIOChannelSocket *lsock;
+    guint lsock_tag;
+    QIOChannelSocket *lwebsock;
+    guint lwebsock_tag;
     bool ws_enabled;
     DisplaySurface *ds;
     DisplayChangeListener dcl;
@@ -248,7 +251,10 @@ struct VncJob
 
 struct VncState
 {
-    int csock;
+    QIOChannelSocket *sioc; /* The underlying socket */
+    QIOChannel *ioc; /* The channel currently used for I/O */
+    guint ioc_tag;
+    gboolean disconnecting;
 
     DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS);
     uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in
@@ -499,8 +505,9 @@ enum {
  *****************************************************************************/
 
 /* Event loop functions */
-void vnc_client_read(void *opaque);
-void vnc_client_write(void *opaque);
+gboolean vnc_client_io(QIOChannel *ioc,
+                       GIOCondition condition,
+                       void *opaque);
 
 ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
 ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
@@ -524,7 +531,7 @@ uint32_t read_u32(uint8_t *data, size_t offset);
 
 /* Protocol stage functions */
 void vnc_client_error(VncState *vs);
-ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno);
+ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp);
 
 void start_client_init(VncState *vs);
 void start_auth_vnc(VncState *vs);
@@ -532,9 +539,6 @@ void start_auth_vnc(VncState *vs);
 
 /* Misc helpers */
 
-char *vnc_socket_local_addr(const char *format, int fd);
-char *vnc_socket_remote_addr(const char *format, int fd);
-
 static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
     return (vs->features & (1 << feature));
 }
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 17/46] ui: convert VNC server to use QIOChannelTLS
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (15 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 16/46] ui: convert VNC server to use QIOChannelSocket Daniel P. Berrange
@ 2015-09-03 15:38 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 18/46] ui: convert VNC server to use QIOChannelWebsock Daniel P. Berrange
                   ` (28 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:38 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Switch VNC server over to using the QIOChannelTLS object for
the TLS session. This removes all remaining VNC specific code
for dealing with TLS handshakes.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 ui/vnc-auth-sasl.c     |  26 ++++++------
 ui/vnc-auth-vencrypt.c | 106 ++++++++++++++-----------------------------------
 ui/vnc-ws.c            |  95 +++++++++++++++++---------------------------
 ui/vnc.c               |  73 ++++------------------------------
 ui/vnc.h               |   5 +--
 5 files changed, 88 insertions(+), 217 deletions(-)

diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index f5bfdd1..98597a0 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -521,7 +521,7 @@ void start_auth_sasl(VncState *vs)
 {
     const char *mechlist = NULL;
     sasl_security_properties_t secprops;
-    int err;
+    int ret;
     char *localAddr, *remoteAddr;
     int mechlistlen;
 
@@ -539,7 +539,7 @@ void start_auth_sasl(VncState *vs)
         goto authabort;
     }
 
-    err = sasl_server_new("vnc",
+    ret = sasl_server_new("vnc",
                           NULL, /* FQDN - just delegates to gethostname */
                           NULL, /* User realm */
                           localAddr,
@@ -551,9 +551,9 @@ void start_auth_sasl(VncState *vs)
     g_free(remoteAddr);
     localAddr = remoteAddr = NULL;
 
-    if (err != SASL_OK) {
+    if (ret != SASL_OK) {
         VNC_DEBUG("sasl context setup failed %d (%s)",
-                  err, sasl_errstring(err, NULL, NULL));
+                  ret, sasl_errstring(ret, NULL, NULL));
         vs->sasl.conn = NULL;
         goto authabort;
     }
@@ -577,10 +577,10 @@ void start_auth_sasl(VncState *vs)
         }
         ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */
 
-        err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
-        if (err != SASL_OK) {
+        ret = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
+        if (ret != SASL_OK) {
             VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
-                      err, sasl_errstring(err, NULL, NULL));
+                      ret, sasl_errstring(ret, NULL, NULL));
             sasl_dispose(&vs->sasl.conn);
             vs->sasl.conn = NULL;
             goto authabort;
@@ -613,16 +613,16 @@ void start_auth_sasl(VncState *vs)
             SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
     }
 
-    err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
-    if (err != SASL_OK) {
+    ret = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
+    if (ret != SASL_OK) {
         VNC_DEBUG("cannot set SASL security props %d (%s)\n",
-                  err, sasl_errstring(err, NULL, NULL));
+                  ret, sasl_errstring(ret, NULL, NULL));
         sasl_dispose(&vs->sasl.conn);
         vs->sasl.conn = NULL;
         goto authabort;
     }
 
-    err = sasl_listmech(vs->sasl.conn,
+    ret = sasl_listmech(vs->sasl.conn,
                         NULL, /* Don't need to set user */
                         "", /* Prefix */
                         ",", /* Separator */
@@ -630,9 +630,9 @@ void start_auth_sasl(VncState *vs)
                         &mechlist,
                         NULL,
                         NULL);
-    if (err != SASL_OK) {
+    if (ret != SASL_OK) {
         VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
-                  err, sasl_errdetail(vs->sasl.conn));
+                  ret, sasl_errdetail(vs->sasl.conn));
         sasl_dispose(&vs->sasl.conn);
         vs->sasl.conn = NULL;
         goto authabort;
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 95a6823..093dd2f 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -63,71 +63,21 @@ static void start_auth_vencrypt_subauth(VncState *vs)
     }
 }
 
-static gboolean vnc_tls_handshake_io(QIOChannel *ioc,
-                                     GIOCondition condition,
-                                     void *opaque);
-
-static int vnc_start_vencrypt_handshake(VncState *vs)
+static void vnc_tls_handshake_done(Object *source,
+                                   Error *err,
+                                   gpointer user_data)
 {
-    Error *err = NULL;
-
-    if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) {
-        goto error;
-    }
+    VncState *vs = user_data;
 
-    switch (qcrypto_tls_session_get_handshake_status(vs->tls)) {
-    case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
-        VNC_DEBUG("Handshake done, checking credentials\n");
-        if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) {
-            goto error;
-        }
-        VNC_DEBUG("Client verification passed, starting TLS I/O\n");
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
+    if (err) {
+        VNC_DEBUG("Handshake failed %s\n",
+                  error_get_pretty(err));
+        vnc_client_error(vs);
+    } else {
         vs->ioc_tag = qio_channel_add_watch(
             vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
-
         start_auth_vencrypt_subauth(vs);
-        break;
-
-    case QCRYPTO_TLS_HANDSHAKE_RECVING:
-        VNC_DEBUG("Handshake interrupted (blocking read)\n");
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
-        vs->ioc_tag = qio_channel_add_watch(
-            vs->ioc, G_IO_IN, vnc_tls_handshake_io, vs, NULL);
-        break;
-
-    case QCRYPTO_TLS_HANDSHAKE_SENDING:
-        VNC_DEBUG("Handshake interrupted (blocking write)\n");
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
-        vs->ioc_tag = qio_channel_add_watch(
-            vs->ioc, G_IO_OUT, vnc_tls_handshake_io, vs, NULL);
-        break;
     }
-
-    return 0;
-
- error:
-    VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
-    error_free(err);
-    vnc_client_error(vs);
-    return -1;
-}
-
-static gboolean vnc_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
-                                     GIOCondition condition G_GNUC_UNUSED,
-                                     void *opaque)
-{
-    VncState *vs = (VncState *)opaque;
-
-    VNC_DEBUG("Handshake IO continue\n");
-    vnc_start_vencrypt_handshake(vs);
-    return TRUE;
 }
 
 
@@ -142,33 +92,37 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
         vnc_client_error(vs);
     } else {
         Error *err = NULL;
+        QIOChannelTLS *tls;
         VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
         vnc_write_u8(vs, 1); /* Accept auth */
         vnc_flush(vs);
 
-        vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
-                                          NULL,
-                                          vs->vd->tlsaclname,
-                                          QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
-                                          &err);
-        if (!vs->tls) {
-            VNC_DEBUG("Failed to setup TLS %s\n",
-                      error_get_pretty(err));
+        if (vs->ioc_tag) {
+            g_source_remove(vs->ioc_tag);
+            vs->ioc_tag = 0;
+        }
+
+        tls = qio_channel_tls_new_server(
+            vs->ioc,
+            vs->vd->tlscreds,
+            vs->vd->tlsaclname,
+            &err);
+        if (!tls) {
+            VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
             error_free(err);
             vnc_client_error(vs);
             return 0;
         }
 
-        qcrypto_tls_session_set_callbacks(vs->tls,
-                                          vnc_tls_push,
-                                          vnc_tls_pull,
-                                          vs);
-
         VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
-        if (vnc_start_vencrypt_handshake(vs) < 0) {
-            VNC_DEBUG("Failed to start TLS handshake\n");
-            return 0;
-        }
+        object_unref(OBJECT(vs->ioc));
+        vs->ioc = QIO_CHANNEL(tls);
+        vs->tls = qio_channel_tls_get_session(tls);
+
+        qio_channel_tls_handshake(tls,
+                                  vnc_tls_handshake_done,
+                                  vs,
+                                  NULL);
     }
     return 0;
 }
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 7b2cc68..4863584 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -22,83 +22,60 @@
 #include "qemu/main-loop.h"
 #include "crypto/hash.h"
 
-static int vncws_start_tls_handshake(VncState *vs)
-{
-    Error *err = NULL;
-
-    if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) {
-        goto error;
-    }
+static void vncws_handshake_read(VncState *vs);
 
-    switch (qcrypto_tls_session_get_handshake_status(vs->tls)) {
-    case QCRYPTO_TLS_HANDSHAKE_COMPLETE:
-        VNC_DEBUG("Handshake done, checking credentials\n");
-        if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) {
-            goto error;
-        }
-        VNC_DEBUG("Client verification passed, starting TLS I/O\n");
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
-        vs->ioc_tag = qio_channel_add_watch(
-            vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL);
-        break;
-
-    case QCRYPTO_TLS_HANDSHAKE_RECVING:
-        VNC_DEBUG("Handshake interrupted (blocking read)\n");
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
-        vs->ioc_tag = qio_channel_add_watch(
-            vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL);
-        break;
+static void vncws_tls_handshake_done(Object *source,
+                                     Error *err,
+                                     gpointer user_data)
+{
+    VncState *vs = user_data;
 
-    case QCRYPTO_TLS_HANDSHAKE_SENDING:
-        VNC_DEBUG("Handshake interrupted (blocking write)\n");
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
+    if (err) {
+        VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
+        vnc_client_error(vs);
+    } else {
         vs->ioc_tag = qio_channel_add_watch(
-            vs->ioc, G_IO_OUT, vncws_tls_handshake_io, vs, NULL);
-        break;
+            QIO_CHANNEL(vs->ioc), G_IO_IN, vncws_handshake_io, vs, NULL);
     }
-
-    return 0;
-
- error:
-    VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
-    error_free(err);
-    vnc_client_error(vs);
-    return -1;
 }
 
+
 gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
                                 GIOCondition condition G_GNUC_UNUSED,
                                 void *opaque)
 {
-    VncState *vs = (VncState *)opaque;
+    VncState *vs = opaque;
+    QIOChannelTLS *tls;
     Error *err = NULL;
 
-    vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
-                                      NULL,
-                                      vs->vd->tlsaclname,
-                                      QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
-                                      &err);
-    if (!vs->tls) {
-        VNC_DEBUG("Failed to setup TLS %s\n",
-                  error_get_pretty(err));
+    VNC_DEBUG("TLS Websocket connection required\n");
+    if (vs->ioc_tag) {
+        g_source_remove(vs->ioc_tag);
+        vs->ioc_tag = 0;
+    }
+
+    tls = qio_channel_tls_new_server(
+        vs->ioc,
+        vs->vd->tlscreds,
+        vs->vd->tlsaclname,
+        &err);
+    if (!tls) {
+        VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
         error_free(err);
         vnc_client_error(vs);
         return TRUE;
     }
 
-    qcrypto_tls_session_set_callbacks(vs->tls,
-                                      vnc_tls_push,
-                                      vnc_tls_pull,
-                                      vs);
-
     VNC_DEBUG("Start TLS WS handshake process\n");
-    vncws_start_tls_handshake(vs);
+    object_unref(OBJECT(vs->ioc));
+    vs->ioc = QIO_CHANNEL(tls);
+    vs->tls = qio_channel_tls_get_session(tls);
+
+    qio_channel_tls_handshake(tls,
+                              vncws_tls_handshake_done,
+                              vs,
+                              NULL);
+
     return TRUE;
 }
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 2f8c15e..3fe5fed 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1163,7 +1163,6 @@ void vnc_disconnect_finish(VncState *vs)
     vnc_tight_clear(vs);
     vnc_zrle_clear(vs);
 
-    qcrypto_tls_session_free(vs->tls);
 #ifdef CONFIG_VNC_SASL
     vnc_sasl_client_cleanup(vs);
 #endif /* CONFIG_VNC_SASL */
@@ -1225,38 +1224,6 @@ void vnc_client_error(VncState *vs)
 }
 
 
-ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque)
-{
-    VncState *vs = opaque;
-    ssize_t ret = qio_channel_read(vs->ioc, buf, len, NULL);
-    if (ret < 0) {
-        if (ret == QIO_CHANNEL_ERR_BLOCK) {
-            errno = EAGAIN;
-        } else {
-            errno = EIO;
-        }
-        return -1;
-    }
-    return ret;
-}
-
-
-ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque)
-{
-    VncState *vs = opaque;
-    ssize_t ret = qio_channel_write(vs->ioc, buf, len, NULL);
-    if (ret < 0) {
-        if (ret == QIO_CHANNEL_ERR_BLOCK) {
-            errno = EAGAIN;
-        } else {
-            errno = EIO;
-        }
-        return -1;
-    }
-    return ret;
-}
-
-
 /*
  * Called to write a chunk of data to the client socket. The data may
  * be the raw data, or may have already been encoded by SASL.
@@ -1276,21 +1243,8 @@ ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
 {
     Error *err = NULL;
     ssize_t ret;
-    if (vs->tls) {
-        ret = qcrypto_tls_session_write(vs->tls, (const char *)data, datalen);
-        if (ret < 0) {
-            if (errno == EAGAIN) {
-                ret = QIO_CHANNEL_ERR_BLOCK;
-            } else {
-                ret = -1;
-                error_setg_errno(&err, errno, "%s",
-                                 "Cannot write to TLS socket");
-            }
-        }
-    } else {
-        ret = qio_channel_write(
-            vs->ioc, (const char *)data, datalen, &err);
-    }
+    ret = qio_channel_write(
+        vs->ioc, (const char *)data, datalen, &err);
     VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
     return vnc_client_io_error(vs, ret, &err);
 }
@@ -1406,21 +1360,8 @@ ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
 {
     ssize_t ret;
     Error *err = NULL;
-    if (vs->tls) {
-        ret = qcrypto_tls_session_read(vs->tls, (char *)data, datalen);
-        if (ret < 0) {
-            if (errno == EAGAIN) {
-                ret = QIO_CHANNEL_ERR_BLOCK;
-            } else {
-                ret = -1;
-                error_setg_errno(&err, errno, "%s",
-                                 "Cannot read from TLS socket");
-            }
-        }
-    } else {
-        ret = qio_channel_read(
-            vs->ioc, (char *)data, datalen, &err);
-    }
+    ret = qio_channel_read(
+        vs->ioc, (char *)data, datalen, &err);
     VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
     return vnc_client_io_error(vs, ret, &err);
 }
@@ -2980,8 +2921,8 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
             vs->subauth = vd->subauth;
         }
     }
-    VNC_DEBUG("Client sock=%d ws=%d auth=%d subauth=%d\n",
-              csock, websocket, vs->auth, vs->subauth);
+    VNC_DEBUG("Client sock=%p ws=%d auth=%d subauth=%d\n",
+              sioc, websocket, vs->auth, vs->subauth);
 
     vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
     for (i = 0; i < VNC_STAT_ROWS; ++i) {
@@ -3710,7 +3651,7 @@ void vnc_display_open(const char *id, Error **errp)
             vs->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
         }
         qemu_acl_init(vs->tlsaclname);
-     }
+    }
 #ifdef CONFIG_VNC_SASL
     if (acl && sasl) {
         char *aclname;
diff --git a/ui/vnc.h b/ui/vnc.h
index e9b4a00..65991c0 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -36,6 +36,7 @@
 #include "crypto/tlssession.h"
 #include "io/buffer.h"
 #include "io/channel-socket.h"
+#include "io/channel-tls.h"
 #include <zlib.h>
 #include <stdbool.h>
 
@@ -281,7 +282,7 @@ struct VncState
     int auth;
     int subauth; /* Used by VeNCrypt */
     char challenge[VNC_AUTH_CHALLENGE_SIZE];
-    QCryptoTLSSession *tls;
+    QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */
 #ifdef CONFIG_VNC_SASL
     VncStateSASL sasl;
 #endif
@@ -511,8 +512,6 @@ gboolean vnc_client_io(QIOChannel *ioc,
 
 ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
 ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
-ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque);
-ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque);
 
 /* Protocol I/O functions */
 void vnc_write(VncState *vs, const void *data, size_t len);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 18/46] ui: convert VNC server to use QIOChannelWebsock
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (16 preceding siblings ...)
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 17/46] ui: convert VNC server to use QIOChannelTLS Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 19/46] char: remove fixed length filename allocation Daniel P. Berrange
                   ` (27 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Remove custom websock handling code from the VNC server and use
the QIOChannelWebsock class instead.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 ui/vnc-ws.c | 328 +++++-------------------------------------------------------
 ui/vnc-ws.h |  63 ------------
 ui/vnc.c    |  25 +----
 ui/vnc.h    |   4 -
 4 files changed, 30 insertions(+), 390 deletions(-)

diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 4863584..8018233 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -19,10 +19,7 @@
  */
 
 #include "vnc.h"
-#include "qemu/main-loop.h"
-#include "crypto/hash.h"
-
-static void vncws_handshake_read(VncState *vs);
+#include "io/channel-websock.h"
 
 static void vncws_tls_handshake_done(Object *source,
                                      Error *err,
@@ -34,6 +31,7 @@ static void vncws_tls_handshake_done(Object *source,
         VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
         vnc_client_error(vs);
     } else {
+        VNC_DEBUG("TLS handshake complete, starting websocket handshake\n");
         vs->ioc_tag = qio_channel_add_watch(
             QIO_CHANNEL(vs->ioc), G_IO_IN, vncws_handshake_io, vs, NULL);
     }
@@ -79,38 +77,21 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
     return TRUE;
 }
 
-static void vncws_handshake_read(VncState *vs)
-{
-    uint8_t *handshake_end;
-    long ret;
-    /* Typical HTTP headers from novnc are 512 bytes, so limiting
-     * total header size to 4096 is easily enough. */
-    size_t want = 4096 - vs->ws_input.offset;
-    qio_buffer_reserve(&vs->ws_input, want);
-    ret = vnc_client_read_buf(vs, qio_buffer_end(&vs->ws_input), want);
 
-    if (!ret) {
-        if (vs->disconnecting) {
-            vnc_disconnect_finish(vs);
-        }
-        return;
-    }
-    vs->ws_input.offset += ret;
+static void vncws_handshake_done(Object *source,
+                                 Error *err,
+                                 gpointer user_data)
+{
+    VncState *vs = user_data;
 
-    handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer,
-            vs->ws_input.offset, WS_HANDSHAKE_END);
-    if (handshake_end) {
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
+    if (err) {
+        VNC_DEBUG("Websock handshake failed %s\n", error_get_pretty(err));
+        vnc_client_error(vs);
+    } else {
+        VNC_DEBUG("Websock handshake complete, starting VNC protocol\n");
+        vnc_init_state(vs);
         vs->ioc_tag = qio_channel_add_watch(
             vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
-        vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset);
-        qio_buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
-                strlen(WS_HANDSHAKE_END));
-    } else if (vs->ws_input.offset >= 4096) {
-        VNC_DEBUG("End of headers not found in first 4096 bytes\n");
-        vnc_client_error(vs);
     }
 }
 
@@ -120,280 +101,23 @@ gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
                             void *opaque)
 {
     VncState *vs = opaque;
-    vncws_handshake_read(vs);
-    return TRUE;
-}
-
-long vnc_client_read_ws(VncState *vs)
-{
-    int ret, err;
-    uint8_t *payload;
-    size_t payload_size, header_size;
-    VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer,
-            vs->ws_input.capacity, vs->ws_input.offset);
-    qio_buffer_reserve(&vs->ws_input, 4096);
-    ret = vnc_client_read_buf(vs, qio_buffer_end(&vs->ws_input), 4096);
-    if (!ret) {
-        return 0;
-    }
-    vs->ws_input.offset += ret;
-
-    ret = 0;
-    /* consume as much of ws_input buffer as possible */
-    do {
-        if (vs->ws_payload_remain == 0) {
-            err = vncws_decode_frame_header(&vs->ws_input,
-                                            &header_size,
-                                            &vs->ws_payload_remain,
-                                            &vs->ws_payload_mask);
-            if (err <= 0) {
-                return err;
-            }
-
-            qio_buffer_advance(&vs->ws_input, header_size);
-        }
-        if (vs->ws_payload_remain != 0) {
-            err = vncws_decode_frame_payload(&vs->ws_input,
-                                             &vs->ws_payload_remain,
-                                             &vs->ws_payload_mask,
-                                             &payload,
-                                             &payload_size);
-            if (err < 0) {
-                return err;
-            }
-            if (err == 0) {
-                return ret;
-            }
-            ret += err;
-
-            qio_buffer_reserve(&vs->input, payload_size);
-            qio_buffer_append(&vs->input, payload, payload_size);
-
-            qio_buffer_advance(&vs->ws_input, payload_size);
-        }
-    } while (vs->ws_input.offset > 0);
-
-    return ret;
-}
-
-long vnc_client_write_ws(VncState *vs)
-{
-    long ret;
-    VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n",
-              vs->output.buffer, vs->output.capacity, vs->output.offset);
-    vncws_encode_frame(&vs->ws_output, vs->output.buffer, vs->output.offset);
-    qio_buffer_reset(&vs->output);
-    ret = vnc_client_write_buf(vs, vs->ws_output.buffer, vs->ws_output.offset);
-    if (!ret) {
-        return 0;
-    }
-
-    qio_buffer_advance(&vs->ws_output, ret);
-
-    if (vs->ws_output.offset == 0) {
-        if (vs->ioc_tag) {
-            g_source_remove(vs->ioc_tag);
-        }
-        vs->ioc_tag = qio_channel_add_watch(
-            vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
-    }
-
-    return ret;
-}
-
-static char *vncws_extract_handshake_entry(const char *handshake,
-        size_t handshake_len, const char *name)
-{
-    char *begin, *end, *ret = NULL;
-    char *line = g_strdup_printf("%s%s: ", WS_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),
-                WS_HANDSHAKE_DELIM);
-        if (end != NULL) {
-            ret = g_strndup(begin, end - begin);
-        }
-    }
-    g_free(line);
-    return ret;
-}
-
-static void vncws_send_handshake_response(VncState *vs, const char* key)
-{
-    char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1];
-    char *accept = NULL, *response = NULL;
-    Error *err = NULL;
-
-    g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1);
-    g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1);
-
-    /* hash and encode it */
-    if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
-                            combined_key,
-                            WS_CLIENT_KEY_LEN + WS_GUID_LEN,
-                            &accept,
-                            &err) < 0) {
-        VNC_DEBUG("Hashing Websocket combined key failed %s\n",
-                  error_get_pretty(err));
-        error_free(err);
-        vnc_client_error(vs);
-        return;
-    }
-
-    response = g_strdup_printf(WS_HANDSHAKE, accept);
-    vnc_client_write_buf(vs, (const uint8_t *)response, strlen(response));
+    QIOChannelWebsock *wioc;
 
-    g_free(accept);
-    g_free(response);
-
-    vs->encode_ws = 1;
-    vnc_init_state(vs);
-}
-
-void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)
-{
-    char *protocols = vncws_extract_handshake_entry((const char *)line, size,
-            "Sec-WebSocket-Protocol");
-    char *version = vncws_extract_handshake_entry((const char *)line, size,
-            "Sec-WebSocket-Version");
-    char *key = vncws_extract_handshake_entry((const char *)line, size,
-            "Sec-WebSocket-Key");
-
-    if (protocols && version && key
-            && g_strrstr(protocols, "binary")
-            && !strcmp(version, WS_SUPPORTED_VERSION)
-            && strlen(key) == WS_CLIENT_KEY_LEN) {
-        vncws_send_handshake_response(vs, key);
-    } else {
-        VNC_DEBUG("Defective Websockets header or unsupported protocol\n");
-        vnc_client_error(vs);
-    }
-
-    g_free(protocols);
-    g_free(version);
-    g_free(key);
-}
-
-void vncws_encode_frame(QIOBuffer *output, const void *payload,
-                        const size_t payload_size)
-{
-    size_t header_size = 0;
-    unsigned char opcode = WS_OPCODE_BINARY_FRAME;
-    union {
-        char buf[WS_HEAD_MAX_LEN];
-        WsHeader ws;
-    } header;
-
-    if (!payload_size) {
-        return;
-    }
-
-    header.ws.b0 = 0x80 | (opcode & 0x0f);
-    if (payload_size <= 125) {
-        header.ws.b1 = (uint8_t)payload_size;
-        header_size = 2;
-    } else if (payload_size < 65536) {
-        header.ws.b1 = 0x7e;
-        header.ws.u.s16.l16 = cpu_to_be16((uint16_t)payload_size);
-        header_size = 4;
-    } else {
-        header.ws.b1 = 0x7f;
-        header.ws.u.s64.l64 = cpu_to_be64(payload_size);
-        header_size = 10;
-    }
-
-    qio_buffer_reserve(output, header_size + payload_size);
-    qio_buffer_append(output, header.buf, header_size);
-    qio_buffer_append(output, payload, payload_size);
-}
-
-int vncws_decode_frame_header(QIOBuffer *input,
-                              size_t *header_size,
-                              size_t *payload_remain,
-                              WsMask *payload_mask)
-{
-    unsigned char opcode = 0, fin = 0, has_mask = 0;
-    size_t payload_len;
-    WsHeader *header = (WsHeader *)input->buffer;
-
-    if (input->offset < WS_HEAD_MIN_LEN + 4) {
-        /* header not complete */
-        return 0;
-    }
-
-    fin = (header->b0 & 0x80) >> 7;
-    opcode = header->b0 & 0x0f;
-    has_mask = (header->b1 & 0x80) >> 7;
-    payload_len = header->b1 & 0x7f;
-
-    if (opcode == WS_OPCODE_CLOSE) {
-        /* disconnect */
-        return -1;
-    }
-
-    /* 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 || !has_mask || opcode != WS_OPCODE_BINARY_FRAME) {
-        VNC_DEBUG("Received faulty/unsupported Websocket frame\n");
-        return -2;
+    VNC_DEBUG("Websocket negotiate starting\n");
+    if (vs->ioc_tag) {
+        g_source_remove(vs->ioc_tag);
+        vs->ioc_tag = 0;
     }
 
-    if (payload_len < 126) {
-        *payload_remain = payload_len;
-        *header_size = 6;
-        *payload_mask = header->u.m;
-    } else if (payload_len == 126 && input->offset >= 8) {
-        *payload_remain = be16_to_cpu(header->u.s16.l16);
-        *header_size = 8;
-        *payload_mask = header->u.s16.m16;
-    } else if (payload_len == 127 && input->offset >= 14) {
-        *payload_remain = be64_to_cpu(header->u.s64.l64);
-        *header_size = 14;
-        *payload_mask = header->u.s64.m64;
-    } else {
-        /* header not complete */
-        return 0;
-    }
+    wioc = qio_channel_websock_new_server(vs->ioc);
 
-    return 1;
-}
-
-int vncws_decode_frame_payload(QIOBuffer *input,
-                               size_t *payload_remain, WsMask *payload_mask,
-                               uint8_t **payload, size_t *payload_size)
-{
-    size_t i;
-    uint32_t *payload32;
-
-    *payload = input->buffer;
-    /* 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 (input->offset < *payload_remain) {
-        *payload_size = input->offset - (input->offset % 4);
-    } else {
-        *payload_size = *payload_remain;
-    }
-    if (*payload_size == 0) {
-        return 0;
-    }
-    *payload_remain -= *payload_size;
+    object_unref(OBJECT(vs->ioc));
+    vs->ioc = QIO_CHANNEL(wioc);
 
-    /* unmask frame */
-    /* process 1 frame (32 bit op) */
-    payload32 = (uint32_t *)(*payload);
-    for (i = 0; i < *payload_size / 4; i++) {
-        payload32[i] ^= payload_mask->u;
-    }
-    /* process the remaining bytes (if any) */
-    for (i *= 4; i < *payload_size; i++) {
-        (*payload)[i] ^= payload_mask->c[i % 4];
-    }
+    qio_channel_websock_handshake(wioc,
+                                  vncws_handshake_done,
+                                  vs,
+                                  NULL);
 
-    return 1;
+    return TRUE;
 }
diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h
index 21caaf3..652b6fc 100644
--- a/ui/vnc-ws.h
+++ b/ui/vnc-ws.h
@@ -21,74 +21,11 @@
 #ifndef __QEMU_UI_VNC_WS_H
 #define __QEMU_UI_VNC_WS_H
 
-#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
-#define SHA1_DIGEST_LEN 20
-
-#define WS_ACCEPT_LEN (B64LEN(SHA1_DIGEST_LEN) + 1)
-#define WS_CLIENT_KEY_LEN 24
-#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-#define WS_GUID_LEN strlen(WS_GUID)
-
-#define WS_HANDSHAKE "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 WS_HANDSHAKE_DELIM "\r\n"
-#define WS_HANDSHAKE_END "\r\n\r\n"
-#define WS_SUPPORTED_VERSION "13"
-
-#define WS_HEAD_MIN_LEN sizeof(uint16_t)
-#define WS_HEAD_MAX_LEN (WS_HEAD_MIN_LEN + sizeof(uint64_t) + sizeof(uint32_t))
-
-typedef union WsMask {
-    char c[4];
-    uint32_t u;
-} WsMask;
-
-typedef struct QEMU_PACKED WsHeader {
-    unsigned char b0;
-    unsigned char b1;
-    union {
-        struct QEMU_PACKED {
-            uint16_t l16;
-            WsMask m16;
-        } s16;
-        struct QEMU_PACKED {
-            uint64_t l64;
-            WsMask m64;
-        } s64;
-        WsMask m;
-    } u;
-} WsHeader;
-
-enum {
-    WS_OPCODE_CONTINUATION = 0x0,
-    WS_OPCODE_TEXT_FRAME = 0x1,
-    WS_OPCODE_BINARY_FRAME = 0x2,
-    WS_OPCODE_CLOSE = 0x8,
-    WS_OPCODE_PING = 0x9,
-    WS_OPCODE_PONG = 0xA
-};
-
 gboolean vncws_tls_handshake_io(QIOChannel *ioc,
                                 GIOCondition condition,
                                 void *opaque);
 gboolean vncws_handshake_io(QIOChannel *ioc,
                             GIOCondition condition,
                             void *opaque);
-long vnc_client_write_ws(VncState *vs);
-long vnc_client_read_ws(VncState *vs);
-void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);
-void vncws_encode_frame(QIOBuffer *output, const void *payload,
-            const size_t payload_size);
-int vncws_decode_frame_header(QIOBuffer *input,
-                              size_t *header_size,
-                              size_t *payload_remain,
-                              WsMask *payload_mask);
-int vncws_decode_frame_payload(QIOBuffer *input,
-                               size_t *payload_remain, WsMask *payload_mask,
-                               uint8_t **payload, size_t *payload_size);
 
 #endif /* __QEMU_UI_VNC_WS_H */
diff --git a/ui/vnc.c b/ui/vnc.c
index 3fe5fed..59cd8a8 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1154,8 +1154,6 @@ void vnc_disconnect_finish(VncState *vs)
 
     qio_buffer_free(&vs->input);
     qio_buffer_free(&vs->output);
-    qio_buffer_free(&vs->ws_input);
-    qio_buffer_free(&vs->ws_output);
 
     qapi_free_VncClientInfo(vs->info);
 
@@ -1310,11 +1308,7 @@ static void vnc_client_write_locked(VncState *vs)
     } else
 #endif /* CONFIG_VNC_SASL */
     {
-        if (vs->encode_ws) {
-            vnc_client_write_ws(vs);
-        } else {
-            vnc_client_write_plain(vs);
-        }
+        vnc_client_write_plain(vs);
     }
 }
 
@@ -1322,7 +1316,7 @@ static void vnc_client_write(VncState *vs)
 {
 
     vnc_lock_output(vs);
-    if (vs->output.offset || vs->ws_output.offset) {
+    if (vs->output.offset) {
         vnc_client_write_locked(vs);
     } else if (vs->ioc != NULL) {
         if (vs->ioc_tag) {
@@ -1409,18 +1403,7 @@ static void vnc_client_read(VncState *vs)
         ret = vnc_client_read_sasl(vs);
     else
 #endif /* CONFIG_VNC_SASL */
-        if (vs->encode_ws) {
-            ret = vnc_client_read_ws(vs);
-            if (ret == -1) {
-                vnc_disconnect_start(vs);
-                return;
-            } else if (ret == -2) {
-                vnc_client_error(vs);
-                return;
-            }
-        } else {
-            ret = vnc_client_read_plain(vs);
-        }
+        ret = vnc_client_read_plain(vs);
     if (!ret) {
         if (vs->disconnecting) {
             vnc_disconnect_finish(vs);
@@ -1510,7 +1493,7 @@ void vnc_write_u8(VncState *vs, uint8_t value)
 void vnc_flush(VncState *vs)
 {
     vnc_lock_output(vs);
-    if (vs->ioc != NULL && (vs->output.offset || vs->ws_output.offset)) {
+    if (vs->ioc != NULL && vs->output.offset) {
         vnc_client_write_locked(vs);
     }
     vnc_unlock_output(vs);
diff --git a/ui/vnc.h b/ui/vnc.h
index 65991c0..17f6455 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -293,10 +293,6 @@ struct VncState
 
     QIOBuffer output;
     QIOBuffer input;
-    QIOBuffer ws_input;
-    QIOBuffer ws_output;
-    size_t ws_payload_remain;
-    WsMask ws_payload_mask;
     /* current output mode information */
     VncWritePixels *write_pixels;
     PixelFormat client_pf;
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 19/46] char: remove fixed length filename allocation
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (17 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 18/46] ui: convert VNC server to use QIOChannelWebsock Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 20/46] char: convert from GIOChannel to QIOChannel Daniel P. Berrange
                   ` (26 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

A variety of places were snprintf()ing into a fixed length
filename buffer. Some of the buffers were stack allocated,
while another was heap allocated with g_malloc(). Switch
them all to heap allocated using g_strdup_printf() avoiding
arbitrary length restrictions.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qemu-char.c | 84 ++++++++++++++++++++++++++++++-------------------------------
 1 file changed, 42 insertions(+), 42 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index ed35f2a..0d702d6 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -88,39 +88,36 @@
 
 #define READ_BUF_LEN 4096
 #define READ_RETRIES 10
-#define CHR_MAX_FILENAME_SIZE 256
 #define TCP_MAX_FDS 16
 
 /***********************************************************/
 /* Socket address helpers */
 
-static int SocketAddress_to_str(char *dest, int max_len,
-                                const char *prefix, SocketAddress *addr,
-                                bool is_listen, bool is_telnet)
+static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
+                                  bool is_listen, bool is_telnet)
 {
     switch (addr->kind) {
     case SOCKET_ADDRESS_KIND_INET:
-        return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix,
-                        is_telnet ? "telnet" : "tcp", addr->inet->host,
-                        addr->inet->port, is_listen ? ",server" : "");
+        return g_strdup_printf("%s%s:%s:%s%s", prefix,
+                               is_telnet ? "telnet" : "tcp", addr->inet->host,
+                               addr->inet->port, is_listen ? ",server" : "");
         break;
     case SOCKET_ADDRESS_KIND_UNIX:
-        return snprintf(dest, max_len, "%sunix:%s%s", prefix,
-                        addr->q_unix->path, is_listen ? ",server" : "");
+        return g_strdup_printf("%sunix:%s%s", prefix,
+                               addr->q_unix->path, is_listen ? ",server" : "");
         break;
     case SOCKET_ADDRESS_KIND_FD:
-        return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->fd->str,
-                        is_listen ? ",server" : "");
+        return g_strdup_printf("%sfd:%s%s", prefix, addr->fd->str,
+                               is_listen ? ",server" : "");
         break;
     default:
         abort();
     }
 }
 
-static int sockaddr_to_str(char *dest, int max_len,
-                           struct sockaddr_storage *ss, socklen_t ss_len,
-                           struct sockaddr_storage *ps, socklen_t ps_len,
-                           bool is_listen, bool is_telnet)
+static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len,
+                             struct sockaddr_storage *ps, socklen_t ps_len,
+                             bool is_listen, bool is_telnet)
 {
     char shost[NI_MAXHOST], sserv[NI_MAXSERV];
     char phost[NI_MAXHOST], pserv[NI_MAXSERV];
@@ -129,9 +126,9 @@ static int sockaddr_to_str(char *dest, int max_len,
     switch (ss->ss_family) {
 #ifndef _WIN32
     case AF_UNIX:
-        return snprintf(dest, max_len, "unix:%s%s",
-                        ((struct sockaddr_un *)(ss))->sun_path,
-                        is_listen ? ",server" : "");
+        return g_strdup_printf("unix:%s%s",
+                               ((struct sockaddr_un *)(ss))->sun_path,
+                               is_listen ? ",server" : "");
 #endif
     case AF_INET6:
         left  = "[";
@@ -142,14 +139,14 @@ static int sockaddr_to_str(char *dest, int max_len,
                     sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV);
         getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
                     pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
-        return snprintf(dest, max_len, "%s:%s%s%s:%s%s <-> %s%s%s:%s",
-                        is_telnet ? "telnet" : "tcp",
-                        left, shost, right, sserv,
-                        is_listen ? ",server" : "",
-                        left, phost, right, pserv);
+        return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s",
+                               is_telnet ? "telnet" : "tcp",
+                               left, shost, right, sserv,
+                               is_listen ? ",server" : "",
+                               left, phost, right, pserv);
 
     default:
-        return snprintf(dest, max_len, "unknown");
+        return g_strdup_printf("unknown");
     }
 }
 
@@ -1057,8 +1054,8 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
 static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
 {
     int fd_in, fd_out;
-    char filename_in[CHR_MAX_FILENAME_SIZE];
-    char filename_out[CHR_MAX_FILENAME_SIZE];
+    char *filename_in;
+    char *filename_out;
     const char *filename = opts->device;
 
     if (filename == NULL) {
@@ -1066,10 +1063,12 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
         return NULL;
     }
 
-    snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename);
-    snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename);
+    filename_in = g_strdup_printf("%s.in", filename);
+    filename_out = g_strdup_printf("%s.out", filename);
     TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
     TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
+    g_free(filename_in);
+    g_free(filename_out);
     if (fd_in < 0 || fd_out < 0) {
 	if (fd_in >= 0)
 	    close(fd_in);
@@ -2061,7 +2060,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
     OVERLAPPED ov;
     int ret;
     DWORD size;
-    char openname[CHR_MAX_FILENAME_SIZE];
+    char *openname;
 
     s->fpipe = TRUE;
 
@@ -2076,11 +2075,12 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
         goto fail;
     }
 
-    snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename);
+    openname = g_strdup_printf("\\\\.\\pipe\\%s", filename);
     s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
                               PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
                               PIPE_WAIT,
                               MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
+    g_free(openname);
     if (s->hcom == INVALID_HANDLE_VALUE) {
         fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
         s->hcom = NULL;
@@ -2817,8 +2817,9 @@ static void tcp_chr_disconnect(CharDriverState *chr)
     s->chan = NULL;
     closesocket(s->fd);
     s->fd = -1;
-    SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
-                         "disconnected:", s->addr, s->is_listen, s->is_telnet);
+    g_free(chr->filename);
+    chr->filename = SocketAddress_to_str("disconnected:", s->addr,
+                                         s->is_listen, s->is_telnet);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
     if (s->reconnect_time) {
         qemu_chr_socket_restart_timer(chr);
@@ -2893,16 +2894,16 @@ static void tcp_chr_connect(void *opaque)
     socklen_t ss_len = sizeof(ss), ps_len = sizeof(ps);
 
     memset(&ss, 0, ss_len);
+    g_free(chr->filename);
     if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) {
-        snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
-                 "Error in getsockname: %s\n", strerror(errno));
+        chr->filename = g_strdup_printf("Error in getsockname: %s\n",
+                                        strerror(errno));
     } else if (getpeername(s->fd, (struct sockaddr *) &ps, &ps_len) != 0) {
-        snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
-                 "Error in getpeername: %s\n", strerror(errno));
+        chr->filename = g_strdup_printf("Error in getpeername: %s\n",
+                                        strerror(errno));
     } else {
-        sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
-                        &ss, ss_len, &ps, ps_len,
-                        s->is_listen, s->is_telnet);
+        chr->filename = sockaddr_to_str(&ss, ss_len, &ps, ps_len,
+                                        s->is_listen, s->is_telnet);
     }
 
     s->connected = 1;
@@ -4144,9 +4145,8 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     /* be isn't opened until we get a connection */
     chr->explicit_be_open = true;
 
-    chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE);
-    SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, "disconnected:",
-                         addr, is_listen, is_telnet);
+    chr->filename = SocketAddress_to_str("disconnected:",
+                                         addr, is_listen, is_telnet);
 
     if (is_listen) {
         if (is_telnet) {
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 20/46] char: convert from GIOChannel to QIOChannel
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (18 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 19/46] char: remove fixed length filename allocation Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 21/46] char: don't assume telnet initialization will not block Daniel P. Berrange
                   ` (25 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

In preparation for introducing TLS support to the TCP chardev
backend, convert existing chardev code from using GIOChannel
to QIOChannel. This simplifies the chardev code by removing
most of the OS platform conditional code for dealing with
file descriptor passing.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qemu-char.c | 646 +++++++++++++++++++++++-------------------------------------
 1 file changed, 247 insertions(+), 399 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 0d702d6..f2724b9 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -32,6 +32,8 @@
 #include "qapi/qmp-input-visitor.h"
 #include "qapi/qmp-output-visitor.h"
 #include "qapi-visit.h"
+#include "io/channel-socket.h"
+#include "io/channel-file.h"
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -756,7 +758,7 @@ typedef struct IOWatchPoll
 {
     GSource parent;
 
-    GIOChannel *channel;
+    QIOChannel *ioc;
     GSource *src;
 
     IOCanReadHandler *fd_can_read;
@@ -779,8 +781,8 @@ static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_)
     }
 
     if (now_active) {
-        iwp->src = g_io_create_watch(iwp->channel,
-                                     G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
+        iwp->src = qio_channel_create_watch(
+            iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
         g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
         g_source_attach(iwp->src, NULL);
     } else {
@@ -826,9 +828,9 @@ static GSourceFuncs io_watch_poll_funcs = {
 };
 
 /* Can only be used for read */
-static guint io_add_watch_poll(GIOChannel *channel,
+static guint io_add_watch_poll(QIOChannel *ioc,
                                IOCanReadHandler *fd_can_read,
-                               GIOFunc fd_read,
+                               QIOChannelFunc fd_read,
                                gpointer user_data)
 {
     IOWatchPoll *iwp;
@@ -837,7 +839,7 @@ static guint io_add_watch_poll(GIOChannel *channel,
     iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll));
     iwp->fd_can_read = fd_can_read;
     iwp->opaque = user_data;
-    iwp->channel = channel;
+    iwp->ioc = ioc;
     iwp->fd_read = (GSourceFunc) fd_read;
     iwp->src = NULL;
 
@@ -873,79 +875,53 @@ static void remove_fd_in_watch(CharDriverState *chr)
     }
 }
 
-#ifndef _WIN32
-static GIOChannel *io_channel_from_fd(int fd)
-{
-    GIOChannel *chan;
-
-    if (fd == -1) {
-        return NULL;
-    }
 
-    chan = g_io_channel_unix_new(fd);
-
-    g_io_channel_set_encoding(chan, NULL, NULL);
-    g_io_channel_set_buffered(chan, FALSE);
-
-    return chan;
-}
-#endif
-
-static GIOChannel *io_channel_from_socket(int fd)
+static int io_channel_send_full(QIOChannel *ioc,
+                                const void *buf, size_t len,
+                                int *fds, size_t nfds)
 {
-    GIOChannel *chan;
+    size_t offset = 0;
 
-    if (fd == -1) {
-        return NULL;
-    }
+    while (offset < len) {
+        ssize_t ret = 0;
+        struct iovec iov = { .iov_base = (char *)buf + offset,
+                             .iov_len = len - offset };
+
+        ret = qio_channel_writev_full(
+            ioc, &iov, 1,
+            fds, nfds, NULL);
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            errno = EAGAIN;
+            return -1;
+        }
 
-#ifdef _WIN32
-    chan = g_io_channel_win32_new_socket(fd);
-#else
-    chan = g_io_channel_unix_new(fd);
-#endif
+        if (ret > 0) {
+            offset += ret;
+        }
+        if (ret < 0) {
+            if (offset) {
+                return offset;
+            }
 
-    g_io_channel_set_encoding(chan, NULL, NULL);
-    g_io_channel_set_buffered(chan, FALSE);
+            errno = EINVAL;
+            return -1;
+        }
+    }
 
-    return chan;
+    return offset;
 }
 
-static int io_channel_send(GIOChannel *fd, const void *buf, size_t len)
-{
-    size_t offset = 0;
-    GIOStatus status = G_IO_STATUS_NORMAL;
-
-    while (offset < len && status == G_IO_STATUS_NORMAL) {
-        gsize bytes_written = 0;
 
-        status = g_io_channel_write_chars(fd, buf + offset, len - offset,
-                                          &bytes_written, NULL);
-        offset += bytes_written;
-    }
-
-    if (offset > 0) {
-        return offset;
-    }
-    switch (status) {
-    case G_IO_STATUS_NORMAL:
-        g_assert(len == 0);
-        return 0;
-    case G_IO_STATUS_AGAIN:
-        errno = EAGAIN;
-        return -1;
-    default:
-        break;
-    }
-    errno = EINVAL;
-    return -1;
+static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len)
+{
+    return io_channel_send_full(ioc, buf, len, NULL, 0);
 }
 
 #ifndef _WIN32
 
 typedef struct FDCharDriver {
     CharDriverState *chr;
-    GIOChannel *fd_in, *fd_out;
+    QIOChannel *ioc_in, *ioc_out;
     int max_size;
 } FDCharDriver;
 
@@ -954,17 +930,16 @@ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     FDCharDriver *s = chr->opaque;
     
-    return io_channel_send(s->fd_out, buf, len);
+    return io_channel_send(s->ioc_out, buf, len);
 }
 
-static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
+static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
     FDCharDriver *s = chr->opaque;
     int len;
     uint8_t buf[READ_BUF_LEN];
-    GIOStatus status;
-    gsize bytes_read;
+    ssize_t ret;
 
     len = sizeof(buf);
     if (len > s->max_size) {
@@ -974,15 +949,15 @@ static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
         return TRUE;
     }
 
-    status = g_io_channel_read_chars(chan, (gchar *)buf,
-                                     len, &bytes_read, NULL);
-    if (status == G_IO_STATUS_EOF) {
+    ret = qio_channel_read(
+        chan, (gchar *)buf, len, NULL);
+    if (ret == 0) {
         remove_fd_in_watch(chr);
         qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
         return FALSE;
     }
-    if (status == G_IO_STATUS_NORMAL) {
-        qemu_chr_be_write(chr, buf, bytes_read);
+    if (ret > 0) {
+        qemu_chr_be_write(chr, buf, ret);
     }
 
     return TRUE;
@@ -1000,7 +975,7 @@ static int fd_chr_read_poll(void *opaque)
 static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
     FDCharDriver *s = chr->opaque;
-    return g_io_create_watch(s->fd_out, cond);
+    return qio_channel_create_watch(s->ioc_out, cond);
 }
 
 static void fd_chr_update_read_handler(CharDriverState *chr)
@@ -1008,8 +983,9 @@ static void fd_chr_update_read_handler(CharDriverState *chr)
     FDCharDriver *s = chr->opaque;
 
     remove_fd_in_watch(chr);
-    if (s->fd_in) {
-        chr->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll,
+    if (s->ioc_in) {
+        chr->fd_in_tag = io_add_watch_poll(s->ioc_in,
+                                           fd_chr_read_poll,
                                            fd_chr_read, chr);
     }
 }
@@ -1019,11 +995,11 @@ static void fd_chr_close(struct CharDriverState *chr)
     FDCharDriver *s = chr->opaque;
 
     remove_fd_in_watch(chr);
-    if (s->fd_in) {
-        g_io_channel_unref(s->fd_in);
+    if (s->ioc_in) {
+        object_unref(OBJECT(s->ioc_in));
     }
-    if (s->fd_out) {
-        g_io_channel_unref(s->fd_out);
+    if (s->ioc_out) {
+        object_unref(OBJECT(s->ioc_out));
     }
 
     g_free(s);
@@ -1038,8 +1014,8 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
 
     chr = qemu_chr_alloc();
     s = g_malloc0(sizeof(FDCharDriver));
-    s->fd_in = io_channel_from_fd(fd_in);
-    s->fd_out = io_channel_from_fd(fd_out);
+    s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
+    s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
     qemu_set_nonblock(fd_out);
     s->chr = chr;
     chr->opaque = s;
@@ -1174,7 +1150,7 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
 #define HAVE_CHARDEV_TTY 1
 
 typedef struct {
-    GIOChannel *fd;
+    QIOChannel *ioc;
     int read_bytes;
 
     /* Protected by the CharDriverState chr_write_lock.  */
@@ -1224,8 +1200,9 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr)
 {
     PtyCharDriver *s = chr->opaque;
     GPollFD pfd;
+    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc);
 
-    pfd.fd = g_io_channel_unix_get_fd(s->fd);
+    pfd.fd = fioc->fd;
     pfd.events = G_IO_OUT;
     pfd.revents = 0;
     g_poll(&pfd, 1, 0);
@@ -1255,7 +1232,7 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
             return 0;
         }
     }
-    return io_channel_send(s->fd, buf, len);
+    return io_channel_send(s->ioc, buf, len);
 }
 
 static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
@@ -1264,7 +1241,7 @@ static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
     if (!s->connected) {
         return NULL;
     }
-    return g_io_create_watch(s->fd, cond);
+    return qio_channel_create_watch(s->ioc, cond);
 }
 
 static int pty_chr_read_poll(void *opaque)
@@ -1276,13 +1253,13 @@ static int pty_chr_read_poll(void *opaque)
     return s->read_bytes;
 }
 
-static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
+static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
     PtyCharDriver *s = chr->opaque;
-    gsize size, len;
+    gsize len;
     uint8_t buf[READ_BUF_LEN];
-    GIOStatus status;
+    ssize_t ret;
 
     len = sizeof(buf);
     if (len > s->read_bytes)
@@ -1290,13 +1267,13 @@ static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
     if (len == 0) {
         return TRUE;
     }
-    status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL);
-    if (status != G_IO_STATUS_NORMAL) {
+    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
+    if (ret <= 0) {
         pty_chr_state(chr, 0);
         return FALSE;
     } else {
         pty_chr_state(chr, 1);
-        qemu_chr_be_write(chr, buf, size);
+        qemu_chr_be_write(chr, buf, ret);
     }
     return TRUE;
 }
@@ -1338,7 +1315,8 @@ static void pty_chr_state(CharDriverState *chr, int connected)
             s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
         }
         if (!chr->fd_in_tag) {
-            chr->fd_in_tag = io_add_watch_poll(s->fd, pty_chr_read_poll,
+            chr->fd_in_tag = io_add_watch_poll(s->ioc,
+                                               pty_chr_read_poll,
                                                pty_chr_read, chr);
         }
     }
@@ -1347,13 +1325,10 @@ static void pty_chr_state(CharDriverState *chr, int connected)
 static void pty_chr_close(struct CharDriverState *chr)
 {
     PtyCharDriver *s = chr->opaque;
-    int fd;
 
     qemu_mutex_lock(&chr->chr_write_lock);
     pty_chr_state(chr, 0);
-    fd = g_io_channel_unix_get_fd(s->fd);
-    g_io_channel_unref(s->fd);
-    close(fd);
+    object_unref(OBJECT(s->ioc));
     if (s->timer_tag) {
         g_source_remove(s->timer_tag);
         s->timer_tag = 0;
@@ -1396,7 +1371,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
     chr->chr_add_watch = pty_chr_add_watch;
     chr->explicit_be_open = true;
 
-    s->fd = io_channel_from_fd(master_fd);
+    s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
     s->timer_tag = 0;
 
     return chr;
@@ -1520,12 +1495,13 @@ static void tty_serial_init(int fd, int speed,
 static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
 {
     FDCharDriver *s = chr->opaque;
+    QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in);
 
     switch(cmd) {
     case CHR_IOCTL_SERIAL_SET_PARAMS:
         {
             QEMUSerialSetParams *ssp = arg;
-            tty_serial_init(g_io_channel_unix_get_fd(s->fd_in),
+            tty_serial_init(fioc->fd,
                             ssp->speed, ssp->parity,
                             ssp->data_bits, ssp->stop_bits);
         }
@@ -1534,7 +1510,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
         {
             int enable = *(int *)arg;
             if (enable) {
-                tcsendbreak(g_io_channel_unix_get_fd(s->fd_in), 1);
+                tcsendbreak(fioc->fd, 1);
             }
         }
         break;
@@ -1542,7 +1518,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
         {
             int sarg = 0;
             int *targ = (int *)arg;
-            ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &sarg);
+            ioctl(fioc->fd, TIOCMGET, &sarg);
             *targ = 0;
             if (sarg & TIOCM_CTS)
                 *targ |= CHR_TIOCM_CTS;
@@ -1562,7 +1538,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
         {
             int sarg = *(int *)arg;
             int targ = 0;
-            ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &targ);
+            ioctl(fioc->fd, TIOCMGET, &targ);
             targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
                      | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
             if (sarg & CHR_TIOCM_CTS)
@@ -1577,7 +1553,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
                 targ |= TIOCM_DTR;
             if (sarg & CHR_TIOCM_RTS)
                 targ |= TIOCM_RTS;
-            ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMSET, &targ);
+            ioctl(fioc->fd, TIOCMSET, &targ);
         }
         break;
     default:
@@ -1588,18 +1564,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
 
 static void qemu_chr_close_tty(CharDriverState *chr)
 {
-    FDCharDriver *s = chr->opaque;
-    int fd = -1;
-
-    if (s) {
-        fd = g_io_channel_unix_get_fd(s->fd_in);
-    }
-
     fd_chr_close(chr);
-
-    if (fd >= 0) {
-        close(fd);
-    }
 }
 
 static CharDriverState *qemu_chr_open_tty_fd(int fd)
@@ -2361,8 +2326,7 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
 /* UDP Net console */
 
 typedef struct {
-    int fd;
-    GIOChannel *chan;
+    QIOChannel *ioc;
     uint8_t buf[READ_BUF_LEN];
     int bufcnt;
     int bufptr;
@@ -2373,17 +2337,9 @@ typedef struct {
 static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     NetCharDriver *s = chr->opaque;
-    gsize bytes_written;
-    GIOStatus status;
-
-    status = g_io_channel_write_chars(s->chan, (const gchar *)buf, len, &bytes_written, NULL);
-    if (status == G_IO_STATUS_EOF) {
-        return 0;
-    } else if (status != G_IO_STATUS_NORMAL) {
-        return -1;
-    }
 
-    return bytes_written;
+    return qio_channel_write(
+        s->ioc, (const char *)buf, len, NULL);
 }
 
 static int udp_chr_read_poll(void *opaque)
@@ -2404,24 +2360,22 @@ static int udp_chr_read_poll(void *opaque)
     return s->max_size;
 }
 
-static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
+static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
     NetCharDriver *s = chr->opaque;
-    gsize bytes_read = 0;
-    GIOStatus status;
+    ssize_t ret;
 
     if (s->max_size == 0) {
         return TRUE;
     }
-    status = g_io_channel_read_chars(s->chan, (gchar *)s->buf, sizeof(s->buf),
-                                     &bytes_read, NULL);
-    s->bufcnt = bytes_read;
-    s->bufptr = s->bufcnt;
-    if (status != G_IO_STATUS_NORMAL) {
+    ret = qio_channel_read(
+        s->ioc, (char *)s->buf, sizeof(s->buf), NULL);
+    if (ret <= 0) {
         remove_fd_in_watch(chr);
         return FALSE;
     }
+    s->bufcnt = ret;
 
     s->bufptr = 0;
     while (s->max_size > 0 && s->bufptr < s->bufcnt) {
@@ -2438,8 +2392,9 @@ static void udp_chr_update_read_handler(CharDriverState *chr)
     NetCharDriver *s = chr->opaque;
 
     remove_fd_in_watch(chr);
-    if (s->chan) {
-        chr->fd_in_tag = io_add_watch_poll(s->chan, udp_chr_read_poll,
+    if (s->ioc) {
+        chr->fd_in_tag = io_add_watch_poll(s->ioc,
+                                           udp_chr_read_poll,
                                            udp_chr_read, chr);
     }
 }
@@ -2449,15 +2404,14 @@ static void udp_chr_close(CharDriverState *chr)
     NetCharDriver *s = chr->opaque;
 
     remove_fd_in_watch(chr);
-    if (s->chan) {
-        g_io_channel_unref(s->chan);
-        closesocket(s->fd);
+    if (s->ioc) {
+        object_unref(OBJECT(s->ioc));
     }
     g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_udp_fd(int fd)
+static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc)
 {
     CharDriverState *chr = NULL;
     NetCharDriver *s = NULL;
@@ -2465,8 +2419,7 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd)
     chr = qemu_chr_alloc();
     s = g_malloc0(sizeof(NetCharDriver));
 
-    s->fd = fd;
-    s->chan = io_channel_from_socket(s->fd);
+    s->ioc = QIO_CHANNEL(sioc);
     s->bufcnt = 0;
     s->bufptr = 0;
     chr->opaque = s;
@@ -2482,19 +2435,18 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd)
 /* TCP Net console */
 
 typedef struct {
-
-    GIOChannel *chan, *listen_chan;
+    QIOChannel *ioc;
+    QIOChannelSocket *listen_ioc;
     guint listen_tag;
-    int fd, listen_fd;
     int connected;
     int max_size;
     int do_telnetopt;
     int do_nodelay;
     int is_unix;
     int *read_msgfds;
-    int read_msgfds_num;
+    size_t read_msgfds_num;
     int *write_msgfds;
-    int write_msgfds_num;
+    size_t write_msgfds_num;
 
     SocketAddress *addr;
     bool is_listen;
@@ -2528,68 +2480,25 @@ static void check_report_connect_error(CharDriverState *chr,
     qemu_chr_socket_restart_timer(chr);
 }
 
-static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
-
-#ifndef _WIN32
-static int unix_send_msgfds(CharDriverState *chr, const uint8_t *buf, int len)
-{
-    TCPCharDriver *s = chr->opaque;
-    struct msghdr msgh;
-    struct iovec iov;
-    int r;
-
-    size_t fd_size = s->write_msgfds_num * sizeof(int);
-    char control[CMSG_SPACE(fd_size)];
-    struct cmsghdr *cmsg;
-
-    memset(&msgh, 0, sizeof(msgh));
-    memset(control, 0, sizeof(control));
-
-    /* set the payload */
-    iov.iov_base = (uint8_t *) buf;
-    iov.iov_len = len;
-
-    msgh.msg_iov = &iov;
-    msgh.msg_iovlen = 1;
-
-    msgh.msg_control = control;
-    msgh.msg_controllen = sizeof(control);
-
-    cmsg = CMSG_FIRSTHDR(&msgh);
-
-    cmsg->cmsg_len = CMSG_LEN(fd_size);
-    cmsg->cmsg_level = SOL_SOCKET;
-    cmsg->cmsg_type = SCM_RIGHTS;
-    memcpy(CMSG_DATA(cmsg), s->write_msgfds, fd_size);
-
-    do {
-        r = sendmsg(s->fd, &msgh, 0);
-    } while (r < 0 && errno == EINTR);
-
-    /* free the written msgfds, no matter what */
-    if (s->write_msgfds_num) {
-        g_free(s->write_msgfds);
-        s->write_msgfds = 0;
-        s->write_msgfds_num = 0;
-    }
-
-    return r;
-}
-#endif
+static gboolean tcp_chr_accept(QIOChannel *chan,
+                               GIOCondition cond,
+                               void *opaque);
 
 /* Called with chr_write_lock held.  */
 static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     TCPCharDriver *s = chr->opaque;
     if (s->connected) {
-#ifndef _WIN32
-        if (s->is_unix && s->write_msgfds_num) {
-            return unix_send_msgfds(chr, buf, len);
-        } else
-#endif
-        {
-            return io_channel_send(s->chan, buf, len);
+        int ret =  io_channel_send(s->ioc, buf, len);
+
+        /* free the written msgfds, no matter what */
+        if (s->write_msgfds_num) {
+            g_free(s->write_msgfds);
+            s->write_msgfds = 0;
+            s->write_msgfds_num = 0;
         }
+
+        return ret;
     } else {
         /* XXX: indicate an error ? */
         return len;
@@ -2700,107 +2609,53 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
     return 0;
 }
 
-#ifndef _WIN32
-static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg)
+static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
 {
     TCPCharDriver *s = chr->opaque;
-    struct cmsghdr *cmsg;
+    struct iovec iov = { .iov_base = buf, .iov_len = len };
+    int ret;
+    size_t i;
 
-    for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
-        int fd_size, i;
+    /* close and clean read_msgfds */
+    for (i = 0; i < s->read_msgfds_num; i++) {
+        close(s->read_msgfds[i]);
+    }
 
-        if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) ||
-            cmsg->cmsg_level != SOL_SOCKET ||
-            cmsg->cmsg_type != SCM_RIGHTS) {
-            continue;
-        }
+    if (s->read_msgfds_num) {
+        g_free(s->read_msgfds);
+    }
 
-        fd_size = cmsg->cmsg_len - CMSG_LEN(0);
+    if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
+        ret = qio_channel_readv_full(s->ioc, &iov, 1,
+                                     &s->read_msgfds, &s->read_msgfds_num,
+                                     NULL);
+    } else {
+        ret = qio_channel_readv_full(s->ioc, &iov, 1,
+                                     NULL, NULL,
+                                     NULL);
+    }
 
-        if (!fd_size) {
+    for (i = 0; i < s->read_msgfds_num; i++) {
+        int fd = s->read_msgfds[i];
+        if (fd < 0) {
             continue;
         }
 
-        /* close and clean read_msgfds */
-        for (i = 0; i < s->read_msgfds_num; i++) {
-            close(s->read_msgfds[i]);
-        }
-
-        if (s->read_msgfds_num) {
-            g_free(s->read_msgfds);
-        }
-
-        s->read_msgfds_num = fd_size / sizeof(int);
-        s->read_msgfds = g_malloc(fd_size);
-        memcpy(s->read_msgfds, CMSG_DATA(cmsg), fd_size);
-
-        for (i = 0; i < s->read_msgfds_num; i++) {
-            int fd = s->read_msgfds[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
-        }
-    }
-}
-
-static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
-{
-    TCPCharDriver *s = chr->opaque;
-    struct msghdr msg = { NULL, };
-    struct iovec iov[1];
-    union {
-        struct cmsghdr cmsg;
-        char control[CMSG_SPACE(sizeof(int) * TCP_MAX_FDS)];
-    } msg_control;
-    int flags = 0;
-    ssize_t ret;
-
-    iov[0].iov_base = buf;
-    iov[0].iov_len = len;
-
-    msg.msg_iov = iov;
-    msg.msg_iovlen = 1;
-    msg.msg_control = &msg_control;
-    msg.msg_controllen = sizeof(msg_control);
+        /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */
+        qemu_set_block(fd);
 
-#ifdef MSG_CMSG_CLOEXEC
-    flags |= MSG_CMSG_CLOEXEC;
+#ifndef MSG_CMSG_CLOEXEC
+        qemu_set_cloexec(fd);
 #endif
-    do {
-        ret = recvmsg(s->fd, &msg, flags);
-    } while (ret == -1 && errno == EINTR);
-
-    if (ret > 0 && s->is_unix) {
-        unix_process_msgfd(chr, &msg);
     }
 
     return ret;
 }
-#else
-static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
-{
-    TCPCharDriver *s = chr->opaque;
-    ssize_t ret;
-
-    do {
-        ret = qemu_recv(s->fd, buf, len, 0);
-    } while (ret == -1 && socket_error() == EINTR);
-
-    return ret;
-}
-#endif
 
 static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 {
     TCPCharDriver *s = chr->opaque;
-    return g_io_create_watch(s->chan, cond);
+    return qio_channel_create_watch(s->ioc, cond);
 }
 
 static void tcp_chr_disconnect(CharDriverState *chr)
@@ -2808,15 +2663,13 @@ static void tcp_chr_disconnect(CharDriverState *chr)
     TCPCharDriver *s = chr->opaque;
 
     s->connected = 0;
-    if (s->listen_chan) {
-        s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN,
-                                       tcp_chr_accept, chr);
+    if (s->listen_ioc) {
+        s->listen_tag = qio_channel_add_watch(
+            QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
     }
     remove_fd_in_watch(chr);
-    g_io_channel_unref(s->chan);
-    s->chan = NULL;
-    closesocket(s->fd);
-    s->fd = -1;
+    object_unref(OBJECT(s->ioc));
+    s->ioc = NULL;
     g_free(chr->filename);
     chr->filename = SocketAddress_to_str("disconnected:", s->addr,
                                          s->is_listen, s->is_telnet);
@@ -2826,7 +2679,7 @@ static void tcp_chr_disconnect(CharDriverState *chr)
     }
 }
 
-static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
+static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
@@ -2840,9 +2693,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
     if (len > s->max_size)
         len = s->max_size;
     size = tcp_chr_recv(chr, (void *)buf, len);
-    if (size == 0 ||
-        (size < 0 &&
-         socket_error() != EAGAIN && socket_error() != EWOULDBLOCK)) {
+    if (size == 0 || size == -1) {
         /* connection closed */
         tcp_chr_disconnect(chr);
     } else if (size > 0) {
@@ -2890,25 +2741,17 @@ static void tcp_chr_connect(void *opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
-    struct sockaddr_storage ss, ps;
-    socklen_t ss_len = sizeof(ss), ps_len = sizeof(ps);
+    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(s->ioc);
 
-    memset(&ss, 0, ss_len);
     g_free(chr->filename);
-    if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) {
-        chr->filename = g_strdup_printf("Error in getsockname: %s\n",
-                                        strerror(errno));
-    } else if (getpeername(s->fd, (struct sockaddr *) &ps, &ps_len) != 0) {
-        chr->filename = g_strdup_printf("Error in getpeername: %s\n",
-                                        strerror(errno));
-    } else {
-        chr->filename = sockaddr_to_str(&ss, ss_len, &ps, ps_len,
-                                        s->is_listen, s->is_telnet);
-    }
+    chr->filename = sockaddr_to_str(&sioc->localAddr, sioc->localAddrLen,
+                                    &sioc->remoteAddr, sioc->remoteAddrLen,
+                                    s->is_listen, s->is_telnet);
 
     s->connected = 1;
-    if (s->chan) {
-        chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
+    if (s->ioc) {
+        chr->fd_in_tag = io_add_watch_poll(s->ioc,
+                                           tcp_chr_read_poll,
                                            tcp_chr_read, chr);
     }
     qemu_chr_be_generic_open(chr);
@@ -2919,38 +2762,42 @@ static void tcp_chr_update_read_handler(CharDriverState *chr)
     TCPCharDriver *s = chr->opaque;
 
     remove_fd_in_watch(chr);
-    if (s->chan) {
-        chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll,
+    if (s->ioc) {
+        chr->fd_in_tag = io_add_watch_poll(s->ioc,
+                                           tcp_chr_read_poll,
                                            tcp_chr_read, chr);
     }
 }
 
 #define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
-static void tcp_chr_telnet_init(int fd)
+static void tcp_chr_telnet_init(QIOChannel *ioc)
 {
     char buf[3];
     /* Send the telnet negotion to put telnet in binary, no echo, single char mode */
     IACSET(buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
-    send(fd, (char *)buf, 3, 0);
+    qio_channel_write(ioc, buf, 3, NULL);
     IACSET(buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
-    send(fd, (char *)buf, 3, 0);
+    qio_channel_write(ioc, buf, 3, NULL);
     IACSET(buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
-    send(fd, (char *)buf, 3, 0);
+    qio_channel_write(ioc, buf, 3, NULL);
     IACSET(buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
-    send(fd, (char *)buf, 3, 0);
+    qio_channel_write(ioc, buf, 3, NULL);
 }
 
-static int tcp_chr_add_client(CharDriverState *chr, int fd)
+static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
 {
     TCPCharDriver *s = chr->opaque;
-    if (s->fd != -1)
+    if (s->ioc != NULL) {
 	return -1;
+    }
 
-    qemu_set_nonblock(fd);
-    if (s->do_nodelay)
-        socket_set_nodelay(fd);
-    s->fd = fd;
-    s->chan = io_channel_from_socket(fd);
+    s->ioc = QIO_CHANNEL(sioc);
+    object_ref(OBJECT(sioc));
+
+    qio_channel_set_blocking(s->ioc, false);
+    if (s->do_nodelay) {
+        qio_channel_socket_set_nodelay(sioc, true);
+    }
     if (s->listen_tag) {
         g_source_remove(s->listen_tag);
         s->listen_tag = 0;
@@ -2960,41 +2807,42 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd)
     return 0;
 }
 
-static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque)
+
+static int tcp_chr_add_client(CharDriverState *chr, int fd)
+{
+    int ret;
+    QIOChannelSocket *sioc;
+
+    sioc = qio_channel_socket_new_fd(fd, NULL);
+    if (!sioc) {
+        return -1;
+    }
+    ret = tcp_chr_new_client(chr, sioc);
+    object_unref(OBJECT(sioc));
+    return ret;
+}
+
+static gboolean tcp_chr_accept(QIOChannel *channel,
+                               GIOCondition cond,
+                               void *opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
-    struct sockaddr_in saddr;
-#ifndef _WIN32
-    struct sockaddr_un uaddr;
-#endif
-    struct sockaddr *addr;
-    socklen_t len;
-    int fd;
+    QIOChannelSocket *sioc;
 
-    for(;;) {
-#ifndef _WIN32
-	if (s->is_unix) {
-	    len = sizeof(uaddr);
-	    addr = (struct sockaddr *)&uaddr;
-	} else
-#endif
-	{
-	    len = sizeof(saddr);
-	    addr = (struct sockaddr *)&saddr;
-	}
-        fd = qemu_accept(s->listen_fd, addr, &len);
-        if (fd < 0 && errno != EINTR) {
-            s->listen_tag = 0;
-            return FALSE;
-        } else if (fd >= 0) {
-            if (s->do_telnetopt)
-                tcp_chr_telnet_init(fd);
-            break;
-        }
+    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
+                                     NULL);
+    if (!sioc) {
+        return TRUE;
     }
-    if (tcp_chr_add_client(chr, fd) < 0)
-	close(fd);
+
+    if (s->do_telnetopt) {
+        tcp_chr_telnet_init(QIO_CHANNEL(sioc));
+    }
+
+    tcp_chr_new_client(chr, sioc);
+
+    object_unref(OBJECT(sioc));
 
     return TRUE;
 }
@@ -3009,22 +2857,16 @@ static void tcp_chr_close(CharDriverState *chr)
         s->reconnect_timer = 0;
     }
     qapi_free_SocketAddress(s->addr);
-    if (s->fd >= 0) {
-        remove_fd_in_watch(chr);
-        if (s->chan) {
-            g_io_channel_unref(s->chan);
-        }
-        closesocket(s->fd);
+    remove_fd_in_watch(chr);
+    if (s->ioc) {
+        object_unref(OBJECT(s->ioc));
     }
-    if (s->listen_fd >= 0) {
-        if (s->listen_tag) {
-            g_source_remove(s->listen_tag);
-            s->listen_tag = 0;
-        }
-        if (s->listen_chan) {
-            g_io_channel_unref(s->listen_chan);
-        }
-        closesocket(s->listen_fd);
+    if (s->listen_tag) {
+        g_source_remove(s->listen_tag);
+        s->listen_tag = 0;
+    }
+    if (s->listen_ioc) {
+        object_unref(OBJECT(s->listen_ioc));
     }
     if (s->read_msgfds_num) {
         for (i = 0; i < s->read_msgfds_num; i++) {
@@ -3039,57 +2881,63 @@ static void tcp_chr_close(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd)
+static void qemu_chr_finish_socket_connection(CharDriverState *chr,
+                                              QIOChannelSocket *sioc)
 {
     TCPCharDriver *s = chr->opaque;
 
     if (s->is_listen) {
-        s->listen_fd = fd;
-        s->listen_chan = io_channel_from_socket(s->listen_fd);
-        s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN,
-                                       tcp_chr_accept, chr);
+        s->listen_ioc = sioc;
+        s->listen_tag = qio_channel_add_watch(
+            QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
     } else {
-        s->connected = 1;
-        s->fd = fd;
-        socket_set_nodelay(fd);
-        s->chan = io_channel_from_socket(s->fd);
-        tcp_chr_connect(chr);
+        tcp_chr_new_client(chr, sioc);
+        object_unref(OBJECT(sioc));
     }
 }
 
-static void qemu_chr_socket_connected(int fd, Error *err, void *opaque)
+static void qemu_chr_socket_connected(Object *src, Error *err, void *opaque)
 {
+    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(src);
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
 
-    if (fd < 0) {
+    if (err) {
         check_report_connect_error(chr, err);
+        object_unref(src);
         return;
     }
 
     s->connect_err_reported = false;
-    qemu_chr_finish_socket_connection(chr, fd);
+    qemu_chr_finish_socket_connection(chr, sioc);
 }
 
 static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp)
 {
     TCPCharDriver *s = chr->opaque;
-    int fd;
+    QIOChannelSocket *sioc = qio_channel_socket_new();
 
     if (s->is_listen) {
-        fd = socket_listen(s->addr, errp);
+        if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
+            goto fail;
+        }
+        qemu_chr_finish_socket_connection(chr, sioc);
     } else if (s->reconnect_time) {
-        fd = socket_connect(s->addr, errp, qemu_chr_socket_connected, chr);
-        return fd >= 0;
+        qio_channel_socket_connect_async(sioc, s->addr,
+                                         qemu_chr_socket_connected,
+                                         chr, NULL);
     } else {
-        fd = socket_connect(s->addr, errp, NULL, NULL);
-    }
-    if (fd < 0) {
-        return false;
+        if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) {
+            goto fail;
+        }
+        qemu_chr_finish_socket_connection(chr, sioc);
     }
 
-    qemu_chr_finish_socket_connection(chr, fd);
     return true;
+
+ fail:
+    object_unref(OBJECT(sioc));
+    return false;
 }
 
 /*********************************************************/
@@ -4125,8 +3973,6 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     chr = qemu_chr_alloc();
     s = g_malloc0(sizeof(TCPCharDriver));
 
-    s->fd = -1;
-    s->listen_fd = -1;
     s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
@@ -4168,8 +4014,8 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     if (is_listen && is_waitconnect) {
         fprintf(stderr, "QEMU waiting for connection on: %s\n",
                 chr->filename);
-        tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
-        qemu_set_nonblock(s->listen_fd);
+        tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr);
+        qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false);
     }
 
     return chr;
@@ -4178,13 +4024,15 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
 static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
                                              Error **errp)
 {
-    int fd;
+    QIOChannelSocket *sioc = qio_channel_socket_new();
 
-    fd = socket_dgram(udp->remote, udp->local, errp);
-    if (fd < 0) {
+    if (qio_channel_socket_dgram_sync(sioc,
+                                      udp->remote, udp->local,
+                                      errp) < 0) {
+        object_unref(OBJECT(sioc));
         return NULL;
     }
-    return qemu_chr_open_udp_fd(fd);
+    return qemu_chr_open_udp(sioc);
 }
 
 ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 21/46] char: don't assume telnet initialization will not block
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (19 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 20/46] char: convert from GIOChannel to QIOChannel Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 22/46] char: introduce support for TLS encrypted TCP chardev backend Daniel P. Berrange
                   ` (24 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The current code for doing telnet initialization is writing to
a socket without checking the return status. While it is highly
unlikely to be a problem when writing to a bare socket, as the
buffers are large enough to prevent blocking, this cannot be
assumed safe with TLS sockets. So write the telnet initialization
code into a memory buffer and then use an I/O watch to fully
send the data.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qemu-char.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 67 insertions(+), 18 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index f2724b9..fe75f09 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2769,19 +2769,68 @@ static void tcp_chr_update_read_handler(CharDriverState *chr)
     }
 }
 
-#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
-static void tcp_chr_telnet_init(QIOChannel *ioc)
+typedef struct {
+    CharDriverState *chr;
+    char buf[12];
+    size_t buflen;
+} TCPCharDriverTelnetInit;
+
+static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
+                                       GIOCondition cond G_GNUC_UNUSED,
+                                       gpointer user_data)
 {
-    char buf[3];
-    /* Send the telnet negotion to put telnet in binary, no echo, single char mode */
-    IACSET(buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
-    qio_channel_write(ioc, buf, 3, NULL);
-    IACSET(buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
-    qio_channel_write(ioc, buf, 3, NULL);
-    IACSET(buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
-    qio_channel_write(ioc, buf, 3, NULL);
-    IACSET(buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
-    qio_channel_write(ioc, buf, 3, NULL);
+    TCPCharDriverTelnetInit *init = user_data;
+    ssize_t ret;
+
+    ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
+    if (ret < 0) {
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            ret = 0;
+        } else {
+            tcp_chr_disconnect(init->chr);
+            return FALSE;
+        }
+    }
+    init->buflen -= ret;
+
+    if (init->buflen == 0) {
+        tcp_chr_connect(init->chr);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void tcp_chr_telnet_init(CharDriverState *chr)
+{
+    TCPCharDriver *s = chr->opaque;
+    TCPCharDriverTelnetInit *init =
+        g_new0(TCPCharDriverTelnetInit, 1);
+    size_t n = 0;
+
+    init->chr = chr;
+    init->buflen = 12;
+
+#define IACSET(x, a, b, c)                      \
+    do {                                        \
+        x[n++] = a;                             \
+        x[n++] = b;                             \
+        x[n++] = c;                             \
+    } while (0)
+
+    /* Prep the telnet negotion to put telnet in binary,
+     * no echo, single char mode */
+    IACSET(init->buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
+    IACSET(init->buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
+    IACSET(init->buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
+    IACSET(init->buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
+
+#undef IACSET
+
+    qio_channel_add_watch(
+        s->ioc, G_IO_OUT,
+        tcp_chr_telnet_init_io,
+        init, NULL);
 }
 
 static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
@@ -2802,7 +2851,12 @@ static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
         g_source_remove(s->listen_tag);
         s->listen_tag = 0;
     }
-    tcp_chr_connect(chr);
+
+    if (s->do_telnetopt) {
+        tcp_chr_telnet_init(chr);
+    } else {
+        tcp_chr_connect(chr);
+    }
 
     return 0;
 }
@@ -2827,7 +2881,6 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
                                void *opaque)
 {
     CharDriverState *chr = opaque;
-    TCPCharDriver *s = chr->opaque;
     QIOChannelSocket *sioc;
 
     sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
@@ -2836,10 +2889,6 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
         return TRUE;
     }
 
-    if (s->do_telnetopt) {
-        tcp_chr_telnet_init(QIO_CHANNEL(sioc));
-    }
-
     tcp_chr_new_client(chr, sioc);
 
     object_unref(OBJECT(sioc));
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 22/46] char: introduce support for TLS encrypted TCP chardev backend
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (20 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 21/46] char: don't assume telnet initialization will not block Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 23/46] nbd: convert to use the QAPI SocketAddress object Daniel P. Berrange
                   ` (23 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

This integrates support for QIOChannelTLS object in the TCP
chardev backend. If the 'tls-creds=NAME' option is passed with
the '-chardev tcp' argument, then it will setup the chardev
such that the client is required to establish a TLS handshake
when connecting. There is no support for checking the client
certificate against ACLs in this initial patch. This is pending
work to QOM-ify the ACL object code.

A complete invocation to run QEMU as the server for a TLS
encrypted serial dev might be

  $ qemu-system-x86_64 \
      -nodefconfig -nodefaults -device sga -display none \
      -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-creds=tls0,server \
      -device isa-serial,chardev=s0 \
      -object tls-creds,id=tls0,credtype=x509,\
        endpoint=server,dir=/home/berrange/security/qemutls,verify-peer=off

To test with the gnutls-cli tool as the client:

  $ gnutls-cli --priority=NORMAL -p 9000 \
       --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
       127.0.0.1

If QEMU was told to use 'anon' credential type, then use the
priority string 'NORMAL:+ANON-DH' with gnutls-cli

Alternatively, if setting up a chardev to operate as a client,
then the TLS credentials registered must be for the client
endpoint. First a TLS server must be setup, which can be done
with the gnutls-serv tool

  $ gnutls-serv --priority=NORMAL -p 9000 \
       --x509cafile=/home/berrange/security/qemutls/ca-cert.pem \
       --x509certfile=/home/berrange/security/qemutls/server-cert.pem \
       --x509keyfile=/home/berrange/security/qemutls/server-key.pem

Then QEMU can connect with

  $ qemu-system-x86_64 \
      -nodefconfig -nodefaults -device sga -display none \
      -chardev socket,id=s0,host=127.0.0.1,port=9000,tls-creds=tls0 \
      -device isa-serial,chardev=s0 \
      -object tls-creds,id=tls0,credtype=x509,\
        endpoint=client,dir=/home/berrange/security/qemutls

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qapi-schema.json |   2 +
 qemu-char.c      | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 qemu-options.hx  |   9 +++-
 3 files changed, 138 insertions(+), 15 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index ae4858c..b6ec943 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2910,6 +2910,7 @@
 #
 # @addr: socket address to listen on (server=true)
 #        or connect to (server=false)
+# @tls-creds: #optional the ID of the TLS credentials object (since 2.4)
 # @server: #optional create server socket (default: true)
 # @wait: #optional wait for incoming connection on server
 #        sockets (default: false).
@@ -2924,6 +2925,7 @@
 # Since: 1.4
 ##
 { 'struct': 'ChardevSocket', 'data': { 'addr'       : 'SocketAddress',
+                                     '*tls-creds'  : 'str',
                                      '*server'    : 'bool',
                                      '*wait'      : 'bool',
                                      '*nodelay'   : 'bool',
diff --git a/qemu-char.c b/qemu-char.c
index fe75f09..69ee044 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -34,6 +34,7 @@
 #include "qapi-visit.h"
 #include "io/channel-socket.h"
 #include "io/channel-file.h"
+#include "io/channel-tls.h"
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -2435,9 +2436,11 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc)
 /* TCP Net console */
 
 typedef struct {
-    QIOChannel *ioc;
+    QIOChannel *ioc; /* Client I/O channel */
+    QIOChannelSocket *sioc; /* Client master channel */
     QIOChannelSocket *listen_ioc;
     guint listen_tag;
+    QCryptoTLSCreds *tls_creds;
     int connected;
     int max_size;
     int do_telnetopt;
@@ -2668,6 +2671,8 @@ static void tcp_chr_disconnect(CharDriverState *chr)
             QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
     }
     remove_fd_in_watch(chr);
+    object_unref(OBJECT(s->sioc));
+    s->sioc = NULL;
     object_unref(OBJECT(s->ioc));
     s->ioc = NULL;
     g_free(chr->filename);
@@ -2741,12 +2746,12 @@ static void tcp_chr_connect(void *opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
-    QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(s->ioc);
 
     g_free(chr->filename);
-    chr->filename = sockaddr_to_str(&sioc->localAddr, sioc->localAddrLen,
-                                    &sioc->remoteAddr, sioc->remoteAddrLen,
-                                    s->is_listen, s->is_telnet);
+    chr->filename = sockaddr_to_str(
+        &s->sioc->localAddr, s->sioc->localAddrLen,
+        &s->sioc->remoteAddr, s->sioc->remoteAddrLen,
+        s->is_listen, s->is_telnet);
 
     s->connected = 1;
     if (s->ioc) {
@@ -2833,6 +2838,57 @@ static void tcp_chr_telnet_init(CharDriverState *chr)
         init, NULL);
 }
 
+
+static void tcp_chr_tls_handshake(Object *source,
+                                  Error *err,
+                                  gpointer user_data)
+{
+    CharDriverState *chr = user_data;
+    TCPCharDriver *s = chr->opaque;
+
+    if (err) {
+        tcp_chr_disconnect(chr);
+    } else {
+        if (s->do_telnetopt) {
+            tcp_chr_telnet_init(chr);
+        } else {
+            tcp_chr_connect(chr);
+        }
+    }
+}
+
+
+static void tcp_chr_tls_init(CharDriverState *chr)
+{
+    TCPCharDriver *s = chr->opaque;
+    QIOChannelTLS *tioc;
+    Error *err = NULL;
+
+    if (s->is_listen) {
+        tioc = qio_channel_tls_new_server(
+            s->ioc, s->tls_creds,
+            NULL, /* XXX Use an ACL */
+            &err);
+    } else {
+        tioc = qio_channel_tls_new_client(
+            s->ioc, s->tls_creds,
+            s->addr->inet->host,
+            &err);
+    }
+    if (tioc == NULL) {
+        error_free(err);
+        tcp_chr_disconnect(chr);
+    }
+    object_unref(OBJECT(s->ioc));
+    s->ioc = QIO_CHANNEL(tioc);
+
+    qio_channel_tls_handshake(tioc,
+                              tcp_chr_tls_handshake,
+                              chr,
+                              NULL);
+}
+
+
 static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
 {
     TCPCharDriver *s = chr->opaque;
@@ -2842,6 +2898,8 @@ static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
 
     s->ioc = QIO_CHANNEL(sioc);
     object_ref(OBJECT(sioc));
+    s->sioc = sioc;
+    object_ref(OBJECT(sioc));
 
     qio_channel_set_blocking(s->ioc, false);
     if (s->do_nodelay) {
@@ -2852,10 +2910,14 @@ static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
         s->listen_tag = 0;
     }
 
-    if (s->do_telnetopt) {
-        tcp_chr_telnet_init(chr);
+    if (s->tls_creds) {
+        tcp_chr_tls_init(chr);
     } else {
-        tcp_chr_connect(chr);
+        if (s->do_telnetopt) {
+            tcp_chr_telnet_init(chr);
+        } else {
+            tcp_chr_connect(chr);
+        }
     }
 
     return 0;
@@ -2923,6 +2985,9 @@ static void tcp_chr_close(CharDriverState *chr)
         }
         g_free(s->read_msgfds);
     }
+    if (s->tls_creds) {
+        object_unref(OBJECT(s->tls_creds));
+    }
     if (s->write_msgfds_num) {
         g_free(s->write_msgfds);
     }
@@ -3415,6 +3480,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     const char *path = qemu_opt_get(opts, "path");
     const char *host = qemu_opt_get(opts, "host");
     const char *port = qemu_opt_get(opts, "port");
+    const char *tls_creds = qemu_opt_get(opts, "tls-creds");
     SocketAddress *addr;
 
     if (!path) {
@@ -3426,6 +3492,11 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
             error_setg(errp, "chardev: socket: no port given");
             return;
         }
+    } else {
+        if (tls_creds) {
+            error_setg(errp, "TLS can only be used over TCP socket");
+            return;
+        }
     }
 
     backend->socket = g_new0(ChardevSocket, 1);
@@ -3440,6 +3511,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     backend->socket->wait = is_waitconnect;
     backend->socket->has_reconnect = true;
     backend->socket->reconnect = reconnect;
+    backend->socket->tls_creds = g_strdup(tls_creds);
 
     addr = g_new0(SocketAddress, 1);
     if (path) {
@@ -3459,6 +3531,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
         addr->inet->ipv6 = qemu_opt_get_bool(opts, "ipv6", 0);
     }
     backend->socket->addr = addr;
+    return;
 }
 
 static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
@@ -3842,6 +3915,9 @@ QemuOptsList qemu_chardev_opts = {
             .name = "telnet",
             .type = QEMU_OPT_BOOL,
         },{
+            .name = "tls-creds",
+            .type = QEMU_OPT_STRING,
+        },{
             .name = "width",
             .type = QEMU_OPT_NUMBER,
         },{
@@ -4008,6 +4084,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
 }
 
 static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
+                                                const char *id,
                                                 Error **errp)
 {
     CharDriverState *chr;
@@ -4026,6 +4103,39 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
     s->do_nodelay = do_nodelay;
+    if (sock->tls_creds) {
+        Object *creds;
+        creds = object_resolve_path_component(
+            object_get_objects_root(), sock->tls_creds);
+        if (!creds) {
+            error_setg(errp, "No TLS credentials with id '%s'",
+                       sock->tls_creds);
+            goto error;
+        }
+        s->tls_creds = (QCryptoTLSCreds *)
+            object_dynamic_cast(creds,
+                                TYPE_QCRYPTO_TLS_CREDS);
+        if (!s->tls_creds) {
+            error_setg(errp, "Object with id '%s' is not TLS credentials",
+                       sock->tls_creds);
+            goto error;
+        }
+        object_ref(OBJECT(s->tls_creds));
+        if (is_listen) {
+            if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+                error_setg(errp, "%s",
+                           "Expected TLS credentials for server endpoint");
+                goto error;
+            }
+        } else {
+            if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+                error_setg(errp, "%s",
+                           "Expected TLS credentials for client endpoint");
+                goto error;
+            }
+        }
+    }
+
     qapi_copy_SocketAddress(&s->addr, sock->addr);
 
     chr->opaque = s;
@@ -4054,10 +4164,7 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     if (s->reconnect_time) {
         socket_try_connect(chr);
     } else if (!qemu_chr_open_socket_fd(chr, errp)) {
-        g_free(s);
-        g_free(chr->filename);
-        g_free(chr);
-        return NULL;
+        goto error;
     }
 
     if (is_listen && is_waitconnect) {
@@ -4068,6 +4175,15 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
     }
 
     return chr;
+
+ error:
+    if (s->tls_creds) {
+        object_unref(OBJECT(s->tls_creds));
+    }
+    g_free(s);
+    g_free(chr->filename);
+    g_free(chr);
+    return NULL;
 }
 
 static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,
@@ -4111,7 +4227,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
         chr = qemu_chr_open_pipe(backend->pipe);
         break;
     case CHARDEV_BACKEND_KIND_SOCKET:
-        chr = qmp_chardev_open_socket(backend->socket, errp);
+        chr = qmp_chardev_open_socket(backend->socket, id, errp);
         break;
     case CHARDEV_BACKEND_KIND_UDP:
         chr = qmp_chardev_open_udp(backend->udp, errp);
diff --git a/qemu-options.hx b/qemu-options.hx
index 9918874..6acd400 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2030,7 +2030,7 @@ ETEXI
 DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev null,id=id[,mux=on|off]\n"
     "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n"
-    "         [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n"
+    "         [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off][,tls-creds=ID] (tcp)\n"
     "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n"
     "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
     "         [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
@@ -2103,7 +2103,7 @@ Options to each backend are described below.
 A void device. This device will not emit any data, and will drop any data it
 receives. The null backend does not take any options.
 
-@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}]
+@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}] [,tls-creds=@var{id}]
 
 Create a two-way stream socket, which can be either a TCP or a unix socket. A
 unix socket will be created if @option{path} is specified. Behaviour is
@@ -2121,6 +2121,11 @@ escape sequences.
 the remote end goes away.  qemu will delay this many seconds and then attempt
 to reconnect.  Zero disables reconnecting, and is the default.
 
+@option{tls-creds} requests enablement of the TLS protocol for encryption,
+and specifies the id of the TLS credentials to use for the handshake. The
+credentials must be previously created with the @option{-object tls-creds}
+argument.
+
 TCP and unix socket options are given below:
 
 @table @option
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 23/46] nbd: convert to use the QAPI SocketAddress object
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (21 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 22/46] char: introduce support for TLS encrypted TCP chardev backend Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 24/46] qemu-nbd: " Daniel P. Berrange
                   ` (22 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The nbd block driver currently uses a QemuOpts objects
when setting up sockets. Switch it over to use the
QAPI SocketAddress objects instead.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 block/nbd.c | 66 +++++++++++++++++++++++++++++++------------------------------
 1 file changed, 34 insertions(+), 32 deletions(-)

diff --git a/block/nbd.c b/block/nbd.c
index 2176186..f896146 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -43,7 +43,6 @@
 
 typedef struct BDRVNBDState {
     NbdClientSession client;
-    QemuOpts *socket_opts;
 } BDRVNBDState;
 
 static int nbd_parse_uri(const char *filename, QDict *options)
@@ -190,10 +189,10 @@ out:
     g_free(file);
 }
 
-static void nbd_config(BDRVNBDState *s, QDict *options, char **export,
-                       Error **errp)
+static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
+                                 Error **errp)
 {
-    Error *local_err = NULL;
+    SocketAddress *saddr;
 
     if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) {
         if (qdict_haskey(options, "path")) {
@@ -201,28 +200,34 @@ static void nbd_config(BDRVNBDState *s, QDict *options, char **export,
         } else {
             error_setg(errp, "one of path and host must be specified.");
         }
-        return;
+        return NULL;
     }
 
-    s->client.is_unix = qdict_haskey(options, "path");
-    s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
-                                      &error_abort);
+    saddr = g_new0(SocketAddress, 1);
 
-    qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
+    if (qdict_haskey(options, "path")) {
+        saddr->kind = SOCKET_ADDRESS_KIND_UNIX;
+        saddr->q_unix = g_new0(UnixSocketAddress, 1);
+        saddr->q_unix->path = g_strdup(qdict_get_str(options, "path"));
+    } else {
+        saddr->kind = SOCKET_ADDRESS_KIND_INET;
+        saddr->inet = g_new0(InetSocketAddress, 1);
+        saddr->inet->host = g_strdup(qdict_get_str(options, "host"));
+        if (!qdict_get_try_str(options, "port")) {
+            saddr->inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
+        } else {
+            saddr->inet->port = g_strdup(qdict_get_str(options, "port"));
+        }
     }
 
-    if (!qemu_opt_get(s->socket_opts, "port")) {
-        qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT,
-                            &error_abort);
-    }
+    s->client.is_unix = qdict_haskey(options, "path");
 
     *export = g_strdup(qdict_get_try_str(options, "export"));
     if (*export) {
         qdict_del(options, "export");
     }
+
+    return saddr;
 }
 
 NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
@@ -231,26 +236,24 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs)
     return &s->client;
 }
 
-static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
+static int nbd_establish_connection(BlockDriverState *bs,
+                                    SocketAddress *saddr,
+                                    Error **errp)
 {
     BDRVNBDState *s = bs->opaque;
     int sock;
 
-    if (s->client.is_unix) {
-        sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL);
-    } else {
-        sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL);
-        if (sock >= 0) {
-            socket_set_nodelay(sock);
-        }
-    }
+    sock = socket_connect(saddr, errp, NULL, NULL);
 
-    /* Failed to establish connection */
     if (sock < 0) {
         logout("Failed to establish connection to NBD server\n");
         return -EIO;
     }
 
+    if (!s->client.is_unix) {
+        socket_set_nodelay(sock);
+    }
+
     return sock;
 }
 
@@ -261,10 +264,11 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
     char *export = NULL;
     int result, sock;
     Error *local_err = NULL;
+    SocketAddress *saddr;
 
     /* Pop the config into our state object. Exit if invalid. */
-    nbd_config(s, options, &export, &local_err);
-    if (local_err) {
+    saddr = nbd_config(s, options, &export, &local_err);
+    if (!saddr) {
         error_propagate(errp, local_err);
         return -EINVAL;
     }
@@ -272,7 +276,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
     /* establish TCP connection, return error if it fails
      * TODO: Configurable retry-until-timeout behaviour.
      */
-    sock = nbd_establish_connection(bs, errp);
+    sock = nbd_establish_connection(bs, saddr, errp);
+    qapi_free_SocketAddress(saddr);
     if (sock < 0) {
         g_free(export);
         return sock;
@@ -315,9 +320,6 @@ static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
 
 static void nbd_close(BlockDriverState *bs)
 {
-    BDRVNBDState *s = bs->opaque;
-
-    qemu_opts_del(s->socket_opts);
     nbd_client_close(bs);
 }
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 24/46] qemu-nbd: convert to use the QAPI SocketAddress object
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (22 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 23/46] nbd: convert to use the QAPI SocketAddress object Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 25/46] sockets: remove use of QemuOpts from header file Daniel P. Berrange
                   ` (21 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The qemu-nbd program currently uses a QemuOpts objects
when setting up sockets. Switch it over to use the
QAPI SocketAddress objects instead.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qemu-nbd.c | 102 +++++++++++++++++++++++--------------------------------------
 1 file changed, 38 insertions(+), 64 deletions(-)

diff --git a/qemu-nbd.c b/qemu-nbd.c
index d9644b2..67ef811 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -49,7 +49,7 @@
 static NBDExport *exp;
 static int verbose;
 static char *srcpath;
-static char *sockpath;
+static SocketAddress *saddr;
 static int persistent = 0;
 static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state;
 static int shared = 1;
@@ -213,52 +213,6 @@ static void termsig_handler(int signum)
     qemu_notify_event();
 }
 
-static void combine_addr(char *buf, size_t len, const char* address,
-                         uint16_t port)
-{
-    /* If the address-part contains a colon, it's an IPv6 IP so needs [] */
-    if (strstr(address, ":")) {
-        snprintf(buf, len, "[%s]:%u", address, port);
-    } else {
-        snprintf(buf, len, "%s:%u", address, port);
-    }
-}
-
-static int tcp_socket_incoming(const char *address, uint16_t port)
-{
-    char address_and_port[128];
-    Error *local_err = NULL;
-
-    combine_addr(address_and_port, 128, address, port);
-    int fd = inet_listen(address_and_port, NULL, 0, SOCK_STREAM, 0, &local_err);
-
-    if (local_err != NULL) {
-        error_report_err(local_err);
-    }
-    return fd;
-}
-
-static int unix_socket_incoming(const char *path)
-{
-    Error *local_err = NULL;
-    int fd = unix_listen(path, NULL, 0, &local_err);
-
-    if (local_err != NULL) {
-        error_report_err(local_err);
-    }
-    return fd;
-}
-
-static int unix_socket_outgoing(const char *path)
-{
-    Error *local_err = NULL;
-    int fd = unix_connect(path, &local_err);
-
-    if (local_err != NULL) {
-        error_report_err(local_err);
-    }
-    return fd;
-}
 
 static void *show_parts(void *arg)
 {
@@ -287,8 +241,10 @@ static void *nbd_client_thread(void *arg)
     pthread_t show_parts_thread;
     Error *local_error = NULL;
 
-    sock = unix_socket_outgoing(sockpath);
+
+    sock = socket_connect(saddr, &local_error, NULL, NULL);
     if (sock < 0) {
+        error_report_err(local_error);
         goto out;
     }
 
@@ -399,6 +355,33 @@ static void nbd_update_server_fd_handler(int fd)
     }
 }
 
+
+static SocketAddress *nbd_build_socket_address(const char *sockpath,
+                                               const char *bindto,
+                                               const char *port)
+{
+    SocketAddress *saddr;
+
+    saddr = g_new0(SocketAddress, 1);
+    if (sockpath) {
+        saddr->kind = SOCKET_ADDRESS_KIND_UNIX;
+        saddr->q_unix = g_new0(UnixSocketAddress, 1);
+        saddr->q_unix->path = g_strdup(sockpath);
+    } else {
+        saddr->kind = SOCKET_ADDRESS_KIND_INET;
+        saddr->inet = g_new0(InetSocketAddress, 1);
+        saddr->inet->host = g_strdup(bindto);
+        if (port) {
+            saddr->inet->port = g_strdup(port);
+        } else  {
+            saddr->inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
+        }
+    }
+
+    return saddr;
+}
+
+
 int main(int argc, char **argv)
 {
     BlockBackend *blk;
@@ -407,8 +390,9 @@ int main(int argc, char **argv)
     uint32_t nbdflags = 0;
     bool disconnect = false;
     const char *bindto = "0.0.0.0";
+    const char *port = NULL;
+    char *sockpath = NULL;
     char *device = NULL;
-    int port = NBD_DEFAULT_PORT;
     off_t fd_size;
     QemuOpts *sn_opts = NULL;
     const char *sn_id_or_name = NULL;
@@ -441,7 +425,6 @@ int main(int argc, char **argv)
     };
     int ch;
     int opt_ind = 0;
-    int li;
     char *end;
     int flags = BDRV_O_RDWR;
     int partition = -1;
@@ -529,14 +512,7 @@ int main(int argc, char **argv)
             bindto = optarg;
             break;
         case 'p':
-            li = strtol(optarg, &end, 0);
-            if (*end) {
-                errx(EXIT_FAILURE, "Invalid port `%s'", optarg);
-            }
-            if (li < 1 || li > 65535) {
-                errx(EXIT_FAILURE, "Port out of range `%s'", optarg);
-            }
-            port = (uint16_t)li;
+            port = optarg;
             break;
         case 'o':
                 dev_offset = strtoll (optarg, &end, 0);
@@ -695,6 +671,8 @@ int main(int argc, char **argv)
         snprintf(sockpath, 128, SOCKET_PATH, basename(device));
     }
 
+    saddr = nbd_build_socket_address(sockpath, bindto, port);
+
     if (qemu_init_main_loop(&local_err)) {
         error_report_err(local_err);
         exit(EXIT_FAILURE);
@@ -752,13 +730,9 @@ int main(int argc, char **argv)
         errx(EXIT_FAILURE, "%s", error_get_pretty(local_err));
     }
 
-    if (sockpath) {
-        fd = unix_socket_incoming(sockpath);
-    } else {
-        fd = tcp_socket_incoming(bindto, port);
-    }
-
+    fd = socket_listen(saddr, &local_err);
     if (fd < 0) {
+        error_report_err(local_err);
         return 1;
     }
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 25/46] sockets: remove use of QemuOpts from header file
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (23 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 24/46] qemu-nbd: " Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 26/46] sockets: remove use of QemuOpts from socket_listen Daniel P. Berrange
                   ` (20 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

There are no callers of the sockets methods which accept
QemuOpts any more. Make all the QemuOpts related functions
static to avoid new callers being added, in preparation
for removal of all QemuOpts usage, in favour of QAPI
SocketAddress.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/qemu/sockets.h |  9 ---------
 util/qemu-sockets.c    | 22 +++++++++++-----------
 2 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 5a183c5..2741b97 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -30,8 +30,6 @@ int inet_aton(const char *cp, struct in_addr *ia);
 #include "qapi/error.h"
 #include "qapi-types.h"
 
-extern QemuOptsList socket_optslist;
-
 /* misc helpers */
 int qemu_socket(int domain, int type, int protocol);
 int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
@@ -56,23 +54,16 @@ int recv_all(int fd, void *buf, int len1, bool single_read);
 typedef void NonBlockingConnectHandler(int fd, Error *errp, void *opaque);
 
 InetSocketAddress *inet_parse(const char *str, Error **errp);
-int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp);
 int inet_listen(const char *str, char *ostr, int olen,
                 int socktype, int port_offset, Error **errp);
-int inet_connect_opts(QemuOpts *opts, Error **errp,
-                      NonBlockingConnectHandler *callback, void *opaque);
 int inet_connect(const char *str, Error **errp);
 int inet_nonblocking_connect(const char *str,
                              NonBlockingConnectHandler *callback,
                              void *opaque, Error **errp);
 
-int inet_dgram_opts(QemuOpts *opts, Error **errp);
 NetworkAddressFamily inet_netfamily(int family);
 
-int unix_listen_opts(QemuOpts *opts, Error **errp);
 int unix_listen(const char *path, char *ostr, int olen, Error **errp);
-int unix_connect_opts(QemuOpts *opts, Error **errp,
-                      NonBlockingConnectHandler *callback, void *opaque);
 int unix_connect(const char *path, Error **errp);
 int unix_nonblocking_connect(const char *str,
                              NonBlockingConnectHandler *callback,
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index f9d2f6e..938cdc2 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -37,7 +37,7 @@
 #endif
 
 /* used temporarily until all users are converted to QemuOpts */
-QemuOptsList socket_optslist = {
+static QemuOptsList socket_optslist = {
     .name = "socket",
     .head = QTAILQ_HEAD_INITIALIZER(socket_optslist.head),
     .desc = {
@@ -114,7 +114,7 @@ NetworkAddressFamily inet_netfamily(int family)
     return NETWORK_ADDRESS_FAMILY_UNKNOWN;
 }
 
-int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
+static int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
 {
     struct addrinfo ai,*res,*e;
     const char *addr;
@@ -392,8 +392,8 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
  * function succeeds, callback will be called when the connection
  * completes, with the file descriptor on success, or -1 on error.
  */
-int inet_connect_opts(QemuOpts *opts, Error **errp,
-                      NonBlockingConnectHandler *callback, void *opaque)
+static int inet_connect_opts(QemuOpts *opts, Error **errp,
+                             NonBlockingConnectHandler *callback, void *opaque)
 {
     Error *local_err = NULL;
     struct addrinfo *res, *e;
@@ -440,7 +440,7 @@ int inet_connect_opts(QemuOpts *opts, Error **errp,
     return sock;
 }
 
-int inet_dgram_opts(QemuOpts *opts, Error **errp)
+static int inet_dgram_opts(QemuOpts *opts, Error **errp)
 {
     struct addrinfo ai, *peer = NULL, *local = NULL;
     const char *addr;
@@ -705,7 +705,7 @@ int inet_nonblocking_connect(const char *str,
 
 #ifndef _WIN32
 
-int unix_listen_opts(QemuOpts *opts, Error **errp)
+static int unix_listen_opts(QemuOpts *opts, Error **errp)
 {
     struct sockaddr_un un;
     const char *path = qemu_opt_get(opts, "path");
@@ -770,8 +770,8 @@ err:
     return -1;
 }
 
-int unix_connect_opts(QemuOpts *opts, Error **errp,
-                      NonBlockingConnectHandler *callback, void *opaque)
+static int unix_connect_opts(QemuOpts *opts, Error **errp,
+                             NonBlockingConnectHandler *callback, void *opaque)
 {
     struct sockaddr_un un;
     const char *path = qemu_opt_get(opts, "path");
@@ -830,15 +830,15 @@ int unix_connect_opts(QemuOpts *opts, Error **errp,
 
 #else
 
-int unix_listen_opts(QemuOpts *opts, Error **errp)
+static int unix_listen_opts(QemuOpts *opts, Error **errp)
 {
     error_setg(errp, "unix sockets are not available on windows");
     errno = ENOTSUP;
     return -1;
 }
 
-int unix_connect_opts(QemuOpts *opts, Error **errp,
-                      NonBlockingConnectHandler *callback, void *opaque)
+static int unix_connect_opts(QemuOpts *opts, Error **errp,
+                             NonBlockingConnectHandler *callback, void *opaque)
 {
     error_setg(errp, "unix sockets are not available on windows");
     errno = ENOTSUP;
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 26/46] sockets: remove use of QemuOpts from socket_listen
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (24 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 25/46] sockets: remove use of QemuOpts from header file Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 27/46] sockets: remove use of QemuOpts from socket_connect Daniel P. Berrange
                   ` (19 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The socket_listen method accepts a QAPI SocketAddress object
which it then turns into QemuOpts before calling the
inet_listen_opts/unix_listen_opts helper methods. By
converting the latter to use QAPI SocketAddress directly,
the QemuOpts conversion step can be eliminated

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 util/qemu-sockets.c | 106 ++++++++++++++++++++++++++--------------------------
 1 file changed, 53 insertions(+), 53 deletions(-)

diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 938cdc2..e5ce7ec 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -114,36 +114,38 @@ NetworkAddressFamily inet_netfamily(int family)
     return NETWORK_ADDRESS_FAMILY_UNKNOWN;
 }
 
-static int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
+static int inet_listen_saddr(InetSocketAddress *saddr,
+                             int port_offset,
+                             bool update_addr,
+                             Error **errp)
 {
     struct addrinfo ai,*res,*e;
-    const char *addr;
     char port[33];
     char uaddr[INET6_ADDRSTRLEN+1];
     char uport[33];
-    int slisten, rc, to, port_min, port_max, p;
+    int slisten, rc, port_min, port_max, p;
 
     memset(&ai,0, sizeof(ai));
     ai.ai_flags = AI_PASSIVE;
     ai.ai_family = PF_UNSPEC;
     ai.ai_socktype = SOCK_STREAM;
 
-    if ((qemu_opt_get(opts, "host") == NULL)) {
+    if (saddr->host == NULL) {
         error_setg(errp, "host not specified");
         return -1;
     }
-    if (qemu_opt_get(opts, "port") != NULL) {
-        pstrcpy(port, sizeof(port), qemu_opt_get(opts, "port"));
+    if (saddr->port != NULL) {
+        pstrcpy(port, sizeof(port), saddr->port);
     } else {
         port[0] = '\0';
     }
-    addr = qemu_opt_get(opts, "host");
 
-    to = qemu_opt_get_number(opts, "to", 0);
-    if (qemu_opt_get_bool(opts, "ipv4", 0))
+    if (saddr->has_ipv4 && saddr->ipv4) {
         ai.ai_family = PF_INET;
-    if (qemu_opt_get_bool(opts, "ipv6", 0))
+    }
+    if (saddr->has_ipv6 && saddr->ipv6) {
         ai.ai_family = PF_INET6;
+    }
 
     /* lookup */
     if (port_offset) {
@@ -163,11 +165,11 @@ static int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
         }
         snprintf(port, sizeof(port), "%d", (int)baseport + port_offset);
     }
-    rc = getaddrinfo(strlen(addr) ? addr : NULL,
+    rc = getaddrinfo(saddr->host ? saddr->host : NULL,
                      strlen(port) ? port : NULL, &ai, &res);
     if (rc != 0) {
-        error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
-                   gai_strerror(rc));
+        error_setg(errp, "address resolution failed for %s:%s: %s",
+                   saddr->host, port, gai_strerror(rc));
         return -1;
     }
 
@@ -195,7 +197,7 @@ static int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp)
 #endif
 
         port_min = inet_getport(e);
-        port_max = to ? to + port_offset : port_min;
+        port_max = saddr->has_to ? saddr->to + port_offset : port_min;
         for (p = port_min; p <= port_max; p++) {
             inet_setport(e, p);
             if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
@@ -219,13 +221,15 @@ listen:
         freeaddrinfo(res);
         return -1;
     }
-    qemu_opt_set(opts, "host", uaddr, &error_abort);
-    qemu_opt_set_number(opts, "port", inet_getport(e) - port_offset,
-                        &error_abort);
-    qemu_opt_set_bool(opts, "ipv6", e->ai_family == PF_INET6,
-                      &error_abort);
-    qemu_opt_set_bool(opts, "ipv4", e->ai_family != PF_INET6,
-                      &error_abort);
+    if (update_addr) {
+        g_free(saddr->host);
+        saddr->host = g_strdup(uaddr);
+        g_free(saddr->port);
+        saddr->port = g_strdup_printf("%d",
+                                      inet_getport(e) - port_offset);
+        saddr->has_ipv6 = saddr->ipv6 = e->ai_family == PF_INET6;
+        saddr->has_ipv4 = saddr->ipv4 = e->ai_family != PF_INET6;
+    }
     freeaddrinfo(res);
     return slisten;
 }
@@ -614,32 +618,28 @@ static void inet_addr_to_opts(QemuOpts *opts, const InetSocketAddress *addr)
 int inet_listen(const char *str, char *ostr, int olen,
                 int socktype, int port_offset, Error **errp)
 {
-    QemuOpts *opts;
     char *optstr;
     int sock = -1;
     InetSocketAddress *addr;
 
     addr = inet_parse(str, errp);
     if (addr != NULL) {
-        opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-        inet_addr_to_opts(opts, addr);
-        qapi_free_InetSocketAddress(addr);
-        sock = inet_listen_opts(opts, port_offset, errp);
+        sock = inet_listen_saddr(addr, port_offset, true, errp);
         if (sock != -1 && ostr) {
             optstr = strchr(str, ',');
-            if (qemu_opt_get_bool(opts, "ipv6", 0)) {
+            if (addr->ipv6) {
                 snprintf(ostr, olen, "[%s]:%s%s",
-                         qemu_opt_get(opts, "host"),
-                         qemu_opt_get(opts, "port"),
+                         addr->host,
+                         addr->port,
                          optstr ? optstr : "");
             } else {
                 snprintf(ostr, olen, "%s:%s%s",
-                         qemu_opt_get(opts, "host"),
-                         qemu_opt_get(opts, "port"),
+                         addr->host,
+                         addr->port,
                          optstr ? optstr : "");
             }
         }
-        qemu_opts_del(opts);
+        qapi_free_InetSocketAddress(addr);
     }
     return sock;
 }
@@ -705,10 +705,11 @@ int inet_nonblocking_connect(const char *str,
 
 #ifndef _WIN32
 
-static int unix_listen_opts(QemuOpts *opts, Error **errp)
+static int unix_listen_saddr(UnixSocketAddress *saddr,
+                             bool update_addr,
+                             Error **errp)
 {
     struct sockaddr_un un;
-    const char *path = qemu_opt_get(opts, "path");
     int sock, fd;
 
     sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
@@ -719,8 +720,8 @@ static int unix_listen_opts(QemuOpts *opts, Error **errp)
 
     memset(&un, 0, sizeof(un));
     un.sun_family = AF_UNIX;
-    if (path && strlen(path)) {
-        snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+    if (saddr->path && strlen(saddr->path)) {
+        snprintf(un.sun_path, sizeof(un.sun_path), "%s", saddr->path);
     } else {
         const char *tmpdir = getenv("TMPDIR");
         tmpdir = tmpdir ? tmpdir : "/tmp";
@@ -745,7 +746,10 @@ static int unix_listen_opts(QemuOpts *opts, Error **errp)
             goto err;
         }
         close(fd);
-        qemu_opt_set(opts, "path", un.sun_path, &error_abort);
+        if (update_addr) {
+            g_free(saddr->path);
+            saddr->path = g_strdup(un.sun_path);
+        }
     }
 
     if ((access(un.sun_path, F_OK) == 0) &&
@@ -830,7 +834,9 @@ static int unix_connect_opts(QemuOpts *opts, Error **errp,
 
 #else
 
-static int unix_listen_opts(QemuOpts *opts, Error **errp)
+static int unix_listen_saddr(UnixSocketAddress *saddr,
+                             bool update_addr,
+                             Error **errp)
 {
     error_setg(errp, "unix sockets are not available on windows");
     errno = ENOTSUP;
@@ -849,11 +855,11 @@ static int unix_connect_opts(QemuOpts *opts, Error **errp,
 /* compatibility wrapper */
 int unix_listen(const char *str, char *ostr, int olen, Error **errp)
 {
-    QemuOpts *opts;
     char *path, *optstr;
     int sock, len;
+    UnixSocketAddress *saddr;
 
-    opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
+    saddr = g_new0(UnixSocketAddress, 1);
 
     optstr = strchr(str, ',');
     if (optstr) {
@@ -861,18 +867,17 @@ int unix_listen(const char *str, char *ostr, int olen, Error **errp)
         if (len) {
             path = g_malloc(len+1);
             snprintf(path, len+1, "%.*s", len, str);
-            qemu_opt_set(opts, "path", path, &error_abort);
-            g_free(path);
+            saddr->path = path;
         }
     } else {
-        qemu_opt_set(opts, "path", str, &error_abort);
+        saddr->path = g_strdup(str);
     }
 
-    sock = unix_listen_opts(opts, errp);
+    sock = unix_listen_saddr(saddr, true, errp);
 
     if (sock != -1 && ostr)
-        snprintf(ostr, olen, "%s%s", qemu_opt_get(opts, "path"), optstr ? optstr : "");
-    qemu_opts_del(opts);
+        snprintf(ostr, olen, "%s%s", saddr->path, optstr ? optstr : "");
+    qapi_free_UnixSocketAddress(saddr);
     return sock;
 }
 
@@ -977,19 +982,15 @@ int socket_connect(SocketAddress *addr, Error **errp,
 
 int socket_listen(SocketAddress *addr, Error **errp)
 {
-    QemuOpts *opts;
     int fd;
 
-    opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
     switch (addr->kind) {
     case SOCKET_ADDRESS_KIND_INET:
-        inet_addr_to_opts(opts, addr->inet);
-        fd = inet_listen_opts(opts, 0, errp);
+        fd = inet_listen_saddr(addr->inet, 0, false, errp);
         break;
 
     case SOCKET_ADDRESS_KIND_UNIX:
-        qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort);
-        fd = unix_listen_opts(opts, errp);
+        fd = unix_listen_saddr(addr->q_unix, false, errp);
         break;
 
     case SOCKET_ADDRESS_KIND_FD:
@@ -999,7 +1000,6 @@ int socket_listen(SocketAddress *addr, Error **errp)
     default:
         abort();
     }
-    qemu_opts_del(opts);
     return fd;
 }
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 27/46] sockets: remove use of QemuOpts from socket_connect
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (25 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 26/46] sockets: remove use of QemuOpts from socket_listen Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 28/46] sockets: remove use of QemuOpts from socket_dgram Daniel P. Berrange
                   ` (18 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The socket_connect method accepts a QAPI SocketAddress object
which it then turns into QemuOpts before calling the
inet_connect_opts/unix_connect_opts helper methods. By
converting the latter to use QAPI SocketAddress directly,
the QemuOpts conversion step can be eliminated

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 util/qemu-sockets.c | 82 +++++++++++++++++++++--------------------------------
 1 file changed, 32 insertions(+), 50 deletions(-)

diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index e5ce7ec..866ef05 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -344,12 +344,11 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
     return sock;
 }
 
-static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
+static struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr,
+                                                 Error **errp)
 {
     struct addrinfo ai, *res;
     int rc;
-    const char *addr;
-    const char *port;
 
     memset(&ai, 0, sizeof(ai));
 
@@ -357,25 +356,23 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
     ai.ai_family = PF_UNSPEC;
     ai.ai_socktype = SOCK_STREAM;
 
-    addr = qemu_opt_get(opts, "host");
-    port = qemu_opt_get(opts, "port");
-    if (addr == NULL || port == NULL) {
+    if (saddr->host == NULL || saddr->port == NULL) {
         error_setg(errp, "host and/or port not specified");
         return NULL;
     }
 
-    if (qemu_opt_get_bool(opts, "ipv4", 0)) {
+    if (saddr->has_ipv4 && saddr->ipv4) {
         ai.ai_family = PF_INET;
     }
-    if (qemu_opt_get_bool(opts, "ipv6", 0)) {
+    if (saddr->has_ipv6 && saddr->ipv6) {
         ai.ai_family = PF_INET6;
     }
 
     /* lookup */
-    rc = getaddrinfo(addr, port, &ai, &res);
+    rc = getaddrinfo(saddr->host, saddr->port, &ai, &res);
     if (rc != 0) {
-        error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
-                   gai_strerror(rc));
+        error_setg(errp, "address resolution failed for %s:%s: %s",
+                   saddr->host, saddr->port, gai_strerror(rc));
         return NULL;
     }
     return res;
@@ -384,8 +381,7 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
 /**
  * Create a socket and connect it to an address.
  *
- * @opts: QEMU options, recognized parameters strings "host" and "port",
- *        bools "ipv4" and "ipv6".
+ * @saddr: Inet socket address specification
  * @errp: set on error
  * @callback: callback function for non-blocking connect
  * @opaque: opaque for callback function
@@ -396,8 +392,8 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
  * function succeeds, callback will be called when the connection
  * completes, with the file descriptor on success, or -1 on error.
  */
-static int inet_connect_opts(QemuOpts *opts, Error **errp,
-                             NonBlockingConnectHandler *callback, void *opaque)
+static int inet_connect_saddr(InetSocketAddress *saddr, Error **errp,
+                              NonBlockingConnectHandler *callback, void *opaque)
 {
     Error *local_err = NULL;
     struct addrinfo *res, *e;
@@ -405,7 +401,7 @@ static int inet_connect_opts(QemuOpts *opts, Error **errp,
     bool in_progress;
     ConnectState *connect_state = NULL;
 
-    res = inet_parse_connect_opts(opts, errp);
+    res = inet_parse_connect_saddr(saddr, errp);
     if (!res) {
         return -1;
     }
@@ -654,17 +650,13 @@ int inet_listen(const char *str, char *ostr, int olen,
  **/
 int inet_connect(const char *str, Error **errp)
 {
-    QemuOpts *opts;
     int sock = -1;
     InetSocketAddress *addr;
 
     addr = inet_parse(str, errp);
     if (addr != NULL) {
-        opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-        inet_addr_to_opts(opts, addr);
+        sock = inet_connect_saddr(addr, errp, NULL, NULL);
         qapi_free_InetSocketAddress(addr);
-        sock = inet_connect_opts(opts, errp, NULL, NULL);
-        qemu_opts_del(opts);
     }
     return sock;
 }
@@ -686,7 +678,6 @@ int inet_nonblocking_connect(const char *str,
                              NonBlockingConnectHandler *callback,
                              void *opaque, Error **errp)
 {
-    QemuOpts *opts;
     int sock = -1;
     InetSocketAddress *addr;
 
@@ -694,11 +685,8 @@ int inet_nonblocking_connect(const char *str,
 
     addr = inet_parse(str, errp);
     if (addr != NULL) {
-        opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-        inet_addr_to_opts(opts, addr);
+        sock = inet_connect_saddr(addr, errp, callback, opaque);
         qapi_free_InetSocketAddress(addr);
-        sock = inet_connect_opts(opts, errp, callback, opaque);
-        qemu_opts_del(opts);
     }
     return sock;
 }
@@ -774,15 +762,14 @@ err:
     return -1;
 }
 
-static int unix_connect_opts(QemuOpts *opts, Error **errp,
-                             NonBlockingConnectHandler *callback, void *opaque)
+static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp,
+                              NonBlockingConnectHandler *callback, void *opaque)
 {
     struct sockaddr_un un;
-    const char *path = qemu_opt_get(opts, "path");
     ConnectState *connect_state = NULL;
     int sock, rc;
 
-    if (path == NULL) {
+    if (saddr->path == NULL) {
         error_setg(errp, "unix connect: no path specified");
         return -1;
     }
@@ -801,7 +788,7 @@ static int unix_connect_opts(QemuOpts *opts, Error **errp,
 
     memset(&un, 0, sizeof(un));
     un.sun_family = AF_UNIX;
-    snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+    snprintf(un.sun_path, sizeof(un.sun_path), "%s", saddr->path);
 
     /* connect to peer */
     do {
@@ -843,8 +830,8 @@ static int unix_listen_saddr(UnixSocketAddress *saddr,
     return -1;
 }
 
-static int unix_connect_opts(QemuOpts *opts, Error **errp,
-                             NonBlockingConnectHandler *callback, void *opaque)
+static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp,
+                              NonBlockingConnectHandler *callback, void *opaque)
 {
     error_setg(errp, "unix sockets are not available on windows");
     errno = ENOTSUP;
@@ -883,13 +870,13 @@ int unix_listen(const char *str, char *ostr, int olen, Error **errp)
 
 int unix_connect(const char *path, Error **errp)
 {
-    QemuOpts *opts;
+    UnixSocketAddress *saddr;
     int sock;
 
-    opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-    qemu_opt_set(opts, "path", path, &error_abort);
-    sock = unix_connect_opts(opts, errp, NULL, NULL);
-    qemu_opts_del(opts);
+    saddr = g_new0(UnixSocketAddress, 1);
+    saddr->path = g_strdup(path);
+    sock = unix_connect_saddr(saddr, errp, NULL, NULL);
+    qapi_free_UnixSocketAddress(saddr);
     return sock;
 }
 
@@ -898,15 +885,15 @@ int unix_nonblocking_connect(const char *path,
                              NonBlockingConnectHandler *callback,
                              void *opaque, Error **errp)
 {
-    QemuOpts *opts;
+    UnixSocketAddress *saddr;
     int sock = -1;
 
     g_assert(callback != NULL);
 
-    opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-    qemu_opt_set(opts, "path", path, &error_abort);
-    sock = unix_connect_opts(opts, errp, callback, opaque);
-    qemu_opts_del(opts);
+    saddr = g_new0(UnixSocketAddress, 1);
+    saddr->path = g_strdup(path);
+    sock = unix_connect_saddr(saddr, errp, callback, opaque);
+    qapi_free_UnixSocketAddress(saddr);
     return sock;
 }
 
@@ -950,19 +937,15 @@ fail:
 int socket_connect(SocketAddress *addr, Error **errp,
                    NonBlockingConnectHandler *callback, void *opaque)
 {
-    QemuOpts *opts;
     int fd;
 
-    opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
     switch (addr->kind) {
     case SOCKET_ADDRESS_KIND_INET:
-        inet_addr_to_opts(opts, addr->inet);
-        fd = inet_connect_opts(opts, errp, callback, opaque);
+        fd = inet_connect_saddr(addr->inet, errp, callback, opaque);
         break;
 
     case SOCKET_ADDRESS_KIND_UNIX:
-        qemu_opt_set(opts, "path", addr->q_unix->path, &error_abort);
-        fd = unix_connect_opts(opts, errp, callback, opaque);
+        fd = unix_connect_saddr(addr->q_unix, errp, callback, opaque);
         break;
 
     case SOCKET_ADDRESS_KIND_FD:
@@ -976,7 +959,6 @@ int socket_connect(SocketAddress *addr, Error **errp,
     default:
         abort();
     }
-    qemu_opts_del(opts);
     return fd;
 }
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 28/46] sockets: remove use of QemuOpts from socket_dgram
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (26 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 27/46] sockets: remove use of QemuOpts from socket_connect Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 29/46] migration: remove use of qemu_bufopen from vmstate tests Daniel P. Berrange
                   ` (17 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The socket_dgram method accepts a QAPI SocketAddress object
which it then turns into QemuOpts before calling the
inet_dgram_opts helper method. By converting the latter to
use QAPI SocketAddress directly, the QemuOpts conversion
step can be eliminated.

This removes the very last use of QemuOpts from the
sockets code, so the socket_optslist[] array is also
removed.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/qemu/sockets.h |  1 -
 util/qemu-sockets.c    | 89 ++++++++++++--------------------------------------
 2 files changed, 21 insertions(+), 69 deletions(-)

diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 2741b97..bf7154c 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -26,7 +26,6 @@ int inet_aton(const char *cp, struct in_addr *ia);
 
 #endif /* !_WIN32 */
 
-#include "qemu/option.h"
 #include "qapi/error.h"
 #include "qapi-types.h"
 
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 866ef05..c1a37d2 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -36,39 +36,6 @@
 # define AI_V4MAPPED 0
 #endif
 
-/* used temporarily until all users are converted to QemuOpts */
-static QemuOptsList socket_optslist = {
-    .name = "socket",
-    .head = QTAILQ_HEAD_INITIALIZER(socket_optslist.head),
-    .desc = {
-        {
-            .name = "path",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "host",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "port",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "localaddr",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "localport",
-            .type = QEMU_OPT_STRING,
-        },{
-            .name = "to",
-            .type = QEMU_OPT_NUMBER,
-        },{
-            .name = "ipv4",
-            .type = QEMU_OPT_BOOL,
-        },{
-            .name = "ipv6",
-            .type = QEMU_OPT_BOOL,
-        },
-        { /* end if list */ }
-    },
-};
 
 static int inet_getport(struct addrinfo *e)
 {
@@ -440,7 +407,9 @@ static int inet_connect_saddr(InetSocketAddress *saddr, Error **errp,
     return sock;
 }
 
-static int inet_dgram_opts(QemuOpts *opts, Error **errp)
+static int inet_dgram_saddr(InetSocketAddress *sraddr,
+                            InetSocketAddress *sladdr,
+                            Error **errp)
 {
     struct addrinfo ai, *peer = NULL, *local = NULL;
     const char *addr;
@@ -453,8 +422,8 @@ static int inet_dgram_opts(QemuOpts *opts, Error **errp)
     ai.ai_family = PF_UNSPEC;
     ai.ai_socktype = SOCK_DGRAM;
 
-    addr = qemu_opt_get(opts, "host");
-    port = qemu_opt_get(opts, "port");
+    addr = sraddr->host;
+    port = sraddr->port;
     if (addr == NULL || strlen(addr) == 0) {
         addr = "localhost";
     }
@@ -463,10 +432,12 @@ static int inet_dgram_opts(QemuOpts *opts, Error **errp)
         return -1;
     }
 
-    if (qemu_opt_get_bool(opts, "ipv4", 0))
+    if (sraddr->has_ipv4 && sraddr->ipv4) {
         ai.ai_family = PF_INET;
-    if (qemu_opt_get_bool(opts, "ipv6", 0))
+    }
+    if (sraddr->has_ipv6 && sraddr->ipv6) {
         ai.ai_family = PF_INET6;
+    }
 
     if (0 != (rc = getaddrinfo(addr, port, &ai, &peer))) {
         error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
@@ -480,13 +451,19 @@ static int inet_dgram_opts(QemuOpts *opts, Error **errp)
     ai.ai_family = peer->ai_family;
     ai.ai_socktype = SOCK_DGRAM;
 
-    addr = qemu_opt_get(opts, "localaddr");
-    port = qemu_opt_get(opts, "localport");
-    if (addr == NULL || strlen(addr) == 0) {
+    if (sladdr) {
+        addr = sladdr->host;
+        port = sladdr->port;
+        if (addr == NULL || strlen(addr) == 0) {
+            addr = NULL;
+        }
+        if (!port || strlen(port) == 0) {
+            port = "0";
+        }
+    } else {
         addr = NULL;
-    }
-    if (!port || strlen(port) == 0)
         port = "0";
+    }
 
     if (0 != (rc = getaddrinfo(addr, port, &ai, &local))) {
         error_setg(errp, "address resolution failed for %s:%s: %s", addr, port,
@@ -595,22 +572,6 @@ fail:
     return NULL;
 }
 
-static void inet_addr_to_opts(QemuOpts *opts, const InetSocketAddress *addr)
-{
-    bool ipv4 = addr->ipv4 || !addr->has_ipv4;
-    bool ipv6 = addr->ipv6 || !addr->has_ipv6;
-
-    if (!ipv4 || !ipv6) {
-        qemu_opt_set_bool(opts, "ipv4", ipv4, &error_abort);
-        qemu_opt_set_bool(opts, "ipv6", ipv6, &error_abort);
-    }
-    if (addr->has_to) {
-        qemu_opt_set_number(opts, "to", addr->to, &error_abort);
-    }
-    qemu_opt_set(opts, "host", addr->host, &error_abort);
-    qemu_opt_set(opts, "port", addr->port, &error_abort);
-}
-
 int inet_listen(const char *str, char *ostr, int olen,
                 int socktype, int port_offset, Error **errp)
 {
@@ -987,25 +948,17 @@ int socket_listen(SocketAddress *addr, Error **errp)
 
 int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp)
 {
-    QemuOpts *opts;
     int fd;
 
-    opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
     switch (remote->kind) {
     case SOCKET_ADDRESS_KIND_INET:
-        inet_addr_to_opts(opts, remote->inet);
-        if (local) {
-            qemu_opt_set(opts, "localaddr", local->inet->host, &error_abort);
-            qemu_opt_set(opts, "localport", local->inet->port, &error_abort);
-        }
-        fd = inet_dgram_opts(opts, errp);
+        fd = inet_dgram_saddr(remote->inet, local ? local->inet : NULL, errp);
         break;
 
     default:
         error_setg(errp, "socket type unsupported for datagram");
         fd = -1;
     }
-    qemu_opts_del(opts);
     return fd;
 }
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 29/46] migration: remove use of qemu_bufopen from vmstate tests
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (27 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 28/46] sockets: remove use of QemuOpts from socket_dgram Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-07 16:08   ` Dr. David Alan Gilbert
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 30/46] migration: remove memory buffer based QEMUFile backend Daniel P. Berrange
                   ` (16 subsequent siblings)
  45 siblings, 1 reply; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The test-vmstate.c file is the only remaining user of the
qemu_bufopen function. Half of the test cases already use
a temporary file, so convert the remaining cases to match

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 tests/Makefile       |  2 +-
 tests/test-vmstate.c | 44 +++++++++++++-------------------------------
 2 files changed, 14 insertions(+), 32 deletions(-)

diff --git a/tests/Makefile b/tests/Makefile
index cbcec26..3d1e83c 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -320,7 +320,7 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
 	hw/core/fw-path-provider.o \
 	$(test-qapi-obj-y)
 tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
-	migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \
+	migration/vmstate.o migration/qemu-file.o \
         migration/qemu-file-unix.o qjson.o \
 	$(test-qom-obj-y)
 
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
index 4d13bd0..0f943d5 100644
--- a/tests/test-vmstate.c
+++ b/tests/test-vmstate.c
@@ -43,11 +43,6 @@ void yield_until_fd_readable(int fd)
     select(fd + 1, &fds, NULL, NULL, NULL);
 }
 
-/*
- * Some tests use 'open_test_file' to work on a real fd, some use
- * an in memory file (QEMUSizedBuffer+qemu_bufopen); we could pick one
- * but this way we test both.
- */
 
 /* Duplicate temp_fd and seek to the beginning of the file */
 static QEMUFile *open_test_file(bool write)
@@ -60,20 +55,6 @@ static QEMUFile *open_test_file(bool write)
     return qemu_fdopen(fd, write ? "wb" : "rb");
 }
 
-/*
- * Check that the contents of the memory-buffered file f match
- * the given size/data.
- */
-static void check_mem_file(QEMUFile *f, void *data, size_t size)
-{
-    uint8_t *result = g_malloc(size);
-    const QEMUSizedBuffer *qsb = qemu_buf_get(f);
-    g_assert_cmpint(qsb_get_length(qsb), ==, size);
-    g_assert_cmpint(qsb_get_buffer(qsb, 0, size, result), ==, size);
-    g_assert_cmpint(memcmp(result, data, size), ==, 0);
-    g_free(result);
-}
-
 #define SUCCESS(val) \
     g_assert_cmpint((val), ==, 0)
 
@@ -391,7 +372,7 @@ static const VMStateDescription vmstate_skipping = {
 
 static void test_save_noskip(void)
 {
-    QEMUFile *fsave = qemu_bufopen("w", NULL);
+    QEMUFile *fsave = open_test_file(true);
     TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
                        .skip_c_e = false };
     vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
@@ -405,13 +386,14 @@ static void test_save_noskip(void)
         0, 0, 0, 5,             /* e */
         0, 0, 0, 0, 0, 0, 0, 6, /* f */
     };
-    check_mem_file(fsave, expected, sizeof(expected));
+
     qemu_fclose(fsave);
+    compare_vmstate(expected, sizeof(expected));
 }
 
 static void test_save_skip(void)
 {
-    QEMUFile *fsave = qemu_bufopen("w", NULL);
+    QEMUFile *fsave = open_test_file(true);
     TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
                        .skip_c_e = true };
     vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
@@ -423,13 +405,14 @@ static void test_save_skip(void)
         0, 0, 0, 0, 0, 0, 0, 4, /* d */
         0, 0, 0, 0, 0, 0, 0, 6, /* f */
     };
-    check_mem_file(fsave, expected, sizeof(expected));
 
     qemu_fclose(fsave);
+    compare_vmstate(expected, sizeof(expected));
 }
 
 static void test_load_noskip(void)
 {
+    QEMUFile *fsave = open_test_file(true);
     uint8_t buf[] = {
         0, 0, 0, 10,             /* a */
         0, 0, 0, 20,             /* b */
@@ -439,10 +422,10 @@ static void test_load_noskip(void)
         0, 0, 0, 0, 0, 0, 0, 60, /* f */
         QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
     };
+    qemu_put_buffer(fsave, buf, sizeof(buf));
+    qemu_fclose(fsave);
 
-    QEMUSizedBuffer *qsb = qsb_create(buf, sizeof(buf));
-    g_assert(qsb);
-    QEMUFile *loading = qemu_bufopen("r", qsb);
+    QEMUFile *loading = open_test_file(false);
     TestStruct obj = { .skip_c_e = false };
     vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
     g_assert(!qemu_file_get_error(loading));
@@ -453,11 +436,11 @@ static void test_load_noskip(void)
     g_assert_cmpint(obj.e, ==, 50);
     g_assert_cmpint(obj.f, ==, 60);
     qemu_fclose(loading);
-    qsb_free(qsb);
 }
 
 static void test_load_skip(void)
 {
+    QEMUFile *fsave = open_test_file(true);
     uint8_t buf[] = {
         0, 0, 0, 10,             /* a */
         0, 0, 0, 20,             /* b */
@@ -465,10 +448,10 @@ static void test_load_skip(void)
         0, 0, 0, 0, 0, 0, 0, 60, /* f */
         QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
     };
+    qemu_put_buffer(fsave, buf, sizeof(buf));
+    qemu_fclose(fsave);
 
-    QEMUSizedBuffer *qsb = qsb_create(buf, sizeof(buf));
-    g_assert(qsb);
-    QEMUFile *loading = qemu_bufopen("r", qsb);
+    QEMUFile *loading = open_test_file(false);
     TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
     vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
     g_assert(!qemu_file_get_error(loading));
@@ -479,7 +462,6 @@ static void test_load_skip(void)
     g_assert_cmpint(obj.e, ==, 500);
     g_assert_cmpint(obj.f, ==, 60);
     qemu_fclose(loading);
-    qsb_free(qsb);
 }
 
 int main(int argc, char **argv)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 30/46] migration: remove memory buffer based QEMUFile backend
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (28 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 29/46] migration: remove use of qemu_bufopen from vmstate tests Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 31/46] migration: move definition of struct QEMUFile back into qemu-file.c Daniel P. Berrange
                   ` (15 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The qemu_bufopen() method is no longer used, so the memory
buffer based QEMUFile backend can be deleted entirely.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/qemu-file.h |  15 --
 migration/Makefile.objs       |   2 +-
 migration/qemu-file-buf.c     | 462 ------------------------------------------
 3 files changed, 1 insertion(+), 478 deletions(-)
 delete mode 100644 migration/qemu-file-buf.c

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index ea49f33..c020b01 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -121,7 +121,6 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode);
 QEMUFile *qemu_fdopen(int fd, const char *mode);
 QEMUFile *qemu_fopen_socket(int fd, const char *mode);
 QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
-QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input);
 int qemu_get_fd(QEMUFile *f);
 int qemu_fclose(QEMUFile *f);
 int64_t qemu_ftell(QEMUFile *f);
@@ -136,20 +135,6 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size);
 bool qemu_file_mode_is_not_valid(const char *mode);
 bool qemu_file_is_writable(QEMUFile *f);
 
-QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len);
-void qsb_free(QEMUSizedBuffer *);
-size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t length);
-size_t qsb_get_length(const QEMUSizedBuffer *qsb);
-ssize_t qsb_get_buffer(const QEMUSizedBuffer *, off_t start, size_t count,
-                       uint8_t *buf);
-ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf,
-                     off_t pos, size_t count);
-
-
-/*
- * For use on files opened with qemu_bufopen
- */
-const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f);
 
 static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
 {
diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index d929e96..ce1e3c7 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,6 +1,6 @@
 common-obj-y += migration.o tcp.o
 common-obj-y += vmstate.o
-common-obj-y += qemu-file.o qemu-file-buf.o qemu-file-unix.o qemu-file-stdio.o
+common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
 common-obj-y += xbzrle.o
 
 common-obj-$(CONFIG_RDMA) += rdma.o
diff --git a/migration/qemu-file-buf.c b/migration/qemu-file-buf.c
deleted file mode 100644
index 556f5dc..0000000
--- a/migration/qemu-file-buf.c
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- * Copyright (c) 2014 IBM Corp.
- *
- * Authors:
- *  Stefan Berger <stefanb@linux.vnet.ibm.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "qemu-common.h"
-#include "qemu/error-report.h"
-#include "qemu/iov.h"
-#include "qemu/sockets.h"
-#include "qemu/coroutine.h"
-#include "migration/migration.h"
-#include "migration/qemu-file.h"
-#include "migration/qemu-file-internal.h"
-#include "trace.h"
-
-#define QSB_CHUNK_SIZE      (1 << 10)
-#define QSB_MAX_CHUNK_SIZE  (16 * QSB_CHUNK_SIZE)
-
-/**
- * Create a QEMUSizedBuffer
- * This type of buffer uses scatter-gather lists internally and
- * can grow to any size. Any data array in the scatter-gather list
- * can hold different amount of bytes.
- *
- * @buffer: Optional buffer to copy into the QSB
- * @len: size of initial buffer; if @buffer is given, buffer must
- *       hold at least len bytes
- *
- * Returns a pointer to a QEMUSizedBuffer or NULL on allocation failure
- */
-QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len)
-{
-    QEMUSizedBuffer *qsb;
-    size_t alloc_len, num_chunks, i, to_copy;
-    size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE)
-                        ? QSB_MAX_CHUNK_SIZE
-                        : QSB_CHUNK_SIZE;
-
-    num_chunks = DIV_ROUND_UP(len ? len : QSB_CHUNK_SIZE, chunk_size);
-    alloc_len = num_chunks * chunk_size;
-
-    qsb = g_try_new0(QEMUSizedBuffer, 1);
-    if (!qsb) {
-        return NULL;
-    }
-
-    qsb->iov = g_try_new0(struct iovec, num_chunks);
-    if (!qsb->iov) {
-        g_free(qsb);
-        return NULL;
-    }
-
-    qsb->n_iov = num_chunks;
-
-    for (i = 0; i < num_chunks; i++) {
-        qsb->iov[i].iov_base = g_try_malloc0(chunk_size);
-        if (!qsb->iov[i].iov_base) {
-            /* qsb_free is safe since g_free can cope with NULL */
-            qsb_free(qsb);
-            return NULL;
-        }
-
-        qsb->iov[i].iov_len = chunk_size;
-        if (buffer) {
-            to_copy = (len - qsb->used) > chunk_size
-                      ? chunk_size : (len - qsb->used);
-            memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy);
-            qsb->used += to_copy;
-        }
-    }
-
-    qsb->size = alloc_len;
-
-    return qsb;
-}
-
-/**
- * Free the QEMUSizedBuffer
- *
- * @qsb: The QEMUSizedBuffer to free
- */
-void qsb_free(QEMUSizedBuffer *qsb)
-{
-    size_t i;
-
-    if (!qsb) {
-        return;
-    }
-
-    for (i = 0; i < qsb->n_iov; i++) {
-        g_free(qsb->iov[i].iov_base);
-    }
-    g_free(qsb->iov);
-    g_free(qsb);
-}
-
-/**
- * Get the number of used bytes in the QEMUSizedBuffer
- *
- * @qsb: A QEMUSizedBuffer
- *
- * Returns the number of bytes currently used in this buffer
- */
-size_t qsb_get_length(const QEMUSizedBuffer *qsb)
-{
-    return qsb->used;
-}
-
-/**
- * Set the length of the buffer; the primary usage of this
- * function is to truncate the number of used bytes in the buffer.
- * The size will not be extended beyond the current number of
- * allocated bytes in the QEMUSizedBuffer.
- *
- * @qsb: A QEMUSizedBuffer
- * @new_len: The new length of bytes in the buffer
- *
- * Returns the number of bytes the buffer was truncated or extended
- * to.
- */
-size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len)
-{
-    if (new_len <= qsb->size) {
-        qsb->used = new_len;
-    } else {
-        qsb->used = qsb->size;
-    }
-    return qsb->used;
-}
-
-/**
- * Get the iovec that holds the data for a given position @pos.
- *
- * @qsb: A QEMUSizedBuffer
- * @pos: The index of a byte in the buffer
- * @d_off: Pointer to an offset that this function will indicate
- *         at what position within the returned iovec the byte
- *         is to be found
- *
- * Returns the index of the iovec that holds the byte at the given
- * index @pos in the byte stream; a negative number if the iovec
- * for the given position @pos does not exist.
- */
-static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb,
-                             off_t pos, off_t *d_off)
-{
-    ssize_t i;
-    off_t curr = 0;
-
-    if (pos > qsb->used) {
-        return -1;
-    }
-
-    for (i = 0; i < qsb->n_iov; i++) {
-        if (curr + qsb->iov[i].iov_len > pos) {
-            *d_off = pos - curr;
-            return i;
-        }
-        curr += qsb->iov[i].iov_len;
-    }
-    return -1;
-}
-
-/*
- * Convert the QEMUSizedBuffer into a flat buffer.
- *
- * Note: If at all possible, try to avoid this function since it
- *       may unnecessarily copy memory around.
- *
- * @qsb: pointer to QEMUSizedBuffer
- * @start: offset to start at
- * @count: number of bytes to copy
- * @buf: a pointer to a buffer to write into (at least @count bytes)
- *
- * Returns the number of bytes copied into the output buffer
- */
-ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start,
-                       size_t count, uint8_t *buffer)
-{
-    const struct iovec *iov;
-    size_t to_copy, all_copy;
-    ssize_t index;
-    off_t s_off;
-    off_t d_off = 0;
-    char *s;
-
-    if (start > qsb->used) {
-        return 0;
-    }
-
-    all_copy = qsb->used - start;
-    if (all_copy > count) {
-        all_copy = count;
-    } else {
-        count = all_copy;
-    }
-
-    index = qsb_get_iovec(qsb, start, &s_off);
-    if (index < 0) {
-        return 0;
-    }
-
-    while (all_copy > 0) {
-        iov = &qsb->iov[index];
-
-        s = iov->iov_base;
-
-        to_copy = iov->iov_len - s_off;
-        if (to_copy > all_copy) {
-            to_copy = all_copy;
-        }
-        memcpy(&buffer[d_off], &s[s_off], to_copy);
-
-        d_off += to_copy;
-        all_copy -= to_copy;
-
-        s_off = 0;
-        index++;
-    }
-
-    return count;
-}
-
-/**
- * Grow the QEMUSizedBuffer to the given size and allocate
- * memory for it.
- *
- * @qsb: A QEMUSizedBuffer
- * @new_size: The new size of the buffer
- *
- * Return:
- *    a negative error code in case of memory allocation failure
- * or
- *    the new size of the buffer. The returned size may be greater or equal
- *    to @new_size.
- */
-static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size)
-{
-    size_t needed_chunks, i;
-
-    if (qsb->size < new_size) {
-        struct iovec *new_iov;
-        size_t size_diff = new_size - qsb->size;
-        size_t chunk_size = (size_diff > QSB_MAX_CHUNK_SIZE)
-                             ? QSB_MAX_CHUNK_SIZE : QSB_CHUNK_SIZE;
-
-        needed_chunks = DIV_ROUND_UP(size_diff, chunk_size);
-
-        new_iov = g_try_new(struct iovec, qsb->n_iov + needed_chunks);
-        if (new_iov == NULL) {
-            return -ENOMEM;
-        }
-
-        /* Allocate new chunks as needed into new_iov */
-        for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) {
-            new_iov[i].iov_base = g_try_malloc0(chunk_size);
-            new_iov[i].iov_len = chunk_size;
-            if (!new_iov[i].iov_base) {
-                size_t j;
-
-                /* Free previously allocated new chunks */
-                for (j = qsb->n_iov; j < i; j++) {
-                    g_free(new_iov[j].iov_base);
-                }
-                g_free(new_iov);
-
-                return -ENOMEM;
-            }
-        }
-
-        /*
-         * Now we can't get any allocation errors, copy over to new iov
-         * and switch.
-         */
-        for (i = 0; i < qsb->n_iov; i++) {
-            new_iov[i] = qsb->iov[i];
-        }
-
-        qsb->n_iov += needed_chunks;
-        g_free(qsb->iov);
-        qsb->iov = new_iov;
-        qsb->size += (needed_chunks * chunk_size);
-    }
-
-    return qsb->size;
-}
-
-/**
- * Write into the QEMUSizedBuffer at a given position and a given
- * number of bytes. This function will automatically grow the
- * QEMUSizedBuffer.
- *
- * @qsb: A QEMUSizedBuffer
- * @source: A byte array to copy data from
- * @pos: The position within the @qsb to write data to
- * @size: The number of bytes to copy into the @qsb
- *
- * Returns @size or a negative error code in case of memory allocation failure,
- *           or with an invalid 'pos'
- */
-ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source,
-                     off_t pos, size_t count)
-{
-    ssize_t rc = qsb_grow(qsb, pos + count);
-    size_t to_copy;
-    size_t all_copy = count;
-    const struct iovec *iov;
-    ssize_t index;
-    char *dest;
-    off_t d_off, s_off = 0;
-
-    if (rc < 0) {
-        return rc;
-    }
-
-    if (pos + count > qsb->used) {
-        qsb->used = pos + count;
-    }
-
-    index = qsb_get_iovec(qsb, pos, &d_off);
-    if (index < 0) {
-        return -EINVAL;
-    }
-
-    while (all_copy > 0) {
-        iov = &qsb->iov[index];
-
-        dest = iov->iov_base;
-
-        to_copy = iov->iov_len - d_off;
-        if (to_copy > all_copy) {
-            to_copy = all_copy;
-        }
-
-        memcpy(&dest[d_off], &source[s_off], to_copy);
-
-        s_off += to_copy;
-        all_copy -= to_copy;
-
-        d_off = 0;
-        index++;
-    }
-
-    return count;
-}
-
-typedef struct QEMUBuffer {
-    QEMUSizedBuffer *qsb;
-    QEMUFile *file;
-    bool qsb_allocated;
-} QEMUBuffer;
-
-static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUBuffer *s = opaque;
-    ssize_t len = qsb_get_length(s->qsb) - pos;
-
-    if (len <= 0) {
-        return 0;
-    }
-
-    if (len > size) {
-        len = size;
-    }
-    return qsb_get_buffer(s->qsb, pos, len, buf);
-}
-
-static int buf_put_buffer(void *opaque, const uint8_t *buf,
-                          int64_t pos, int size)
-{
-    QEMUBuffer *s = opaque;
-
-    return qsb_write_at(s->qsb, buf, pos, size);
-}
-
-static int buf_close(void *opaque)
-{
-    QEMUBuffer *s = opaque;
-
-    if (s->qsb_allocated) {
-        qsb_free(s->qsb);
-    }
-
-    g_free(s);
-
-    return 0;
-}
-
-const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f)
-{
-    QEMUBuffer *p;
-
-    qemu_fflush(f);
-
-    p = f->opaque;
-
-    return p->qsb;
-}
-
-static const QEMUFileOps buf_read_ops = {
-    .get_buffer = buf_get_buffer,
-    .close =      buf_close,
-};
-
-static const QEMUFileOps buf_write_ops = {
-    .put_buffer = buf_put_buffer,
-    .close =      buf_close,
-};
-
-QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input)
-{
-    QEMUBuffer *s;
-
-    if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') ||
-        mode[1] != '\0') {
-        error_report("qemu_bufopen: Argument validity check failed");
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUBuffer));
-    s->qsb = input;
-
-    if (s->qsb == NULL) {
-        s->qsb = qsb_create(NULL, 0);
-        s->qsb_allocated = true;
-    }
-    if (!s->qsb) {
-        g_free(s);
-        error_report("qemu_bufopen: qsb_create failed");
-        return NULL;
-    }
-
-
-    if (mode[0] == 'r') {
-        s->file = qemu_fopen_ops(s, &buf_read_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &buf_write_ops);
-    }
-    return s->file;
-}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 31/46] migration: move definition of struct QEMUFile back into qemu-file.c
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (29 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 30/46] migration: remove memory buffer based QEMUFile backend Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 32/46] migration: split migration hooks out of QEMUFileOps Daniel P. Berrange
                   ` (14 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Now that the memory buffer based QEMUFile impl is gone, there
is no need for any backend to be accessing internals of the
QEMUFile struct, so it can be moved back into qemu-file.c

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/qemu-file-internal.h | 53 ------------------------------------------
 migration/qemu-file-unix.c     |  1 -
 migration/qemu-file.c          | 23 +++++++++++++++++-
 3 files changed, 22 insertions(+), 55 deletions(-)
 delete mode 100644 migration/qemu-file-internal.h

diff --git a/migration/qemu-file-internal.h b/migration/qemu-file-internal.h
deleted file mode 100644
index d95e853..0000000
--- a/migration/qemu-file-internal.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#ifndef QEMU_FILE_INTERNAL_H
-#define QEMU_FILE_INTERNAL_H 1
-
-#include "qemu-common.h"
-#include "qemu/iov.h"
-
-#define IO_BUF_SIZE 32768
-#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
-
-struct QEMUFile {
-    const QEMUFileOps *ops;
-    void *opaque;
-
-    int64_t bytes_xfer;
-    int64_t xfer_limit;
-
-    int64_t pos; /* start of buffer when writing, end of buffer
-                    when reading */
-    int buf_index;
-    int buf_size; /* 0 when writing */
-    uint8_t buf[IO_BUF_SIZE];
-
-    struct iovec iov[MAX_IOV_SIZE];
-    unsigned int iovcnt;
-
-    int last_error;
-};
-
-#endif
diff --git a/migration/qemu-file-unix.c b/migration/qemu-file-unix.c
index e4f195a..3891012 100644
--- a/migration/qemu-file-unix.c
+++ b/migration/qemu-file-unix.c
@@ -26,7 +26,6 @@
 #include "qemu/sockets.h"
 #include "qemu/coroutine.h"
 #include "migration/qemu-file.h"
-#include "migration/qemu-file-internal.h"
 
 typedef struct QEMUFileSocket {
     int fd;
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index d2359c4..3b281b2 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -29,9 +29,30 @@
 #include "qemu/coroutine.h"
 #include "migration/migration.h"
 #include "migration/qemu-file.h"
-#include "migration/qemu-file-internal.h"
 #include "trace.h"
 
+#define IO_BUF_SIZE 32768
+#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
+
+struct QEMUFile {
+    const QEMUFileOps *ops;
+    void *opaque;
+
+    int64_t bytes_xfer;
+    int64_t xfer_limit;
+
+    int64_t pos; /* start of buffer when writing, end of buffer
+                    when reading */
+    int buf_index;
+    int buf_size; /* 0 when writing */
+    uint8_t buf[IO_BUF_SIZE];
+
+    struct iovec iov[MAX_IOV_SIZE];
+    unsigned int iovcnt;
+
+    int last_error;
+};
+
 /*
  * Stop a file from being read/written - not all backing files can do this
  * typically only sockets can.
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 32/46] migration: split migration hooks out of QEMUFileOps
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (30 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 31/46] migration: move definition of struct QEMUFile back into qemu-file.c Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 33/46] migration: ensure qemu_fflush() always writes full data amount Daniel P. Berrange
                   ` (13 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The QEMUFileOps struct contains the I/O subsystem callbacks
and the migration stage hooks. Split the hooks out into a
separate QEMUFileHooks struct to make it easier to refactor
the I/O side of QEMUFile without affecting the hooks.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/qemu-file.h |  8 ++++++--
 migration/qemu-file.c         | 25 ++++++++++++++++---------
 migration/rdma.c              |  8 ++++++++
 3 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index c020b01..f9242e8 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -102,12 +102,15 @@ typedef struct QEMUFileOps {
     QEMUFileCloseFunc *close;
     QEMUFileGetFD *get_fd;
     QEMUFileWritevBufferFunc *writev_buffer;
+    QEMUFileShutdownFunc *shut_down;
+} QEMUFileOps;
+
+typedef struct QEMUFileHooks {
     QEMURamHookFunc *before_ram_iterate;
     QEMURamHookFunc *after_ram_iterate;
     QEMURamHookFunc *hook_ram_load;
     QEMURamSaveFunc *save_page;
-    QEMUFileShutdownFunc *shut_down;
-} QEMUFileOps;
+} QEMUFileHooks;
 
 struct QEMUSizedBuffer {
     struct iovec *iov;
@@ -121,6 +124,7 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode);
 QEMUFile *qemu_fdopen(int fd, const char *mode);
 QEMUFile *qemu_fopen_socket(int fd, const char *mode);
 QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
+void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks);
 int qemu_get_fd(QEMUFile *f);
 int qemu_fclose(QEMUFile *f);
 int64_t qemu_ftell(QEMUFile *f);
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index 3b281b2..d1d2c17 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -36,6 +36,7 @@
 
 struct QEMUFile {
     const QEMUFileOps *ops;
+    const QEMUFileHooks *hooks;
     void *opaque;
 
     int64_t bytes_xfer;
@@ -88,6 +89,12 @@ QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
     return f;
 }
 
+
+void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks)
+{
+    f->hooks = hooks;
+}
+
 /*
  * Get last error for stream f
  *
@@ -149,8 +156,8 @@ void ram_control_before_iterate(QEMUFile *f, uint64_t flags)
 {
     int ret = 0;
 
-    if (f->ops->before_ram_iterate) {
-        ret = f->ops->before_ram_iterate(f, f->opaque, flags, NULL);
+    if (f->hooks && f->hooks->before_ram_iterate) {
+        ret = f->hooks->before_ram_iterate(f, f->opaque, flags, NULL);
         if (ret < 0) {
             qemu_file_set_error(f, ret);
         }
@@ -161,8 +168,8 @@ void ram_control_after_iterate(QEMUFile *f, uint64_t flags)
 {
     int ret = 0;
 
-    if (f->ops->after_ram_iterate) {
-        ret = f->ops->after_ram_iterate(f, f->opaque, flags, NULL);
+    if (f->hooks && f->hooks->after_ram_iterate) {
+        ret = f->hooks->after_ram_iterate(f, f->opaque, flags, NULL);
         if (ret < 0) {
             qemu_file_set_error(f, ret);
         }
@@ -173,8 +180,8 @@ void ram_control_load_hook(QEMUFile *f, uint64_t flags, void *data)
 {
     int ret = -EINVAL;
 
-    if (f->ops->hook_ram_load) {
-        ret = f->ops->hook_ram_load(f, f->opaque, flags, data);
+    if (f->hooks && f->hooks->hook_ram_load) {
+        ret = f->hooks->hook_ram_load(f, f->opaque, flags, data);
         if (ret < 0) {
             qemu_file_set_error(f, ret);
         }
@@ -193,9 +200,9 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
                              ram_addr_t offset, size_t size,
                              uint64_t *bytes_sent)
 {
-    if (f->ops->save_page) {
-        int ret = f->ops->save_page(f, f->opaque, block_offset,
-                                    offset, size, bytes_sent);
+    if (f->hooks && f->hooks->save_page) {
+        int ret = f->hooks->save_page(f, f->opaque, block_offset,
+                                      offset, size, bytes_sent);
 
         if (ret != RAM_SAVE_CONTROL_DELAYED) {
             if (bytes_sent && *bytes_sent > 0) {
diff --git a/migration/rdma.c b/migration/rdma.c
index 9c64546..ca6b100 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -3380,12 +3380,18 @@ static const QEMUFileOps rdma_read_ops = {
     .get_buffer    = qemu_rdma_get_buffer,
     .get_fd        = qemu_rdma_get_fd,
     .close         = qemu_rdma_close,
+};
+
+static const QEMUFileHooks rdma_read_hooks = {
     .hook_ram_load = rdma_load_hook,
 };
 
 static const QEMUFileOps rdma_write_ops = {
     .put_buffer         = qemu_rdma_put_buffer,
     .close              = qemu_rdma_close,
+};
+
+static const QEMUFileHooks rdma_write_hooks = {
     .before_ram_iterate = qemu_rdma_registration_start,
     .after_ram_iterate  = qemu_rdma_registration_stop,
     .save_page          = qemu_rdma_save_page,
@@ -3404,8 +3410,10 @@ static void *qemu_fopen_rdma(RDMAContext *rdma, const char *mode)
 
     if (mode[0] == 'w') {
         r->file = qemu_fopen_ops(r, &rdma_write_ops);
+        qemu_file_set_hooks(r->file, &rdma_write_hooks);
     } else {
         r->file = qemu_fopen_ops(r, &rdma_read_ops);
+        qemu_file_set_hooks(r->file, &rdma_read_hooks);
     }
 
     return r->file;
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 33/46] migration: ensure qemu_fflush() always writes full data amount
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (31 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 32/46] migration: split migration hooks out of QEMUFileOps Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 34/46] migration: introduce qemu_fset_blocking function on QEMUFile Daniel P. Berrange
                   ` (12 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

The QEMUFile writev_buffer / put_buffer functions are expected
to write out the full set of requested data, blocking until
complete. The qemu_fflush() caller does not expect to deal with
partial writes. Clarify the function comments and add a sanity
check to the code to catch mistaken implementations.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/qemu-file.h |  6 ++++--
 migration/qemu-file.c         | 16 ++++++++++++----
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index f9242e8..a4e5468 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -29,7 +29,8 @@
 
 /* This function writes a chunk of data to a file at the given position.
  * The pos argument can be ignored if the file is only being used for
- * streaming.  The handler should try to write all of the data it can.
+ * streaming.  The handler must write all of the data or return a negative
+ * errno value.
  */
 typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf,
                                     int64_t pos, int size);
@@ -55,7 +56,8 @@ typedef int (QEMUFileCloseFunc)(void *opaque);
 typedef int (QEMUFileGetFD)(void *opaque);
 
 /*
- * This function writes an iovec to file.
+ * This function writes an iovec to file. The handler must write all
+ * of the data or return a negative errno value.
  */
 typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov,
                                            int iovcnt, int64_t pos);
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index d1d2c17..9f42f5c 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -123,11 +123,13 @@ bool qemu_file_is_writable(QEMUFile *f)
  * Flushes QEMUFile buffer
  *
  * If there is writev_buffer QEMUFileOps it uses it otherwise uses
- * put_buffer ops.
+ * put_buffer ops. This will flush all pending data. If data was
+ * only partially flushed, it will set an error state.
  */
 void qemu_fflush(QEMUFile *f)
 {
     ssize_t ret = 0;
+    ssize_t expect = 0;
 
     if (!qemu_file_is_writable(f)) {
         return;
@@ -135,21 +137,27 @@ void qemu_fflush(QEMUFile *f)
 
     if (f->ops->writev_buffer) {
         if (f->iovcnt > 0) {
+            expect = iov_size(f->iov, f->iovcnt);
             ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
         }
     } else {
         if (f->buf_index > 0) {
+            expect = f->buf_index;
             ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
         }
     }
+
     if (ret >= 0) {
         f->pos += ret;
     }
-    f->buf_index = 0;
-    f->iovcnt = 0;
-    if (ret < 0) {
+    /* We expect the QEMUFile write impl to send the full
+     * data set we requested, so sanity check that.
+     */
+    if (ret < 0 || ret != expect) {
         qemu_file_set_error(f, ret);
     }
+    f->buf_index = 0;
+    f->iovcnt = 0;
 }
 
 void ram_control_before_iterate(QEMUFile *f, uint64_t flags)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 34/46] migration: introduce qemu_fset_blocking function on QEMUFile
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (32 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 33/46] migration: ensure qemu_fflush() always writes full data amount Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 35/46] migration: force QEMUFile to blocking mode for outgoing migration Daniel P. Berrange
                   ` (11 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Remove the assumption that every QEMUFile implementation has
a file descriptor available by introducing a new function
to change the blocking state of a QEMUFile.

Provide a default impl of the new method based on the get_fd
method.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/qemu-file.h |  6 ++++++
 migration/migration.c         |  4 +---
 migration/qemu-file.c         | 15 +++++++++++++++
 3 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index a4e5468..2625d9b 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -55,6 +55,10 @@ typedef int (QEMUFileCloseFunc)(void *opaque);
  */
 typedef int (QEMUFileGetFD)(void *opaque);
 
+/* Called to change the blocking mode of the file
+ */
+typedef int (QEMUFileSetBlocking)(void *opaque, bool enabled);
+
 /*
  * This function writes an iovec to file. The handler must write all
  * of the data or return a negative errno value.
@@ -103,6 +107,7 @@ typedef struct QEMUFileOps {
     QEMUFileGetBufferFunc *get_buffer;
     QEMUFileCloseFunc *close;
     QEMUFileGetFD *get_fd;
+    QEMUFileSetBlocking *set_blocking;
     QEMUFileWritevBufferFunc *writev_buffer;
     QEMUFileShutdownFunc *shut_down;
 } QEMUFileOps;
@@ -128,6 +133,7 @@ QEMUFile *qemu_fopen_socket(int fd, const char *mode);
 QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
 void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks);
 int qemu_get_fd(QEMUFile *f);
+int qemu_fset_blocking(QEMUFile *f, bool enabled);
 int qemu_fclose(QEMUFile *f);
 int64_t qemu_ftell(QEMUFile *f);
 int64_t qemu_ftell_fast(QEMUFile *f);
diff --git a/migration/migration.c b/migration/migration.c
index 662e77e..83a3960 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -317,11 +317,9 @@ static void process_incoming_migration_co(void *opaque)
 void process_incoming_migration(QEMUFile *f)
 {
     Coroutine *co = qemu_coroutine_create(process_incoming_migration_co);
-    int fd = qemu_get_fd(f);
 
-    assert(fd != -1);
     migrate_decompress_threads_create();
-    qemu_set_nonblock(fd);
+    qemu_fset_blocking(f, false);
     qemu_coroutine_enter(co, f);
 }
 
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index 9f42f5c..c8b0b79 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -270,6 +270,21 @@ int qemu_get_fd(QEMUFile *f)
     return -1;
 }
 
+int qemu_fset_blocking(QEMUFile *f, bool enabled)
+{
+    if (f->ops->set_blocking) {
+        return f->ops->set_blocking(f->opaque, enabled);
+    } else if (f->ops->get_fd) {
+        int fd = f->ops->get_fd(f->opaque);
+        if (enabled) {
+            qemu_set_block(fd);
+        } else {
+            qemu_set_nonblock(fd);
+        }
+    }
+    return -1;
+}
+
 void qemu_update_position(QEMUFile *f, size_t size)
 {
     f->pos += size;
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 35/46] migration: force QEMUFile to blocking mode for outgoing migration
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (33 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 34/46] migration: introduce qemu_fset_blocking function on QEMUFile Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 36/46] migration: introduce a new QEMUFile impl based on QIOChannel Daniel P. Berrange
                   ` (10 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Instead of relying on the default QEMUFile I/O blocking flag
state, explicitly turn on blocking I/O for outgoing migration
since it takes place in a background thread.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/migration.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/migration/migration.c b/migration/migration.c
index 83a3960..aa24a7a 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1034,6 +1034,7 @@ void migrate_fd_connect(MigrationState *s)
     s->expected_downtime = max_downtime/1000000;
     s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s);
 
+    qemu_fset_blocking(s->file, true);
     qemu_file_set_rate_limit(s->file,
                              s->bandwidth_limit / XFER_LIMIT_RATIO);
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 36/46] migration: introduce a new QEMUFile impl based on QIOChannel
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (34 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 35/46] migration: force QEMUFile to blocking mode for outgoing migration Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 37/46] migration: convert unix socket protocol to use QIOChannel Daniel P. Berrange
                   ` (9 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Introduce a new QEMUFile implementation that is based on
the QIOChannel objects. This impl is different from existing
impls in that there is no file descriptor that can be made
available, as some channels may be based on higher level
protocols such as TLS.

The QIOChannel based implementation provides bi-directional
streams from the start, so there's no mode flag provided
when opening one.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/qemu-file.h |   4 +
 migration/Makefile.objs       |   1 +
 migration/qemu-file-channel.c | 200 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 205 insertions(+)
 create mode 100644 migration/qemu-file-channel.c

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index 2625d9b..0d9f08e 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -23,7 +23,9 @@
  */
 #ifndef QEMU_FILE_H
 #define QEMU_FILE_H 1
+#include "qemu-common.h"
 #include "exec/cpu-common.h"
+#include "io/channel.h"
 
 #include <stdint.h>
 
@@ -130,6 +132,8 @@ QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops);
 QEMUFile *qemu_fopen(const char *filename, const char *mode);
 QEMUFile *qemu_fdopen(int fd, const char *mode);
 QEMUFile *qemu_fopen_socket(int fd, const char *mode);
+QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc);
+QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc);
 QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
 void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks);
 int qemu_get_fd(QEMUFile *f);
diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index ce1e3c7..32fd449 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,6 +1,7 @@
 common-obj-y += migration.o tcp.o
 common-obj-y += vmstate.o
 common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
+common-obj-y += qemu-file-channel.o
 common-obj-y += xbzrle.o
 
 common-obj-$(CONFIG_RDMA) += rdma.o
diff --git a/migration/qemu-file-channel.c b/migration/qemu-file-channel.c
new file mode 100644
index 0000000..56ef8c8
--- /dev/null
+++ b/migration/qemu-file-channel.c
@@ -0,0 +1,200 @@
+/*
+ * QEMUFile backend for QIOChannel objects
+ *
+ * Copyright (c) 2015 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "migration/qemu-file.h"
+#include "io/channel-socket.h"
+#include "qemu/iov.h"
+
+
+/**
+ * @skip: number of bytes to advance head of @iov
+ * @iov: pointer to iov array, updated on success
+ * @iovcnt: number of elements in @iov, updated on success
+ * @oldiov: pointer single element to hold old info from @iov
+ *
+ * This will update @iov so that its head is advanced
+ * by @skip bytes. To do this, zero or more complete
+ * elements of @iov will be skipped over. The new head
+ * of @iov will then have its base & len updated to
+ * skip the remaining number of bytes. @oldiov will
+ * hold the original data from the new head of @iov.
+ */
+static void channel_skip_iov(size_t skip,
+                             struct iovec **iov,
+                             int *iovcnt,
+                             struct iovec *oldiov)
+{
+    ssize_t done = 0;
+    size_t i;
+    for (i = 0; i < *iovcnt; i++) {
+        if ((*iov)[i].iov_len <= skip) {
+            done += (*iov)[i].iov_len;
+            skip -= (*iov)[i].iov_len;
+        } else {
+            done += skip;
+            oldiov->iov_base = (*iov)[i].iov_base;
+            oldiov->iov_len = (*iov)[i].iov_len;
+            (*iov)[i].iov_len -= skip;
+            (*iov)[i].iov_base += skip;
+            *iov = *iov + i;
+            *iovcnt = *iovcnt - i;
+            break;
+        }
+    }
+}
+
+static ssize_t channel_writev_buffer(void *opaque,
+                                     struct iovec *iov,
+                                     int iovcnt,
+                                     int64_t pos)
+{
+    QIOChannel *ioc = QIO_CHANNEL(opaque);
+    ssize_t done = 0;
+    ssize_t want = iov_size(iov, iovcnt);
+    struct iovec oldiov = { NULL, 0 };
+
+    while (done < want) {
+        ssize_t len;
+        struct iovec *cur = iov;
+        int curcnt = iovcnt;
+
+        channel_skip_iov(done, &cur, &curcnt, &oldiov);
+
+        len = qio_channel_writev(ioc, cur, curcnt, NULL);
+        if (oldiov.iov_base) {
+            /* Restore original caller's info in @iov */
+            cur[0].iov_base = oldiov.iov_base;
+            cur[0].iov_len = oldiov.iov_len;
+            oldiov.iov_base = NULL;
+            oldiov.iov_len = 0;
+        }
+        if (len == QIO_CHANNEL_ERR_BLOCK) {
+            qio_channel_wait(ioc, G_IO_OUT);
+            continue;
+        }
+        if (len < 0) {
+            /* XXX handle Error objects */
+            return -EIO;
+        }
+
+        done += len;
+    }
+    return done;
+}
+
+
+static int channel_get_buffer(void *opaque,
+                              uint8_t *buf,
+                              int64_t pos,
+                              int size)
+{
+    QIOChannel *ioc = QIO_CHANNEL(opaque);
+    ssize_t ret;
+
+ reread:
+    ret = qio_channel_read(ioc, (char *)buf, size, NULL);
+    if (ret < 0) {
+        if (ret == QIO_CHANNEL_ERR_BLOCK) {
+            qio_channel_yield(ioc, G_IO_IN);
+            goto reread;
+        } else {
+            /* XXX handle Error * object */
+            return -EIO;
+        }
+    }
+    return ret;
+}
+
+
+static int channel_close(void *opaque)
+{
+    QIOChannel *ioc = QIO_CHANNEL(opaque);
+    qio_channel_close(ioc, NULL);
+    object_unref(OBJECT(ioc));
+    return 0;
+}
+
+
+static int channel_shutdown(void *opaque,
+                            bool rd,
+                            bool wr)
+{
+    QIOChannel *ioc = QIO_CHANNEL(opaque);
+
+    if (object_dynamic_cast(OBJECT(ioc),
+                            TYPE_QIO_CHANNEL_SOCKET)) {
+        QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
+        QIOChannelSocketShutdown mode;
+        if (rd && wr) {
+            mode = QIO_CHANNEL_SOCKET_SHUTDOWN_BOTH;
+        } else if (rd) {
+            mode = QIO_CHANNEL_SOCKET_SHUTDOWN_READ;
+        } else {
+            mode = QIO_CHANNEL_SOCKET_SHUTDOWN_WRITE;
+        }
+        if (qio_channel_socket_shutdown(sioc, mode, NULL) < 0) {
+            /* XXX handler Error * object */
+            return -EIO;
+        }
+    }
+    return 0;
+}
+
+
+static int channel_set_blocking(void *opaque,
+                                bool enabled)
+{
+    QIOChannel *ioc = QIO_CHANNEL(opaque);
+
+    qio_channel_set_blocking(ioc, enabled);
+    return 0;
+}
+
+
+static const QEMUFileOps channel_input_ops = {
+    .get_buffer = channel_get_buffer,
+    .close = channel_close,
+    .shut_down = channel_shutdown,
+    .set_blocking = channel_set_blocking,
+};
+
+
+static const QEMUFileOps channel_output_ops = {
+    .writev_buffer = channel_writev_buffer,
+    .close = channel_close,
+    .shut_down = channel_shutdown,
+    .set_blocking = channel_set_blocking,
+};
+
+
+QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc)
+{
+    object_ref(OBJECT(ioc));
+    return qemu_fopen_ops(ioc, &channel_input_ops);
+}
+
+QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc)
+{
+    object_ref(OBJECT(ioc));
+    return qemu_fopen_ops(ioc, &channel_output_ops);
+}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 37/46] migration: convert unix socket protocol to use QIOChannel
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (35 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 36/46] migration: introduce a new QEMUFile impl based on QIOChannel Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 38/46] migration: convert tcp " Daniel P. Berrange
                   ` (8 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Convert the unix socket migration protocol driver to use
QIOChannel and QEMUFileChannel, instead of plain sockets
APIs. It can be unconditionally built, since the socket
impl of QIOChannel will report a suitable error on platforms
where UNIX sockets are unavailable.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/Makefile.objs |   4 +-
 migration/migration.c   |   4 ++
 migration/unix.c        | 103 +++++++++++++++++++++++++++++++-----------------
 3 files changed, 72 insertions(+), 39 deletions(-)

diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index 32fd449..521a83b 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,11 +1,11 @@
-common-obj-y += migration.o tcp.o
+common-obj-y += migration.o tcp.o unix.o
 common-obj-y += vmstate.o
 common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
 common-obj-y += qemu-file-channel.o
 common-obj-y += xbzrle.o
 
 common-obj-$(CONFIG_RDMA) += rdma.o
-common-obj-$(CONFIG_POSIX) += exec.o unix.o fd.o
+common-obj-$(CONFIG_POSIX) += exec.o fd.o
 
 common-obj-y += block.o
 
diff --git a/migration/migration.c b/migration/migration.c
index aa24a7a..bd7024f 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -256,8 +256,10 @@ void qemu_start_incoming_migration(const char *uri, Error **errp)
 #if !defined(WIN32)
     } else if (strstart(uri, "exec:", &p)) {
         exec_start_incoming_migration(p, errp);
+#endif
     } else if (strstart(uri, "unix:", &p)) {
         unix_start_incoming_migration(p, errp);
+#if !defined(WIN32)
     } else if (strstart(uri, "fd:", &p)) {
         fd_start_incoming_migration(p, errp);
 #endif
@@ -747,8 +749,10 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
 #if !defined(WIN32)
     } else if (strstart(uri, "exec:", &p)) {
         exec_start_outgoing_migration(s, p, &local_err);
+#endif
     } else if (strstart(uri, "unix:", &p)) {
         unix_start_outgoing_migration(s, p, &local_err);
+#if !defined(WIN32)
     } else if (strstart(uri, "fd:", &p)) {
         fd_start_outgoing_migration(s, p, &local_err);
 #endif
diff --git a/migration/unix.c b/migration/unix.c
index b591813..4263ad1 100644
--- a/migration/unix.c
+++ b/migration/unix.c
@@ -1,10 +1,11 @@
 /*
  * QEMU live migration via Unix Domain Sockets
  *
- * Copyright Red Hat, Inc. 2009
+ * Copyright Red Hat, Inc. 2009-2015
  *
  * Authors:
  *  Chris Lalancette <clalance@redhat.com>
+ *  Daniel P. Berrange <berrange@redhat.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2.  See
  * the COPYING file in the top-level directory.
@@ -17,11 +18,9 @@
 
 #include "qemu-common.h"
 #include "qemu/error-report.h"
-#include "qemu/sockets.h"
-#include "qemu/main-loop.h"
 #include "migration/migration.h"
 #include "migration/qemu-file.h"
-#include "block/block.h"
+#include "io/channel-socket.h"
 
 //#define DEBUG_MIGRATION_UNIX
 
@@ -33,71 +32,101 @@
     do { } while (0)
 #endif
 
-static void unix_wait_for_connect(int fd, Error *err, void *opaque)
+
+static SocketAddress *unix_build_address(const char *path)
+{
+    SocketAddress *saddr;
+
+    saddr = g_new0(SocketAddress, 1);
+    saddr->kind = SOCKET_ADDRESS_KIND_UNIX;
+    saddr->q_unix = g_new0(UnixSocketAddress, 1);
+    saddr->q_unix->path = g_strdup(path);
+
+    return saddr;
+}
+
+
+static void unix_outgoing_migration(Object *src,
+                                    Error *err,
+                                    gpointer opaque)
 {
     MigrationState *s = opaque;
+    QIOChannel *sioc = QIO_CHANNEL(src);
 
-    if (fd < 0) {
+    if (err) {
         DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
         s->file = NULL;
         migrate_fd_error(s);
     } else {
         DPRINTF("migrate connect success\n");
-        s->file = qemu_fopen_socket(fd, "wb");
+        s->file = qemu_fopen_channel_output(sioc);
         migrate_fd_connect(s);
     }
+    object_unref(src);
 }
 
+
 void unix_start_outgoing_migration(MigrationState *s, const char *path, Error **errp)
 {
-    unix_nonblocking_connect(path, unix_wait_for_connect, s, errp);
+    SocketAddress *saddr = unix_build_address(path);
+    QIOChannelSocket *sioc;
+    sioc = qio_channel_socket_new();
+    qio_channel_socket_connect_async(sioc,
+                                     saddr,
+                                     unix_outgoing_migration,
+                                     s,
+                                     NULL);
+    qapi_free_SocketAddress(saddr);
 }
 
-static void unix_accept_incoming_migration(void *opaque)
+
+static gboolean unix_accept_incoming_migration(QIOChannel *ioc,
+                                               GIOCondition condition,
+                                               gpointer opaque)
 {
-    struct sockaddr_un addr;
-    socklen_t addrlen = sizeof(addr);
-    int s = (intptr_t)opaque;
     QEMUFile *f;
-    int c, err;
-
-    do {
-        c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen);
-        err = errno;
-    } while (c < 0 && err == EINTR);
-    qemu_set_fd_handler(s, NULL, NULL, NULL);
-    close(s);
+    QIOChannelSocket *cioc;
+    Error *err = NULL;
 
-    DPRINTF("accepted migration\n");
-
-    if (c < 0) {
+    cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
+                                     &err);
+    if (!cioc) {
         error_report("could not accept migration connection (%s)",
-                     strerror(err));
-        return;
-    }
-
-    f = qemu_fopen_socket(c, "rb");
-    if (f == NULL) {
-        error_report("could not qemu_fopen socket");
+                     error_get_pretty(err));
         goto out;
     }
 
+    DPRINTF("accepted migration\n");
+
+    f = qemu_fopen_channel_input(QIO_CHANNEL(cioc));
+    object_unref(OBJECT(cioc));
+
     process_incoming_migration(f);
-    return;
 
 out:
-    close(c);
+    /* Close listening socket as its no longer needed */
+    qio_channel_close(ioc, NULL);
+    return FALSE;
 }
 
+
 void unix_start_incoming_migration(const char *path, Error **errp)
 {
-    int s;
+    SocketAddress *saddr = unix_build_address(path);
+    QIOChannelSocket *listen_ioc;
 
-    s = unix_listen(path, NULL, 0, errp);
-    if (s < 0) {
+    listen_ioc = qio_channel_socket_new();
+    if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) {
+        object_unref(OBJECT(listen_ioc));
+        qapi_free_SocketAddress(saddr);
         return;
     }
 
-    qemu_set_fd_handler(s, unix_accept_incoming_migration, NULL,
-                        (void *)(intptr_t)s);
+    qio_channel_add_watch(QIO_CHANNEL(listen_ioc),
+                          G_IO_IN,
+                          unix_accept_incoming_migration,
+                          listen_ioc,
+                          (GDestroyNotify)object_unref);
+
+    qapi_free_SocketAddress(saddr);
 }
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 38/46] migration: convert tcp socket protocol to use QIOChannel
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (36 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 37/46] migration: convert unix socket protocol to use QIOChannel Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 39/46] migration: convert fd " Daniel P. Berrange
                   ` (7 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Convert the tcp socket migration protocol driver to use
QIOChannel and QEMUFileChannel, instead of plain sockets
APIs.

While this now looks pretty similar to the migration/unix.c
file from the previous patch, it was decided not to merge
the two, because when TLS is added to the TCP impl later,
this file diverge from unix.c once again.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/tcp.c | 119 ++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 82 insertions(+), 37 deletions(-)

diff --git a/migration/tcp.c b/migration/tcp.c
index ae89172..2347d9d 100644
--- a/migration/tcp.c
+++ b/migration/tcp.c
@@ -2,9 +2,11 @@
  * QEMU live migration
  *
  * Copyright IBM, Corp. 2008
+ * Copyright Red Hat, Inc. 2015
  *
  * Authors:
  *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Daniel P. Berrange <berrange@redhat.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2.  See
  * the COPYING file in the top-level directory.
@@ -17,11 +19,9 @@
 
 #include "qemu-common.h"
 #include "qemu/error-report.h"
-#include "qemu/sockets.h"
 #include "migration/migration.h"
 #include "migration/qemu-file.h"
-#include "block/block.h"
-#include "qemu/main-loop.h"
+#include "io/channel-socket.h"
 
 //#define DEBUG_MIGRATION_TCP
 
@@ -33,71 +33,116 @@
     do { } while (0)
 #endif
 
-static void tcp_wait_for_connect(int fd, Error *err, void *opaque)
+
+static SocketAddress *tcp_build_address(const char *host_port, Error **errp)
+{
+    InetSocketAddress *iaddr = inet_parse(host_port, errp);
+    SocketAddress *saddr;
+
+    if (!iaddr) {
+        return NULL;
+    }
+
+    saddr = g_new0(SocketAddress, 1);
+    saddr->kind = SOCKET_ADDRESS_KIND_INET;
+    saddr->inet = iaddr;
+
+    return saddr;
+}
+
+
+static void tcp_outgoing_migration(Object *src,
+                                   Error *err,
+                                   gpointer opaque)
 {
     MigrationState *s = opaque;
+    QIOChannel *sioc = QIO_CHANNEL(src);
 
-    if (fd < 0) {
+    if (err) {
         DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
         s->file = NULL;
         migrate_fd_error(s);
     } else {
         DPRINTF("migrate connect success\n");
-        s->file = qemu_fopen_socket(fd, "wb");
+        s->file = qemu_fopen_channel_output(sioc);
         migrate_fd_connect(s);
     }
+    object_unref(src);
 }
 
-void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp)
+
+void tcp_start_outgoing_migration(MigrationState *s,
+                                  const char *host_port,
+                                  Error **errp)
 {
-    inet_nonblocking_connect(host_port, tcp_wait_for_connect, s, errp);
+    SocketAddress *saddr = tcp_build_address(host_port, errp);
+    QIOChannelSocket *sioc;
+
+    if (!saddr) {
+        return;
+    }
+
+    sioc = qio_channel_socket_new();
+    qio_channel_socket_connect_async(sioc,
+                                     saddr,
+                                     tcp_outgoing_migration,
+                                     s,
+                                     NULL);
+    qapi_free_SocketAddress(saddr);
 }
 
-static void tcp_accept_incoming_migration(void *opaque)
+
+static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
+                                              GIOCondition condition,
+                                              gpointer opaque)
 {
-    struct sockaddr_in addr;
-    socklen_t addrlen = sizeof(addr);
-    int s = (intptr_t)opaque;
     QEMUFile *f;
-    int c, err;
+    QIOChannelSocket *cioc;
+    Error *err = NULL;
 
-    do {
-        c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen);
-        err = socket_error();
-    } while (c < 0 && err == EINTR);
-    qemu_set_fd_handler(s, NULL, NULL, NULL);
-    closesocket(s);
-
-    DPRINTF("accepted migration\n");
-
-    if (c < 0) {
+    cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
+                                     &err);
+    if (!cioc) {
         error_report("could not accept migration connection (%s)",
-                     strerror(err));
-        return;
-    }
-
-    f = qemu_fopen_socket(c, "rb");
-    if (f == NULL) {
-        error_report("could not qemu_fopen socket");
+                     error_get_pretty(err));
         goto out;
     }
 
+    DPRINTF("accepted migration\n");
+
+    f = qemu_fopen_channel_input(QIO_CHANNEL(cioc));
+    object_unref(OBJECT(cioc));
+
     process_incoming_migration(f);
-    return;
 
 out:
-    closesocket(c);
+    /* Close listening socket as its no longer needed */
+    qio_channel_close(ioc, NULL);
+    return FALSE;
 }
 
+
 void tcp_start_incoming_migration(const char *host_port, Error **errp)
 {
-    int s;
+    SocketAddress *saddr = tcp_build_address(host_port, errp);
+    QIOChannelSocket *listen_ioc;
 
-    s = inet_listen(host_port, NULL, 256, SOCK_STREAM, 0, errp);
-    if (s < 0) {
+    if (!saddr) {
         return;
     }
 
-    qemu_set_fd_handler(s, tcp_accept_incoming_migration, NULL,
-                        (void *)(intptr_t)s);
+    listen_ioc = qio_channel_socket_new();
+    if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) {
+        object_unref(OBJECT(listen_ioc));
+        qapi_free_SocketAddress(saddr);
+        return;
+    }
+
+    qio_channel_add_watch(QIO_CHANNEL(listen_ioc),
+                          G_IO_IN,
+                          tcp_accept_incoming_migration,
+                          listen_ioc,
+                          (GDestroyNotify)object_unref);
+
+    qapi_free_SocketAddress(saddr);
 }
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 39/46] migration: convert fd socket protocol to use QIOChannel
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (37 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 38/46] migration: convert tcp " Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 40/46] migration: convert exec " Daniel P. Berrange
                   ` (6 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Convert the fd socket migration protocol driver to use
QIOChannel and QEMUFileChannel, instead of plain sockets
APIs. It can be unconditionally built because the
QIOChannel APIs it uses will take care to report suitable
error messages if needed.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/Makefile.objs |  4 ++--
 migration/fd.c          | 57 ++++++++++++++++++++++++++++++++-----------------
 migration/migration.c   |  4 ----
 3 files changed, 39 insertions(+), 26 deletions(-)

diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index 521a83b..2d1d8cd 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,11 +1,11 @@
-common-obj-y += migration.o tcp.o unix.o
+common-obj-y += migration.o tcp.o unix.o fd.o
 common-obj-y += vmstate.o
 common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
 common-obj-y += qemu-file-channel.o
 common-obj-y += xbzrle.o
 
 common-obj-$(CONFIG_RDMA) += rdma.o
-common-obj-$(CONFIG_POSIX) += exec.o fd.o
+common-obj-$(CONFIG_POSIX) += exec.o
 
 common-obj-y += block.o
 
diff --git a/migration/fd.c b/migration/fd.c
index 3e4bed0..8d48e0d 100644
--- a/migration/fd.c
+++ b/migration/fd.c
@@ -20,6 +20,8 @@
 #include "monitor/monitor.h"
 #include "migration/qemu-file.h"
 #include "block/block.h"
+#include "io/channel-file.h"
+#include "io/channel-socket.h"
 
 //#define DEBUG_MIGRATION_FD
 
@@ -33,56 +35,71 @@
 
 static bool fd_is_socket(int fd)
 {
-    struct stat stat;
-    int ret = fstat(fd, &stat);
-    if (ret == -1) {
-        /* When in doubt say no */
-        return false;
-    }
-    return S_ISSOCK(stat.st_mode);
+    int optval;
+    socklen_t optlen;
+    optlen = sizeof(optval);
+    return getsockopt(fd,
+                      SOL_SOCKET,
+                      SO_TYPE,
+                      (char *)&optval,
+                      &optlen) == 0;
 }
 
 void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp)
 {
+    QIOChannel *ioc;
     int fd = monitor_get_fd(cur_mon, fdname, errp);
     if (fd == -1) {
         return;
     }
 
     if (fd_is_socket(fd)) {
-        s->file = qemu_fopen_socket(fd, "wb");
+        ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, errp));
+        if (!ioc) {
+            close(fd);
+            return;
+        }
     } else {
-        s->file = qemu_fdopen(fd, "wb");
+        ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd));
     }
+    s->file = qemu_fopen_channel_output(ioc);
 
     migrate_fd_connect(s);
 }
 
-static void fd_accept_incoming_migration(void *opaque)
+static gboolean fd_accept_incoming_migration(QIOChannel *ioc,
+                                             GIOCondition condition,
+                                             gpointer opaque)
 {
     QEMUFile *f = opaque;
-
-    qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL);
     process_incoming_migration(f);
+    return FALSE;
 }
 
 void fd_start_incoming_migration(const char *infd, Error **errp)
 {
-    int fd;
     QEMUFile *f;
+    QIOChannel *ioc;
+    int fd;
 
     DPRINTF("Attempting to start an incoming migration via fd\n");
 
     fd = strtol(infd, NULL, 0);
     if (fd_is_socket(fd)) {
-        f = qemu_fopen_socket(fd, "rb");
+        ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, errp));
+        if (!ioc) {
+            close(fd);
+            return;
+        }
     } else {
-        f = qemu_fdopen(fd, "rb");
-    }
-    if(f == NULL) {
-        error_setg_errno(errp, errno, "failed to open the source descriptor");
-        return;
+        ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd));
     }
+    f = qemu_fopen_channel_input(ioc);
+    object_unref(OBJECT(ioc));
 
-    qemu_set_fd_handler(fd, fd_accept_incoming_migration, NULL, f);
+    qio_channel_add_watch(ioc,
+                          G_IO_IN,
+                          fd_accept_incoming_migration,
+                          f,
+                          NULL);
 }
diff --git a/migration/migration.c b/migration/migration.c
index bd7024f..f8cf8dc 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -259,10 +259,8 @@ void qemu_start_incoming_migration(const char *uri, Error **errp)
 #endif
     } else if (strstart(uri, "unix:", &p)) {
         unix_start_incoming_migration(p, errp);
-#if !defined(WIN32)
     } else if (strstart(uri, "fd:", &p)) {
         fd_start_incoming_migration(p, errp);
-#endif
     } else {
         error_setg(errp, "unknown migration protocol: %s", uri);
     }
@@ -752,10 +750,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
 #endif
     } else if (strstart(uri, "unix:", &p)) {
         unix_start_outgoing_migration(s, p, &local_err);
-#if !defined(WIN32)
     } else if (strstart(uri, "fd:", &p)) {
         fd_start_outgoing_migration(s, p, &local_err);
-#endif
     } else {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "uri",
                    "a valid migration protocol");
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 40/46] migration: convert exec socket protocol to use QIOChannel
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (38 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 39/46] migration: convert fd " Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 41/46] migration: convert RDMA to use QIOChannel interface Daniel P. Berrange
                   ` (5 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Convert the exec socket migration protocol driver to use
QIOChannel and QEMUFileChannel, instead of the stdio
popen APIs. It can be unconditionally built because the
QIOChannelCommand class can report suitable error messages
on platforms which can't fork processes.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/Makefile.objs |  3 +--
 migration/exec.c        | 48 ++++++++++++++++++++++++++++++------------------
 migration/migration.c   |  4 ----
 3 files changed, 31 insertions(+), 24 deletions(-)

diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index 2d1d8cd..c43c78a 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,11 +1,10 @@
-common-obj-y += migration.o tcp.o unix.o fd.o
+common-obj-y += migration.o tcp.o unix.o fd.o exec.o
 common-obj-y += vmstate.o
 common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
 common-obj-y += qemu-file-channel.o
 common-obj-y += xbzrle.o
 
 common-obj-$(CONFIG_RDMA) += rdma.o
-common-obj-$(CONFIG_POSIX) += exec.o
 
 common-obj-y += block.o
 
diff --git a/migration/exec.c b/migration/exec.c
index 8406d2b..6159aba 100644
--- a/migration/exec.c
+++ b/migration/exec.c
@@ -15,14 +15,8 @@
  * GNU GPL, version 2 or (at your option) any later version.
  */
 
-#include "qemu-common.h"
-#include "qemu/sockets.h"
-#include "qemu/main-loop.h"
 #include "migration/migration.h"
-#include "migration/qemu-file.h"
-#include "block/block.h"
-#include <sys/types.h>
-#include <sys/wait.h>
+#include "io/channel-command.h"
 
 //#define DEBUG_MIGRATION_EXEC
 
@@ -36,34 +30,52 @@
 
 void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp)
 {
-    s->file = qemu_popen_cmd(command, "w");
-    if (s->file == NULL) {
-        error_setg_errno(errp, errno, "failed to popen the migration target");
+    QIOChannel *ioc;
+    const char *argv[] = { "/bin/sh", "-c", command, NULL };
+
+    DPRINTF("Attempting to start an outgoing migration\n");
+    ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,
+                                                    O_WRONLY,
+                                                    errp));
+    if (!ioc) {
         return;
     }
 
+    s->file = qemu_fopen_channel_output(ioc);
+    object_unref(OBJECT(ioc));
+
     migrate_fd_connect(s);
 }
 
-static void exec_accept_incoming_migration(void *opaque)
+static gboolean exec_accept_incoming_migration(QIOChannel *ioc,
+                                               GIOCondition condition,
+                                               gpointer opaque)
 {
     QEMUFile *f = opaque;
-
-    qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL);
     process_incoming_migration(f);
+    return FALSE;
 }
 
 void exec_start_incoming_migration(const char *command, Error **errp)
 {
     QEMUFile *f;
+    QIOChannel *ioc;
+    const char *argv[] = { "/bin/sh", "-c", command, NULL };
 
     DPRINTF("Attempting to start an incoming migration\n");
-    f = qemu_popen_cmd(command, "r");
-    if(f == NULL) {
-        error_setg_errno(errp, errno, "failed to popen the migration source");
+    ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv,
+                                                    O_RDONLY,
+                                                    errp));
+    if (!ioc) {
         return;
     }
 
-    qemu_set_fd_handler(qemu_get_fd(f), exec_accept_incoming_migration, NULL,
-                        f);
+    f = qemu_fopen_channel_input(ioc);
+    object_unref(OBJECT(ioc));
+
+    qio_channel_add_watch(ioc,
+                          G_IO_IN,
+                          exec_accept_incoming_migration,
+                          f,
+                          NULL);
 }
diff --git a/migration/migration.c b/migration/migration.c
index f8cf8dc..f025226 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -253,10 +253,8 @@ void qemu_start_incoming_migration(const char *uri, Error **errp)
     } else if (strstart(uri, "rdma:", &p)) {
         rdma_start_incoming_migration(p, errp);
 #endif
-#if !defined(WIN32)
     } else if (strstart(uri, "exec:", &p)) {
         exec_start_incoming_migration(p, errp);
-#endif
     } else if (strstart(uri, "unix:", &p)) {
         unix_start_incoming_migration(p, errp);
     } else if (strstart(uri, "fd:", &p)) {
@@ -744,10 +742,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
     } else if (strstart(uri, "rdma:", &p)) {
         rdma_start_outgoing_migration(s, p, &local_err);
 #endif
-#if !defined(WIN32)
     } else if (strstart(uri, "exec:", &p)) {
         exec_start_outgoing_migration(s, p, &local_err);
-#endif
     } else if (strstart(uri, "unix:", &p)) {
         unix_start_outgoing_migration(s, p, &local_err);
     } else if (strstart(uri, "fd:", &p)) {
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 41/46] migration: convert RDMA to use QIOChannel interface
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (39 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 40/46] migration: convert exec " Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 42/46] migration: convert savevm to use QIOChannel for writing to files Daniel P. Berrange
                   ` (4 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

This converts the RDMA code to provide a subclass of
QIOChannel that uses RDMA for the data transport.

The RDMA code would be much better off it it could
be split up in a generic RDMA layer, a QIOChannel
impl based on RMDA, and then the RMDA migration
glue. This is left as a future exercise for the brave.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/rdma.c | 254 ++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 156 insertions(+), 98 deletions(-)

diff --git a/migration/rdma.c b/migration/rdma.c
index ca6b100..4e41c73 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -374,14 +374,19 @@ typedef struct RDMAContext {
     GHashTable *blockmap;
 } RDMAContext;
 
-/*
- * Interface to the rest of the migration call stack.
- */
-typedef struct QEMUFileRDMA {
+#define TYPE_QIO_CHANNEL_RDMA "qio-channel-rdma"
+#define QIO_CHANNEL_RDMA(obj)                                     \
+    OBJECT_CHECK(QIOChannelRDMA, (obj), TYPE_QIO_CHANNEL_RDMA)
+
+typedef struct QIOChannelRDMA QIOChannelRDMA;
+
+
+struct QIOChannelRDMA {
+    QIOChannel parent;
     RDMAContext *rdma;
+    QEMUFile *file;
     size_t len;
-    void *file;
-} QEMUFileRDMA;
+};
 
 /*
  * Main structure for IB Send/Recv control messages.
@@ -2519,15 +2524,19 @@ static void *qemu_rdma_data_init(const char *host_port, Error **errp)
  * SEND messages for control only.
  * VM's ram is handled with regular RDMA messages.
  */
-static int qemu_rdma_put_buffer(void *opaque, const uint8_t *buf,
-                                int64_t pos, int size)
-{
-    QEMUFileRDMA *r = opaque;
-    QEMUFile *f = r->file;
-    RDMAContext *rdma = r->rdma;
-    size_t remaining = size;
-    uint8_t * data = (void *) buf;
+static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
+                                       const struct iovec *iov,
+                                       size_t niov,
+                                       int *fds,
+                                       size_t nfds,
+                                       Error **errp)
+{
+    QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
+    QEMUFile *f = rioc->file;
+    RDMAContext *rdma = rioc->rdma;
     int ret;
+    ssize_t done = 0;
+    size_t i;
 
     CHECK_ERROR_STATE();
 
@@ -2541,26 +2550,31 @@ static int qemu_rdma_put_buffer(void *opaque, const uint8_t *buf,
         return ret;
     }
 
-    while (remaining) {
-        RDMAControlHeader head;
+    for (i = 0; i < niov; i++) {
+        size_t remaining = iov[i].iov_len;
+        uint8_t * data = (void *)iov[i].iov_base;
+        while (remaining) {
+            RDMAControlHeader head;
 
-        r->len = MIN(remaining, RDMA_SEND_INCREMENT);
-        remaining -= r->len;
+            rioc->len = MIN(remaining, RDMA_SEND_INCREMENT);
+            remaining -= rioc->len;
 
-        head.len = r->len;
-        head.type = RDMA_CONTROL_QEMU_FILE;
+            head.len = rioc->len;
+            head.type = RDMA_CONTROL_QEMU_FILE;
 
-        ret = qemu_rdma_exchange_send(rdma, &head, data, NULL, NULL, NULL);
+            ret = qemu_rdma_exchange_send(rdma, &head, data, NULL, NULL, NULL);
 
-        if (ret < 0) {
-            rdma->error_state = ret;
-            return ret;
-        }
+            if (ret < 0) {
+                rdma->error_state = ret;
+                return ret;
+            }
 
-        data += r->len;
+            data += rioc->len;
+            done += rioc->len;
+        }
     }
 
-    return size;
+    return done;
 }
 
 static size_t qemu_rdma_fill(RDMAContext *rdma, uint8_t *buf,
@@ -2585,41 +2599,65 @@ static size_t qemu_rdma_fill(RDMAContext *rdma, uint8_t *buf,
  * RDMA links don't use bytestreams, so we have to
  * return bytes to QEMUFile opportunistically.
  */
-static int qemu_rdma_get_buffer(void *opaque, uint8_t *buf,
-                                int64_t pos, int size)
-{
-    QEMUFileRDMA *r = opaque;
-    RDMAContext *rdma = r->rdma;
+static ssize_t qio_channel_rdma_readv(QIOChannel *ioc,
+                                      const struct iovec *iov,
+                                      size_t niov,
+                                      int **fds,
+                                      size_t *nfds,
+                                      Error **errp)
+{
+    QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
+    RDMAContext *rdma = rioc->rdma;
     RDMAControlHeader head;
     int ret = 0;
+    ssize_t i;
+    size_t done = 0;
 
     CHECK_ERROR_STATE();
 
-    /*
-     * First, we hold on to the last SEND message we
-     * were given and dish out the bytes until we run
-     * out of bytes.
-     */
-    r->len = qemu_rdma_fill(r->rdma, buf, size, 0);
-    if (r->len) {
-        return r->len;
-    }
+    for (i = 0; i < niov; i++) {
+        size_t want = iov[i].iov_len;
+        uint8_t *data = (void *)iov[i].iov_base;
 
-    /*
-     * Once we run out, we block and wait for another
-     * SEND message to arrive.
-     */
-    ret = qemu_rdma_exchange_recv(rdma, &head, RDMA_CONTROL_QEMU_FILE);
+        /*
+         * First, we hold on to the last SEND message we
+         * were given and dish out the bytes until we run
+         * out of bytes.
+         */
+        ret = qemu_rdma_fill(rioc->rdma, data, want, 0);
+        if (ret > 0) {
+            done += ret;
+            if (ret < want) {
+                break;
+            } else {
+                continue;
+            }
+        }
 
-    if (ret < 0) {
-        rdma->error_state = ret;
-        return ret;
-    }
+        /*
+         * Once we run out, we block and wait for another
+         * SEND message to arrive.
+         */
+        ret = qemu_rdma_exchange_recv(rdma, &head, RDMA_CONTROL_QEMU_FILE);
 
-    /*
-     * SEND was received with new bytes, now try again.
-     */
-    return qemu_rdma_fill(r->rdma, buf, size, 0);
+        if (ret < 0) {
+            rdma->error_state = ret;
+            return ret;
+        }
+
+        /*
+         * SEND was received with new bytes, now try again.
+         */
+        ret = qemu_rdma_fill(rioc->rdma, data, want, 0);
+        if (ret > 0) {
+            done += ret;
+            if (ret < want) {
+                break;
+            }
+        }
+    }
+    rioc->len = done;
+    return rioc->len;
 }
 
 /*
@@ -2646,15 +2684,16 @@ static int qemu_rdma_drain_cq(QEMUFile *f, RDMAContext *rdma)
     return 0;
 }
 
-static int qemu_rdma_close(void *opaque)
+static int qio_channel_rdma_close(QIOChannel *ioc,
+                                  Error **errp)
 {
+    QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
     trace_qemu_rdma_close();
-    QEMUFileRDMA *r = opaque;
-    if (r->rdma) {
-        qemu_rdma_cleanup(r->rdma);
-        g_free(r->rdma);
+    if (rioc->rdma) {
+        qemu_rdma_cleanup(rioc->rdma);
+        g_free(rioc->rdma);
+        rioc->rdma = NULL;
     }
-    g_free(r);
     return 0;
 }
 
@@ -2696,8 +2735,8 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque,
                                   ram_addr_t block_offset, ram_addr_t offset,
                                   size_t size, uint64_t *bytes_sent)
 {
-    QEMUFileRDMA *rfile = opaque;
-    RDMAContext *rdma = rfile->rdma;
+    QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
+    RDMAContext *rdma = rioc->rdma;
     int ret;
 
     CHECK_ERROR_STATE();
@@ -2951,8 +2990,8 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque)
                              };
     RDMAControlHeader blocks = { .type = RDMA_CONTROL_RAM_BLOCKS_RESULT,
                                  .repeat = 1 };
-    QEMUFileRDMA *rfile = opaque;
-    RDMAContext *rdma = rfile->rdma;
+    QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
+    RDMAContext *rdma = rioc->rdma;
     RDMALocalBlocks *local = &rdma->local_ram_blocks;
     RDMAControlHeader head;
     RDMARegister *reg, *registers;
@@ -3207,9 +3246,9 @@ out:
  * We've already built our local RAMBlock list, but not yet sent the list to
  * the source.
  */
-static int rdma_block_notification_handle(QEMUFileRDMA *rfile, const char *name)
+static int rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name)
 {
-    RDMAContext *rdma = rfile->rdma;
+    RDMAContext *rdma = rioc->rdma;
     int curr;
     int found = -1;
 
@@ -3251,8 +3290,8 @@ static int rdma_load_hook(QEMUFile *f, void *opaque, uint64_t flags, void *data)
 static int qemu_rdma_registration_start(QEMUFile *f, void *opaque,
                                         uint64_t flags, void *data)
 {
-    QEMUFileRDMA *rfile = opaque;
-    RDMAContext *rdma = rfile->rdma;
+    QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
+    RDMAContext *rdma = rioc->rdma;
 
     CHECK_ERROR_STATE();
 
@@ -3271,8 +3310,8 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
                                        uint64_t flags, void *data)
 {
     Error *local_err = NULL, **errp = &local_err;
-    QEMUFileRDMA *rfile = opaque;
-    RDMAContext *rdma = rfile->rdma;
+    QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque);
+    RDMAContext *rdma = rioc->rdma;
     RDMAControlHeader head = { .len = 0, .repeat = 1 };
     int ret = 0;
 
@@ -3368,55 +3407,74 @@ err:
     return ret;
 }
 
-static int qemu_rdma_get_fd(void *opaque)
-{
-    QEMUFileRDMA *rfile = opaque;
-    RDMAContext *rdma = rfile->rdma;
-
-    return rdma->comp_channel->fd;
-}
-
-static const QEMUFileOps rdma_read_ops = {
-    .get_buffer    = qemu_rdma_get_buffer,
-    .get_fd        = qemu_rdma_get_fd,
-    .close         = qemu_rdma_close,
-};
-
 static const QEMUFileHooks rdma_read_hooks = {
     .hook_ram_load = rdma_load_hook,
 };
 
-static const QEMUFileOps rdma_write_ops = {
-    .put_buffer         = qemu_rdma_put_buffer,
-    .close              = qemu_rdma_close,
-};
-
 static const QEMUFileHooks rdma_write_hooks = {
     .before_ram_iterate = qemu_rdma_registration_start,
     .after_ram_iterate  = qemu_rdma_registration_stop,
     .save_page          = qemu_rdma_save_page,
 };
 
-static void *qemu_fopen_rdma(RDMAContext *rdma, const char *mode)
+
+static void qio_channel_rdma_finalize(Object *obj)
+{
+    QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(obj);
+    if (rioc->rdma) {
+        qemu_rdma_cleanup(rioc->rdma);
+        g_free(rioc->rdma);
+        rioc->rdma = NULL;
+    }
+}
+
+static void qio_channel_rdma_class_init(ObjectClass *klass,
+                                        void *class_data G_GNUC_UNUSED)
+{
+    QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
+
+    ioc_klass->io_writev = qio_channel_rdma_writev;
+    ioc_klass->io_readv = qio_channel_rdma_readv;
+    //ioc_klass->io_set_blocking = qio_channel_rdma_set_blocking;
+    ioc_klass->io_close = qio_channel_rdma_close;
+    //ioc_klass->io_create_watch = qio_channel_rdma_create_watch;
+}
+
+static const TypeInfo qio_channel_rdma_info = {
+    .parent = TYPE_QIO_CHANNEL,
+    .name = TYPE_QIO_CHANNEL_RDMA,
+    .instance_size = sizeof(QIOChannelRDMA),
+    .instance_finalize = qio_channel_rdma_finalize,
+    .class_init = qio_channel_rdma_class_init,
+};
+
+static void qio_channel_rdma_register_types(void)
+{
+    type_register_static(&qio_channel_rdma_info);
+}
+
+type_init(qio_channel_rdma_register_types);
+
+static QEMUFile *qemu_fopen_rdma(RDMAContext *rdma, const char *mode)
 {
-    QEMUFileRDMA *r;
+    QIOChannelRDMA *rioc;
 
     if (qemu_file_mode_is_not_valid(mode)) {
         return NULL;
     }
 
-    r = g_malloc0(sizeof(QEMUFileRDMA));
-    r->rdma = rdma;
+    rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA));
+    rioc->rdma = rdma;
 
     if (mode[0] == 'w') {
-        r->file = qemu_fopen_ops(r, &rdma_write_ops);
-        qemu_file_set_hooks(r->file, &rdma_write_hooks);
+        rioc->file = qemu_fopen_channel_output(QIO_CHANNEL(rioc));
+        qemu_file_set_hooks(rioc->file, &rdma_write_hooks);
     } else {
-        r->file = qemu_fopen_ops(r, &rdma_read_ops);
-        qemu_file_set_hooks(r->file, &rdma_read_hooks);
+        rioc->file = qemu_fopen_channel_input(QIO_CHANNEL(rioc));
+        qemu_file_set_hooks(rioc->file, &rdma_read_hooks);
     }
 
-    return r->file;
+    return rioc->file;
 }
 
 static void rdma_accept_incoming_migration(void *opaque)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 42/46] migration: convert savevm to use QIOChannel for writing to files
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (40 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 41/46] migration: convert RDMA to use QIOChannel interface Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 43/46] migration: delete QEMUFile sockets implementation Daniel P. Berrange
                   ` (3 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Convert the exec savevm code to use QIOChannel and QEMUFileChannel,
instead of the stdio APIs.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/savevm.c   |  8 +++++---
 tests/Makefile       |  4 ++--
 tests/test-vmstate.c | 11 ++++++++++-
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index 6071215..82febef 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -48,6 +48,7 @@
 #include "qemu/iov.h"
 #include "block/snapshot.h"
 #include "block/qapi.h"
+#include "io/channel-file.h"
 
 
 #ifndef ETH_P_RARP
@@ -1389,6 +1390,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict)
 void qmp_xen_save_devices_state(const char *filename, Error **errp)
 {
     QEMUFile *f;
+    QIOChannelFile *ioc;
     int saved_vm_running;
     int ret;
 
@@ -1396,11 +1398,11 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp)
     vm_stop(RUN_STATE_SAVE_VM);
     global_state_store_running();
 
-    f = qemu_fopen(filename, "wb");
-    if (!f) {
-        error_setg_file_open(errp, errno, filename);
+    ioc = qio_channel_file_new_path(filename, O_WRONLY | O_CREAT, 0660, errp);
+    if (!ioc) {
         goto the_end;
     }
+    f = qemu_fopen_channel_output(QIO_CHANNEL(ioc));
     ret = qemu_save_device_state(f);
     qemu_fclose(f);
     if (ret < 0) {
diff --git a/tests/Makefile b/tests/Makefile
index 3d1e83c..32b9eac 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -321,8 +321,8 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
 	$(test-qapi-obj-y)
 tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
 	migration/vmstate.o migration/qemu-file.o \
-        migration/qemu-file-unix.o qjson.o \
-	$(test-qom-obj-y)
+        migration/qemu-file-channel.o qjson.o \
+	$(test-io-obj-y)
 
 tests/test-qapi-types.c tests/test-qapi-types.h :\
 $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
index 0f943d5..6bbda8a 100644
--- a/tests/test-vmstate.c
+++ b/tests/test-vmstate.c
@@ -28,6 +28,7 @@
 #include "migration/migration.h"
 #include "migration/vmstate.h"
 #include "qemu/coroutine.h"
+#include "io/channel-file.h"
 
 static char temp_file[] = "/tmp/vmst.test.XXXXXX";
 static int temp_fd;
@@ -48,11 +49,17 @@ void yield_until_fd_readable(int fd)
 static QEMUFile *open_test_file(bool write)
 {
     int fd = dup(temp_fd);
+    QIOChannel *ioc;
     lseek(fd, 0, SEEK_SET);
     if (write) {
         g_assert_cmpint(ftruncate(fd, 0), ==, 0);
     }
-    return qemu_fdopen(fd, write ? "wb" : "rb");
+    ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd));
+    if (write) {
+        return qemu_fopen_channel_output(ioc);
+    } else {
+        return qemu_fopen_channel_input(ioc);
+    }
 }
 
 #define SUCCESS(val) \
@@ -468,6 +475,8 @@ int main(int argc, char **argv)
 {
     temp_fd = mkstemp(temp_file);
 
+    module_call_init(MODULE_INIT_QOM);
+
     g_test_init(&argc, &argv, NULL);
     g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);
     g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 43/46] migration: delete QEMUFile sockets implementation
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (41 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 42/46] migration: convert savevm to use QIOChannel for writing to files Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 44/46] migration: delete QEMUFile stdio implementation Daniel P. Berrange
                   ` (2 subsequent siblings)
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Now that the tcp, unix and fd migration backends have converted
to use the QIOChannel based QEMUFile, there is no user remaining
for the sockets based QEMUFile impl and it can be deleted.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/qemu-file.h |   2 -
 migration/Makefile.objs       |   2 +-
 migration/qemu-file-unix.c    | 237 ------------------------------------------
 3 files changed, 1 insertion(+), 240 deletions(-)
 delete mode 100644 migration/qemu-file-unix.c

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index 0d9f08e..f28db54 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -130,8 +130,6 @@ struct QEMUSizedBuffer {
 
 QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops);
 QEMUFile *qemu_fopen(const char *filename, const char *mode);
-QEMUFile *qemu_fdopen(int fd, const char *mode);
-QEMUFile *qemu_fopen_socket(int fd, const char *mode);
 QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc);
 QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc);
 QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index c43c78a..5091b47 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,6 +1,6 @@
 common-obj-y += migration.o tcp.o unix.o fd.o exec.o
 common-obj-y += vmstate.o
-common-obj-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
+common-obj-y += qemu-file.o qemu-file-stdio.o
 common-obj-y += qemu-file-channel.o
 common-obj-y += xbzrle.o
 
diff --git a/migration/qemu-file-unix.c b/migration/qemu-file-unix.c
deleted file mode 100644
index 3891012..0000000
--- a/migration/qemu-file-unix.c
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "qemu-common.h"
-#include "qemu/iov.h"
-#include "qemu/sockets.h"
-#include "qemu/coroutine.h"
-#include "migration/qemu-file.h"
-
-typedef struct QEMUFileSocket {
-    int fd;
-    QEMUFile *file;
-} QEMUFileSocket;
-
-static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
-                                    int64_t pos)
-{
-    QEMUFileSocket *s = opaque;
-    ssize_t len;
-    ssize_t size = iov_size(iov, iovcnt);
-
-    len = iov_send(s->fd, iov, iovcnt, 0, size);
-    if (len < size) {
-        len = -socket_error();
-    }
-    return len;
-}
-
-static int socket_get_fd(void *opaque)
-{
-    QEMUFileSocket *s = opaque;
-
-    return s->fd;
-}
-
-static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileSocket *s = opaque;
-    ssize_t len;
-
-    for (;;) {
-        len = qemu_recv(s->fd, buf, size, 0);
-        if (len != -1) {
-            break;
-        }
-        if (socket_error() == EAGAIN) {
-            yield_until_fd_readable(s->fd);
-        } else if (socket_error() != EINTR) {
-            break;
-        }
-    }
-
-    if (len == -1) {
-        len = -socket_error();
-    }
-    return len;
-}
-
-static int socket_close(void *opaque)
-{
-    QEMUFileSocket *s = opaque;
-    closesocket(s->fd);
-    g_free(s);
-    return 0;
-}
-
-static int socket_shutdown(void *opaque, bool rd, bool wr)
-{
-    QEMUFileSocket *s = opaque;
-
-    if (shutdown(s->fd, rd ? (wr ? SHUT_RDWR : SHUT_RD) : SHUT_WR)) {
-        return -errno;
-    } else {
-        return 0;
-    }
-}
-
-static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
-                                  int64_t pos)
-{
-    QEMUFileSocket *s = opaque;
-    ssize_t len, offset;
-    ssize_t size = iov_size(iov, iovcnt);
-    ssize_t total = 0;
-
-    assert(iovcnt > 0);
-    offset = 0;
-    while (size > 0) {
-        /* Find the next start position; skip all full-sized vector elements  */
-        while (offset >= iov[0].iov_len) {
-            offset -= iov[0].iov_len;
-            iov++, iovcnt--;
-        }
-
-        /* skip `offset' bytes from the (now) first element, undo it on exit */
-        assert(iovcnt > 0);
-        iov[0].iov_base += offset;
-        iov[0].iov_len -= offset;
-
-        do {
-            len = writev(s->fd, iov, iovcnt);
-        } while (len == -1 && errno == EINTR);
-        if (len == -1) {
-            return -errno;
-        }
-
-        /* Undo the changes above */
-        iov[0].iov_base -= offset;
-        iov[0].iov_len += offset;
-
-        /* Prepare for the next iteration */
-        offset += len;
-        total += len;
-        size -= len;
-    }
-
-    return total;
-}
-
-static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileSocket *s = opaque;
-    ssize_t len;
-
-    for (;;) {
-        len = read(s->fd, buf, size);
-        if (len != -1) {
-            break;
-        }
-        if (errno == EAGAIN) {
-            yield_until_fd_readable(s->fd);
-        } else if (errno != EINTR) {
-            break;
-        }
-    }
-
-    if (len == -1) {
-        len = -errno;
-    }
-    return len;
-}
-
-static int unix_close(void *opaque)
-{
-    QEMUFileSocket *s = opaque;
-    close(s->fd);
-    g_free(s);
-    return 0;
-}
-
-static const QEMUFileOps unix_read_ops = {
-    .get_fd =     socket_get_fd,
-    .get_buffer = unix_get_buffer,
-    .close =      unix_close
-};
-
-static const QEMUFileOps unix_write_ops = {
-    .get_fd =     socket_get_fd,
-    .writev_buffer = unix_writev_buffer,
-    .close =      unix_close
-};
-
-QEMUFile *qemu_fdopen(int fd, const char *mode)
-{
-    QEMUFileSocket *s;
-
-    if (mode == NULL ||
-        (mode[0] != 'r' && mode[0] != 'w') ||
-        mode[1] != 'b' || mode[2] != 0) {
-        fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileSocket));
-    s->fd = fd;
-
-    if (mode[0] == 'r') {
-        s->file = qemu_fopen_ops(s, &unix_read_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &unix_write_ops);
-    }
-    return s->file;
-}
-
-static const QEMUFileOps socket_read_ops = {
-    .get_fd     = socket_get_fd,
-    .get_buffer = socket_get_buffer,
-    .close      = socket_close,
-    .shut_down  = socket_shutdown
-
-};
-
-static const QEMUFileOps socket_write_ops = {
-    .get_fd        = socket_get_fd,
-    .writev_buffer = socket_writev_buffer,
-    .close         = socket_close,
-    .shut_down     = socket_shutdown
-};
-
-QEMUFile *qemu_fopen_socket(int fd, const char *mode)
-{
-    QEMUFileSocket *s;
-
-    if (qemu_file_mode_is_not_valid(mode)) {
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileSocket));
-    s->fd = fd;
-    if (mode[0] == 'w') {
-        qemu_set_block(s->fd);
-        s->file = qemu_fopen_ops(s, &socket_write_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &socket_read_ops);
-    }
-    return s->file;
-}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 44/46] migration: delete QEMUFile stdio implementation
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (42 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 43/46] migration: delete QEMUFile sockets implementation Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend Daniel P. Berrange
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 46/46] migration: remove support for non-iovec based write handlers Daniel P. Berrange
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

Now that the exec migration backend and savevm have converted
to use the QIOChannel based QEMUFile, there is no user remaining
for the stdio based QEMUFile impl and it can be deleted.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/qemu-file.h |   2 -
 migration/Makefile.objs       |   2 +-
 migration/qemu-file-stdio.c   | 194 ------------------------------------------
 3 files changed, 1 insertion(+), 197 deletions(-)
 delete mode 100644 migration/qemu-file-stdio.c

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index f28db54..ce6b421 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -129,10 +129,8 @@ struct QEMUSizedBuffer {
 };
 
 QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops);
-QEMUFile *qemu_fopen(const char *filename, const char *mode);
 QEMUFile *qemu_fopen_channel_input(QIOChannel *ioc);
 QEMUFile *qemu_fopen_channel_output(QIOChannel *ioc);
-QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
 void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks);
 int qemu_get_fd(QEMUFile *f);
 int qemu_fset_blocking(QEMUFile *f, bool enabled);
diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index 5091b47..999c44e 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -1,6 +1,6 @@
 common-obj-y += migration.o tcp.o unix.o fd.o exec.o
 common-obj-y += vmstate.o
-common-obj-y += qemu-file.o qemu-file-stdio.o
+common-obj-y += qemu-file.o
 common-obj-y += qemu-file-channel.o
 common-obj-y += xbzrle.o
 
diff --git a/migration/qemu-file-stdio.c b/migration/qemu-file-stdio.c
deleted file mode 100644
index 002dc5d..0000000
--- a/migration/qemu-file-stdio.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "qemu-common.h"
-#include "qemu/coroutine.h"
-#include "migration/qemu-file.h"
-
-typedef struct QEMUFileStdio {
-    FILE *stdio_file;
-    QEMUFile *file;
-} QEMUFileStdio;
-
-static int stdio_get_fd(void *opaque)
-{
-    QEMUFileStdio *s = opaque;
-
-    return fileno(s->stdio_file);
-}
-
-static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos,
-                            int size)
-{
-    QEMUFileStdio *s = opaque;
-    int res;
-
-    res = fwrite(buf, 1, size, s->stdio_file);
-
-    if (res != size) {
-        return -errno;
-    }
-    return res;
-}
-
-static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
-    QEMUFileStdio *s = opaque;
-    FILE *fp = s->stdio_file;
-    int bytes;
-
-    for (;;) {
-        clearerr(fp);
-        bytes = fread(buf, 1, size, fp);
-        if (bytes != 0 || !ferror(fp)) {
-            break;
-        }
-        if (errno == EAGAIN) {
-            yield_until_fd_readable(fileno(fp));
-        } else if (errno != EINTR) {
-            break;
-        }
-    }
-    return bytes;
-}
-
-static int stdio_pclose(void *opaque)
-{
-    QEMUFileStdio *s = opaque;
-    int ret;
-    ret = pclose(s->stdio_file);
-    if (ret == -1) {
-        ret = -errno;
-    } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
-        /* close succeeded, but non-zero exit code: */
-        ret = -EIO; /* fake errno value */
-    }
-    g_free(s);
-    return ret;
-}
-
-static int stdio_fclose(void *opaque)
-{
-    QEMUFileStdio *s = opaque;
-    int ret = 0;
-
-    if (qemu_file_is_writable(s->file)) {
-        int fd = fileno(s->stdio_file);
-        struct stat st;
-
-        ret = fstat(fd, &st);
-        if (ret == 0 && S_ISREG(st.st_mode)) {
-            /*
-             * If the file handle is a regular file make sure the
-             * data is flushed to disk before signaling success.
-             */
-            ret = fsync(fd);
-            if (ret != 0) {
-                ret = -errno;
-                return ret;
-            }
-        }
-    }
-    if (fclose(s->stdio_file) == EOF) {
-        ret = -errno;
-    }
-    g_free(s);
-    return ret;
-}
-
-static const QEMUFileOps stdio_pipe_read_ops = {
-    .get_fd =     stdio_get_fd,
-    .get_buffer = stdio_get_buffer,
-    .close =      stdio_pclose
-};
-
-static const QEMUFileOps stdio_pipe_write_ops = {
-    .get_fd =     stdio_get_fd,
-    .put_buffer = stdio_put_buffer,
-    .close =      stdio_pclose
-};
-
-QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
-{
-    FILE *stdio_file;
-    QEMUFileStdio *s;
-
-    if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
-        fprintf(stderr, "qemu_popen: Argument validity check failed\n");
-        return NULL;
-    }
-
-    stdio_file = popen(command, mode);
-    if (stdio_file == NULL) {
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileStdio));
-
-    s->stdio_file = stdio_file;
-
-    if (mode[0] == 'r') {
-        s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
-    }
-    return s->file;
-}
-
-static const QEMUFileOps stdio_file_read_ops = {
-    .get_fd =     stdio_get_fd,
-    .get_buffer = stdio_get_buffer,
-    .close =      stdio_fclose
-};
-
-static const QEMUFileOps stdio_file_write_ops = {
-    .get_fd =     stdio_get_fd,
-    .put_buffer = stdio_put_buffer,
-    .close =      stdio_fclose
-};
-
-QEMUFile *qemu_fopen(const char *filename, const char *mode)
-{
-    QEMUFileStdio *s;
-
-    if (qemu_file_mode_is_not_valid(mode)) {
-        return NULL;
-    }
-
-    s = g_malloc0(sizeof(QEMUFileStdio));
-
-    s->stdio_file = fopen(filename, mode);
-    if (!s->stdio_file) {
-        goto fail;
-    }
-
-    if (mode[0] == 'w') {
-        s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
-    } else {
-        s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
-    }
-    return s->file;
-fail:
-    g_free(s);
-    return NULL;
-}
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (43 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 44/46] migration: delete QEMUFile stdio implementation Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  2015-09-07 16:23   ` Dr. David Alan Gilbert
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 46/46] migration: remove support for non-iovec based write handlers Daniel P. Berrange
  45 siblings, 1 reply; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

This extends the TCP migration backend so that it can make use
of QIOChannelTLS to provide transparent TLS encryption. To
trigger enablement the URI on the incoming and outgoing sides
should have 'tls-creds=ID' appended, eg

   tcp:$HOST:$PORT,tls-creds=ID

where ID is the object identifier of a set of TLS credentials
previously created using object_add / -object. There is not
currently any support for checking the migration client
certificate names against ACLs. This is pending a conversion
of the ACL code to QOM.

There is no support for dynamically negotiating use of TLS
between the incoming/outgoing side. Both sides must agree
on use of TLS out of band and set the URI accordingly. In
practice it is expected that the administrator will just
turn on use of TLS on their hosts in the libvirt config
and then libvirt will instruct QEMU to use TLS for migration.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 migration/tcp.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 qemu-options.hx |   7 +-
 2 files changed, 264 insertions(+), 16 deletions(-)

diff --git a/migration/tcp.c b/migration/tcp.c
index 2347d9d..68ecaa4 100644
--- a/migration/tcp.c
+++ b/migration/tcp.c
@@ -22,6 +22,8 @@
 #include "migration/migration.h"
 #include "migration/qemu-file.h"
 #include "io/channel-socket.h"
+#include "io/channel-tls.h"
+#include "crypto/tlscreds.h"
 
 //#define DEBUG_MIGRATION_TCP
 
@@ -33,6 +35,22 @@
     do { } while (0)
 #endif
 
+typedef struct {
+    MigrationState *s;
+    QCryptoTLSCreds *tlscreds;
+    char *hostname;
+} TCPConnectData;
+
+typedef struct {
+    MigrationState *s;
+    QCryptoTLSCreds *tlscreds;
+} TCPListenData;
+
+typedef struct {
+    MigrationState *s;
+    QIOChannel *ioc;
+} TCPConnectTLSData;
+
 
 static SocketAddress *tcp_build_address(const char *host_port, Error **errp)
 {
@@ -51,21 +69,174 @@ static SocketAddress *tcp_build_address(const char *host_port, Error **errp)
 }
 
 
+static void tcp_connect_data_free(gpointer opaque)
+{
+    TCPConnectData *data = opaque;
+    if (!data) {
+        return;
+    }
+    g_free(data->hostname);
+    object_unref(OBJECT(data->tlscreds));
+    g_free(data);
+}
+
+
+static void tcp_listen_data_free(gpointer opaque)
+{
+    TCPListenData *data = opaque;
+    if (!data) {
+        return;
+    }
+    object_unref(OBJECT(data->tlscreds));
+    g_free(data);
+}
+
+
+static void tcp_connect_tls_data_free(gpointer opaque)
+{
+    TCPConnectTLSData *data = opaque;
+    if (!data) {
+        return;
+    }
+    object_unref(OBJECT(data->ioc));
+    g_free(data);
+}
+
+
+static char *tcp_get_opt_str(const char *host_port,
+                             const char *key)
+{
+    const char *offset, *end;
+
+    offset = strstr(host_port, key);
+    if (!offset) {
+        return NULL;
+    }
+
+    offset += strlen(key);
+    if (offset[0] != '=') {
+        return NULL;
+    }
+    offset++;
+    end = strchr(offset, ',');
+    if (end) {
+        return g_strndup(offset, end - offset);
+    } else {
+        return g_strdup(offset);
+    }
+}
+
+
+static QCryptoTLSCreds *tcp_get_tls_creds(const char *host_port,
+                                          bool is_listen,
+                                          Error **errp)
+{
+    char *credname = NULL;
+    Object *creds;
+    QCryptoTLSCreds *ret;
+
+    credname = tcp_get_opt_str(host_port, "tls-creds");
+    if (!credname) {
+        return NULL;
+    }
+
+    creds = object_resolve_path_component(
+        object_get_objects_root(), credname);
+    if (!creds) {
+        error_setg(errp, "No TLS credentials with id '%s'",
+                   credname);
+        goto error;
+    }
+    ret = (QCryptoTLSCreds *)object_dynamic_cast(
+        creds, TYPE_QCRYPTO_TLS_CREDS);
+    if (!ret) {
+        error_setg(errp, "Object with id '%s' is not TLS credentials",
+                   credname);
+        goto error;
+    }
+    if (is_listen) {
+        if (ret->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+            error_setg(errp, "%s",
+                       "Expected TLS credentials for server endpoint");
+            goto error;
+        }
+    } else {
+        if (ret->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
+            error_setg(errp, "%s",
+                       "Expected TLS credentials for client endpoint");
+            goto error;
+        }
+    }
+
+    g_free(credname);
+    object_ref(OBJECT(ret));
+    return ret;
+
+ error:
+    g_free(credname);
+    return NULL;
+}
+
+
+static void tcp_outgoing_migration_tls(Object *src,
+                                       Error *err,
+                                       gpointer opaque)
+{
+    TCPConnectTLSData *data = opaque;
+    QIOChannel *ioc = QIO_CHANNEL(src);
+
+    if (err) {
+        DPRINTF("migrate connect TLS error: %s\n", error_get_pretty(err));
+        data->s->file = NULL;
+        migrate_fd_error(data->s);
+    } else {
+        DPRINTF("migrate connect success\n");
+        data->s->file = qemu_fopen_channel_output(ioc);
+        migrate_fd_connect(data->s);
+    }
+}
+
+
 static void tcp_outgoing_migration(Object *src,
                                    Error *err,
                                    gpointer opaque)
 {
-    MigrationState *s = opaque;
+    TCPConnectData *data = opaque;
     QIOChannel *sioc = QIO_CHANNEL(src);
 
     if (err) {
         DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
-        s->file = NULL;
-        migrate_fd_error(s);
+        data->s->file = NULL;
+        migrate_fd_error(data->s);
     } else {
-        DPRINTF("migrate connect success\n");
-        s->file = qemu_fopen_channel_output(sioc);
-        migrate_fd_connect(s);
+        if (data->tlscreds) {
+            Error *local_err = NULL;
+            QIOChannelTLS *tioc = qio_channel_tls_new_client(
+                sioc, data->tlscreds, data->hostname,
+                &local_err);
+            if (!tioc) {
+                DPRINTF("migrate tls setup error: %s\n",
+                        error_get_pretty(local_err));
+                error_free(local_err);
+                data->s->file = NULL;
+                migrate_fd_error(data->s);
+            } else {
+                TCPConnectTLSData *tdata =
+                    g_new0(TCPConnectTLSData, 1);
+                DPRINTF("migrate connect tls handshake begin\n");
+                tdata->s = data->s;
+                tdata->ioc = sioc;
+                object_ref(OBJECT(sioc));
+                qio_channel_tls_handshake(tioc,
+                                          tcp_outgoing_migration_tls,
+                                          tdata,
+                                          tcp_connect_tls_data_free);
+            }
+        } else {
+            DPRINTF("migrate connect success\n");
+            data->s->file = qemu_fopen_channel_output(sioc);
+            migrate_fd_connect(data->s);
+        }
     }
     object_unref(src);
 }
@@ -77,21 +248,56 @@ void tcp_start_outgoing_migration(MigrationState *s,
 {
     SocketAddress *saddr = tcp_build_address(host_port, errp);
     QIOChannelSocket *sioc;
+    Error *local_err = NULL;
+    QCryptoTLSCreds *creds;
+    TCPConnectData *data;
 
     if (!saddr) {
         return;
     }
 
+    creds = tcp_get_tls_creds(host_port, false, errp);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        qapi_free_SocketAddress(saddr);
+        return;
+    }
+
+    data = g_new0(TCPConnectData, 1);
+    data->s = s;
+    if (creds) {
+        data->hostname = g_strdup(saddr->inet->host);
+        data->tlscreds = creds;
+    }
+
     sioc = qio_channel_socket_new();
     qio_channel_socket_connect_async(sioc,
                                      saddr,
                                      tcp_outgoing_migration,
-                                     s,
-                                     NULL);
+                                     data,
+                                     tcp_connect_data_free);
     qapi_free_SocketAddress(saddr);
 }
 
 
+static void tcp_incoming_migration_tls(Object *src,
+                                       Error *err,
+                                       gpointer opaque)
+{
+    QIOChannel *ioc = QIO_CHANNEL(src);
+
+    if (err) {
+        DPRINTF("migrate listen TLS error: %s\n", error_get_pretty(err));
+        object_unref(OBJECT(ioc));
+    } else {
+        DPRINTF("migrate listen success\n");
+        QEMUFile *f = qemu_fopen_channel_input(ioc);
+
+        process_incoming_migration(f);
+    }
+}
+
+
 static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
                                               GIOCondition condition,
                                               gpointer opaque)
@@ -99,6 +305,7 @@ static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
     QEMUFile *f;
     QIOChannelSocket *cioc;
     Error *err = NULL;
+    TCPListenData *data = opaque;
 
     cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
                                      &err);
@@ -108,16 +315,38 @@ static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
         goto out;
     }
 
-    DPRINTF("accepted migration\n");
+    if (data->tlscreds) {
+        DPRINTF("Starting client TLS\n");
+        QIOChannelTLS *tioc = qio_channel_tls_new_server(
+            QIO_CHANNEL(cioc), data->tlscreds,
+            NULL, /* XXX pass ACL name */
+            &err);
+        object_unref(OBJECT(cioc));
+        if (!tioc) {
+            DPRINTF("migrate tls setup error: %s\n",
+                    error_get_pretty(err));
+            error_free(err);
+            goto out;
+        } else {
+            DPRINTF("migrate connect tls handshake begin\n");
+            qio_channel_tls_handshake(tioc,
+                                      tcp_incoming_migration_tls,
+                                      NULL,
+                                      NULL);
+        }
+    } else {
+        DPRINTF("accepted migration\n");
 
-    f = qemu_fopen_channel_input(QIO_CHANNEL(cioc));
-    object_unref(OBJECT(cioc));
+        f = qemu_fopen_channel_input(QIO_CHANNEL(cioc));
+        object_unref(OBJECT(cioc));
 
-    process_incoming_migration(f);
+        process_incoming_migration(f);
+    }
 
 out:
     /* Close listening socket as its no longer needed */
     qio_channel_close(ioc, NULL);
+    object_unref(OBJECT(ioc));
     return FALSE;
 }
 
@@ -126,23 +355,39 @@ void tcp_start_incoming_migration(const char *host_port, Error **errp)
 {
     SocketAddress *saddr = tcp_build_address(host_port, errp);
     QIOChannelSocket *listen_ioc;
+    TCPListenData *data;
+    Error *local_err = NULL;
+    QCryptoTLSCreds *creds;
 
     if (!saddr) {
         return;
     }
 
+    creds = tcp_get_tls_creds(host_port, true, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        qapi_free_SocketAddress(saddr);
+        return;
+    }
+
     listen_ioc = qio_channel_socket_new();
     if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) {
         object_unref(OBJECT(listen_ioc));
         qapi_free_SocketAddress(saddr);
+        object_unref(OBJECT(creds));
         return;
     }
 
+    data = g_new0(TCPListenData, 1);
+    if (creds) {
+        data->tlscreds = creds;
+    }
+
     qio_channel_add_watch(QIO_CHANNEL(listen_ioc),
                           G_IO_IN,
                           tcp_accept_incoming_migration,
-                          listen_ioc,
-                          (GDestroyNotify)object_unref);
+                          data,
+                          tcp_listen_data_free);
 
     qapi_free_SocketAddress(saddr);
 }
diff --git a/qemu-options.hx b/qemu-options.hx
index 6acd400..c1bf4a7 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3299,7 +3299,7 @@ Set TB size.
 ETEXI
 
 DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
-    "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6]\n" \
+    "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6][,tls-creds=ID]\n" \
     "-incoming rdma:host:port[,ipv4][,ipv6]\n" \
     "-incoming unix:socketpath\n" \
     "                prepare for incoming migration, listen on\n" \
@@ -3312,11 +3312,14 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
     "                wait for the URI to be specified via migrate_incoming\n",
     QEMU_ARCH_ALL)
 STEXI
-@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6]
+@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6][,tls-creds=ID]
 @itemx -incoming rdma:@var{host}:@var{port}[,ipv4][,ipv6]
 @findex -incoming
 Prepare for incoming migration, listen on a given tcp port.
 
+If the @var{tls-creds} parameter is specified, it should refer to the ID
+of a TLS credentials object previously created with @var{-object}.
+
 @item -incoming unix:@var{socketpath}
 Prepare for incoming migration, listen on a given unix socket.
 
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH FYI 46/46] migration: remove support for non-iovec based write handlers
  2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
                   ` (44 preceding siblings ...)
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend Daniel P. Berrange
@ 2015-09-03 15:39 ` Daniel P. Berrange
  45 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-03 15:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Juan Quintela, Dr. David Alan Gilbert, Gerd Hoffmann, Amit Shah,
	Paolo Bonzini

All the remaining QEMUFile implementations provide an iovec
based write handler, so the put_buffer callback can be removed
to simplify the code.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/migration/qemu-file.h |  9 ---------
 migration/qemu-file.c         | 36 ++++++++----------------------------
 migration/savevm.c            |  8 --------
 3 files changed, 8 insertions(+), 45 deletions(-)

diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index ce6b421..a603efc 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -29,14 +29,6 @@
 
 #include <stdint.h>
 
-/* This function writes a chunk of data to a file at the given position.
- * The pos argument can be ignored if the file is only being used for
- * streaming.  The handler must write all of the data or return a negative
- * errno value.
- */
-typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf,
-                                    int64_t pos, int size);
-
 /* Read a chunk of data from a file at the given position.  The pos argument
  * can be ignored if the file is only be used for streaming.  The number of
  * bytes actually read should be returned.
@@ -105,7 +97,6 @@ typedef size_t (QEMURamSaveFunc)(QEMUFile *f, void *opaque,
 typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr);
 
 typedef struct QEMUFileOps {
-    QEMUFilePutBufferFunc *put_buffer;
     QEMUFileGetBufferFunc *get_buffer;
     QEMUFileCloseFunc *close;
     QEMUFileGetFD *get_fd;
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index c8b0b79..573dd73 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -116,7 +116,7 @@ void qemu_file_set_error(QEMUFile *f, int ret)
 
 bool qemu_file_is_writable(QEMUFile *f)
 {
-    return f->ops->writev_buffer || f->ops->put_buffer;
+    return f->ops->writev_buffer;
 }
 
 /**
@@ -135,16 +135,9 @@ void qemu_fflush(QEMUFile *f)
         return;
     }
 
-    if (f->ops->writev_buffer) {
-        if (f->iovcnt > 0) {
-            expect = iov_size(f->iov, f->iovcnt);
-            ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
-        }
-    } else {
-        if (f->buf_index > 0) {
-            expect = f->buf_index;
-            ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
-        }
+    if (f->iovcnt > 0) {
+        expect = iov_size(f->iov, f->iovcnt);
+        ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
     }
 
     if (ret >= 0) {
@@ -339,11 +332,6 @@ static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size)
 
 void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size)
 {
-    if (!f->ops->writev_buffer) {
-        qemu_put_buffer(f, buf, size);
-        return;
-    }
-
     if (f->last_error) {
         return;
     }
@@ -367,9 +355,7 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
         }
         memcpy(f->buf + f->buf_index, buf, l);
         f->bytes_xfer += l;
-        if (f->ops->writev_buffer) {
-            add_to_iovec(f, f->buf + f->buf_index, l);
-        }
+        add_to_iovec(f, f->buf + f->buf_index, l);
         f->buf_index += l;
         if (f->buf_index == IO_BUF_SIZE) {
             qemu_fflush(f);
@@ -390,9 +376,7 @@ void qemu_put_byte(QEMUFile *f, int v)
 
     f->buf[f->buf_index] = v;
     f->bytes_xfer++;
-    if (f->ops->writev_buffer) {
-        add_to_iovec(f, f->buf + f->buf_index, 1);
-    }
+    add_to_iovec(f, f->buf + f->buf_index, 1);
     f->buf_index++;
     if (f->buf_index == IO_BUF_SIZE) {
         qemu_fflush(f);
@@ -519,12 +503,8 @@ int64_t qemu_ftell_fast(QEMUFile *f)
     int64_t ret = f->pos;
     int i;
 
-    if (f->ops->writev_buffer) {
-        for (i = 0; i < f->iovcnt; i++) {
-            ret += f->iov[i].iov_len;
-        }
-    } else {
-        ret += f->buf_index;
+    for (i = 0; i < f->iovcnt; i++) {
+        ret += f->iov[i].iov_len;
     }
 
     return ret;
diff --git a/migration/savevm.c b/migration/savevm.c
index 82febef..d1116f2 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -139,13 +139,6 @@ static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
     return qiov.size;
 }
 
-static int block_put_buffer(void *opaque, const uint8_t *buf,
-                           int64_t pos, int size)
-{
-    bdrv_save_vmstate(opaque, buf, pos, size);
-    return size;
-}
-
 static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
 {
     return bdrv_load_vmstate(opaque, buf, pos, size);
@@ -162,7 +155,6 @@ static const QEMUFileOps bdrv_read_ops = {
 };
 
 static const QEMUFileOps bdrv_write_ops = {
-    .put_buffer     = block_put_buffer,
     .writev_buffer  = block_writev_buffer,
     .close          = bdrv_fclose
 };
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class Daniel P. Berrange
@ 2015-09-07 15:31   ` Dr. David Alan Gilbert
  2015-09-07 15:41     ` Daniel P. Berrange
  0 siblings, 1 reply; 57+ messages in thread
From: Dr. David Alan Gilbert @ 2015-09-07 15:31 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

* Daniel P. Berrange (berrange@redhat.com) wrote:
> Add a QIOChannel subclass that can run the TLS protocol over
> the top of another QIOChannel instance. The object provides a
> simplified API to perform the handshake when starting the TLS
> session. The layering of TLS over the underlying channel does
> not have to be setup immediately. It is possible to take an
> existing QIOChannel that has done some handshake and then swap
> in the QIOChannelTLS layer. This allows for use with protocols
> which start TLS right away, and those which start plain text
> and then negotiate TLS.
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>

> ---
> +#ifdef QIO_DEBUG
> +#define DPRINTF(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...) do { } while (0)
> +#endif

Can you use the trace_ stuff rather than dprintf's; I've been trying
to remove them all from the migration code (and with trace configured in
stderr mode it works pretty easily).

On a different question; if this TLS channel is backed by a socket, can I do
a shutdown call that will bubble down to the socket?

Dave

> +
> +
> +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);
> +
> +    return ioc;
> +
> + error:
> +    DPRINTF("Session setup failed %s\n",
> +            error_get_pretty(*errp));
> +    object_unref(OBJECT(ioc));
> +    return NULL;
> +}
> +
> +QIOChannelTLS *
> +qio_channel_tls_new_client(QIOChannel *master,
> +                           QCryptoTLSCreds *creds,
> +                           const char *hostname,
> +                           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,
> +        hostname,
> +        NULL,
> +        QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT,
> +        errp);
> +    if (!ioc->session) {
> +        goto error;
> +    }
> +
> +    qcrypto_tls_session_set_callbacks(
> +        ioc->session,
> +        qio_channel_tls_write_handler,
> +        qio_channel_tls_read_handler,
> +        ioc);
> +
> +    return ioc;
> +
> + error:
> +    DPRINTF("Session setup failed %s\n",
> +            error_get_pretty(*errp));
> +    object_unref(OBJECT(ioc));
> +    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) {
> +        qio_task_abort(task, err);
> +        goto cleanup;
> +    }
> +
> +    status = qcrypto_tls_session_get_handshake_status(ioc->session);
> +    if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) {
> +        if (qcrypto_tls_session_check_credentials(ioc->session,
> +                                                  &err) < 0) {
> +            DPRINTF("Check creds failed session=%p err=%s\n",
> +                    ioc->session, error_get_pretty(err));
> +            qio_task_abort(task, err);
> +            goto cleanup;
> +        }
> +        DPRINTF("Handshake compelte session=%p\n",
> +                ioc->session);
> +        qio_task_complete(task);
> +    } else {
> +        GIOCondition condition;
> +        DPRINTF("Handshake still running %d\n", status);
> +        if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) {
> +            condition = G_IO_OUT;
> +        } else {
> +            condition = G_IO_IN;
> +        }
> +
> +        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);
> +
> +    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;
> +
> +    if (fds || nfds) {
> +        error_setg(errp, "%s",
> +                   _("Cannot receive file descriptors over TLS channel"));
> +        return -1;
> +    }
> +
> +    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, "%s",
> +                             _("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;
> +
> +    if (fds || nfds) {
> +        error_setg(errp, "%s",
> +                   _("Cannot send file descriptors over TLS channel"));
> +        return -1;
> +    }
> +
> +    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, "%s",
> +                             _("Cannot write to TLS channel"));
> +            return -1;
> +        }
> +        done += ret;
> +        if (ret < iov[i].iov_len) {
> +            break;
> +        }
> +    }
> +    return done;
> +}
> +
> +static void qio_channel_tls_set_blocking(QIOChannel *ioc,
> +                                         bool enabled)
> +{
> +    QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
> +
> +    qio_channel_set_blocking(tioc->master, enabled);
> +}
> +
> +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_close = qio_channel_tls_close;
> +    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/tests/.gitignore b/tests/.gitignore
> index bb66d94..aa90bb2 100644
> --- a/tests/.gitignore
> +++ b/tests/.gitignore
> @@ -26,6 +26,7 @@ test-iov
>  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
> diff --git a/tests/Makefile b/tests/Makefile
> index f896051..8138362 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -81,6 +81,7 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(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-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
>  
> @@ -366,6 +367,9 @@ 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)
>  
>  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/test-io-channel-tls.c b/tests/test-io-channel-tls.c
> new file mode 100644
> index 0000000..75a1396
> --- /dev/null
> +++ b/tests/test-io-channel-tls.c
> @@ -0,0 +1,335 @@
> +/*
> + * 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;
> +    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);
> +    qio_channel_set_blocking(QIO_CHANNEL(serverChanSock), false);
> +
> +    /* 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_io_channel_comms(false,
> +                          QIO_CHANNEL(clientChanTLS),
> +                          QIO_CHANNEL(serverChanTLS));
> +
> +    test_io_channel_comms(true,
> +                          QIO_CHANNEL(clientChanTLS),
> +                          QIO_CHANNEL(serverChanTLS));
> +
> +    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 */
> -- 
> 2.4.3
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class
  2015-09-07 15:31   ` Dr. David Alan Gilbert
@ 2015-09-07 15:41     ` Daniel P. Berrange
  2015-09-07 15:51       ` Dr. David Alan Gilbert
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-07 15:41 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

On Mon, Sep 07, 2015 at 04:31:08PM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrange (berrange@redhat.com) wrote:
> > Add a QIOChannel subclass that can run the TLS protocol over
> > the top of another QIOChannel instance. The object provides a
> > simplified API to perform the handshake when starting the TLS
> > session. The layering of TLS over the underlying channel does
> > not have to be setup immediately. It is possible to take an
> > existing QIOChannel that has done some handshake and then swap
> > in the QIOChannelTLS layer. This allows for use with protocols
> > which start TLS right away, and those which start plain text
> > and then negotiate TLS.
> > 
> > Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> 
> > ---
> > +#ifdef QIO_DEBUG
> > +#define DPRINTF(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> > +#else
> > +#define DPRINTF(fmt, ...) do { } while (0)
> > +#endif
> 
> Can you use the trace_ stuff rather than dprintf's; I've been trying
> to remove them all from the migration code (and with trace configured in
> stderr mode it works pretty easily).

Yeah, that's a good idea.

> On a different question; if this TLS channel is backed by a socket, can I do
> a shutdown call that will bubble down to the socket?

The QIOChannel abstract base class did not define any shutdown method,
since that's not a generally applicable concept - essentially only the
sockets interface can do that. So I defined it as a method just on the
QIOChannelSocket class. Given this, the QIOChannelTLS class does not
know about the shutdown call.

This isn't a big deal though - the QIOChannelTLS struct exposes a
pointer to the underling QIOChannel transport, so code that needs
to do a shutdown, can get hold of the underlying channel and call
shutdown on that.

I forgot to do this properly when I integrated with the migration
QEMUFile interface, so I'll fix that up, so shutdown works correctly
with migration when TLS is enabled.

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 13/46] io: add QIOChannelWebsock class
  2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 13/46] io: add QIOChannelWebsock class Daniel P. Berrange
@ 2015-09-07 15:44   ` Dr. David Alan Gilbert
  2015-09-07 15:50     ` Daniel P. Berrange
  0 siblings, 1 reply; 57+ messages in thread
From: Dr. David Alan Gilbert @ 2015-09-07 15:44 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

* Daniel P. Berrange (berrange@redhat.com) wrote:
> Add a QIOChannel subclass that can run the websocket protocol over
> the top of another QIOChannel instance. This initial implementation
> is only capable of acting as a websockets server. There is no support
> for acting as a websockets client yet.
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  include/io/channel-websock.h | 108 +++++
>  io/Makefile.objs             |   1 +
>  io/channel-websock.c         | 965 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1074 insertions(+)
>  create mode 100644 include/io/channel-websock.h
>  create mode 100644 io/channel-websock.c
> 
> diff --git a/include/io/channel-websock.h b/include/io/channel-websock.h
> new file mode 100644
> index 0000000..8e69d86
> --- /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 "io/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;
> +    QIOBuffer encinput;
> +    QIOBuffer encoutput;
> +    QIOBuffer rawinput;
> +    QIOBuffer 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/io/Makefile.objs b/io/Makefile.objs
> index 2b33d3c..9f93087 100644
> --- a/io/Makefile.objs
> +++ b/io/Makefile.objs
> @@ -5,3 +5,4 @@ io-obj-y += channel-watch.o
>  io-obj-y += channel-socket.o
>  io-obj-y += channel-file.o
>  io-obj-y += channel-tls.o
> +io-obj-y += channel-websock.o
> diff --git a/io/channel-websock.c b/io/channel-websock.c
> new file mode 100644
> index 0000000..0345b90
> --- /dev/null
> +++ b/io/channel-websock.c
> @@ -0,0 +1,965 @@
> +/*
> + * 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 <glib/gi18n.h>
> +
> +/* #define DEBUG_IOC */
> +
> +#ifdef DEBUG_IOC
> +#define DPRINTF(fmt, ...) \
> +    do { fprintf(stderr, "channel-websock: " fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...) \
> +    do { } while (0)
> +#endif
> +
> +/* Max amount to allow in rawinput/rawoutput buffers */
> +#define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192
> +
> +#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)

Where is that magic calculation used?

> +#define SHA1_DIGEST_LEN 20
> +
> +#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_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"
> +
> +#define QIO_CHANNEL_WEBSOCK_HEAD_MIN_LEN sizeof(uint16_t)
> +#define QIO_CHANNEL_WEBSOCK_HEAD_MAX_LEN \
> +    (QIO_CHANNEL_WEBSOCK_HEAD_MIN_LEN + sizeof(uint64_t) + sizeof(uint32_t))
> +
> +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);
> +    qio_buffer_reserve(&ioc->encoutput, responselen);
> +    qio_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,
> +            "Sec-WebSocket-Protocol");
> +    char *version = qio_channel_websock_handshake_entry(line, size,
> +            "Sec-WebSocket-Version");
> +    char *key = qio_channel_websock_handshake_entry(line, size,
> +            "Sec-WebSocket-Key");
> +
> +    if (!protocols) {
> +        error_setg(errp, "%s", _("Missing websocket protocol header data"));
> +        goto cleanup;
> +    }
> +
> +    if (!version) {
> +        error_setg(errp, "%s", _("Missing websocket version header data"));
> +        goto cleanup;
> +    }
> +
> +    if (!key) {
> +        error_setg(errp, "%s", _("Missing websocket key header data"));
> +        goto cleanup;
> +    }
> +
> +    if (!g_strrstr(protocols, "binary")) {
> +        error_setg(errp, _("No 'binary' protocol is supported by client '%s'"),
> +                   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;
> +    qio_buffer_reserve(&ioc->encinput, want);
> +    ret = qio_channel_read(ioc->master,
> +                           (char *)qio_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, "%s",
> +                       _("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;
> +    }
> +
> +    qio_buffer_advance(&ioc->encinput,
> +                       handshake_end - (char *)ioc->encinput.buffer +
> +                       strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_END));
> +    return 1;

Can you comment the return values for this function; I guess -1 is error,
1 is good, what's 0 ?

> +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;
> +
> +    DPRINTF("Sending websock handshake reply %p\n", wioc);
> +    ret = qio_channel_write(wioc->master,
> +                            (char *)wioc->encoutput.buffer,
> +                            wioc->encoutput.offset,
> +                            &err);
> +
> +    if (ret < 0) {
> +        qio_task_abort(task, err);
> +        DPRINTF("Error sending websock handshake reply %p: %s\n",
> +                wioc, error_get_pretty(err));
> +        error_free(err);
> +        return FALSE;
> +    }
> +
> +    qio_buffer_advance(&wioc->encoutput, ret);
> +    if (wioc->encoutput.offset == 0) {
> +        DPRINTF("Finished sending websock handshake %p\n",
> +                wioc);
> +        qio_task_complete(task);
> +        return FALSE;
> +    }
> +    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;
> +
> +    DPRINTF("Reading websock handshake request %p\n", wioc);
> +    ret = qio_channel_websock_handshake_read(wioc, &err);
> +    if (ret < 0) {
> +        DPRINTF("Error reading websock handshake %s\n",
> +                error_get_pretty(err));
> +        qio_task_abort(task, err);
> +        error_free(err);
> +        return FALSE;
> +    }
> +    if (ret == 0) {
> +        DPRINTF("Blocking on more request data\n");
> +        /* need more data still */
> +        return TRUE;
> +    }
> +
> +    DPRINTF("Websock request complete, adding watch for reply %p\n",
> +            wioc);
> +
> +    object_ref(OBJECT(task));
> +    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 = 0;
> +    unsigned char opcode = QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME;
> +    union {
> +        char buf[QIO_CHANNEL_WEBSOCK_HEAD_MAX_LEN];
> +        QIOChannelWebsockHeader ws;
> +    } header;
> +
> +    DPRINTF("Encoding pending raw output %zu %p\n",
> +            ioc->rawoutput.offset, ioc);
> +    if (!ioc->rawoutput.offset) {
> +        return;
> +    }
> +
> +    header.ws.b0 = 0x80 | (opcode & 0x0f);

There are quite a few magic header sizes 125, (and I think I saw
some other sizes below) - some comments on them, or constants?

> +    if (ioc->rawoutput.offset <= 125) {

Dave

> +        header.ws.b1 = (uint8_t)ioc->rawoutput.offset;
> +        header_size = 2;
> +    } else if (ioc->rawoutput.offset < 65536) {
> +        header.ws.b1 = 0x7e;
> +        header.ws.u.s16.l16 = cpu_to_be16((uint16_t)ioc->rawoutput.offset);
> +        header_size = 4;
> +    } else {
> +        header.ws.b1 = 0x7f;
> +        header.ws.u.s64.l64 = cpu_to_be64(ioc->rawoutput.offset);
> +        header_size = 10;
> +    }
> +
> +    qio_buffer_reserve(&ioc->encoutput, header_size + ioc->rawoutput.offset);
> +    qio_buffer_append(&ioc->encoutput, header.buf, header_size);
> +    qio_buffer_append(&ioc->encoutput, ioc->rawoutput.buffer,
> +                      ioc->rawoutput.offset);
> +    qio_buffer_reset(&ioc->rawoutput);
> +    DPRINTF("Have %zu bytes encoded output %p\n",
> +            ioc->encoutput.offset, ioc);
> +}
> +
> +
> +static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
> +                                                 Error **errp)
> +{
> +    unsigned char opcode = 0, fin = 0, has_mask = 0;
> +    size_t header_size = 0;
> +    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_HEAD_MIN_LEN + 4) {
> +        /* header not complete */
> +        return QIO_CHANNEL_ERR_BLOCK;
> +    }
> +
> +    fin = (header->b0 & 0x80) >> 7;
> +    opcode = header->b0 & 0x0f;
> +    has_mask = (header->b1 & 0x80) >> 7;
> +    payload_len = header->b1 & 0x7f;
> +
> +    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, "%s", _("websocket fragmentation is not supported"));
> +        return -1;
> +    }
> +    if (!has_mask) {
> +        error_setg(errp, "%s", _("websocket frames must be masked"));
> +        return -1;
> +    }
> +    if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
> +        error_setg(errp, "%s", _("only binary websocket frames are supported"));
> +        return -1;
> +    }
> +
> +    if (payload_len < 126) {
> +        ioc->payload_remain = payload_len;
> +        header_size = 6;
> +        ioc->mask = header->u.m;
> +    } else if (payload_len == 126 && ioc->encinput.offset >= 8) {
> +        ioc->payload_remain = be16_to_cpu(header->u.s16.l16);
> +        header_size = 8;
> +        ioc->mask = header->u.s16.m16;
> +    } else if (payload_len == 127 && ioc->encinput.offset >= 14) {
> +        ioc->payload_remain = be64_to_cpu(header->u.s64.l64);
> +        header_size = 14;
> +        ioc->mask = header->u.s64.m64;
> +    } else {
> +        /* header not complete */
> +        return QIO_CHANNEL_ERR_BLOCK;
> +    }
> +
> +    qio_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, "%s",
> +                   _("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];
> +    }
> +
> +    qio_buffer_reserve(&ioc->rawinput, payload_len);
> +    qio_buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len);
> +    qio_buffer_advance(&ioc->encinput, payload_len);
> +    return payload_len;
> +}
> +
> +
> +QIOChannelWebsock *
> +qio_channel_websock_new_server(QIOChannel *master)
> +{
> +    QIOChannelWebsock *wioc;
> +
> +    wioc = QIO_CHANNEL_WEBSOCK(object_new(TYPE_QIO_CHANNEL_WEBSOCK));
> +
> +    wioc->master = master;
> +    object_ref(OBJECT(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);
> +
> +    DPRINTF("Adding watch on master %p for websocket %p handshake\n",
> +            ioc->master, ioc);
> +    qio_channel_add_watch(ioc->master,
> +                          G_IO_IN,
> +                          qio_channel_websock_handshake_io,
> +                          task,
> +                          NULL);
> +}
> +
> +static void qio_channel_websock_init(Object *obj G_GNUC_UNUSED)
> +{
> +}
> +
> +
> +static void qio_channel_websock_finalize(Object *obj)
> +{
> +    QIOChannelWebsock *ioc = QIO_CHANNEL_WEBSOCK(obj);
> +
> +    qio_buffer_free(&ioc->encinput);
> +    qio_buffer_free(&ioc->encoutput);
> +    qio_buffer_free(&ioc->rawinput);
> +    qio_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;
> +
> +    DPRINTF("Have %zu bytes %p\n", ioc->encoutput.offset, ioc);
> +    if (ioc->encinput.offset < 4096) {
> +        size_t want = 4096 - ioc->encinput.offset;
> +
> +        qio_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) {
> +            DPRINTF("EOF on wire & no more enc data availabl\n");
> +            return 0;
> +        }
> +        ioc->encinput.offset += ret;
> +        DPRINTF("Now have %zu bytes enc input\n", ioc->encinput.offset);
> +    }
> +
> +    if (ioc->payload_remain == 0) {
> +        DPRINTF("Looking to decode header\n");
> +        ret = qio_channel_websock_decode_header(ioc, errp);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +        if (ret == 0) {
> +            DPRINTF("EOF when decoding header\n");
> +            return 0;
> +        }
> +    }
> +    DPRINTF("Looking to decode payload %zu\n", ioc->payload_remain);
> +
> +    ret = qio_channel_websock_decode_payload(ioc, errp);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +    DPRINTF("Now have %zu bytes raw input\n", ioc->rawinput.offset);
> +    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);
> +
> +    DPRINTF("Writing %zu bytes %p\n", ioc->encoutput.offset, 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) {
> +                DPRINTF("Blocking but wrote %zu\n", done);
> +                return done;
> +            } else {
> +                DPRINTF("Error while writing %s\n",
> +                        error_get_pretty(*errp));
> +                return ret;
> +            }
> +        }
> +        qio_buffer_advance(&ioc->encoutput, ret);
> +        done += ret;
> +    }
> +    DPRINTF("Wrote %zu total\n", done);
> +    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;
> +
> +    DPRINTF("Websock master flush %p %d\n", ioc, condition);
> +    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) {
> +            DPRINTF("Got EOF when reading %p\n", wioc);
> +            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) {
> +        DPRINTF("Removing old master watch %u %p\n", ioc->io_tag, ioc);
> +        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) {
> +        DPRINTF("Not adding master watch due to error %p %s\n",
> +                ioc, error_get_pretty(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;
> +    }
> +
> +    DPRINTF("Cond %d output=%zu input=%zu eof=%d\n",
> +            cond, ioc->encoutput.offset, ioc->encinput.offset, ioc->io_eof);
> +    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;
> +    DPRINTF("Read ioc %p %zu %p\n", iov, niov, ioc);
> +    if (fds || nfds) {
> +        error_setg(errp, "%s",
> +                   _("Cannot receive file descriptors over websocket channel"));
> +        return -1;
> +    }
> +
> +    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;
> +        }
> +    }
> +
> +    qio_buffer_advance(&wioc->rawinput, got);
> +    qio_channel_websock_set_watch(wioc);
> +    DPRINTF("Returning %zu\n", got);
> +    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 (fds || nfds) {
> +        error_setg(errp, "%s",
> +                   _("Cannot send file descriptors over websocket channel"));
> +        return -1;
> +    }
> +
> +    DPRINTF("Writev %p %zu %p err=%p\n", iov, niov, ioc, wioc->io_err);
> +    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;
> +        }
> +
> +        qio_buffer_reserve(&wioc->rawoutput, want);
> +        qio_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 void qio_channel_websock_set_blocking(QIOChannel *ioc,
> +                                             bool enabled)
> +{
> +    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
> +
> +    qio_channel_set_blocking(wioc->master, enabled);
> +}
> +
> +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;
> +    }
> +
> +#if 0
> +    DPRINTF("Prep source %d cond %d input=%zu output=%zu\n",
> +            wsource->condition, cond,
> +            wsource->wioc->rawinput.offset,
> +            wsource->wioc->rawoutput.offset);
> +#endif
> +
> +    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;
> +    }
> +
> +    if (cond & wsource->condition) {
> +        DPRINTF("Check source %d cond %d input=%zu output=%zu\n",
> +                wsource->condition, cond,
> +                wsource->wioc->rawinput.offset,
> +                wsource->wioc->rawoutput.offset);
> +    }
> +    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;
> +    }
> +
> +    DPRINTF("Disp source %d cond %d input=%zu output=%zu\n",
> +            wsource->condition, cond,
> +            wsource->wioc->rawinput.offset,
> +            wsource->wioc->rawoutput.offset);
> +    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;
> +
> +    DPRINTF("Creating websock watch %p cond=%d\n", wioc, condition);
> +    source = g_source_new(&qio_channel_websock_source_funcs,
> +                          sizeof(QIOChannelWebsockSource));
> +    g_source_set_name(source, "QIOChannelWebsock");
> +    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_close = qio_channel_websock_close;
> +    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_init = qio_channel_websock_init,
> +    .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);
> -- 
> 2.4.3
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 13/46] io: add QIOChannelWebsock class
  2015-09-07 15:44   ` Dr. David Alan Gilbert
@ 2015-09-07 15:50     ` Daniel P. Berrange
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-07 15:50 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

On Mon, Sep 07, 2015 at 04:44:18PM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrange (berrange@redhat.com) wrote:
> > Add a QIOChannel subclass that can run the websocket protocol over
> > the top of another QIOChannel instance. This initial implementation
> > is only capable of acting as a websockets server. There is no support
> > for acting as a websockets client yet.
> > 
> > Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> > ---
> >  include/io/channel-websock.h | 108 +++++
> >  io/Makefile.objs             |   1 +
> >  io/channel-websock.c         | 965 +++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 1074 insertions(+)
> >  create mode 100644 include/io/channel-websock.h
> >  create mode 100644 io/channel-websock.c


> > diff --git a/io/channel-websock.c b/io/channel-websock.c
> > new file mode 100644
> > index 0000000..0345b90
> > --- /dev/null
> > +++ b/io/channel-websock.c
> > @@ -0,0 +1,965 @@
> > +
> > +#include "io/channel-websock.h"
> > +#include "crypto/hash.h"
> > +
> > +#include <glib/gi18n.h>
> > +
> > +/* #define DEBUG_IOC */
> > +
> > +#ifdef DEBUG_IOC
> > +#define DPRINTF(fmt, ...) \
> > +    do { fprintf(stderr, "channel-websock: " fmt, ## __VA_ARGS__); } while (0)
> > +#else
> > +#define DPRINTF(fmt, ...) \
> > +    do { } while (0)
> > +#endif
> > +
> > +/* Max amount to allow in rawinput/rawoutput buffers */
> > +#define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192
> > +
> > +#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
> 
> Where is that magic calculation used?

I copied these constants from the current ui/vnc-ws.h header file
originally. In there it was referenced by another constant
WS_ACCEPT_LEN, which was itself unused. I deleted WS_ACCEPT_LEN
when I found it unused, but forgot to delete this B64LEN too.

> > +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;
> > +    qio_buffer_reserve(&ioc->encinput, want);
> > +    ret = qio_channel_read(ioc->master,
> > +                           (char *)qio_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, "%s",
> > +                       _("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;
> > +    }
> > +
> > +    qio_buffer_advance(&ioc->encinput,
> > +                       handshake_end - (char *)ioc->encinput.buffer +
> > +                       strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_END));
> > +    return 1;
> 
> Can you comment the return values for this function; I guess -1 is error,
> 1 is good, what's 0 ?

1 means we've read the full handshake response, 0 means we need to
receive more data still.


> > +static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
> > +{
> > +    size_t header_size = 0;
> > +    unsigned char opcode = QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME;
> > +    union {
> > +        char buf[QIO_CHANNEL_WEBSOCK_HEAD_MAX_LEN];
> > +        QIOChannelWebsockHeader ws;
> > +    } header;
> > +
> > +    DPRINTF("Encoding pending raw output %zu %p\n",
> > +            ioc->rawoutput.offset, ioc);
> > +    if (!ioc->rawoutput.offset) {
> > +        return;
> > +    }
> > +
> > +    header.ws.b0 = 0x80 | (opcode & 0x0f);
> 
> There are quite a few magic header sizes 125, (and I think I saw
> some other sizes below) - some comments on them, or constants?

This code was derived from ui/vnc-ws.c which is full of magic,
so I've mostly just inherited decisions made by that original
author. I'll fix up this new code to be less magic and use
constants and/or comments to clarify

> 
> > +    if (ioc->rawoutput.offset <= 125) {
> 
> Dave
> 
> > +        header.ws.b1 = (uint8_t)ioc->rawoutput.offset;
> > +        header_size = 2;
> > +    } else if (ioc->rawoutput.offset < 65536) {
> > +        header.ws.b1 = 0x7e;
> > +        header.ws.u.s16.l16 = cpu_to_be16((uint16_t)ioc->rawoutput.offset);
> > +        header_size = 4;
> > +    } else {
> > +        header.ws.b1 = 0x7f;
> > +        header.ws.u.s64.l64 = cpu_to_be64(ioc->rawoutput.offset);
> > +        header_size = 10;
> > +    }
> > +
> > +    qio_buffer_reserve(&ioc->encoutput, header_size + ioc->rawoutput.offset);
> > +    qio_buffer_append(&ioc->encoutput, header.buf, header_size);
> > +    qio_buffer_append(&ioc->encoutput, ioc->rawoutput.buffer,
> > +                      ioc->rawoutput.offset);
> > +    qio_buffer_reset(&ioc->rawoutput);
> > +    DPRINTF("Have %zu bytes encoded output %p\n",
> > +            ioc->encoutput.offset, ioc);
> > +}

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class
  2015-09-07 15:41     ` Daniel P. Berrange
@ 2015-09-07 15:51       ` Dr. David Alan Gilbert
  2015-09-07 16:04         ` Daniel P. Berrange
  0 siblings, 1 reply; 57+ messages in thread
From: Dr. David Alan Gilbert @ 2015-09-07 15:51 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

* Daniel P. Berrange (berrange@redhat.com) wrote:
> On Mon, Sep 07, 2015 at 04:31:08PM +0100, Dr. David Alan Gilbert wrote:
> > * Daniel P. Berrange (berrange@redhat.com) wrote:
> > > Add a QIOChannel subclass that can run the TLS protocol over
> > > the top of another QIOChannel instance. The object provides a
> > > simplified API to perform the handshake when starting the TLS
> > > session. The layering of TLS over the underlying channel does
> > > not have to be setup immediately. It is possible to take an
> > > existing QIOChannel that has done some handshake and then swap
> > > in the QIOChannelTLS layer. This allows for use with protocols
> > > which start TLS right away, and those which start plain text
> > > and then negotiate TLS.
> > > 
> > > Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> > 
> > > ---
> > > +#ifdef QIO_DEBUG
> > > +#define DPRINTF(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> > > +#else
> > > +#define DPRINTF(fmt, ...) do { } while (0)
> > > +#endif
> > 
> > Can you use the trace_ stuff rather than dprintf's; I've been trying
> > to remove them all from the migration code (and with trace configured in
> > stderr mode it works pretty easily).
> 
> Yeah, that's a good idea.
> 
> > On a different question; if this TLS channel is backed by a socket, can I do
> > a shutdown call that will bubble down to the socket?
> 
> The QIOChannel abstract base class did not define any shutdown method,
> since that's not a generally applicable concept - essentially only the
> sockets interface can do that. So I defined it as a method just on the
> QIOChannelSocket class. Given this, the QIOChannelTLS class does not
> know about the shutdown call.
> 
> This isn't a big deal though - the QIOChannelTLS struct exposes a
> pointer to the underling QIOChannel transport, so code that needs
> to do a shutdown, can get hold of the underlying channel and call
> shutdown on that.

You can imagine something like   compression->TLS->socket  and then it gets 
into the caller having to do a generic walk to figure out if it can
do it;  I'd rather not have to do that in caller.
I think I'd rather it was a facility on QIOChannel and then it gets
some type of ENOTSUPP error if it hits a layer that doesn't support it;
I guess the same might be true for socket behaviours like nagling and
maybe blocking.

> I forgot to do this properly when I integrated with the migration
> QEMUFile interface, so I'll fix that up, so shutdown works correctly
> with migration when TLS is enabled.

Dave

> 
> Regards,
> Daniel
> -- 
> |: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
> |: http://libvirt.org              -o-             http://virt-manager.org :|
> |: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
> |: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class
  2015-09-07 15:51       ` Dr. David Alan Gilbert
@ 2015-09-07 16:04         ` Daniel P. Berrange
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-07 16:04 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

On Mon, Sep 07, 2015 at 04:51:59PM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrange (berrange@redhat.com) wrote:
> > On Mon, Sep 07, 2015 at 04:31:08PM +0100, Dr. David Alan Gilbert wrote:
> > > * Daniel P. Berrange (berrange@redhat.com) wrote:
> > > > Add a QIOChannel subclass that can run the TLS protocol over
> > > > the top of another QIOChannel instance. The object provides a
> > > > simplified API to perform the handshake when starting the TLS
> > > > session. The layering of TLS over the underlying channel does
> > > > not have to be setup immediately. It is possible to take an
> > > > existing QIOChannel that has done some handshake and then swap
> > > > in the QIOChannelTLS layer. This allows for use with protocols
> > > > which start TLS right away, and those which start plain text
> > > > and then negotiate TLS.
> > > > 
> > > > Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> > > 
> > > > ---
> > > > +#ifdef QIO_DEBUG
> > > > +#define DPRINTF(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
> > > > +#else
> > > > +#define DPRINTF(fmt, ...) do { } while (0)
> > > > +#endif
> > > 
> > > Can you use the trace_ stuff rather than dprintf's; I've been trying
> > > to remove them all from the migration code (and with trace configured in
> > > stderr mode it works pretty easily).
> > 
> > Yeah, that's a good idea.
> > 
> > > On a different question; if this TLS channel is backed by a socket, can I do
> > > a shutdown call that will bubble down to the socket?
> > 
> > The QIOChannel abstract base class did not define any shutdown method,
> > since that's not a generally applicable concept - essentially only the
> > sockets interface can do that. So I defined it as a method just on the
> > QIOChannelSocket class. Given this, the QIOChannelTLS class does not
> > know about the shutdown call.
> > 
> > This isn't a big deal though - the QIOChannelTLS struct exposes a
> > pointer to the underling QIOChannel transport, so code that needs
> > to do a shutdown, can get hold of the underlying channel and call
> > shutdown on that.
> 
> You can imagine something like   compression->TLS->socket  and then it gets 
> into the caller having to do a generic walk to figure out if it can
> do it;  I'd rather not have to do that in caller.
> I think I'd rather it was a facility on QIOChannel and then it gets
> some type of ENOTSUPP error if it hits a layer that doesn't support it;
> I guess the same might be true for socket behaviours like nagling and
> maybe blocking.

Yeah, nagling I have defined as an API only on QIOChannelSocket.
The blocking flag though was on the base QIOChannel interface.

On the QIOChannel interface I did in fact provide the facility
to do UNIX FD passing, which is a UNIX socket only feature,
along with a feature query mechanism. So I guess I already
have precedent for putting such features in the base class.
So I'll update this series to move some of those methods like
shutdown, nagle, cork, in the base QIOChannel interface.


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 29/46] migration: remove use of qemu_bufopen from vmstate tests
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 29/46] migration: remove use of qemu_bufopen from vmstate tests Daniel P. Berrange
@ 2015-09-07 16:08   ` Dr. David Alan Gilbert
  2015-09-07 16:17     ` Daniel P. Berrange
  0 siblings, 1 reply; 57+ messages in thread
From: Dr. David Alan Gilbert @ 2015-09-07 16:08 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

* Daniel P. Berrange (berrange@redhat.com) wrote:
> The test-vmstate.c file is the only remaining user of the
> qemu_bufopen function. Half of the test cases already use
> a temporary file, so convert the remaining cases to match

As mentioned on irc; the buffered stuff is used by the postcopy
series and COLO.

Dave

> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  tests/Makefile       |  2 +-
>  tests/test-vmstate.c | 44 +++++++++++++-------------------------------
>  2 files changed, 14 insertions(+), 32 deletions(-)
> 
> diff --git a/tests/Makefile b/tests/Makefile
> index cbcec26..3d1e83c 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -320,7 +320,7 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
>  	hw/core/fw-path-provider.o \
>  	$(test-qapi-obj-y)
>  tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
> -	migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \
> +	migration/vmstate.o migration/qemu-file.o \
>          migration/qemu-file-unix.o qjson.o \
>  	$(test-qom-obj-y)
>  
> diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
> index 4d13bd0..0f943d5 100644
> --- a/tests/test-vmstate.c
> +++ b/tests/test-vmstate.c
> @@ -43,11 +43,6 @@ void yield_until_fd_readable(int fd)
>      select(fd + 1, &fds, NULL, NULL, NULL);
>  }
>  
> -/*
> - * Some tests use 'open_test_file' to work on a real fd, some use
> - * an in memory file (QEMUSizedBuffer+qemu_bufopen); we could pick one
> - * but this way we test both.
> - */
>  
>  /* Duplicate temp_fd and seek to the beginning of the file */
>  static QEMUFile *open_test_file(bool write)
> @@ -60,20 +55,6 @@ static QEMUFile *open_test_file(bool write)
>      return qemu_fdopen(fd, write ? "wb" : "rb");
>  }
>  
> -/*
> - * Check that the contents of the memory-buffered file f match
> - * the given size/data.
> - */
> -static void check_mem_file(QEMUFile *f, void *data, size_t size)
> -{
> -    uint8_t *result = g_malloc(size);
> -    const QEMUSizedBuffer *qsb = qemu_buf_get(f);
> -    g_assert_cmpint(qsb_get_length(qsb), ==, size);
> -    g_assert_cmpint(qsb_get_buffer(qsb, 0, size, result), ==, size);
> -    g_assert_cmpint(memcmp(result, data, size), ==, 0);
> -    g_free(result);
> -}
> -
>  #define SUCCESS(val) \
>      g_assert_cmpint((val), ==, 0)
>  
> @@ -391,7 +372,7 @@ static const VMStateDescription vmstate_skipping = {
>  
>  static void test_save_noskip(void)
>  {
> -    QEMUFile *fsave = qemu_bufopen("w", NULL);
> +    QEMUFile *fsave = open_test_file(true);
>      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
>                         .skip_c_e = false };
>      vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> @@ -405,13 +386,14 @@ static void test_save_noskip(void)
>          0, 0, 0, 5,             /* e */
>          0, 0, 0, 0, 0, 0, 0, 6, /* f */
>      };
> -    check_mem_file(fsave, expected, sizeof(expected));
> +
>      qemu_fclose(fsave);
> +    compare_vmstate(expected, sizeof(expected));
>  }
>  
>  static void test_save_skip(void)
>  {
> -    QEMUFile *fsave = qemu_bufopen("w", NULL);
> +    QEMUFile *fsave = open_test_file(true);
>      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
>                         .skip_c_e = true };
>      vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> @@ -423,13 +405,14 @@ static void test_save_skip(void)
>          0, 0, 0, 0, 0, 0, 0, 4, /* d */
>          0, 0, 0, 0, 0, 0, 0, 6, /* f */
>      };
> -    check_mem_file(fsave, expected, sizeof(expected));
>  
>      qemu_fclose(fsave);
> +    compare_vmstate(expected, sizeof(expected));
>  }
>  
>  static void test_load_noskip(void)
>  {
> +    QEMUFile *fsave = open_test_file(true);
>      uint8_t buf[] = {
>          0, 0, 0, 10,             /* a */
>          0, 0, 0, 20,             /* b */
> @@ -439,10 +422,10 @@ static void test_load_noskip(void)
>          0, 0, 0, 0, 0, 0, 0, 60, /* f */
>          QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
>      };
> +    qemu_put_buffer(fsave, buf, sizeof(buf));
> +    qemu_fclose(fsave);
>  
> -    QEMUSizedBuffer *qsb = qsb_create(buf, sizeof(buf));
> -    g_assert(qsb);
> -    QEMUFile *loading = qemu_bufopen("r", qsb);
> +    QEMUFile *loading = open_test_file(false);
>      TestStruct obj = { .skip_c_e = false };
>      vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
>      g_assert(!qemu_file_get_error(loading));
> @@ -453,11 +436,11 @@ static void test_load_noskip(void)
>      g_assert_cmpint(obj.e, ==, 50);
>      g_assert_cmpint(obj.f, ==, 60);
>      qemu_fclose(loading);
> -    qsb_free(qsb);
>  }
>  
>  static void test_load_skip(void)
>  {
> +    QEMUFile *fsave = open_test_file(true);
>      uint8_t buf[] = {
>          0, 0, 0, 10,             /* a */
>          0, 0, 0, 20,             /* b */
> @@ -465,10 +448,10 @@ static void test_load_skip(void)
>          0, 0, 0, 0, 0, 0, 0, 60, /* f */
>          QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
>      };
> +    qemu_put_buffer(fsave, buf, sizeof(buf));
> +    qemu_fclose(fsave);
>  
> -    QEMUSizedBuffer *qsb = qsb_create(buf, sizeof(buf));
> -    g_assert(qsb);
> -    QEMUFile *loading = qemu_bufopen("r", qsb);
> +    QEMUFile *loading = open_test_file(false);
>      TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
>      vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
>      g_assert(!qemu_file_get_error(loading));
> @@ -479,7 +462,6 @@ static void test_load_skip(void)
>      g_assert_cmpint(obj.e, ==, 500);
>      g_assert_cmpint(obj.f, ==, 60);
>      qemu_fclose(loading);
> -    qsb_free(qsb);
>  }
>  
>  int main(int argc, char **argv)
> -- 
> 2.4.3
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 29/46] migration: remove use of qemu_bufopen from vmstate tests
  2015-09-07 16:08   ` Dr. David Alan Gilbert
@ 2015-09-07 16:17     ` Daniel P. Berrange
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-07 16:17 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

On Mon, Sep 07, 2015 at 05:08:17PM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrange (berrange@redhat.com) wrote:
> > The test-vmstate.c file is the only remaining user of the
> > qemu_bufopen function. Half of the test cases already use
> > a temporary file, so convert the remaining cases to match
> 
> As mentioned on irc; the buffered stuff is used by the postcopy
> series and COLO.

Yep, there's nothing in my series (yet) that really requires
that we delete this QEMUFile impl. I was just trying to see
if it was possible to get to a point where the /only/ QEMUFile
impl was the QIOChannel one. I almost succeeded in that, the
exception being the impl needed for writing internal QCow2
snapshots. I'll look at creating a memory buffer based
QIOChannel, or alternatively just drop this patch, to avoid
breaking COLO/postcopy.

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend
  2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend Daniel P. Berrange
@ 2015-09-07 16:23   ` Dr. David Alan Gilbert
  2015-09-07 16:29     ` Daniel P. Berrange
  0 siblings, 1 reply; 57+ messages in thread
From: Dr. David Alan Gilbert @ 2015-09-07 16:23 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

* Daniel P. Berrange (berrange@redhat.com) wrote:
> This extends the TCP migration backend so that it can make use
> of QIOChannelTLS to provide transparent TLS encryption. To
> trigger enablement the URI on the incoming and outgoing sides
> should have 'tls-creds=ID' appended, eg
> 
>    tcp:$HOST:$PORT,tls-creds=ID

What makes this tcp specifc? Would it work on any bidirectional
transport?

Dave

> where ID is the object identifier of a set of TLS credentials
> previously created using object_add / -object. There is not
> currently any support for checking the migration client
> certificate names against ACLs. This is pending a conversion
> of the ACL code to QOM.
> 
> There is no support for dynamically negotiating use of TLS
> between the incoming/outgoing side. Both sides must agree
> on use of TLS out of band and set the URI accordingly. In
> practice it is expected that the administrator will just
> turn on use of TLS on their hosts in the libvirt config
> and then libvirt will instruct QEMU to use TLS for migration.
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  migration/tcp.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  qemu-options.hx |   7 +-
>  2 files changed, 264 insertions(+), 16 deletions(-)
> 
> diff --git a/migration/tcp.c b/migration/tcp.c
> index 2347d9d..68ecaa4 100644
> --- a/migration/tcp.c
> +++ b/migration/tcp.c
> @@ -22,6 +22,8 @@
>  #include "migration/migration.h"
>  #include "migration/qemu-file.h"
>  #include "io/channel-socket.h"
> +#include "io/channel-tls.h"
> +#include "crypto/tlscreds.h"
>  
>  //#define DEBUG_MIGRATION_TCP
>  
> @@ -33,6 +35,22 @@
>      do { } while (0)
>  #endif
>  
> +typedef struct {
> +    MigrationState *s;
> +    QCryptoTLSCreds *tlscreds;
> +    char *hostname;
> +} TCPConnectData;
> +
> +typedef struct {
> +    MigrationState *s;
> +    QCryptoTLSCreds *tlscreds;
> +} TCPListenData;
> +
> +typedef struct {
> +    MigrationState *s;
> +    QIOChannel *ioc;
> +} TCPConnectTLSData;
> +
>  
>  static SocketAddress *tcp_build_address(const char *host_port, Error **errp)
>  {
> @@ -51,21 +69,174 @@ static SocketAddress *tcp_build_address(const char *host_port, Error **errp)
>  }
>  
>  
> +static void tcp_connect_data_free(gpointer opaque)
> +{
> +    TCPConnectData *data = opaque;
> +    if (!data) {
> +        return;
> +    }
> +    g_free(data->hostname);
> +    object_unref(OBJECT(data->tlscreds));
> +    g_free(data);
> +}
> +
> +
> +static void tcp_listen_data_free(gpointer opaque)
> +{
> +    TCPListenData *data = opaque;
> +    if (!data) {
> +        return;
> +    }
> +    object_unref(OBJECT(data->tlscreds));
> +    g_free(data);
> +}
> +
> +
> +static void tcp_connect_tls_data_free(gpointer opaque)
> +{
> +    TCPConnectTLSData *data = opaque;
> +    if (!data) {
> +        return;
> +    }
> +    object_unref(OBJECT(data->ioc));
> +    g_free(data);
> +}
> +
> +
> +static char *tcp_get_opt_str(const char *host_port,
> +                             const char *key)
> +{
> +    const char *offset, *end;
> +
> +    offset = strstr(host_port, key);
> +    if (!offset) {
> +        return NULL;
> +    }
> +
> +    offset += strlen(key);
> +    if (offset[0] != '=') {
> +        return NULL;
> +    }
> +    offset++;
> +    end = strchr(offset, ',');
> +    if (end) {
> +        return g_strndup(offset, end - offset);
> +    } else {
> +        return g_strdup(offset);
> +    }
> +}
> +
> +
> +static QCryptoTLSCreds *tcp_get_tls_creds(const char *host_port,
> +                                          bool is_listen,
> +                                          Error **errp)
> +{
> +    char *credname = NULL;
> +    Object *creds;
> +    QCryptoTLSCreds *ret;
> +
> +    credname = tcp_get_opt_str(host_port, "tls-creds");
> +    if (!credname) {
> +        return NULL;
> +    }
> +
> +    creds = object_resolve_path_component(
> +        object_get_objects_root(), credname);
> +    if (!creds) {
> +        error_setg(errp, "No TLS credentials with id '%s'",
> +                   credname);
> +        goto error;
> +    }
> +    ret = (QCryptoTLSCreds *)object_dynamic_cast(
> +        creds, TYPE_QCRYPTO_TLS_CREDS);
> +    if (!ret) {
> +        error_setg(errp, "Object with id '%s' is not TLS credentials",
> +                   credname);
> +        goto error;
> +    }
> +    if (is_listen) {
> +        if (ret->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> +            error_setg(errp, "%s",
> +                       "Expected TLS credentials for server endpoint");
> +            goto error;
> +        }
> +    } else {
> +        if (ret->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
> +            error_setg(errp, "%s",
> +                       "Expected TLS credentials for client endpoint");
> +            goto error;
> +        }
> +    }
> +
> +    g_free(credname);
> +    object_ref(OBJECT(ret));
> +    return ret;
> +
> + error:
> +    g_free(credname);
> +    return NULL;
> +}
> +
> +
> +static void tcp_outgoing_migration_tls(Object *src,
> +                                       Error *err,
> +                                       gpointer opaque)
> +{
> +    TCPConnectTLSData *data = opaque;
> +    QIOChannel *ioc = QIO_CHANNEL(src);
> +
> +    if (err) {
> +        DPRINTF("migrate connect TLS error: %s\n", error_get_pretty(err));
> +        data->s->file = NULL;
> +        migrate_fd_error(data->s);
> +    } else {
> +        DPRINTF("migrate connect success\n");
> +        data->s->file = qemu_fopen_channel_output(ioc);
> +        migrate_fd_connect(data->s);
> +    }
> +}
> +
> +
>  static void tcp_outgoing_migration(Object *src,
>                                     Error *err,
>                                     gpointer opaque)
>  {
> -    MigrationState *s = opaque;
> +    TCPConnectData *data = opaque;
>      QIOChannel *sioc = QIO_CHANNEL(src);
>  
>      if (err) {
>          DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
> -        s->file = NULL;
> -        migrate_fd_error(s);
> +        data->s->file = NULL;
> +        migrate_fd_error(data->s);
>      } else {
> -        DPRINTF("migrate connect success\n");
> -        s->file = qemu_fopen_channel_output(sioc);
> -        migrate_fd_connect(s);
> +        if (data->tlscreds) {
> +            Error *local_err = NULL;
> +            QIOChannelTLS *tioc = qio_channel_tls_new_client(
> +                sioc, data->tlscreds, data->hostname,
> +                &local_err);
> +            if (!tioc) {
> +                DPRINTF("migrate tls setup error: %s\n",
> +                        error_get_pretty(local_err));
> +                error_free(local_err);
> +                data->s->file = NULL;
> +                migrate_fd_error(data->s);
> +            } else {
> +                TCPConnectTLSData *tdata =
> +                    g_new0(TCPConnectTLSData, 1);
> +                DPRINTF("migrate connect tls handshake begin\n");
> +                tdata->s = data->s;
> +                tdata->ioc = sioc;
> +                object_ref(OBJECT(sioc));
> +                qio_channel_tls_handshake(tioc,
> +                                          tcp_outgoing_migration_tls,
> +                                          tdata,
> +                                          tcp_connect_tls_data_free);
> +            }
> +        } else {
> +            DPRINTF("migrate connect success\n");
> +            data->s->file = qemu_fopen_channel_output(sioc);
> +            migrate_fd_connect(data->s);
> +        }
>      }
>      object_unref(src);
>  }
> @@ -77,21 +248,56 @@ void tcp_start_outgoing_migration(MigrationState *s,
>  {
>      SocketAddress *saddr = tcp_build_address(host_port, errp);
>      QIOChannelSocket *sioc;
> +    Error *local_err = NULL;
> +    QCryptoTLSCreds *creds;
> +    TCPConnectData *data;
>  
>      if (!saddr) {
>          return;
>      }
>  
> +    creds = tcp_get_tls_creds(host_port, false, errp);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        qapi_free_SocketAddress(saddr);
> +        return;
> +    }
> +
> +    data = g_new0(TCPConnectData, 1);
> +    data->s = s;
> +    if (creds) {
> +        data->hostname = g_strdup(saddr->inet->host);
> +        data->tlscreds = creds;
> +    }
> +
>      sioc = qio_channel_socket_new();
>      qio_channel_socket_connect_async(sioc,
>                                       saddr,
>                                       tcp_outgoing_migration,
> -                                     s,
> -                                     NULL);
> +                                     data,
> +                                     tcp_connect_data_free);
>      qapi_free_SocketAddress(saddr);
>  }
>  
>  
> +static void tcp_incoming_migration_tls(Object *src,
> +                                       Error *err,
> +                                       gpointer opaque)
> +{
> +    QIOChannel *ioc = QIO_CHANNEL(src);
> +
> +    if (err) {
> +        DPRINTF("migrate listen TLS error: %s\n", error_get_pretty(err));
> +        object_unref(OBJECT(ioc));
> +    } else {
> +        DPRINTF("migrate listen success\n");
> +        QEMUFile *f = qemu_fopen_channel_input(ioc);
> +
> +        process_incoming_migration(f);
> +    }
> +}
> +
> +
>  static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
>                                                GIOCondition condition,
>                                                gpointer opaque)
> @@ -99,6 +305,7 @@ static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
>      QEMUFile *f;
>      QIOChannelSocket *cioc;
>      Error *err = NULL;
> +    TCPListenData *data = opaque;
>  
>      cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
>                                       &err);
> @@ -108,16 +315,38 @@ static gboolean tcp_accept_incoming_migration(QIOChannel *ioc,
>          goto out;
>      }
>  
> -    DPRINTF("accepted migration\n");
> +    if (data->tlscreds) {
> +        DPRINTF("Starting client TLS\n");
> +        QIOChannelTLS *tioc = qio_channel_tls_new_server(
> +            QIO_CHANNEL(cioc), data->tlscreds,
> +            NULL, /* XXX pass ACL name */
> +            &err);
> +        object_unref(OBJECT(cioc));
> +        if (!tioc) {
> +            DPRINTF("migrate tls setup error: %s\n",
> +                    error_get_pretty(err));
> +            error_free(err);
> +            goto out;
> +        } else {
> +            DPRINTF("migrate connect tls handshake begin\n");
> +            qio_channel_tls_handshake(tioc,
> +                                      tcp_incoming_migration_tls,
> +                                      NULL,
> +                                      NULL);
> +        }
> +    } else {
> +        DPRINTF("accepted migration\n");
>  
> -    f = qemu_fopen_channel_input(QIO_CHANNEL(cioc));
> -    object_unref(OBJECT(cioc));
> +        f = qemu_fopen_channel_input(QIO_CHANNEL(cioc));
> +        object_unref(OBJECT(cioc));
>  
> -    process_incoming_migration(f);
> +        process_incoming_migration(f);
> +    }
>  
>  out:
>      /* Close listening socket as its no longer needed */
>      qio_channel_close(ioc, NULL);
> +    object_unref(OBJECT(ioc));
>      return FALSE;
>  }
>  
> @@ -126,23 +355,39 @@ void tcp_start_incoming_migration(const char *host_port, Error **errp)
>  {
>      SocketAddress *saddr = tcp_build_address(host_port, errp);
>      QIOChannelSocket *listen_ioc;
> +    TCPListenData *data;
> +    Error *local_err = NULL;
> +    QCryptoTLSCreds *creds;
>  
>      if (!saddr) {
>          return;
>      }
>  
> +    creds = tcp_get_tls_creds(host_port, true, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        qapi_free_SocketAddress(saddr);
> +        return;
> +    }
> +
>      listen_ioc = qio_channel_socket_new();
>      if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) {
>          object_unref(OBJECT(listen_ioc));
>          qapi_free_SocketAddress(saddr);
> +        object_unref(OBJECT(creds));
>          return;
>      }
>  
> +    data = g_new0(TCPListenData, 1);
> +    if (creds) {
> +        data->tlscreds = creds;
> +    }
> +
>      qio_channel_add_watch(QIO_CHANNEL(listen_ioc),
>                            G_IO_IN,
>                            tcp_accept_incoming_migration,
> -                          listen_ioc,
> -                          (GDestroyNotify)object_unref);
> +                          data,
> +                          tcp_listen_data_free);
>  
>      qapi_free_SocketAddress(saddr);
>  }
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 6acd400..c1bf4a7 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3299,7 +3299,7 @@ Set TB size.
>  ETEXI
>  
>  DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
> -    "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6]\n" \
> +    "-incoming tcp:[host]:port[,to=maxport][,ipv4][,ipv6][,tls-creds=ID]\n" \
>      "-incoming rdma:host:port[,ipv4][,ipv6]\n" \
>      "-incoming unix:socketpath\n" \
>      "                prepare for incoming migration, listen on\n" \
> @@ -3312,11 +3312,14 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
>      "                wait for the URI to be specified via migrate_incoming\n",
>      QEMU_ARCH_ALL)
>  STEXI
> -@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6]
> +@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6][,tls-creds=ID]
>  @itemx -incoming rdma:@var{host}:@var{port}[,ipv4][,ipv6]
>  @findex -incoming
>  Prepare for incoming migration, listen on a given tcp port.
>  
> +If the @var{tls-creds} parameter is specified, it should refer to the ID
> +of a TLS credentials object previously created with @var{-object}.
> +
>  @item -incoming unix:@var{socketpath}
>  Prepare for incoming migration, listen on a given unix socket.
>  
> -- 
> 2.4.3
> 
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend
  2015-09-07 16:23   ` Dr. David Alan Gilbert
@ 2015-09-07 16:29     ` Daniel P. Berrange
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel P. Berrange @ 2015-09-07 16:29 UTC (permalink / raw)
  To: Dr. David Alan Gilbert
  Cc: Juan Quintela, qemu-devel, Gerd Hoffmann, Amit Shah, Paolo Bonzini

On Mon, Sep 07, 2015 at 05:23:01PM +0100, Dr. David Alan Gilbert wrote:
> * Daniel P. Berrange (berrange@redhat.com) wrote:
> > This extends the TCP migration backend so that it can make use
> > of QIOChannelTLS to provide transparent TLS encryption. To
> > trigger enablement the URI on the incoming and outgoing sides
> > should have 'tls-creds=ID' appended, eg
> > 
> >    tcp:$HOST:$PORT,tls-creds=ID
> 
> What makes this tcp specifc? Would it work on any bidirectional
> transport?

TLS is capable of working on any bi-directional transport.
When using x509 certificates with TLS though, the client
would typically validate the server identity by comparing
the hostname it connected to, with the hostname encoded
in the server's x509 certificate. So if we want to enable
use of TLS over a transport that isn't TCP, we'd need to
figure out the policy around x509 certificate validation.
This isn't neccessarily hard, but it would need someone
to describe the usage scenario, so we can figure out what
makes sense from the security POV.

Since I've not heard of anyone asking for TLS support on
non-TCP transports, I figured it was fine to only plumb
it into tcp: for migration for now and avoid these questions.

Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|

^ permalink raw reply	[flat|nested] 57+ messages in thread

end of thread, other threads:[~2015-09-07 16:29 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-03 15:38 [Qemu-devel] [PATCH FYI 00/46] Generic TLS support across VNC/chardev/migration Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 01/46] sockets: add helpers for creating SocketAddress from a socket Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 02/46] sockets: move qapi_copy_SocketAddress into qemu-sockets.c Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 03/46] sockets: allow port to be NULL when listening on IP address Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 04/46] osdep: add qemu_fork() wrapper for safely handling signals Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 05/46] coroutine: move into libqemuutil.a library Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 06/46] io: add abstract QIOChannel classes Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 07/46] io: add helper module for creating watches on FDs Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 08/46] io: pull Buffer code out of VNC module Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 09/46] io: add QIOTask class for async operations Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 10/46] io: add QIOChannelSocket class Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 11/46] io: add QIOChannelFile class Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 12/46] io: add QIOChannelTLS class Daniel P. Berrange
2015-09-07 15:31   ` Dr. David Alan Gilbert
2015-09-07 15:41     ` Daniel P. Berrange
2015-09-07 15:51       ` Dr. David Alan Gilbert
2015-09-07 16:04         ` Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 13/46] io: add QIOChannelWebsock class Daniel P. Berrange
2015-09-07 15:44   ` Dr. David Alan Gilbert
2015-09-07 15:50     ` Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 14/46] io: add QIOChannelCommand class Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 15/46] ui: convert VNC startup code to use SocketAddress Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 16/46] ui: convert VNC server to use QIOChannelSocket Daniel P. Berrange
2015-09-03 15:38 ` [Qemu-devel] [PATCH FYI 17/46] ui: convert VNC server to use QIOChannelTLS Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 18/46] ui: convert VNC server to use QIOChannelWebsock Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 19/46] char: remove fixed length filename allocation Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 20/46] char: convert from GIOChannel to QIOChannel Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 21/46] char: don't assume telnet initialization will not block Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 22/46] char: introduce support for TLS encrypted TCP chardev backend Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 23/46] nbd: convert to use the QAPI SocketAddress object Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 24/46] qemu-nbd: " Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 25/46] sockets: remove use of QemuOpts from header file Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 26/46] sockets: remove use of QemuOpts from socket_listen Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 27/46] sockets: remove use of QemuOpts from socket_connect Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 28/46] sockets: remove use of QemuOpts from socket_dgram Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 29/46] migration: remove use of qemu_bufopen from vmstate tests Daniel P. Berrange
2015-09-07 16:08   ` Dr. David Alan Gilbert
2015-09-07 16:17     ` Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 30/46] migration: remove memory buffer based QEMUFile backend Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 31/46] migration: move definition of struct QEMUFile back into qemu-file.c Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 32/46] migration: split migration hooks out of QEMUFileOps Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 33/46] migration: ensure qemu_fflush() always writes full data amount Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 34/46] migration: introduce qemu_fset_blocking function on QEMUFile Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 35/46] migration: force QEMUFile to blocking mode for outgoing migration Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 36/46] migration: introduce a new QEMUFile impl based on QIOChannel Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 37/46] migration: convert unix socket protocol to use QIOChannel Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 38/46] migration: convert tcp " Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 39/46] migration: convert fd " Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 40/46] migration: convert exec " Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 41/46] migration: convert RDMA to use QIOChannel interface Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 42/46] migration: convert savevm to use QIOChannel for writing to files Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 43/46] migration: delete QEMUFile sockets implementation Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 44/46] migration: delete QEMUFile stdio implementation Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 45/46] migration: support TLS encryption with TCP migration backend Daniel P. Berrange
2015-09-07 16:23   ` Dr. David Alan Gilbert
2015-09-07 16:29     ` Daniel P. Berrange
2015-09-03 15:39 ` [Qemu-devel] [PATCH FYI 46/46] migration: remove support for non-iovec based write handlers Daniel P. Berrange

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.