[Top][All Lists]
[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 :|