From nobody Wed Nov 5 16:42:19 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1535395431799861.8621067140348; Mon, 27 Aug 2018 11:43:51 -0700 (PDT) Received: from localhost ([::1]:34689 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fuMUO-0004TB-U4 for importer@patchew.org; Mon, 27 Aug 2018 14:43:48 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57571) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fuMSR-00015V-Ne for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fuMS9-0007TG-9Z for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:43 -0400 Received: from smtp58.i.mail.ru ([217.69.128.38]:44942) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fuMS8-0007OK-Jt for qemu-devel@nongnu.org; Mon, 27 Aug 2018 14:41:29 -0400 Received: by smtp58.i.mail.ru with esmtpa (envelope-from ) id 1fuMS6-00033W-Hu; Mon, 27 Aug 2018 21:41:26 +0300 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.ru; s=mail2; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From; bh=f3lVU5F9A+SqvcBiyU50Fb537Q0tVf1VCuxUDYKBqfE=; b=NK5vVHvZ0yYCUDheIblZJ8nwZMBBIzNTJ9MXnsW7aXY+Ck6zdw5FMxSC5QYmRZtpFuS/ecHnvWB9qSY7PZzoKVfF5vypXAiDdScjZjvT4dVxgq8bgngUwv+jv+mWpZDwxSlWGiwN4ywQg5sK6+02trv0D4LnGzGGmW+XEacKZVc=; To: qemu-devel@nongnu.org Date: Mon, 27 Aug 2018 21:41:02 +0300 Message-Id: <20180827184103.32190-2-jusual@mail.ru> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180827184103.32190-1-jusual@mail.ru> References: <20180827184103.32190-1-jusual@mail.ru> Authentication-Results: smtp58.i.mail.ru; auth=pass smtp.auth=jusual@mail.ru smtp.mailfrom=jusual@mail.ru X-7FA49CB5: 0D63561A33F958A5C1BA94FE51B3A637028798E0FEBB25D60D9903CC397465C58941B15DA834481FA18204E546F3947CB861051D4BA689FCF6B57BC7E64490618DEB871D839B7333395957E7521B51C2545D4CF71C94A83E9FA2833FD35BB23D27C277FBC8AE2E8B974A882099E279BDA471835C12D1D977C4224003CC8364767815B9869FA544D8090A508E0FED62991661749BA6B97735D94E105876FE7799B3661434B16C20ACE7DDDDC251EA7DABAAAE862A0553A39223F8577A6DFFEA7CF26DBECE9D91448F7AD949B7F7BF34B5EFF80C71ABB335746BA297DBC24807EA27F269C8F02392CD5E72292F11C3671E27F269C8F02392CD5571747095F342E88FB05168BE4CE3AF X-Mailru-Sender: 7766D515518070DE138AAC7428EA760D1E5583EEC6F8F87CB2E38ADA9C1E2CC9114698FBA59F30EE7C4160E8B47E48163DDE9B364B0DF2898CB68AF7A628805D594FB4C9F0DBF412AE208404248635DF X-Mras: OK X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 217.69.128.38 Subject: [Qemu-devel] [PATCH v2 1/2] 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: , From: Julia Suvorova via Qemu-devel Reply-To: Julia Suvorova Cc: Jim Mussared , =?UTF-8?q?Steffen=20G=C3=B6rtz?= , Stefan Hajnoczi , Julia Suvorova , Joel Stanley , Stefan Hajnoczi , marcandre.lureau@redhat.com, Paolo Bonzini Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" New option "websock" added to allow using websocket protocol for chardev socket backend. Example: -chardev socket,websock,id=3D... Signed-off-by: Julia Suvorova --- chardev/char-socket.c | 124 +++++++++++++++++++++++++++++++----------- chardev/char.c | 8 ++- qapi/char.json | 3 + 3 files changed, 103 insertions(+), 32 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index efbad6ee7c..1d3c14d85d 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; =20 + bool is_websock; + GSource *reconnect_timer; int64_t reconnect_time; bool connect_err_reported; @@ -385,30 +388,37 @@ static void tcp_chr_free_connection(Chardev *chr) s->connected =3D 0; } =20 -static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr, - bool is_listen, bool is_telnet) +static const char *qemu_chr_socket_protocol(SocketChardev *s) +{ + if (s->is_telnet) { + return "telnet"; + } + return s->is_websock ? "websock" : "tcp"; +} + +static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix) { - switch (addr->type) { + switch (s->addr->type) { case SOCKET_ADDRESS_TYPE_INET: 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" : ""); + qemu_chr_socket_protocol(s), + s->addr->u.inet.host, + s->addr->u.inet.port, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_UNIX: return g_strdup_printf("%sunix:%s%s", prefix, - addr->u.q_unix.path, - is_listen ? ",server" : ""); + s->addr->u.q_unix.path, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.str, - is_listen ? ",server" : ""); + return g_strdup_printf("%sfd:%s%s", prefix, s->addr->u.fd.str, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_VSOCK: return g_strdup_printf("%svsock:%s:%s", prefix, - addr->u.vsock.cid, - addr->u.vsock.port); + s->addr->u.vsock.cid, + s->addr->u.vsock.port); default: abort(); } @@ -419,8 +429,7 @@ static void update_disconnected_filename(SocketChardev = *s) Chardev *chr =3D CHARDEV(s); =20 g_free(chr->filename); - chr->filename =3D SocketAddress_to_str("disconnected:", s->addr, - s->is_listen, s->is_telnet); + chr->filename =3D qemu_chr_socket_address(s, "disconnected:"); } =20 /* NB may be called even if tcp_chr_connect has not been @@ -506,10 +515,12 @@ static int tcp_chr_sync_read(Chardev *chr, const uint= 8_t *buf, int len) return size; } =20 -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) +static char *qemu_chr_compute_filename(SocketChardev *s) { + struct sockaddr_storage *ss =3D &s->sioc->localAddr; + struct sockaddr_storage *ps =3D &s->sioc->remoteAddr; + socklen_t ss_len =3D s->sioc->localAddrLen; + socklen_t ps_len =3D s->sioc->remoteAddrLen; char shost[NI_MAXHOST], sserv[NI_MAXSERV]; char phost[NI_MAXHOST], pserv[NI_MAXSERV]; const char *left =3D "", *right =3D ""; @@ -519,7 +530,7 @@ static char *sockaddr_to_str(struct sockaddr_storage *s= s, socklen_t ss_len, case AF_UNIX: return g_strdup_printf("unix:%s%s", ((struct sockaddr_un *)(ss))->sun_path, - is_listen ? ",server" : ""); + s->is_listen ? ",server" : ""); #endif case AF_INET6: left =3D "["; @@ -531,9 +542,9 @@ static char *sockaddr_to_str(struct sockaddr_storage *s= s, 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", + qemu_chr_socket_protocol(s), left, shost, right, sserv, - is_listen ? ",server" : "", + s->is_listen ? ",server" : "", left, phost, right, pserv); =20 default: @@ -547,10 +558,7 @@ static void tcp_chr_connect(void *opaque) SocketChardev *s =3D SOCKET_CHARDEV(opaque); =20 g_free(chr->filename); - chr->filename =3D sockaddr_to_str( - &s->sioc->localAddr, s->sioc->localAddrLen, - &s->sioc->remoteAddr, s->sioc->remoteAddrLen, - s->is_listen, s->is_telnet); + chr->filename =3D qemu_chr_compute_filename(s); =20 s->connected =3D 1; chr->gsource =3D io_add_watch_poll(chr, s->ioc, @@ -699,6 +707,48 @@ cont: } =20 =20 +static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data) +{ + Chardev *chr =3D user_data; + SocketChardev *s =3D user_data; + + if (qio_task_propagate_error(task, NULL)) { + tcp_chr_disconnect(chr); + } else { + if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); + } else { + tcp_chr_connect(chr); + } + } +} + + +static void tcp_chr_websock_init(Chardev *chr) +{ + SocketChardev *s =3D SOCKET_CHARDEV(chr); + QIOChannelWebsock *wioc =3D NULL; + gchar *name; + + if (s->is_listen) { + wioc =3D qio_channel_websock_new_server(s->ioc); + }; + + if (wioc =3D=3D NULL) { + tcp_chr_disconnect(chr); + return; + } + + name =3D 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 =3D QIO_CHANNEL(wioc); + + qio_channel_websock_handshake(wioc, tcp_chr_websock_handshake, chr, NU= LL); +} + + static void tcp_chr_tls_handshake(QIOTask *task, gpointer user_data) { @@ -708,7 +758,9 @@ static void tcp_chr_tls_handshake(QIOTask *task, if (qio_task_propagate_error(task, NULL)) { tcp_chr_disconnect(chr); } else { - if (s->do_telnetopt) { + if (s->is_websock) { + tcp_chr_websock_init(chr); + } else if (s->do_telnetopt) { tcp_chr_telnet_init(chr); } else { tcp_chr_connect(chr); @@ -799,12 +851,12 @@ static int tcp_chr_new_client(Chardev *chr, QIOChanne= lSocket *sioc) =20 if (s->tls_creds) { tcp_chr_tls_init(chr); + } else if (s->is_websock) { + tcp_chr_websock_init(chr); + } else if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); } else { - if (s->do_telnetopt) { - tcp_chr_telnet_init(chr); - } else { - tcp_chr_connect(chr); - } + tcp_chr_connect(chr); } =20 return 0; @@ -948,14 +1000,21 @@ static void qmp_chardev_open_socket(Chardev *chr, bool is_listen =3D sock->has_server ? sock->server : true; bool is_telnet =3D sock->has_telnet ? sock->telnet : false; bool is_tn3270 =3D sock->has_tn3270 ? sock->tn3270 : false; + bool is_websock =3D sock->has_websock ? sock->websock : false; bool is_waitconnect =3D sock->has_wait ? sock->wait : false; int64_t reconnect =3D sock->has_reconnect ? sock->reconnect : 0; QIOChannelSocket *sioc =3D NULL; SocketAddress *addr; =20 + if (!is_listen && is_websock) { + error_setg(errp, "%s", "Websocket client is not implemented"); + goto error; + } + s->is_listen =3D is_listen; s->is_telnet =3D is_telnet; s->is_tn3270 =3D is_tn3270; + s->is_websock =3D is_websock; s->do_nodelay =3D do_nodelay; if (sock->tls_creds) { Object *creds; @@ -1061,6 +1120,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, Cha= rdevBackend *backend, bool is_waitconnect =3D is_listen && qemu_opt_get_bool(opts, "wait", t= rue); bool is_telnet =3D qemu_opt_get_bool(opts, "telnet", false); bool is_tn3270 =3D qemu_opt_get_bool(opts, "tn3270", false); + bool is_websock =3D qemu_opt_get_bool(opts, "websock", false); bool do_nodelay =3D !qemu_opt_get_bool(opts, "delay", true); int64_t reconnect =3D qemu_opt_get_number(opts, "reconnect", 0); const char *path =3D qemu_opt_get(opts, "path"); @@ -1109,6 +1169,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, Cha= rdevBackend *backend, sock->telnet =3D is_telnet; sock->has_tn3270 =3D true; sock->tn3270 =3D is_tn3270; + sock->has_websock =3D true; + sock->websock =3D is_websock; sock->has_wait =3D true; sock->wait =3D is_waitconnect; sock->has_reconnect =3D true; diff --git a/chardev/char.c b/chardev/char.c index 1ab80e0f68..c045b8183b 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -404,7 +404,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, cons= t char *filename) } if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p) || - strstart(filename, "tn3270:", &p)) { + strstart(filename, "tn3270:", &p) || + strstart(filename, "websock:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { host[0] =3D 0; if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) @@ -424,6 +425,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, cons= t char *filename) qemu_opt_set(opts, "telnet", "on", &error_abort); } else if (strstart(filename, "tn3270:", &p)) { qemu_opt_set(opts, "tn3270", "on", &error_abort); + } else if (strstart(filename, "websock:", &p)) { + qemu_opt_set(opts, "websock", "on", &error_abort); } return opts; } @@ -837,6 +840,9 @@ QemuOptsList qemu_chardev_opts =3D { },{ .name =3D "tls-creds", .type =3D QEMU_OPT_STRING, + },{ + .name =3D "websock", + .type =3D QEMU_OPT_BOOL, },{ .name =3D "width", .type =3D QEMU_OPT_NUMBER, diff --git a/qapi/char.json b/qapi/char.json index b7b2a05766..aa48a150a8 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) (Since: 3.1) # @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' } =20 --=20 2.17.1