All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] chardev: Add websocket support
@ 2018-08-13 10:20 Julia Suvorova
  2018-08-13 12:02 ` Paolo Bonzini
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Julia Suvorova @ 2018-08-13 10:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Eric Blake, marcandre.lureau, Paolo Bonzini, Stefan Hajnoczi,
	Stefan Hajnoczi, Joel Stanley, Jim Mussared, Steffen Görtz,
	Julia Suvorova

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 | 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' }
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-08-14 17:22 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-08-13 10:20 [Qemu-devel] [PATCH] chardev: Add websocket support Julia Suvorova
2018-08-13 12:02 ` Paolo Bonzini
2018-08-14 10:13   ` Julia Suvorova
2018-08-13 12:21 ` Daniel P. Berrangé
2018-08-14 10:22   ` Julia Suvorova
2018-08-14 16:11 ` Stefan Hajnoczi

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.