From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MM2f5-0006ab-Q5 for qemu-devel@nongnu.org; Wed, 01 Jul 2009 12:27:55 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MM2f0-0006ZO-0D for qemu-devel@nongnu.org; Wed, 01 Jul 2009 12:27:55 -0400 Received: from [199.232.76.173] (port=44572 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MM2ez-0006ZL-Qq for qemu-devel@nongnu.org; Wed, 01 Jul 2009 12:27:49 -0400 Received: from mx1.redhat.com ([66.187.233.31]:45013) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MM2ez-0002sB-57 for qemu-devel@nongnu.org; Wed, 01 Jul 2009 12:27:49 -0400 Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com [172.16.52.254]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id n61GRmhH025871 for ; Wed, 1 Jul 2009 12:27:48 -0400 Received: from file.fab.redhat.com (file.fab.redhat.com [10.33.63.6]) by int-mx1.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n61GRlcX016358 for ; Wed, 1 Jul 2009 12:27:47 -0400 Received: from file.fab.redhat.com (localhost.localdomain [127.0.0.1]) by file.fab.redhat.com (8.13.1/8.13.1) with ESMTP id n61GRliQ029385 for ; Wed, 1 Jul 2009 17:27:47 +0100 Received: (from berrange@localhost) by file.fab.redhat.com (8.13.1/8.13.1/Submit) id n61GRl7E029381 for qemu-devel@nongnu.org; Wed, 1 Jul 2009 17:27:47 +0100 Date: Wed, 1 Jul 2009 17:27:47 +0100 From: "Daniel P. Berrange" Subject: Re: [Qemu-devel] [PATCH 2/2] VNC char device data stream tunnelling Message-ID: <20090701162747.GD24296@redhat.com> References: <20090701162114.GB24296@redhat.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20090701162114.GB24296@redhat.com> Reply-To: "Daniel P. Berrange" List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org commit 71000d52fb4f9a4bef1f4841dd97561416fe5eed Author: Daniel P. Berrange 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 #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 :|