All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support
@ 2016-01-19 11:14 Daniel P. Berrange
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 1/4] char: remove fixed length filename allocation Daniel P. Berrange
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Daniel P. Berrange @ 2016-01-19 11:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini

This is an update of patches previously shown in an RFC posting

  RFC: https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00829.html
   v1: https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg04222.html
   v2: https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg03823.html
   v3: https://lists.gnu.org/archive/html/qemu-devel/2016-01/msg01601.html

This short series converts the chardev backends to use the new
QIOChannel framework. After doing so it then adds support for
TLS encryption of TCP chardevs. The commit message in the last
patch explains the TLS encryption in detail.

The GIOChannel -> QIOChannel conversion has been validated by
running the qtest framework, which indeed found a few bugs
initially which I have since fixed.

The TLS support has been tested for interoperability using
the gnutls-serv and gnutls-client programs which provide
stub TLS endpoints/clients respectively.

Changed in v4:

 - Rebase to resolve conflicts with recent merged patches

Changed in v3:

 - Fix buffer update after partial send of telnet data

Daniel P. Berrange (4):
  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

 qapi-schema.json |   2 +
 qemu-char.c      | 913 ++++++++++++++++++++++++++++---------------------------
 qemu-options.hx  |   9 +-
 tests/Makefile   |   2 +-
 4 files changed, 479 insertions(+), 447 deletions(-)

-- 
2.5.0

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

* [Qemu-devel] [PATCH v4 1/4] char: remove fixed length filename allocation
  2016-01-19 11:14 [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Daniel P. Berrange
@ 2016-01-19 11:14 ` Daniel P. Berrange
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 2/4] char: convert from GIOChannel to QIOChannel Daniel P. Berrange
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Daniel P. Berrange @ 2016-01-19 11:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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.

This also facilitates later patches which will want to
populate the filename by calling external functions
which do not support use of a pre-allocated buffer.

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

diff --git a/qemu-char.c b/qemu-char.c
index e133f4f..8e96f90 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -88,39 +88,37 @@
 
 #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->type) {
     case SOCKET_ADDRESS_KIND_INET:
-        return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix,
-                        is_telnet ? "telnet" : "tcp", addr->u.inet->host,
-                        addr->u.inet->port, is_listen ? ",server" : "");
+        return g_strdup_printf("%s%s:%s:%s%s", prefix,
+                               is_telnet ? "telnet" : "tcp", addr->u.inet->host,
+                               addr->u.inet->port, is_listen ? ",server" : "");
         break;
     case SOCKET_ADDRESS_KIND_UNIX:
-        return snprintf(dest, max_len, "%sunix:%s%s", prefix,
-                        addr->u.q_unix->path, is_listen ? ",server" : "");
+        return g_strdup_printf("%sunix:%s%s", prefix,
+                               addr->u.q_unix->path,
+                               is_listen ? ",server" : "");
         break;
     case SOCKET_ADDRESS_KIND_FD:
-        return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->u.fd->str,
-                        is_listen ? ",server" : "");
+        return g_strdup_printf("%sfd:%s%s", prefix, addr->u.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 +127,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 +140,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");
     }
 }
 
@@ -1074,15 +1072,18 @@ static CharDriverState *qemu_chr_open_pipe(const char *id,
 {
     ChardevHostdev *opts = backend->u.pipe;
     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;
     ChardevCommon *common = qapi_ChardevHostdev_base(backend->u.pipe);
 
-    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);
@@ -2115,7 +2116,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;
 
@@ -2130,11 +2131,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) {
         error_setg(errp, "Failed CreateNamedPipe (%lu)", GetLastError());
         s->hcom = NULL;
@@ -2913,8 +2915,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);
@@ -2989,16 +2992,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;
@@ -4335,9 +4338,8 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     /* 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.5.0

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

* [Qemu-devel] [PATCH v4 2/4] char: convert from GIOChannel to QIOChannel
  2016-01-19 11:14 [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Daniel P. Berrange
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 1/4] char: remove fixed length filename allocation Daniel P. Berrange
@ 2016-01-19 11:14 ` Daniel P. Berrange
  2016-03-18 16:43   ` Laurent Vivier
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 3/4] char: don't assume telnet initialization will not block Daniel P. Berrange
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Daniel P. Berrange @ 2016-01-19 11:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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    | 648 ++++++++++++++++++++++-----------------------------------
 tests/Makefile |   2 +-
 2 files changed, 254 insertions(+), 396 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 8e96f90..8e9156a 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -33,6 +33,8 @@
 #include "qapi/qmp-output-visitor.h"
 #include "qapi-visit.h"
 #include "qemu/base64.h"
+#include "io/channel-socket.h"
+#include "io/channel-file.h"
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -766,7 +768,7 @@ typedef struct IOWatchPoll
 {
     GSource parent;
 
-    GIOChannel *channel;
+    QIOChannel *ioc;
     GSource *src;
 
     IOCanReadHandler *fd_can_read;
@@ -789,8 +791,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 {
@@ -836,9 +838,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;
@@ -847,7 +849,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;
 
@@ -883,79 +885,50 @@ 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;
+        } else if (ret < 0) {
+            if (offset) {
+                return offset;
+            }
 
-#ifdef _WIN32
-    chan = g_io_channel_win32_new_socket(fd);
-#else
-    chan = g_io_channel_unix_new(fd);
-#endif
+            errno = EINVAL;
+            return -1;
+        }
 
-    g_io_channel_set_encoding(chan, NULL, NULL);
-    g_io_channel_set_buffered(chan, FALSE);
+        offset += ret;
+    }
 
-    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;
 
@@ -964,17 +937,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) {
@@ -984,15 +956,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;
@@ -1010,7 +982,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)
@@ -1018,8 +990,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);
     }
 }
@@ -1029,11 +1002,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);
@@ -1052,8 +1025,8 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
         return NULL;
     }
     s = g_new0(FDCharDriver, 1);
-    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;
@@ -1196,7 +1169,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
 #define HAVE_CHARDEV_PTY 1
 
 typedef struct {
-    GIOChannel *fd;
+    QIOChannel *ioc;
     int read_bytes;
 
     /* Protected by the CharDriverState chr_write_lock.  */
@@ -1247,8 +1220,9 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr)
     PtyCharDriver *s = chr->opaque;
     GPollFD pfd;
     int rc;
+    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;
     do {
@@ -1282,7 +1256,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)
@@ -1291,7 +1265,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)
@@ -1303,13 +1277,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)
@@ -1317,13 +1291,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;
 }
@@ -1365,7 +1339,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);
         }
     }
@@ -1374,13 +1349,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;
@@ -1431,7 +1403,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;
@@ -1555,12 +1527,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);
         }
@@ -1569,7 +1542,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;
@@ -1577,7 +1550,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;
@@ -1597,7 +1570,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)
@@ -1612,7 +1585,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:
@@ -1623,18 +1596,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,
@@ -2456,8 +2418,7 @@ err1:
 /* UDP Net console */
 
 typedef struct {
-    int fd;
-    GIOChannel *chan;
+    QIOChannel *ioc;
     uint8_t buf[READ_BUF_LEN];
     int bufcnt;
     int bufptr;
@@ -2468,17 +2429,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)
@@ -2499,24 +2452,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) {
@@ -2533,8 +2484,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);
     }
 }
@@ -2544,17 +2496,16 @@ 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,
-                                             ChardevCommon *backend,
-                                             Error **errp)
+static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
+                                          ChardevCommon *backend,
+                                          Error **errp)
 {
     CharDriverState *chr = NULL;
     NetCharDriver *s = NULL;
@@ -2565,8 +2516,7 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd,
     }
     s = g_new0(NetCharDriver, 1);
 
-    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;
@@ -2582,19 +2532,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;
@@ -2628,68 +2577,27 @@ 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_full(s->ioc, buf, len,
+                                        s->write_msgfds,
+                                        s->write_msgfds_num);
+
+        /* 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;
@@ -2785,6 +2693,10 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
 {
     TCPCharDriver *s = chr->opaque;
 
+    if (!qio_channel_has_feature(s->ioc,
+                                 QIO_CHANNEL_FEATURE_FD_PASS)) {
+        return -1;
+    }
     /* clear old pending fd array */
     g_free(s->write_msgfds);
 
@@ -2798,27 +2710,26 @@ 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;
-
-    for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
-        int fd_size, i;
-
-        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;
-        }
+    struct iovec iov = { .iov_base = buf, .iov_len = len };
+    int ret;
+    size_t i;
+    int *msgfds = NULL;
+    size_t msgfds_num = 0;
+
+    if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
+        ret = qio_channel_readv_full(s->ioc, &iov, 1,
+                                     &msgfds, &msgfds_num,
+                                     NULL);
+    } else {
+        ret = qio_channel_readv_full(s->ioc, &iov, 1,
+                                     NULL, NULL,
+                                     NULL);
+    }
 
+    if (msgfds_num) {
         /* close and clean read_msgfds */
         for (i = 0; i < s->read_msgfds_num; i++) {
             close(s->read_msgfds[i]);
@@ -2828,77 +2739,31 @@ static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg)
             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
-        }
+        s->read_msgfds = msgfds;
+        s->read_msgfds_num = msgfds_num;
     }
-}
 
-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;
+    for (i = 0; i < s->read_msgfds_num; i++) {
+        int fd = s->read_msgfds[i];
+        if (fd < 0) {
+            continue;
+        }
 
-    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)
@@ -2906,15 +2771,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);
@@ -2924,7 +2787,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;
@@ -2938,9 +2801,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) {
@@ -2988,25 +2849,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);
@@ -3017,38 +2870,41 @@ 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));
+
+    if (s->do_nodelay) {
+        qio_channel_set_delay(s->ioc, false);
+    }
     if (s->listen_tag) {
         g_source_remove(s->listen_tag);
         s->listen_tag = 0;
@@ -3058,41 +2914,43 @@ 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;
+    }
+    qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
+    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;
 }
@@ -3107,22 +2965,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++) {
@@ -3137,57 +2989,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;
 }
 
 /*********************************************************/
@@ -4318,8 +4176,6 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     }
     s = g_new0(TCPCharDriver, 1);
 
-    s->fd = -1;
-    s->listen_fd = -1;
     s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
     s->is_listen = is_listen;
     s->is_telnet = is_telnet;
@@ -4360,8 +4216,8 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     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, NULL);
     }
 
     return chr;
@@ -4374,13 +4230,15 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
 {
     ChardevUdp *udp = backend->u.udp;
     ChardevCommon *common = qapi_ChardevUdp_base(backend->u.udp);
-    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, common, errp);
+    return qemu_chr_open_udp(sioc, common, errp);
 }
 
 ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
diff --git a/tests/Makefile b/tests/Makefile
index b7352f1..650e654 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -558,7 +558,7 @@ tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y)
 tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
 tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
 tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o
-tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y)
+tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) $(test-io-obj-y)
 tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
 tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
 tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
-- 
2.5.0

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

* [Qemu-devel] [PATCH v4 3/4] char: don't assume telnet initialization will not block
  2016-01-19 11:14 [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Daniel P. Berrange
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 1/4] char: remove fixed length filename allocation Daniel P. Berrange
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 2/4] char: convert from GIOChannel to QIOChannel Daniel P. Berrange
@ 2016-01-19 11:14 ` Daniel P. Berrange
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 4/4] char: introduce support for TLS encrypted TCP chardev backend Daniel P. Berrange
  2016-01-19 13:14 ` [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Paolo Bonzini
  4 siblings, 0 replies; 9+ messages in thread
From: Daniel P. Berrange @ 2016-01-19 11:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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 | 87 ++++++++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 69 insertions(+), 18 deletions(-)

diff --git a/qemu-char.c b/qemu-char.c
index 8e9156a..f0cea8a 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2877,19 +2877,70 @@ 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)
+{
+    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;
+    }
+
+    memmove(init->buf, init->buf + ret, init->buflen);
+
+    return TRUE;
+}
+
+static void tcp_chr_telnet_init(CharDriverState *chr)
 {
-    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);
+    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)
@@ -2909,7 +2960,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;
 }
@@ -2935,7 +2991,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),
@@ -2944,10 +2999,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.5.0

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

* [Qemu-devel] [PATCH v4 4/4] char: introduce support for TLS encrypted TCP chardev backend
  2016-01-19 11:14 [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Daniel P. Berrange
                   ` (2 preceding siblings ...)
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 3/4] char: don't assume telnet initialization will not block Daniel P. Berrange
@ 2016-01-19 11:14 ` Daniel P. Berrange
  2016-01-19 13:14 ` [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Paolo Bonzini
  4 siblings, 0 replies; 9+ messages in thread
From: Daniel P. Berrange @ 2016-01-19 11:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: 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-x509,id=tls0,endpoint=server,verify-peer=off,\
         dir=/home/berrange/security/qemutls

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 --echo \
       --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-x509,id=tls0,endpoint=client,\
        dir=/home/berrange/security/qemutls

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

diff --git a/qapi-schema.json b/qapi-schema.json
index b3038b2..8d04897 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3146,6 +3146,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.6)
 # @server: #optional create server socket (default: true)
 # @wait: #optional wait for incoming connection on server
 #        sockets (default: false).
@@ -3160,6 +3161,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 f0cea8a..7ded3c2 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -35,6 +35,7 @@
 #include "qemu/base64.h"
 #include "io/channel-socket.h"
 #include "io/channel-file.h"
+#include "io/channel-tls.h"
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -2532,9 +2533,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;
@@ -2776,6 +2779,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);
@@ -2849,12 +2854,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) {
@@ -2943,6 +2948,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->u.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;
@@ -2952,6 +3008,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));
 
     if (s->do_nodelay) {
         qio_channel_set_delay(s->ioc, false);
@@ -2961,10 +3019,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;
@@ -3033,6 +3095,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);
     }
@@ -3563,6 +3628,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) {
@@ -3574,6 +3640,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->u.socket = g_new0(ChardevSocket, 1);
@@ -3589,6 +3660,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     backend->u.socket->wait = is_waitconnect;
     backend->u.socket->has_reconnect = true;
     backend->u.socket->reconnect = reconnect;
+    backend->u.socket->tls_creds = g_strdup(tls_creds);
 
     addr = g_new0(SocketAddress, 1);
     if (path) {
@@ -4016,6 +4088,9 @@ QemuOptsList qemu_chardev_opts = {
             .name = "telnet",
             .type = QEMU_OPT_BOOL,
         },{
+            .name = "tls-creds",
+            .type = QEMU_OPT_STRING,
+        },{
             .name = "width",
             .type = QEMU_OPT_NUMBER,
         },{
@@ -4231,6 +4306,39 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     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;
@@ -4259,9 +4367,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     if (s->reconnect_time) {
         socket_try_connect(chr);
     } else if (!qemu_chr_open_socket_fd(chr, errp)) {
-        g_free(s);
-        qemu_chr_free_common(chr);
-        return NULL;
+        goto error;
     }
 
     if (is_listen && is_waitconnect) {
@@ -4272,6 +4378,14 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
     }
 
     return chr;
+
+ error:
+    if (s->tls_creds) {
+        object_unref(OBJECT(s->tls_creds));
+    }
+    g_free(s);
+    qemu_chr_free_common(chr);
+    return NULL;
 }
 
 static CharDriverState *qmp_chardev_open_udp(const char *id,
diff --git a/qemu-options.hx b/qemu-options.hx
index b4763ba..f31a240 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2092,7 +2092,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev null,id=id[,mux=on|off][,logfile=PATH][,logappend=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]\n"
-    "         [,logfile=PATH][,logappend=on|off] (tcp)\n"
+    "         [,logfile=PATH][,logappend=on|off][,tls-creds=ID] (tcp)\n"
     "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds]\n"
     "         [,mux=on|off][,logfile=PATH][,logappend=on|off] (unix)\n"
     "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
@@ -2172,7 +2172,7 @@ Further 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
@@ -2190,6 +2190,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.5.0

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

* Re: [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support
  2016-01-19 11:14 [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Daniel P. Berrange
                   ` (3 preceding siblings ...)
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 4/4] char: introduce support for TLS encrypted TCP chardev backend Daniel P. Berrange
@ 2016-01-19 13:14 ` Paolo Bonzini
  4 siblings, 0 replies; 9+ messages in thread
From: Paolo Bonzini @ 2016-01-19 13:14 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel

On 19/01/2016 12:14, Daniel P. Berrange wrote:
> This is an update of patches previously shown in an RFC posting
> 
>   RFC: https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg00829.html
>    v1: https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg04222.html
>    v2: https://lists.gnu.org/archive/html/qemu-devel/2015-12/msg03823.html
>    v3: https://lists.gnu.org/archive/html/qemu-devel/2016-01/msg01601.html
> 
> This short series converts the chardev backends to use the new
> QIOChannel framework. After doing so it then adds support for
> TLS encryption of TCP chardevs. The commit message in the last
> patch explains the TLS encryption in detail.
> 
> The GIOChannel -> QIOChannel conversion has been validated by
> running the qtest framework, which indeed found a few bugs
> initially which I have since fixed.
> 
> The TLS support has been tested for interoperability using
> the gnutls-serv and gnutls-client programs which provide
> stub TLS endpoints/clients respectively.
> 
> Changed in v4:
> 
>  - Rebase to resolve conflicts with recent merged patches

Thanks, this looks good!

Paolo

> Changed in v3:
> 
>  - Fix buffer update after partial send of telnet data
> 
> Daniel P. Berrange (4):
>   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
> 
>  qapi-schema.json |   2 +
>  qemu-char.c      | 913 ++++++++++++++++++++++++++++---------------------------
>  qemu-options.hx  |   9 +-
>  tests/Makefile   |   2 +-
>  4 files changed, 479 insertions(+), 447 deletions(-)
> 

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

* Re: [Qemu-devel] [PATCH v4 2/4] char: convert from GIOChannel to QIOChannel
  2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 2/4] char: convert from GIOChannel to QIOChannel Daniel P. Berrange
@ 2016-03-18 16:43   ` Laurent Vivier
  2016-03-18 16:56     ` Daniel P. Berrange
  0 siblings, 1 reply; 9+ messages in thread
From: Laurent Vivier @ 2016-03-18 16:43 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel; +Cc: Paolo Bonzini

Hi,

testing something else (migration...) I've discovered (by bisecting)
that this patch can allow to lock the machine. I'm using the pseries
machine, but I think it should happen with PC too.

I start a machine with:

	...
	-device virtio-serial-pci,id=serial0 \
	-chardev socket,id=channel0,path=/tmp/serial_socket,server,nowait \
	-device virtserialport,bus=serial0.0,nr=1,chardev=channel0

and I open the unix socket /tmp/serial_socket without reading it:

$ python
import socket
sock = socket.socket(socket.AF_UNIX)
sock.connect("/tmp/serial_socket_1")

Then in the guest:

cat /dev/zero > /dev/vport1p1

-> at this point, the machine hangs until we read data in unix socket
(we can't interact with monitor, we can't ping the machine...)

Laurent

On 19/01/2016 12:14, Daniel P. Berrange wrote:
> 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    | 648 ++++++++++++++++++++++-----------------------------------
>  tests/Makefile |   2 +-
>  2 files changed, 254 insertions(+), 396 deletions(-)
> 
> diff --git a/qemu-char.c b/qemu-char.c
> index 8e96f90..8e9156a 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -33,6 +33,8 @@
>  #include "qapi/qmp-output-visitor.h"
>  #include "qapi-visit.h"
>  #include "qemu/base64.h"
> +#include "io/channel-socket.h"
> +#include "io/channel-file.h"
>  
>  #include <unistd.h>
>  #include <fcntl.h>
> @@ -766,7 +768,7 @@ typedef struct IOWatchPoll
>  {
>      GSource parent;
>  
> -    GIOChannel *channel;
> +    QIOChannel *ioc;
>      GSource *src;
>  
>      IOCanReadHandler *fd_can_read;
> @@ -789,8 +791,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 {
> @@ -836,9 +838,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;
> @@ -847,7 +849,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;
>  
> @@ -883,79 +885,50 @@ 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;
> +        } else if (ret < 0) {
> +            if (offset) {
> +                return offset;
> +            }
>  
> -#ifdef _WIN32
> -    chan = g_io_channel_win32_new_socket(fd);
> -#else
> -    chan = g_io_channel_unix_new(fd);
> -#endif
> +            errno = EINVAL;
> +            return -1;
> +        }
>  
> -    g_io_channel_set_encoding(chan, NULL, NULL);
> -    g_io_channel_set_buffered(chan, FALSE);
> +        offset += ret;
> +    }
>  
> -    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;
>  
> @@ -964,17 +937,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) {
> @@ -984,15 +956,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;
> @@ -1010,7 +982,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)
> @@ -1018,8 +990,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);
>      }
>  }
> @@ -1029,11 +1002,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);
> @@ -1052,8 +1025,8 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
>          return NULL;
>      }
>      s = g_new0(FDCharDriver, 1);
> -    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;
> @@ -1196,7 +1169,7 @@ static CharDriverState *qemu_chr_open_stdio(const char *id,
>  #define HAVE_CHARDEV_PTY 1
>  
>  typedef struct {
> -    GIOChannel *fd;
> +    QIOChannel *ioc;
>      int read_bytes;
>  
>      /* Protected by the CharDriverState chr_write_lock.  */
> @@ -1247,8 +1220,9 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr)
>      PtyCharDriver *s = chr->opaque;
>      GPollFD pfd;
>      int rc;
> +    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;
>      do {
> @@ -1282,7 +1256,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)
> @@ -1291,7 +1265,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)
> @@ -1303,13 +1277,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)
> @@ -1317,13 +1291,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;
>  }
> @@ -1365,7 +1339,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);
>          }
>      }
> @@ -1374,13 +1349,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;
> @@ -1431,7 +1403,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;
> @@ -1555,12 +1527,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);
>          }
> @@ -1569,7 +1542,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;
> @@ -1577,7 +1550,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;
> @@ -1597,7 +1570,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)
> @@ -1612,7 +1585,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:
> @@ -1623,18 +1596,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,
> @@ -2456,8 +2418,7 @@ err1:
>  /* UDP Net console */
>  
>  typedef struct {
> -    int fd;
> -    GIOChannel *chan;
> +    QIOChannel *ioc;
>      uint8_t buf[READ_BUF_LEN];
>      int bufcnt;
>      int bufptr;
> @@ -2468,17 +2429,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)
> @@ -2499,24 +2452,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) {
> @@ -2533,8 +2484,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);
>      }
>  }
> @@ -2544,17 +2496,16 @@ 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,
> -                                             ChardevCommon *backend,
> -                                             Error **errp)
> +static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
> +                                          ChardevCommon *backend,
> +                                          Error **errp)
>  {
>      CharDriverState *chr = NULL;
>      NetCharDriver *s = NULL;
> @@ -2565,8 +2516,7 @@ static CharDriverState *qemu_chr_open_udp_fd(int fd,
>      }
>      s = g_new0(NetCharDriver, 1);
>  
> -    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;
> @@ -2582,19 +2532,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;
> @@ -2628,68 +2577,27 @@ 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_full(s->ioc, buf, len,
> +                                        s->write_msgfds,
> +                                        s->write_msgfds_num);
> +
> +        /* 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;
> @@ -2785,6 +2693,10 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
>  {
>      TCPCharDriver *s = chr->opaque;
>  
> +    if (!qio_channel_has_feature(s->ioc,
> +                                 QIO_CHANNEL_FEATURE_FD_PASS)) {
> +        return -1;
> +    }
>      /* clear old pending fd array */
>      g_free(s->write_msgfds);
>  
> @@ -2798,27 +2710,26 @@ 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;
> -
> -    for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
> -        int fd_size, i;
> -
> -        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;
> -        }
> +    struct iovec iov = { .iov_base = buf, .iov_len = len };
> +    int ret;
> +    size_t i;
> +    int *msgfds = NULL;
> +    size_t msgfds_num = 0;
> +
> +    if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
> +        ret = qio_channel_readv_full(s->ioc, &iov, 1,
> +                                     &msgfds, &msgfds_num,
> +                                     NULL);
> +    } else {
> +        ret = qio_channel_readv_full(s->ioc, &iov, 1,
> +                                     NULL, NULL,
> +                                     NULL);
> +    }
>  
> +    if (msgfds_num) {
>          /* close and clean read_msgfds */
>          for (i = 0; i < s->read_msgfds_num; i++) {
>              close(s->read_msgfds[i]);
> @@ -2828,77 +2739,31 @@ static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg)
>              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
> -        }
> +        s->read_msgfds = msgfds;
> +        s->read_msgfds_num = msgfds_num;
>      }
> -}
>  
> -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;
> +    for (i = 0; i < s->read_msgfds_num; i++) {
> +        int fd = s->read_msgfds[i];
> +        if (fd < 0) {
> +            continue;
> +        }
>  
> -    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)
> @@ -2906,15 +2771,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);
> @@ -2924,7 +2787,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;
> @@ -2938,9 +2801,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) {
> @@ -2988,25 +2849,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);
> @@ -3017,38 +2870,41 @@ 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));
> +
> +    if (s->do_nodelay) {
> +        qio_channel_set_delay(s->ioc, false);
> +    }
>      if (s->listen_tag) {
>          g_source_remove(s->listen_tag);
>          s->listen_tag = 0;
> @@ -3058,41 +2914,43 @@ 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;
> +    }
> +    qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
> +    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;
>  }
> @@ -3107,22 +2965,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++) {
> @@ -3137,57 +2989,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;
>  }
>  
>  /*********************************************************/
> @@ -4318,8 +4176,6 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
>      }
>      s = g_new0(TCPCharDriver, 1);
>  
> -    s->fd = -1;
> -    s->listen_fd = -1;
>      s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX;
>      s->is_listen = is_listen;
>      s->is_telnet = is_telnet;
> @@ -4360,8 +4216,8 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
>      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, NULL);
>      }
>  
>      return chr;
> @@ -4374,13 +4230,15 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
>  {
>      ChardevUdp *udp = backend->u.udp;
>      ChardevCommon *common = qapi_ChardevUdp_base(backend->u.udp);
> -    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, common, errp);
> +    return qemu_chr_open_udp(sioc, common, errp);
>  }
>  
>  ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
> diff --git a/tests/Makefile b/tests/Makefile
> index b7352f1..650e654 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -558,7 +558,7 @@ tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y)
>  tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
>  tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
>  tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o
> -tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y)
> +tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) $(test-io-obj-y)
>  tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
>  tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
>  tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
> 

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

* Re: [Qemu-devel] [PATCH v4 2/4] char: convert from GIOChannel to QIOChannel
  2016-03-18 16:43   ` Laurent Vivier
@ 2016-03-18 16:56     ` Daniel P. Berrange
  2016-03-18 17:10       ` Laurent Vivier
  0 siblings, 1 reply; 9+ messages in thread
From: Daniel P. Berrange @ 2016-03-18 16:56 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: Paolo Bonzini, qemu-devel

On Fri, Mar 18, 2016 at 05:43:42PM +0100, Laurent Vivier wrote:
> Hi,
> 
> testing something else (migration...) I've discovered (by bisecting)
> that this patch can allow to lock the machine. I'm using the pseries
> machine, but I think it should happen with PC too.
> 
> I start a machine with:
> 
> 	...
> 	-device virtio-serial-pci,id=serial0 \
> 	-chardev socket,id=channel0,path=/tmp/serial_socket,server,nowait \
> 	-device virtserialport,bus=serial0.0,nr=1,chardev=channel0
> 
> and I open the unix socket /tmp/serial_socket without reading it:
> 
> $ python
> import socket
> sock = socket.socket(socket.AF_UNIX)
> sock.connect("/tmp/serial_socket_1")
> 
> Then in the guest:
> 
> cat /dev/zero > /dev/vport1p1
> 
> -> at this point, the machine hangs until we read data in unix socket
> (we can't interact with monitor, we can't ping the machine...)

Pretty sure that'll be the same issue Andrew reported here

https://lists.gnu.org/archive/html/qemu-devel/2016-03/msg02843.html

can you see if his suggested addition works for you too

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] 9+ messages in thread

* Re: [Qemu-devel] [PATCH v4 2/4] char: convert from GIOChannel to QIOChannel
  2016-03-18 16:56     ` Daniel P. Berrange
@ 2016-03-18 17:10       ` Laurent Vivier
  0 siblings, 0 replies; 9+ messages in thread
From: Laurent Vivier @ 2016-03-18 17:10 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: Paolo Bonzini, qemu-devel



On 18/03/2016 17:56, Daniel P. Berrange wrote:
> On Fri, Mar 18, 2016 at 05:43:42PM +0100, Laurent Vivier wrote:
>> Hi,
>>
>> testing something else (migration...) I've discovered (by bisecting)
>> that this patch can allow to lock the machine. I'm using the pseries
>> machine, but I think it should happen with PC too.
>>
>> I start a machine with:
>>
>> 	...
>> 	-device virtio-serial-pci,id=serial0 \
>> 	-chardev socket,id=channel0,path=/tmp/serial_socket,server,nowait \
>> 	-device virtserialport,bus=serial0.0,nr=1,chardev=channel0
>>
>> and I open the unix socket /tmp/serial_socket without reading it:
>>
>> $ python
>> import socket
>> sock = socket.socket(socket.AF_UNIX)
>> sock.connect("/tmp/serial_socket_1")
>>
>> Then in the guest:
>>
>> cat /dev/zero > /dev/vport1p1
>>
>> -> at this point, the machine hangs until we read data in unix socket
>> (we can't interact with monitor, we can't ping the machine...)
> 
> Pretty sure that'll be the same issue Andrew reported here
> 
> https://lists.gnu.org/archive/html/qemu-devel/2016-03/msg02843.html
> 
> can you see if his suggested addition works for you too

Yes, it works :)

Thanks,
Laurent

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

end of thread, other threads:[~2016-03-18 17:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-19 11:14 [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Daniel P. Berrange
2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 1/4] char: remove fixed length filename allocation Daniel P. Berrange
2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 2/4] char: convert from GIOChannel to QIOChannel Daniel P. Berrange
2016-03-18 16:43   ` Laurent Vivier
2016-03-18 16:56     ` Daniel P. Berrange
2016-03-18 17:10       ` Laurent Vivier
2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 3/4] char: don't assume telnet initialization will not block Daniel P. Berrange
2016-01-19 11:14 ` [Qemu-devel] [PATCH v4 4/4] char: introduce support for TLS encrypted TCP chardev backend Daniel P. Berrange
2016-01-19 13:14 ` [Qemu-devel] [PATCH v4 0/4] Convert chardevs to QIOChannel & add TLS support Paolo Bonzini

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.