qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 2/2] VNC char device data stream tunnelling


From: Daniel P. Berrange
Subject: Re: [Qemu-devel] [PATCH 2/2] VNC char device data stream tunnelling
Date: Wed, 1 Jul 2009 17:27:47 +0100
User-agent: Mutt/1.4.1i

commit 71000d52fb4f9a4bef1f4841dd97561416fe5eed
Author: Daniel P. Berrange <address@hidden>
Date:   Wed Jul 1 17:03:46 2009 +0100

    Add a new VNC extension to allowing tunnelling of gemeric data streams.
    Map QEMU character devices onto VNC data streams

diff --git a/Makefile b/Makefile
index 2a4b3f3..a7de1a5 100644
--- a/Makefile
+++ b/Makefile
@@ -156,7 +156,7 @@ obj-y += $(addprefix audio/, $(audio-obj-y))
 obj-y += keymaps.o
 obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
 obj-$(CONFIG_CURSES) += curses.o
-obj-y += vnc.o acl.o d3des.o
+obj-y += vnc.o vnc-char.o acl.o d3des.o
 obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
 obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
 obj-$(CONFIG_COCOA) += cocoa.o
diff --git a/vnc-char.c b/vnc-char.c
new file mode 100644
index 0000000..b7b4606
--- /dev/null
+++ b/vnc-char.c
@@ -0,0 +1,401 @@
+/*
+ * QEMU VNC display driver: Char device capture
+ *
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+
+/*
+ * Encoding #: -260
+ *
+ * The server notifies the client of available streams using the
+ * framebuffer update for our psuedo encoding #.
+ *
+ * The 'x' field is '0' for a device addition, '1' for removal
+ *
+ * The payload of the update is the device name
+ *  - u32 - length of device name
+ *  - u8 * len - text of device name, not including \0
+ *
+ * When client indicates it accepts the stream encoding,
+ * the server will send back a stream addition update for
+ * each initial device. Hotplug/unplug for streams will
+ * trigger further updates.
+ *
+ * For QEMU, the streams extension maps 1-stream to 1 char device
+ *
+ * Stream capture uses an new 'aliguori' message sub-type. There
+ * are 3 client -> server messages, and 3 server -> client messages
+ *
+ * Client starts capture by supplying a stream name. Server replies
+ * givng the stream name -> stream ID mapping. All further messages
+ * use the unique stream ID.
+ *
+ * Client -> server messages:
+ *
+ *   Message   == u8: 255 (aliguouri)
+ *   sub-type  == u8: 2  (streams)
+ *   operation == u8:
+ *        0 == start capture
+ *        1 == end capture
+ *        2 == send data
+ *
+ * For operation == start capture:
+ *   content:
+ *      u32 - length of device name
+ *      u8 *len - device name to capture
+ *
+ * For operation == end capture
+ *   content:
+ *      u32 - capture session ID
+ *
+ * For operation == send data
+ *   content:
+ *      u32 - capture session ID
+ *      u32 - length of data
+ *      u8*len - raw data
+ *
+ *
+ *
+ * Server -> client messages:
+ *
+ *   Message   == u8: 255 (aliguouri)
+ *   sub-type  == u8: 2  (streams)
+ *   operation == u8:
+ *        0 == start capture
+ *        1 == end capture
+ *        2 == send data
+ *
+ * For operation == start capture
+ *   content:
+ *      u32 - capture session ID
+ *      u32 - length of device name
+ *      u8 *len - device name to capture
+ *
+ * For operation == end capture
+ *   content:
+ *      u32 - capture session ID
+ *
+ * For operation == send data
+ *   content:
+ *      u32 - capture session ID
+ *      u32 - length of data
+ *      u8*len - raw data
+ */
+
+static void vnc_send_start_capture(VncState *client,
+                                  VncCharCaptureState *chr);
+static void vnc_send_end_capture(VncState *client,
+                                VncCharCaptureState *chr);
+static void vnc_send_data_capture(VncState *client,
+                                 VncCharCaptureState *chr,
+                                 const void *buf,
+                                 int len);
+
+
+static VncCharCaptureState *vnc_char_get_capture(VncState *client,
+                                                int id)
+{
+    VncCharCaptureState *cap;
+
+    TAILQ_FOREACH(cap, &client->char_cap, next) {
+       if (cap->id == id)
+           return cap;
+    }
+
+    return NULL;
+}
+
+
+/*
+ * From guest device to VNC client
+ */
+static void vnc_char_capture(void *opaque, const void *buf, int len)
+{
+    VncCharCaptureState *cap = opaque;
+
+    if (!cap)
+       return;
+
+    vnc_send_data_capture(cap->client, cap, buf, len);
+}
+
+static void vnc_char_destroy(void *opaque)
+{
+    VncCharCaptureState *cap = opaque;
+
+    if (!cap)
+       return;
+
+    qemu_chr_del_capture(cap->chr, cap->cap);
+    cap->chr = NULL;
+    cap->cap = NULL;
+}
+
+static struct CharCaptureOps vnc_char_capture_ops = {
+    vnc_char_capture,
+    vnc_char_destroy,
+};
+
+
+
+int vnc_char_add_capture(VncState *client,
+                        const char *chrname)
+{
+    VncCharCaptureState *cap;
+    CharDriverState *chr;
+
+    chr = qemu_chr_fetch("serial0");
+    if (!chr)
+       return -1;
+
+    cap = qemu_mallocz(sizeof(*cap));
+    cap->id = client->char_cap_next_id++;
+    cap->chr = chr;
+    cap->cap = qemu_chr_add_capture(chr, &vnc_char_capture_ops, cap);
+    cap->client = client;
+
+    TAILQ_INSERT_TAIL(&client->char_cap, cap, next);
+
+    vnc_send_start_capture(client, cap);
+
+    return cap->id;
+}
+
+void vnc_char_del_capture(VncState *client,
+                         int id)
+{
+    VncCharCaptureState *cap = vnc_char_get_capture(client, id);
+    if (!cap)
+       return;
+
+    TAILQ_REMOVE(&client->char_cap, cap, next);
+
+    if (cap->chr)
+       qemu_chr_del_capture(cap->chr, cap->cap);
+
+    vnc_send_end_capture(client, cap);
+
+    qemu_free(cap);
+}
+
+void vnc_char_clear_capture(VncState *client)
+{
+    VncCharCaptureState *cap, *tmp;
+
+    TAILQ_FOREACH_SAFE(cap, &client->char_cap, next, tmp) {
+       if (cap->chr)
+           qemu_chr_del_capture(cap->chr, cap->cap);
+
+       qemu_free(cap);
+    }
+}
+
+
+/*
+ * From VNC client to guest device
+ */
+void vnc_char_write(VncState *client,
+                   int id,
+                   const void *buf,
+                   int len)
+{
+    VncCharCaptureState *cap = vnc_char_get_capture(client, id);
+    if (!cap)
+       return; /* XXX kill client */
+
+    if (cap->chr)
+       qemu_chr_write(cap->chr, buf, len);
+}
+
+
+static void
+vnc_framebuffer_update_stream(VncState *client,
+                             CharDriverState *chr,
+                             int deleted)
+{
+    if (strcmp(chr->label, "monitor") == 0)
+       return;
+
+    vnc_write_u8(client, 0);
+    vnc_write_u8(client, 0);
+    vnc_write_u16(client, 1);
+    vnc_framebuffer_update(client,
+                          deleted,
+                          0,
+                          ds_get_width(client->ds),
+                          ds_get_height(client->ds),
+                           VNC_ENCODING_STREAMS);
+    vnc_write_u32(client, strlen(chr->label));
+    vnc_write(client, chr->label, strlen(chr->label));
+}
+
+static int vnc_chr_iterator(CharDriverState *chr, void *opaque) {
+    VncState *client = opaque;
+
+    vnc_framebuffer_update_stream(client, chr, 0);
+    return 0;
+}
+
+
+static void vnc_chr_opened(CharDriverState *chr, void *opaque) {
+    VncState *client = opaque;
+
+    vnc_framebuffer_update_stream(client, chr, 0);
+    vnc_flush(client);
+}
+
+static void vnc_chr_closing(CharDriverState *chr, void *opaque) {
+    VncState *client = opaque;
+
+    vnc_framebuffer_update_stream(client, chr, 1);
+    vnc_flush(client);
+}
+
+static struct CharMonitorOps vnc_monops = {
+    vnc_chr_opened, vnc_chr_closing
+};
+
+void vnc_streams_ack(VncState *client)
+{
+    if (client->char_mon)
+       return;
+
+    qemu_chr_iterate(vnc_chr_iterator, client);
+    vnc_flush(client);
+
+    client->char_mon = qemu_chr_add_monitor(&vnc_monops, client);
+}
+
+
+static void vnc_send_start_capture(VncState *client,
+                                  VncCharCaptureState *chr) {
+    vnc_write_u8(client, 255);
+    vnc_write_u8(client, 2);
+    vnc_write_u8(client, 0);
+
+    vnc_write_u32(client, chr->id);
+    vnc_write_u32(client, strlen(chr->chr->label));
+    vnc_write(client, chr->chr->label, strlen(chr->chr->label));
+    vnc_flush(client);
+}
+
+
+static void vnc_send_end_capture(VncState *client,
+                                VncCharCaptureState *chr) {
+    vnc_write_u8(client, 255);
+    vnc_write_u8(client, 2);
+    vnc_write_u8(client, 1);
+
+    vnc_write_u32(client, chr->id);
+    vnc_flush(client);
+}
+
+
+static void vnc_send_data_capture(VncState *client,
+                                 VncCharCaptureState *chr,
+                                 const void *data,
+                                 int len) {
+    vnc_write_u8(client, 255);
+    vnc_write_u8(client, 2);
+    vnc_write_u8(client, 2);
+
+    vnc_write_u32(client, chr->id);
+    vnc_write_u32(client, len);
+    vnc_write(client, data, len);
+    vnc_flush(client);
+}
+
+
+int vnc_streams_msg(VncState *client, uint8_t *data, int len) {
+    if (len == 2)
+       return 3;
+
+    switch (read_u8(data, 2)) {
+    case 0: {
+       uint32_t n_name;
+       char *name;
+
+       if (len == 3)
+           return 7;
+
+       n_name = read_u32(data, 3);
+       if (n_name > 4096) {
+           vnc_client_error(client);
+           break;
+       }
+
+       if (len == 7)
+           return 7 + n_name;
+
+       name = qemu_malloc(n_name + 1);
+       memcpy(name, data + 7, n_name);
+       name[n_name] = '\0';
+
+       if (strcmp(name, "monitor") == 0) {
+           vnc_client_error(client);
+           qemu_free(name);
+           break;
+       }
+
+       vnc_char_add_capture(client, name);
+       qemu_free(name);
+    } break;
+
+    case 1: {
+       uint32_t id;
+       if (len == 3)
+           return 7;
+
+       id = read_u32(data, 3);
+
+       vnc_char_del_capture(client, id);
+    } break;
+
+    case 2: {
+       uint32_t id;
+       uint32_t n_data;
+       if (len == 3)
+           return 11;
+
+       id = read_u32(data, 3);
+       n_data = read_u32(data, 7);
+
+       if (n_data > 4096) {
+           vnc_client_error(client);
+           break;
+       }
+
+       if (len == 11)
+           return 11 + n_data;
+
+       vnc_char_write(client, id, data + 11, n_data);
+    } break;
+
+    default:
+       printf ("Invalid audio message %d\n", read_u8(data, 4));
+       vnc_client_error(client);
+       break;
+    }
+
+    return 0;
+}
diff --git a/vnc-char.h b/vnc-char.h
new file mode 100644
index 0000000..b421448
--- /dev/null
+++ b/vnc-char.h
@@ -0,0 +1,60 @@
+/*
+ * QEMU VNC display driver: Char device capture
+ *
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __QEMU_VNC_CHAR_H__
+#define __QEMU_VNC_CHAR_H__
+
+typedef struct VncCharCaptureState VncCharCaptureState;
+struct VncCharCaptureState
+{
+    int id;
+    CharDriverState *chr;
+    CharCaptureState *cap;
+
+    VncState *client;
+
+    TAILQ_ENTRY(VncCharCaptureState) next;
+};
+
+
+int vnc_char_add_capture(VncState *client,
+                        const char *chrname);
+
+void vnc_char_del_capture(VncState *client,
+                         int id);
+
+void vnc_char_clear_capture(VncState *client);
+
+void vnc_char_write(VncState *client,
+                   int id,
+                   const void *buf,
+                   int len);
+
+void vnc_streams_ack(VncState *client);
+
+int vnc_streams_msg(VncState *client, uint8_t *data, int len);
+
+
+#endif /* __QEMU_VNC_CHAR_H__ */
diff --git a/vnc.c b/vnc.c
index de0ff87..d73ed80 100644
--- a/vnc.c
+++ b/vnc.c
@@ -296,8 +296,8 @@ static void vnc_dpy_update(DisplayState *ds, int x, int y, 
int w, int h)
     }
 }
 
-static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
-                                   int32_t encoding)
+void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+                           int32_t encoding)
 {
     vnc_write_u16(vs, x);
     vnc_write_u16(vs, y);
@@ -901,6 +901,8 @@ static void vnc_disconnect_finish(VncState *vs)
 #endif /* CONFIG_VNC_SASL */
     audio_del(vs);
 
+    vnc_char_clear_capture(vs);
+
     VncState *p, *parent = NULL;
     for (p = vs->vd->clients; p != NULL; p = p->next) {
         if (p == vs) {
@@ -1523,6 +1525,7 @@ static void send_ext_audio_ack(VncState *vs)
     vnc_flush(vs);
 }
 
+
 static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
 {
     int i;
@@ -1564,6 +1567,9 @@ static void set_encodings(VncState *vs, int32_t 
*encodings, size_t n_encodings)
         case VNC_ENCODING_AUDIO:
             send_ext_audio_ack(vs);
             break;
+        case VNC_ENCODING_STREAMS:
+           vnc_streams_ack(vs);
+            break;
         case VNC_ENCODING_WMVi:
             vs->features |= VNC_FEATURE_WMVI_MASK;
             break;
@@ -1706,6 +1712,7 @@ static int protocol_client_msg(VncState *vs, uint8_t 
*data, size_t len)
 {
     int i;
     uint16_t limit;
+    int ret;
 
     switch (data[0]) {
     case 0:
@@ -1822,6 +1829,12 @@ static int protocol_client_msg(VncState *vs, uint8_t 
*data, size_t len)
             }
             break;
 
+        case 2:
+           ret = vnc_streams_msg(vs, data, len);
+           if (ret)
+               return ret;
+           break;
+
         default:
             printf("Msg: %d\n", read_u16(data, 0));
             vnc_client_error(vs);
@@ -2082,6 +2095,8 @@ static void vnc_connect(VncDisplay *vd, int csock)
     vnc_read_when(vs, protocol_version, 12);
     reset_keys(vs);
 
+    TAILQ_INIT(&vs->char_cap);
+
     vs->next = vd->clients;
     vd->clients = vs;
 
diff --git a/vnc.h b/vnc.h
index 3ae95f3..73d9ee5 100644
--- a/vnc.h
+++ b/vnc.h
@@ -31,6 +31,7 @@
 #include "console.h"
 #include "monitor.h"
 #include "audio/audio.h"
+#include "qemu-char.h"
 #include <zlib.h>
 
 #include "keymaps.h"
@@ -84,6 +85,7 @@ typedef struct VncDisplay VncDisplay;
 #include "vnc-auth-sasl.h"
 #endif
 
+#include "vnc-char.h"
 
 struct VncDisplay
 {
@@ -152,6 +154,10 @@ struct VncState
     CaptureVoiceOut *audio_cap;
     struct audsettings as;
 
+    TAILQ_HEAD(VncCharCaptureStateHead, VncCharCaptureState) char_cap;
+    int char_cap_next_id;
+    CharMonitorState *char_mon;
+
     VncReadEvent *read_handler;
     size_t read_handler_expect;
     /* input */
@@ -224,6 +230,7 @@ enum {
 #define VNC_ENCODING_POINTER_TYPE_CHANGE  0XFFFFFEFF /* -257 */
 #define VNC_ENCODING_EXT_KEY_EVENT        0XFFFFFEFE /* -258 */
 #define VNC_ENCODING_AUDIO                0XFFFFFEFD /* -259 */
+#define VNC_ENCODING_STREAMS              0XFFFFFEFC /* -260 */
 #define VNC_ENCODING_WMVi                 0x574D5669
 
 /*****************************************************************************
@@ -303,6 +310,9 @@ int vnc_client_io_error(VncState *vs, int ret, int 
last_errno);
 void start_client_init(VncState *vs);
 void start_auth_vnc(VncState *vs);
 
+void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+                           int32_t encoding);
+
 /* Buffer management */
 void buffer_reserve(Buffer *buffer, size_t len);
 int buffer_empty(Buffer *buffer);

-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




reply via email to

[Prev in Thread] Current Thread [Next in Thread]