* [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
* 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
* [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
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.