From nobody Tue Feb 10 03:37:39 2026 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 (208.118.235.17 [208.118.235.17]) by mx.zohomail.com with SMTPS id 153990224442634.125314704516086; Thu, 18 Oct 2018 15:37:24 -0700 (PDT) Received: from localhost ([::1]:45061 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gDGue-00088o-6u for importer@patchew.org; Thu, 18 Oct 2018 18:37:04 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38407) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gDGtG-0007Y1-HK for qemu-devel@nongnu.org; Thu, 18 Oct 2018 18:35:41 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gDGtD-0003CP-D9 for qemu-devel@nongnu.org; Thu, 18 Oct 2018 18:35:38 -0400 Received: from smtp50.i.mail.ru ([94.100.177.110]:35804) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gDGtC-000304-Nz for qemu-devel@nongnu.org; Thu, 18 Oct 2018 18:35:35 -0400 Received: by smtp50.i.mail.ru with esmtpa (envelope-from ) id 1gDGt6-0000Im-48; Fri, 19 Oct 2018 01:35:28 +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=V3mAqIKJrXSc6Qm7iF8fkyYSTujCttUHerM8O/lvPr4=; b=L64hofzcholn9e8x1mxIXQhRs1i5J1fjwcGnKVHpsz1FkifZdS68tMkmpbseaVw7aHLfCYZWWQg0nlm3srnxVxqKO4lktuQ/ErkgqljfaBvc4e6SULTgSC4jZ1o03OuiHzHmLyKwv/QltB0f2JGGzT443BqocTa2VhtSUPLZdCo=; To: qemu-devel@nongnu.org Date: Fri, 19 Oct 2018 01:35:00 +0300 Message-Id: <20181018223501.21683-3-jusual@mail.ru> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181018223501.21683-1-jusual@mail.ru> References: <20181018223501.21683-1-jusual@mail.ru> Authentication-Results: smtp50.i.mail.ru; auth=pass smtp.auth=jusual@mail.ru smtp.mailfrom=jusual@mail.ru X-7FA49CB5: 0D63561A33F958A5B01F79FC880152898D3DD5DEE0CB5ADD36A2807E4187C3608941B15DA834481FA18204E546F3947CEDCF5861DED71B2F389733CBF5DBD5E9C8A9BA7A39EFB7666BA297DBC24807EA117882F44604297287769387670735209ECD01F8117BC8BEA471835C12D1D977C4224003CC8364767815B9869FA544D8090A508E0FED629923F8577A6DFFEA7C2AE38A8E97BAFFB1B3661434B16C20ACE7DDDDC251EA7DABAAAE862A0553A39223F8577A6DFFEA7C4BB58A5F94B308007130902DED7CAE83731C566533BA786A40A5AABA2AD371193C9F3DD0FB1AF5EB1248E41960EB3BA62623479134186CDE6BA297DBC24807EABDAD6C7F3747799A X-Mailru-Sender: 42ECD68FB001EF9589BB0A53C8470BCA98CA4E558F01F8C10B32D730D3FD174D9EF95AA7B19AEE03BCD5BA2C93075E1EC77752E0C033A69E882C431543FD75E20226C39053983FF03453F38A29522196 X-Mras: OK X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 94.100.177.110 Subject: [Qemu-devel] [PATCH v3 2/3] 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?= , 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 "websocket" added to allow using WebSocket protocol for chardev socket backend. Example: -chardev socket,websocket,server,id=3D... Signed-off-by: Julia Suvorova Reviewed-by: Daniel P. Berrang=C3=A9 --- chardev/char-socket.c | 64 ++++++++++++++++++++++++++++++++++++++----- chardev/char.c | 8 +++++- qapi/char.json | 3 ++ qemu-options.hx | 13 +++++++-- 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index effde65a89..ba4ae9dfb0 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" @@ -68,6 +69,8 @@ typedef struct { GSource *telnet_source; TCPChardevTelnetInit *telnet_init; =20 + bool is_websock; + GSource *reconnect_timer; int64_t reconnect_time; bool connect_err_reported; @@ -394,7 +397,7 @@ static const char *qemu_chr_socket_protocol(SocketChard= ev *s) if (s->is_telnet) { return "telnet"; } - return "tcp"; + return s->is_websock ? "websocket" : "tcp"; } =20 static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix) @@ -714,6 +717,41 @@ 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; + + wioc =3D qio_channel_websock_new_server(s->ioc); + + name =3D g_strdup_printf("chardev-websocket-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) { @@ -723,7 +761,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); @@ -809,12 +849,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; @@ -959,13 +999,20 @@ static void qmp_chardev_open_socket(Chardev *chr, bool is_telnet =3D sock->has_telnet ? sock->telnet : false; bool is_tn3270 =3D sock->has_tn3270 ? sock->tn3270 : false; bool is_waitconnect =3D sock->has_wait ? sock->wait : false; + bool is_websock =3D sock->has_websocket ? sock->websocket : 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; @@ -1072,6 +1119,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, "websocket", 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"); @@ -1120,6 +1168,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_websocket =3D true; + sock->websocket =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 e115166995..a8790017e6 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -409,7 +409,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, "websocket:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { host[0] =3D 0; if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) @@ -429,6 +430,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, "websocket:", &p)) { + qemu_opt_set(opts, "websocket", "on", &error_abort); } return opts; } @@ -860,6 +863,9 @@ QemuOptsList qemu_chardev_opts =3D { },{ .name =3D "tls-creds", .type =3D QEMU_OPT_STRING, + },{ + .name =3D "websocket", + .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..79bac598a0 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) +# @websocket: 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', + '*websocket' : 'bool', '*reconnect' : 'int' }, 'base': 'ChardevCommon' } =20 diff --git a/qemu-options.hx b/qemu-options.hx index f139459e80..022c793162 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2409,9 +2409,9 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev help\n" "-chardev null,id=3Did[,mux=3Don|off][,logfile=3DPATH][,logappend=3Don= |off]\n" "-chardev socket,id=3Did[,host=3Dhost],port=3Dport[,to=3Dto][,ipv4][,i= pv6][,nodelay][,reconnect=3Dseconds]\n" - " [,server][,nowait][,telnet][,reconnect=3Dseconds][,mux=3Don|= off]\n" + " [,server][,nowait][,telnet][,websocket][,reconnect=3Dseconds= ][,mux=3Don|off]\n" " [,logfile=3DPATH][,logappend=3Don|off][,tls-creds=3DID] (tcp= )\n" - "-chardev socket,id=3Did,path=3Dpath[,server][,nowait][,telnet][,recon= nect=3Dseconds]\n" + "-chardev socket,id=3Did,path=3Dpath[,server][,nowait][,telnet][,webso= cket][,reconnect=3Dseconds]\n" " [,mux=3Don|off][,logfile=3DPATH][,logappend=3Don|off] (unix)= \n" "-chardev udp,id=3Did[,host=3Dhost],port=3Dport[,localaddr=3Dlocaladdr= ]\n" " [,localport=3Dlocalport][,ipv4][,ipv6][,mux=3Don|off]\n" @@ -2539,7 +2539,7 @@ The available backends are: 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. =20 -@item -chardev socket,id=3D@var{id}[,@var{TCP options} or @var{unix option= s}][,server][,nowait][,telnet][,reconnect=3D@var{seconds}][,tls-creds=3D@va= r{id}] +@item -chardev socket,id=3D@var{id}[,@var{TCP options} or @var{unix option= s}][,server][,nowait][,telnet][,websocket][,reconnect=3D@var{seconds}][,tls= -creds=3D@var{id}] =20 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 @@ -2553,6 +2553,9 @@ connect to a listening socket. @option{telnet} specifies that traffic on the socket should interpret teln= et escape sequences. =20 +@option{websocket} specifies that the socket uses WebSocket protocol for +communication. + @option{reconnect} sets the timeout for reconnecting on non-server sockets= when the remote end goes away. qemu will delay this many seconds and then atte= mpt to reconnect. Zero disables reconnecting, and is the default. @@ -3101,6 +3104,10 @@ MAGIC_SYSRQ sequence if you use a telnet that suppor= ts sending the break sequence. Typically in unix telnet you do it with Control-] and then type "send break" followed by pressing the enter key. =20 +@item websocket:@var{host}:@var{port},server[,nowait][,nodelay] +The WebSocket protocol is used instead of raw tcp socket. The port acts as +a WebSocket server. Client mode is not supported. + @item unix:@var{path}[,server][,nowait][,reconnect=3D@var{seconds}] A unix domain socket is used instead of a tcp socket. The option works the same as if you had specified @code{-serial tcp} except the unix domain soc= ket --=20 2.17.1