From: Julia Suvorova <jusual@mail.ru>
To: qemu-devel@nongnu.org
Cc: "Daniel P . Berrangé" <berrange@redhat.com>,
"Eric Blake" <eblake@redhat.com>,
marcandre.lureau@redhat.com,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Stefan Hajnoczi" <stefanha@redhat.com>,
"Stefan Hajnoczi" <stefanha@gmail.com>,
"Joel Stanley" <joel@jms.id.au>,
"Jim Mussared" <jim@groklearning.com>,
"Steffen Görtz" <mail@steffen-goertz.de>,
"Julia Suvorova" <jusual@mail.ru>
Subject: [Qemu-devel] [PATCH v2 1/2] chardev: Add websocket support
Date: Mon, 27 Aug 2018 21:41:02 +0300 [thread overview]
Message-ID: <20180827184103.32190-2-jusual@mail.ru> (raw)
In-Reply-To: <20180827184103.32190-1-jusual@mail.ru>
New option "websock" added to allow using websocket protocol for
chardev socket backend.
Example:
-chardev socket,websock,id=...
Signed-off-by: Julia Suvorova <jusual@mail.ru>
---
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;
+ 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 = 0;
}
-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 = CHARDEV(s);
g_free(chr->filename);
- chr->filename = SocketAddress_to_str("disconnected:", s->addr,
- s->is_listen, s->is_telnet);
+ chr->filename = qemu_chr_socket_address(s, "disconnected:");
}
/* 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 uint8_t *buf, int len)
return size;
}
-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 = &s->sioc->localAddr;
+ struct sockaddr_storage *ps = &s->sioc->remoteAddr;
+ socklen_t ss_len = s->sioc->localAddrLen;
+ socklen_t ps_len = s->sioc->remoteAddrLen;
char shost[NI_MAXHOST], sserv[NI_MAXSERV];
char phost[NI_MAXHOST], pserv[NI_MAXSERV];
const char *left = "", *right = "";
@@ -519,7 +530,7 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, 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 = "[";
@@ -531,9 +542,9 @@ 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",
+ qemu_chr_socket_protocol(s),
left, shost, right, sserv,
- is_listen ? ",server" : "",
+ s->is_listen ? ",server" : "",
left, phost, right, pserv);
default:
@@ -547,10 +558,7 @@ static void tcp_chr_connect(void *opaque)
SocketChardev *s = SOCKET_CHARDEV(opaque);
g_free(chr->filename);
- chr->filename = sockaddr_to_str(
- &s->sioc->localAddr, s->sioc->localAddrLen,
- &s->sioc->remoteAddr, s->sioc->remoteAddrLen,
- s->is_listen, s->is_telnet);
+ chr->filename = qemu_chr_compute_filename(s);
s->connected = 1;
chr->gsource = io_add_watch_poll(chr, s->ioc,
@@ -699,6 +707,48 @@ cont:
}
+static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data)
+{
+ Chardev *chr = user_data;
+ SocketChardev *s = 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 = SOCKET_CHARDEV(chr);
+ QIOChannelWebsock *wioc = NULL;
+ gchar *name;
+
+ if (s->is_listen) {
+ wioc = qio_channel_websock_new_server(s->ioc);
+ };
+
+ 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)
{
@@ -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, QIOChannelSocket *sioc)
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);
}
return 0;
@@ -948,14 +1000,21 @@ 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;
SocketAddress *addr;
+ if (!is_listen && is_websock) {
+ error_setg(errp, "%s", "Websocket client is not implemented");
+ goto error;
+ }
+
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 +1120,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 +1169,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..c045b8183b 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -404,7 +404,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const 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] = 0;
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
@@ -424,6 +425,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const 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 = {
},{
.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..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' }
--
2.17.1
next prev parent reply other threads:[~2018-08-27 18:41 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-08-27 18:41 [Qemu-devel] [PATCH v2 0/2] chardev: Add websocket support Julia Suvorova
2018-08-27 18:41 ` Julia Suvorova [this message]
2018-08-28 9:02 ` [Qemu-devel] [PATCH v2 1/2] " Daniel P. Berrangé
2018-08-28 10:04 ` Julia Suvorova
2018-08-28 10:09 ` Daniel P. Berrangé
2018-08-28 10:18 ` Julia Suvorova
2018-08-28 10:37 ` Daniel P. Berrangé
2018-10-03 14:34 ` Stefan Hajnoczi
2018-08-27 18:41 ` [Qemu-devel] [PATCH v2 2/2] tests/test-char: Check websocket chardev functionality Julia Suvorova
2018-08-28 9:07 ` Daniel P. Berrangé
2018-10-03 14:37 ` Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180827184103.32190-2-jusual@mail.ru \
--to=jusual@mail.ru \
--cc=berrange@redhat.com \
--cc=eblake@redhat.com \
--cc=jim@groklearning.com \
--cc=joel@jms.id.au \
--cc=mail@steffen-goertz.de \
--cc=marcandre.lureau@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@gmail.com \
--cc=stefanha@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).