From patchwork Mon Aug 13 10:20:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Denis V. Lunev\" via" X-Patchwork-Id: 10564059 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 75F7A1390 for ; Mon, 13 Aug 2018 10:43:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6801D284B1 for ; Mon, 13 Aug 2018 10:43:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5BF3A284C0; Mon, 13 Aug 2018 10:43:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 27D8F284B1 for ; Mon, 13 Aug 2018 10:42:59 +0000 (UTC) Received: from localhost ([::1]:38763 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fpAJO-0008GE-Ch for patchwork-qemu-devel@patchwork.kernel.org; Mon, 13 Aug 2018 06:42:58 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45719) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fp9y5-00042I-05 for qemu-devel@nongnu.org; Mon, 13 Aug 2018 06:20:58 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fp9y1-0004sH-R6 for qemu-devel@nongnu.org; Mon, 13 Aug 2018 06:20:56 -0400 Received: from smtp35.i.mail.ru ([94.100.177.95]:33126) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fp9y1-0004qt-9R for qemu-devel@nongnu.org; Mon, 13 Aug 2018 06:20:53 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=Message-Id:Date:Subject:Cc:To:From; bh=J/3r9CKp7IRQYrLfg9DTi50uwRSgAe/vrjNdssxDfyU=; b=D3MdezqaXmjdHKN+98bSjApVUoJi7nj58ZCP2vML2Vqhli+v48+MR9A1MhOGopQG16IIiElge1mBm3Q/mRi5CzI//iyqqzWay/HPn2aPDB0YkA8QR/ZeBVpT87oMtfFZomLFfPxE1nYozkRJCrFaxflpdXU3uVA/kdRGl+j/ta4=; Received: by smtp35.i.mail.ru with esmtpa (envelope-from ) id 1fp9xz-0000xV-6D; Mon, 13 Aug 2018 13:20:51 +0300 To: qemu-devel@nongnu.org Date: Mon, 13 Aug 2018 13:20:37 +0300 Message-Id: <20180813102037.12097-1-jusual@mail.ru> X-Mailer: git-send-email 2.17.1 Authentication-Results: smtp35.i.mail.ru; auth=pass smtp.auth=jusual@mail.ru smtp.mailfrom=jusual@mail.ru X-7FA49CB5: 0D63561A33F958A50577F5F7384CCE64C02CE25577A898F789E4B33300D080A58941B15DA834481F8AA50765F7900637CAEE156C82D3D7D9389733CBF5DBD5E9B5C8C57E37DE458B4DA2F55E57A558BE49FD398EE364050F042285CD7A5C321FBA3038C0950A5D36B5C8C57E37DE458B4C7702A67D5C33162DBA43225CD8A89FC0F9454058DFE53CF6AAEE55EFDD55BCF4A36B7032468C7E43847C11F186F3C5E7DDDDC251EA7DABCC89B49CDF41148F5111EB80E82A49BE3B503F486389A921A5CC5B56E945C8DA X-Mailru-Sender: 7766D515518070DE138AAC7428EA760D34A91116CCB9008B65CABF423A9D5C190E8F58CB380BC13F7C4160E8B47E48163DDE9B364B0DF2898CB68AF7A628805D594FB4C9F0DBF412AE208404248635DF X-Mras: OK X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 94.100.177.95 Subject: [Qemu-devel] [PATCH] chardev: Add websocket support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Julia Suvorova via Qemu-devel From: "Denis V. Lunev\" via" Reply-To: Julia Suvorova Cc: Jim Mussared , =?utf-8?q?Steffen_G=C3=B6rtz?= , Stefan Hajnoczi , Julia Suvorova , Joel Stanley , Stefan Hajnoczi , Paolo Bonzini , marcandre.lureau@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP New option "websock" added to allow using websocket protocol for chardev socket backend. Example: -chardev socket,websock,id=... Signed-off-by: Julia Suvorova --- chardev/char-socket.c | 75 ++++++++++++++++++++++++++++++++++++------- chardev/char.c | 3 ++ qapi/char.json | 3 ++ 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index efbad6ee7c..4464446511 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -26,6 +26,7 @@ #include "chardev/char.h" #include "io/channel-socket.h" #include "io/channel-tls.h" +#include "io/channel-websock.h" #include "io/net-listener.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -69,6 +70,8 @@ typedef struct { GSource *telnet_source; TCPChardevTelnetInit *telnet_init; + bool is_websock; + GSource *reconnect_timer; int64_t reconnect_time; bool connect_err_reported; @@ -386,12 +389,14 @@ static void tcp_chr_free_connection(Chardev *chr) } static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr, - bool is_listen, bool is_telnet) + bool is_listen, bool is_telnet, + bool is_websock) { switch (addr->type) { case SOCKET_ADDRESS_TYPE_INET: return g_strdup_printf("%s%s:%s:%s%s", prefix, - is_telnet ? "telnet" : "tcp", + is_telnet ? "telnet" + : (is_websock ? "websock" : "tcp"), addr->u.inet.host, addr->u.inet.port, is_listen ? ",server" : ""); @@ -420,7 +425,8 @@ static void update_disconnected_filename(SocketChardev *s) g_free(chr->filename); chr->filename = SocketAddress_to_str("disconnected:", s->addr, - s->is_listen, s->is_telnet); + s->is_listen, s->is_telnet, + s->is_websock); } /* NB may be called even if tcp_chr_connect has not been @@ -508,7 +514,7 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) 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) + bool is_listen, bool is_telnet, bool is_websock) { char shost[NI_MAXHOST], sserv[NI_MAXSERV]; char phost[NI_MAXHOST], pserv[NI_MAXSERV]; @@ -531,7 +537,8 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len, getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost), pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV); return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s", - is_telnet ? "telnet" : "tcp", + is_telnet ? "telnet" + : (is_websock ? "websock" : "tcp"), left, shost, right, sserv, is_listen ? ",server" : "", left, phost, right, pserv); @@ -550,7 +557,7 @@ static void tcp_chr_connect(void *opaque) chr->filename = sockaddr_to_str( &s->sioc->localAddr, s->sioc->localAddrLen, &s->sioc->remoteAddr, s->sioc->remoteAddrLen, - s->is_listen, s->is_telnet); + s->is_listen, s->is_telnet, s->is_websock); s->connected = 1; chr->gsource = io_add_watch_poll(chr, s->ioc, @@ -699,6 +706,45 @@ cont: } +static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data) +{ + Chardev *chr = user_data; + + if (qio_task_propagate_error(task, NULL)) { + tcp_chr_disconnect(chr); + } else { + tcp_chr_connect(chr); + } +} + + +static void tcp_chr_websock_init(Chardev *chr) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + QIOChannelWebsock *wioc; + gchar *name; + + if (s->is_listen) { + wioc = qio_channel_websock_new_server(s->ioc); + } else { + /* Websocket client is not yet implemented */ + return; + } + if (wioc == NULL) { + tcp_chr_disconnect(chr); + return; + } + + name = g_strdup_printf("chardev-websock-server-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(wioc), name); + g_free(name); + object_unref(OBJECT(s->ioc)); + s->ioc = QIO_CHANNEL(wioc); + + qio_channel_websock_handshake(wioc, tcp_chr_websock_handshake, chr, NULL); +} + + static void tcp_chr_tls_handshake(QIOTask *task, gpointer user_data) { @@ -710,6 +756,8 @@ static void tcp_chr_tls_handshake(QIOTask *task, } else { if (s->do_telnetopt) { tcp_chr_telnet_init(chr); + } else if (s->is_websock) { + tcp_chr_websock_init(chr); } else { tcp_chr_connect(chr); } @@ -799,12 +847,12 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) if (s->tls_creds) { tcp_chr_tls_init(chr); + } else if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); + } else if (s->is_websock) { + tcp_chr_websock_init(chr); } else { - if (s->do_telnetopt) { - tcp_chr_telnet_init(chr); - } else { - tcp_chr_connect(chr); - } + tcp_chr_connect(chr); } return 0; @@ -948,6 +996,7 @@ static void qmp_chardev_open_socket(Chardev *chr, bool is_listen = sock->has_server ? sock->server : true; bool is_telnet = sock->has_telnet ? sock->telnet : false; bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false; + bool is_websock = sock->has_websock ? sock->websock : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; QIOChannelSocket *sioc = NULL; @@ -956,6 +1005,7 @@ static void qmp_chardev_open_socket(Chardev *chr, s->is_listen = is_listen; s->is_telnet = is_telnet; s->is_tn3270 = is_tn3270; + s->is_websock = is_websock; s->do_nodelay = do_nodelay; if (sock->tls_creds) { Object *creds; @@ -1061,6 +1111,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true); bool is_telnet = qemu_opt_get_bool(opts, "telnet", false); bool is_tn3270 = qemu_opt_get_bool(opts, "tn3270", false); + bool is_websock = qemu_opt_get_bool(opts, "websock", false); bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true); int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0); const char *path = qemu_opt_get(opts, "path"); @@ -1109,6 +1160,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, sock->telnet = is_telnet; sock->has_tn3270 = true; sock->tn3270 = is_tn3270; + sock->has_websock = true; + sock->websock = is_websock; sock->has_wait = true; sock->wait = is_waitconnect; sock->has_reconnect = true; diff --git a/chardev/char.c b/chardev/char.c index 1ab80e0f68..d90e113428 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -837,6 +837,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "tls-creds", .type = QEMU_OPT_STRING, + },{ + .name = "websock", + .type = QEMU_OPT_BOOL, },{ .name = "width", .type = QEMU_OPT_NUMBER, diff --git a/qapi/char.json b/qapi/char.json index b7b2a05766..135ccddaf7 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -251,6 +251,8 @@ # sockets (default: false) # @tn3270: enable tn3270 protocol on server # sockets (default: false) (Since: 2.10) +# @websock: enable websocket protocol on server +# sockets (default: false) # @reconnect: For a client socket, if a socket is disconnected, # then attempt a reconnect after the given number of seconds. # Setting this to zero disables this function. (default: 0) @@ -265,6 +267,7 @@ '*nodelay' : 'bool', '*telnet' : 'bool', '*tn3270' : 'bool', + '*websock' : 'bool', '*reconnect' : 'int' }, 'base': 'ChardevCommon' }