* [Qemu-devel] [PATCH] Support multiple VNC clients
@ 2009-02-13 19:49 Brian Kress
2009-02-14 10:07 ` malc
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Brian Kress @ 2009-02-13 19:49 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 586 bytes --]
Attached is a patch that allows qemu to support multiple vnc clients.
Each client can have their own
VNC encodings and VNC state.
General idea:
Change structure associated with a display from VncState to a new
structure VncDisplay.
Remove client specific fields from VncDisplay.
Remove display specific fields from VncState.
Maintain a linked list of VncStates per VncDisplay structure, update as
necessary.
When updates/resizes/copies come in from the hardware, dispatch to all
clients.
Patch is against current SVN (6618)
Any ideas/suggestions/comments/flames appreciated.
[-- Attachment #2: patch.vnc --]
[-- Type: text/plain, Size: 22251 bytes --]
Index: vnc.c
===================================================================
--- vnc.c (revision 6618)
+++ vnc.c (working copy)
@@ -90,12 +90,35 @@
#define VNC_AUTH_CHALLENGE_SIZE 16
+typedef struct VncDisplay VncDisplay;
+
+struct VncDisplay
+{
+ int lsock;
+ DisplayState *ds;
+ VncState *clients;
+ kbd_layout_t *kbd_layout;
+
+ char *display;
+ char *password;
+ int auth;
+#ifdef CONFIG_VNC_TLS
+ int subauth;
+ int x509verify;
+
+ char *x509cacert;
+ char *x509cacrl;
+ char *x509cert;
+ char *x509key;
+#endif
+};
+
struct VncState
{
QEMUTimer *timer;
- int lsock;
int csock;
DisplayState *ds;
+ VncDisplay *vd;
int need_update;
uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
char *old_data;
@@ -111,18 +134,6 @@
int major;
int minor;
- char *display;
- char *password;
- int auth;
-#ifdef CONFIG_VNC_TLS
- int subauth;
- int x509verify;
-
- char *x509cacert;
- char *x509cacrl;
- char *x509cert;
- char *x509key;
-#endif
char challenge[VNC_AUTH_CHALLENGE_SIZE];
#ifdef CONFIG_VNC_TLS
@@ -132,7 +143,6 @@
Buffer output;
Buffer input;
- kbd_layout_t *kbd_layout;
/* current output mode information */
VncWritePixels *write_pixels;
VncSendHextileTile *send_hextile_tile;
@@ -149,21 +159,23 @@
Buffer zlib;
Buffer zlib_tmp;
z_stream zlib_stream[4];
+
+ VncState *next;
};
-static VncState *vnc_state; /* needed for info vnc */
+static VncDisplay *vnc_display; /* needed for info vnc */
static DisplayChangeListener *dcl;
void do_info_vnc(void)
{
- if (vnc_state == NULL || vnc_state->display == NULL)
+ if (vnc_display == NULL || vnc_display->display == NULL)
term_printf("VNC server disabled\n");
else {
term_printf("VNC server active on: ");
- term_print_filename(vnc_state->display);
+ term_print_filename(vnc_display->display);
term_printf("\n");
- if (vnc_state->csock == -1)
+ if (vnc_display->clients == NULL)
term_printf("No client connected\n");
else
term_printf("Client connected\n");
@@ -190,7 +202,7 @@
static void vnc_update_client(void *opaque);
static void vnc_client_read(void *opaque);
-static void vnc_colordepth(DisplayState *ds);
+static void vnc_colordepth(VncState *vs);
static inline void vnc_set_bit(uint32_t *d, int k)
{
@@ -233,9 +245,8 @@
return 0;
}
-static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+static void vnc_update(VncState *vs, int x, int y, int w, int h)
{
- VncState *vs = ds->opaque;
int i;
h += y;
@@ -257,6 +268,16 @@
vnc_set_bit(vs->dirty_row[y], (x + i) / 16);
}
+static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs = vd->clients;
+ while (vs != NULL) {
+ vnc_update(vs, x, y, w, h);
+ vs = vs->next;
+ }
+}
+
static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
int32_t encoding)
{
@@ -301,10 +322,11 @@
buffer->offset += len;
}
-static void vnc_dpy_resize(DisplayState *ds)
+static void vnc_resize(VncState *vs)
{
+ DisplayState *ds = vs->ds;
+
int size_changed;
- VncState *vs = ds->opaque;
vs->old_data = qemu_realloc(vs->old_data, ds_get_linesize(ds) * ds_get_height(ds));
@@ -315,7 +337,7 @@
if (ds_get_bytes_per_pixel(ds) != vs->serverds.pf.bytes_per_pixel)
console_color_init(ds);
- vnc_colordepth(ds);
+ vnc_colordepth(vs);
size_changed = ds_get_width(ds) != vs->serverds.width ||
ds_get_height(ds) != vs->serverds.height;
vs->serverds = *(ds->surface);
@@ -334,6 +356,16 @@
memset(vs->old_data, 42, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
}
+static void vnc_dpy_resize(DisplayState *ds)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs = vd->clients;
+ while (vs != NULL) {
+ vnc_resize(vs);
+ vs = vs->next;
+ }
+}
+
/* fastest code */
static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size)
{
@@ -599,10 +631,8 @@
}
}
-static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
- VncState *vs = ds->opaque;
-
vnc_update_client(vs);
vnc_write_u8(vs, 0); /* msg id */
@@ -614,6 +644,19 @@
vnc_flush(vs);
}
+static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs = vd->clients;
+ while (vs != NULL) {
+ if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
+ vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
+ else /* TODO */
+ vnc_update(vs, dst_x, dst_y, w, h);
+ vs = vs->next;
+ }
+}
+
static int find_dirty_height(VncState *vs, int y, int last_x, int x)
{
int h;
@@ -632,7 +675,6 @@
static void vnc_update_client(void *opaque)
{
VncState *vs = opaque;
-
if (vs->need_update && vs->csock != -1) {
int y;
uint8_t *row;
@@ -725,14 +767,6 @@
}
-static int vnc_listen_poll(void *opaque)
-{
- VncState *vs = opaque;
- if (vs->csock == -1)
- return 1;
- return 0;
-}
-
/* audio */
static void audio_capture_notify(void *opaque, audcnotification_e cmd)
{
@@ -817,19 +851,35 @@
VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);
qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
closesocket(vs->csock);
- vs->csock = -1;
- dcl->idle = 1;
- buffer_reset(&vs->input);
- buffer_reset(&vs->output);
- vs->need_update = 0;
+ qemu_del_timer(vs->timer);
+ qemu_free_timer(vs->timer);
+ if (vs->input.buffer) qemu_free(vs->input.buffer);
+ if (vs->output.buffer) qemu_free(vs->output.buffer);
#ifdef CONFIG_VNC_TLS
if (vs->tls_session) {
gnutls_deinit(vs->tls_session);
vs->tls_session = NULL;
}
- vs->wiremode = VNC_WIREMODE_CLEAR;
#endif /* CONFIG_VNC_TLS */
audio_del(vs);
+
+ VncState *p, *parent = NULL;
+ for (p = vs->vd->clients; p != NULL; p = p->next) {
+ if (p == vs) {
+ if (parent)
+ parent->next = p->next;
+ else
+ vs->vd->clients = p->next;
+ break;
+ }
+ parent = p;
+ }
+ if (!vs->vd->clients)
+ dcl->idle = 1;
+
+ qemu_free(vs->old_data);
+ qemu_free(vs);
+
return 0;
}
return ret;
@@ -1095,8 +1145,8 @@
static void press_key(VncState *vs, int keysym)
{
- kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) & 0x7f);
- kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80);
+ kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) & 0x7f);
+ kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) | 0x80);
}
static void do_key_event(VncState *vs, int down, int keycode, int sym)
@@ -1129,12 +1179,12 @@
break;
}
- if (keycode_is_keypad(vs->kbd_layout, keycode)) {
+ if (keycode_is_keypad(vs->vd->kbd_layout, keycode)) {
/* If the numlock state needs to change then simulate an additional
keypress before sending this one. This will happen if the user
toggles numlock away from the VNC window.
*/
- if (keysym_is_numlock(vs->kbd_layout, sym & 0xFFFF)) {
+ if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
if (!vs->modifiers_state[0x45]) {
vs->modifiers_state[0x45] = 1;
press_key(vs, 0xff7f);
@@ -1207,7 +1257,7 @@
if (sym >= 'A' && sym <= 'Z' && is_graphic_console())
sym = sym - 'A' + 'a';
- keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
+ keycode = keysym2scancode(vs->vd->kbd_layout, sym & 0xFFFF);
do_key_event(vs, down, keycode, sym);
}
@@ -1279,7 +1329,6 @@
vs->tight_compression = 9;
vs->tight_quality = 9;
vs->absolute = -1;
- dcl->dpy_copy = NULL;
for (i = n_encodings - 1; i >= 0; i--) {
enc = encodings[i];
@@ -1288,7 +1337,7 @@
vs->vnc_encoding = enc;
break;
case VNC_ENCODING_COPYRECT:
- dcl->dpy_copy = vnc_copy;
+ vs->features |= VNC_FEATURE_COPYRECT_MASK;
break;
case VNC_ENCODING_HEXTILE:
vs->features |= VNC_FEATURE_HEXTILE_MASK;
@@ -1432,17 +1481,15 @@
/* We don't have to do anything */
}
-static void vnc_colordepth(DisplayState *ds)
+static void vnc_colordepth(VncState *vs)
{
- struct VncState *vs = ds->opaque;
-
- if (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
+ if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
/* Sending a WMVi message to notify the client*/
vnc_write_u8(vs, 0); /* msg id */
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
- vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds),
- VNC_ENCODING_WMVi);
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
+ ds_get_height(vs->ds), VNC_ENCODING_WMVi);
pixel_format_message(vs);
vnc_flush(vs);
} else {
@@ -1626,7 +1673,7 @@
int i, j, pwlen;
unsigned char key[8];
- if (!vs->password || !vs->password[0]) {
+ if (!vs->vd->password || !vs->vd->password[0]) {
VNC_DEBUG("No password configured on server");
vnc_write_u32(vs, 1); /* Reject auth */
if (vs->minor >= 8) {
@@ -1642,9 +1689,9 @@
memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
/* Calculate the expected challenge response */
- pwlen = strlen(vs->password);
+ pwlen = strlen(vs->vd->password);
for (i=0; i<sizeof(key); i++)
- key[i] = i<pwlen ? vs->password[i] : 0;
+ key[i] = i<pwlen ? vs->vd->password[i] : 0;
deskey(key, EN0);
for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
des(response+j, response+j);
@@ -1733,15 +1780,15 @@
gnutls_certificate_credentials_t x509_cred;
int ret;
- if (!vs->x509cacert) {
+ if (!vs->vd->x509cacert) {
VNC_DEBUG("No CA x509 certificate specified\n");
return NULL;
}
- if (!vs->x509cert) {
+ if (!vs->vd->x509cert) {
VNC_DEBUG("No server x509 certificate specified\n");
return NULL;
}
- if (!vs->x509key) {
+ if (!vs->vd->x509key) {
VNC_DEBUG("No server private key specified\n");
return NULL;
}
@@ -1751,7 +1798,7 @@
return NULL;
}
if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
- vs->x509cacert,
+ vs->vd->x509cacert,
GNUTLS_X509_FMT_PEM)) < 0) {
VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials(x509_cred);
@@ -1759,17 +1806,17 @@
}
if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
- vs->x509cert,
- vs->x509key,
+ vs->vd->x509cert,
+ vs->vd->x509key,
GNUTLS_X509_FMT_PEM)) < 0) {
VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials(x509_cred);
return NULL;
}
- if (vs->x509cacrl) {
+ if (vs->vd->x509cacrl) {
if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
- vs->x509cacrl,
+ vs->vd->x509cacrl,
GNUTLS_X509_FMT_PEM)) < 0) {
VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials(x509_cred);
@@ -1863,7 +1910,7 @@
static int start_auth_vencrypt_subauth(VncState *vs)
{
- switch (vs->subauth) {
+ switch (vs->vd->subauth) {
case VNC_AUTH_VENCRYPT_TLSNONE:
case VNC_AUTH_VENCRYPT_X509NONE:
VNC_DEBUG("Accept TLS auth none\n");
@@ -1877,7 +1924,7 @@
return start_auth_vnc(vs);
default: /* Should not be possible, but just in case */
- VNC_DEBUG("Reject auth %d\n", vs->auth);
+ VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
vnc_write_u8(vs, 1);
if (vs->minor >= 8) {
static const char err[] = "Unsupported authentication type";
@@ -1909,7 +1956,7 @@
return -1;
}
- if (vs->x509verify) {
+ if (vs->vd->x509verify) {
if (vnc_validate_certificate(vs) < 0) {
VNC_DEBUG("Client verification failed\n");
vnc_client_error(vs);
@@ -1934,9 +1981,9 @@
}
#define NEED_X509_AUTH(vs) \
- ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
+ ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
static int vnc_start_tls(struct VncState *vs) {
@@ -2000,7 +2047,7 @@
vnc_client_error(vs);
return -1;
}
- if (vs->x509verify) {
+ if (vs->vd->x509verify) {
VNC_DEBUG("Requesting a client certificate\n");
gnutls_certificate_server_set_request (vs->tls_session, GNUTLS_CERT_REQUEST);
}
@@ -2035,7 +2082,7 @@
{
int auth = read_u32(data, 0);
- if (auth != vs->subauth) {
+ if (auth != vs->vd->subauth) {
VNC_DEBUG("Rejecting auth %d\n", auth);
vnc_write_u8(vs, 0); /* Reject auth */
vnc_flush(vs);
@@ -2070,10 +2117,10 @@
vnc_flush(vs);
vnc_client_error(vs);
} else {
- VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
+ VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
vnc_write_u8(vs, 0); /* Accept version */
vnc_write_u8(vs, 1); /* Number of sub-auths */
- vnc_write_u32(vs, vs->subauth); /* The supported auth */
+ vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
vnc_flush(vs);
vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
}
@@ -2095,7 +2142,7 @@
{
/* We only advertise 1 auth scheme at a time, so client
* must pick the one we sent. Verify this */
- if (data[0] != vs->auth) { /* Reject auth */
+ if (data[0] != vs->vd->auth) { /* Reject auth */
VNC_DEBUG("Reject auth %d\n", (int)data[0]);
vnc_write_u32(vs, 1);
if (vs->minor >= 8) {
@@ -2106,7 +2153,7 @@
vnc_client_error(vs);
} else { /* Accept requested auth */
VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
- switch (vs->auth) {
+ switch (vs->vd->auth) {
case VNC_AUTH_NONE:
VNC_DEBUG("Accept auth none\n");
if (vs->minor >= 8) {
@@ -2127,7 +2174,7 @@
#endif /* CONFIG_VNC_TLS */
default: /* Should not be possible, but just in case */
- VNC_DEBUG("Reject auth %d\n", vs->auth);
+ VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
vnc_write_u8(vs, 1);
if (vs->minor >= 8) {
static const char err[] = "Authentication failed";
@@ -2172,26 +2219,26 @@
vs->minor = 3;
if (vs->minor == 3) {
- if (vs->auth == VNC_AUTH_NONE) {
+ if (vs->vd->auth == VNC_AUTH_NONE) {
VNC_DEBUG("Tell client auth none\n");
- vnc_write_u32(vs, vs->auth);
+ vnc_write_u32(vs, vs->vd->auth);
vnc_flush(vs);
vnc_read_when(vs, protocol_client_init, 1);
- } else if (vs->auth == VNC_AUTH_VNC) {
+ } else if (vs->vd->auth == VNC_AUTH_VNC) {
VNC_DEBUG("Tell client VNC auth\n");
- vnc_write_u32(vs, vs->auth);
+ vnc_write_u32(vs, vs->vd->auth);
vnc_flush(vs);
start_auth_vnc(vs);
} else {
- VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
+ VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth);
vnc_write_u32(vs, VNC_AUTH_INVALID);
vnc_flush(vs);
vnc_client_error(vs);
}
} else {
- VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
+ VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
vnc_write_u8(vs, 1); /* num auth */
- vnc_write_u8(vs, vs->auth);
+ vnc_write_u8(vs, vs->vd->auth);
vnc_read_when(vs, protocol_client_auth, 1);
vnc_flush(vs);
}
@@ -2199,55 +2246,67 @@
return 0;
}
-static void vnc_connect(VncState *vs)
+static void vnc_connect(VncDisplay *vd, int csock)
{
- VNC_DEBUG("New client on socket %d\n", vs->csock);
+ VncState *vs = qemu_mallocz(sizeof(VncState));
+ vs->csock = csock;
+
+ VNC_DEBUG("New client on socket %d\n", csock);
dcl->idle = 0;
socket_set_nonblock(vs->csock);
qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+
+ vs->vd = vd;
+ vs->ds = vd->ds;
+ vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+ vs->last_x = -1;
+ vs->last_y = -1;
+
+ vs->as.freq = 44100;
+ vs->as.nchannels = 2;
+ vs->as.fmt = AUD_FMT_S16;
+ vs->as.endianness = 0;
+
+ vnc_resize(vs);
vnc_write(vs, "RFB 003.008\n", 12);
vnc_flush(vs);
vnc_read_when(vs, protocol_version, 12);
memset(vs->old_data, 0, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
- vs->features = 0;
- dcl->dpy_copy = NULL;
vnc_update_client(vs);
reset_keys(vs);
+
+ vs->next = vd->clients;
+ vd->clients = vs;
}
static void vnc_listen_read(void *opaque)
{
- VncState *vs = opaque;
+ VncDisplay *vs = opaque;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
/* Catch-up */
vga_hw_update();
- vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
- if (vs->csock != -1) {
- vnc_connect(vs);
+ int csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
+ if (csock != -1) {
+ vnc_connect(vs, csock);
}
}
void vnc_display_init(DisplayState *ds)
{
- VncState *vs;
+ VncDisplay *vs;
vs = qemu_mallocz(sizeof(VncState));
dcl = qemu_mallocz(sizeof(DisplayChangeListener));
ds->opaque = vs;
dcl->idle = 1;
- vnc_state = vs;
- vs->display = NULL;
- vs->password = NULL;
+ vnc_display = vs;
vs->lsock = -1;
- vs->csock = -1;
- vs->last_x = -1;
- vs->last_y = -1;
vs->ds = ds;
@@ -2259,22 +2318,15 @@
if (!vs->kbd_layout)
exit(1);
- vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
-
+ dcl->dpy_copy = vnc_dpy_copy;
dcl->dpy_update = vnc_dpy_update;
dcl->dpy_resize = vnc_dpy_resize;
dcl->dpy_setdata = vnc_dpy_setdata;
- dcl->dpy_refresh = NULL;
register_displaychangelistener(ds, dcl);
-
- vs->as.freq = 44100;
- vs->as.nchannels = 2;
- vs->as.fmt = AUD_FMT_S16;
- vs->as.endianness = 0;
}
#ifdef CONFIG_VNC_TLS
-static int vnc_set_x509_credential(VncState *vs,
+static int vnc_set_x509_credential(VncDisplay *vs,
const char *certdir,
const char *filename,
char **cred,
@@ -2305,7 +2357,7 @@
return 0;
}
-static int vnc_set_x509_credential_dir(VncState *vs,
+static int vnc_set_x509_credential_dir(VncDisplay *vs,
const char *certdir)
{
if (vnc_set_x509_credential(vs, certdir, X509_CA_CERT_FILE, &vs->x509cacert, 0) < 0)
@@ -2331,7 +2383,7 @@
void vnc_display_close(DisplayState *ds)
{
- VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
if (!vs)
return;
@@ -2344,32 +2396,16 @@
close(vs->lsock);
vs->lsock = -1;
}
- if (vs->csock != -1) {
- qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
- closesocket(vs->csock);
- vs->csock = -1;
- buffer_reset(&vs->input);
- buffer_reset(&vs->output);
- vs->need_update = 0;
-#ifdef CONFIG_VNC_TLS
- if (vs->tls_session) {
- gnutls_deinit(vs->tls_session);
- vs->tls_session = NULL;
- }
- vs->wiremode = VNC_WIREMODE_CLEAR;
-#endif /* CONFIG_VNC_TLS */
- }
vs->auth = VNC_AUTH_INVALID;
#ifdef CONFIG_VNC_TLS
vs->subauth = VNC_AUTH_INVALID;
vs->x509verify = 0;
#endif
- audio_del(vs);
}
int vnc_display_password(DisplayState *ds, const char *password)
{
- VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
if (vs->password) {
qemu_free(vs->password);
@@ -2385,7 +2421,7 @@
int vnc_display_open(DisplayState *ds, const char *display)
{
- VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
const char *options;
int password = 0;
int reverse = 0;
@@ -2394,7 +2430,7 @@
int tls = 0, x509 = 0;
#endif
- if (!vnc_state)
+ if (!vnc_display)
return -1;
vnc_display_close(ds);
if (strcmp(display, "none") == 0)
@@ -2499,9 +2535,9 @@
vs->display = NULL;
return -1;
} else {
- vs->csock = vs->lsock;
+ int csock = vs->lsock;
vs->lsock = -1;
- vnc_connect(vs);
+ vnc_connect(vs, csock);
}
return 0;
@@ -2523,6 +2559,5 @@
vs->display = dpy;
}
}
-
- return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
+ return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
}
Index: vnc.h
===================================================================
--- vnc.h (revision 6618)
+++ vnc.h (working copy)
@@ -101,6 +101,7 @@
#define VNC_FEATURE_WMVI 3
#define VNC_FEATURE_TIGHT 4
#define VNC_FEATURE_ZLIB 5
+#define VNC_FEATURE_COPYRECT 6
#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE)
#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE)
@@ -108,5 +109,6 @@
#define VNC_FEATURE_WMVI_MASK (1 << VNC_FEATURE_WMVI)
#define VNC_FEATURE_TIGHT_MASK (1 << VNC_FEATURE_TIGHT)
#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB)
+#define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT)
#endif /* __VNCTIGHT_H */
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-13 19:49 [Qemu-devel] [PATCH] Support multiple VNC clients Brian Kress
@ 2009-02-14 10:07 ` malc
2009-02-14 22:07 ` Anthony Liguori
2009-02-15 11:35 ` Daniel P. Berrange
2 siblings, 0 replies; 10+ messages in thread
From: malc @ 2009-02-14 10:07 UTC (permalink / raw)
To: qemu-devel
On Fri, 13 Feb 2009, Brian Kress wrote:
> Attached is a patch that allows qemu to support multiple vnc clients. Each
> client can have their own
> VNC encodings and VNC state.
>
> General idea:
>
> Change structure associated with a display from VncState to a new structure
> VncDisplay. Remove client specific fields from VncDisplay.
> Remove display specific fields from VncState.
> Maintain a linked list of VncStates per VncDisplay structure, update as
> necessary.
> When updates/resizes/copies come in from the hardware, dispatch to all
> clients.
>
>
> Patch is against current SVN (6618)
> Any ideas/suggestions/comments/flames appreciated.
Very nice. With this it's possible to have simulataneous interaction and
A/V capturing via VNC, good work.
--
mailto:av1474@comtv.ru
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-13 19:49 [Qemu-devel] [PATCH] Support multiple VNC clients Brian Kress
2009-02-14 10:07 ` malc
@ 2009-02-14 22:07 ` Anthony Liguori
2009-02-15 1:13 ` Brian Kress
2009-02-15 11:35 ` Daniel P. Berrange
2 siblings, 1 reply; 10+ messages in thread
From: Anthony Liguori @ 2009-02-14 22:07 UTC (permalink / raw)
To: qemu-devel
Brian Kress wrote:
> Attached is a patch that allows qemu to support multiple vnc clients.
> Each client can have their own
> VNC encodings and VNC state.
>
> General idea:
>
> Change structure associated with a display from VncState to a new
> structure VncDisplay. Remove client specific fields from VncDisplay.
> Remove display specific fields from VncState.
> Maintain a linked list of VncStates per VncDisplay structure, update
> as necessary.
> When updates/resizes/copies come in from the hardware, dispatch to all
> clients.
>
>
> Patch is against current SVN (6618)
> Any ideas/suggestions/comments/flames appreciated.
Can you add a Signed-off-by line?
The patch looks good to me.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-14 22:07 ` Anthony Liguori
@ 2009-02-15 1:13 ` Brian Kress
2009-02-16 14:59 ` Anthony Liguori
0 siblings, 1 reply; 10+ messages in thread
From: Brian Kress @ 2009-02-15 1:13 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 762 bytes --]
Anthony Liguori wrote:
> Brian Kress wrote:
>> Attached is a patch that allows qemu to support multiple vnc
>> clients. Each client can have their own
>> VNC encodings and VNC state.
>>
>> General idea:
>>
>> Change structure associated with a display from VncState to a new
>> structure VncDisplay. Remove client specific fields from VncDisplay.
>> Remove display specific fields from VncState.
>> Maintain a linked list of VncStates per VncDisplay structure, update
>> as necessary.
>> When updates/resizes/copies come in from the hardware, dispatch to
>> all clients.
>>
>>
>> Patch is against current SVN (6618)
>> Any ideas/suggestions/comments/flames appreciated.
>
> Can you add a Signed-off-by line?
>
> The patch looks good to me.
Attached.
[-- Attachment #2: patch.vnc --]
[-- Type: text/plain, Size: 22298 bytes --]
Signed-off-by: Brian Kress <kressb@moose.net>
Index: vnc.c
===================================================================
--- vnc.c (revision 6618)
+++ vnc.c (working copy)
@@ -90,12 +90,35 @@
#define VNC_AUTH_CHALLENGE_SIZE 16
+typedef struct VncDisplay VncDisplay;
+
+struct VncDisplay
+{
+ int lsock;
+ DisplayState *ds;
+ VncState *clients;
+ kbd_layout_t *kbd_layout;
+
+ char *display;
+ char *password;
+ int auth;
+#ifdef CONFIG_VNC_TLS
+ int subauth;
+ int x509verify;
+
+ char *x509cacert;
+ char *x509cacrl;
+ char *x509cert;
+ char *x509key;
+#endif
+};
+
struct VncState
{
QEMUTimer *timer;
- int lsock;
int csock;
DisplayState *ds;
+ VncDisplay *vd;
int need_update;
uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
char *old_data;
@@ -111,18 +134,6 @@
int major;
int minor;
- char *display;
- char *password;
- int auth;
-#ifdef CONFIG_VNC_TLS
- int subauth;
- int x509verify;
-
- char *x509cacert;
- char *x509cacrl;
- char *x509cert;
- char *x509key;
-#endif
char challenge[VNC_AUTH_CHALLENGE_SIZE];
#ifdef CONFIG_VNC_TLS
@@ -132,7 +143,6 @@
Buffer output;
Buffer input;
- kbd_layout_t *kbd_layout;
/* current output mode information */
VncWritePixels *write_pixels;
VncSendHextileTile *send_hextile_tile;
@@ -149,21 +159,23 @@
Buffer zlib;
Buffer zlib_tmp;
z_stream zlib_stream[4];
+
+ VncState *next;
};
-static VncState *vnc_state; /* needed for info vnc */
+static VncDisplay *vnc_display; /* needed for info vnc */
static DisplayChangeListener *dcl;
void do_info_vnc(void)
{
- if (vnc_state == NULL || vnc_state->display == NULL)
+ if (vnc_display == NULL || vnc_display->display == NULL)
term_printf("VNC server disabled\n");
else {
term_printf("VNC server active on: ");
- term_print_filename(vnc_state->display);
+ term_print_filename(vnc_display->display);
term_printf("\n");
- if (vnc_state->csock == -1)
+ if (vnc_display->clients == NULL)
term_printf("No client connected\n");
else
term_printf("Client connected\n");
@@ -190,7 +202,7 @@
static void vnc_update_client(void *opaque);
static void vnc_client_read(void *opaque);
-static void vnc_colordepth(DisplayState *ds);
+static void vnc_colordepth(VncState *vs);
static inline void vnc_set_bit(uint32_t *d, int k)
{
@@ -233,9 +245,8 @@
return 0;
}
-static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+static void vnc_update(VncState *vs, int x, int y, int w, int h)
{
- VncState *vs = ds->opaque;
int i;
h += y;
@@ -257,6 +268,16 @@
vnc_set_bit(vs->dirty_row[y], (x + i) / 16);
}
+static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs = vd->clients;
+ while (vs != NULL) {
+ vnc_update(vs, x, y, w, h);
+ vs = vs->next;
+ }
+}
+
static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
int32_t encoding)
{
@@ -301,10 +322,11 @@
buffer->offset += len;
}
-static void vnc_dpy_resize(DisplayState *ds)
+static void vnc_resize(VncState *vs)
{
+ DisplayState *ds = vs->ds;
+
int size_changed;
- VncState *vs = ds->opaque;
vs->old_data = qemu_realloc(vs->old_data, ds_get_linesize(ds) * ds_get_height(ds));
@@ -315,7 +337,7 @@
if (ds_get_bytes_per_pixel(ds) != vs->serverds.pf.bytes_per_pixel)
console_color_init(ds);
- vnc_colordepth(ds);
+ vnc_colordepth(vs);
size_changed = ds_get_width(ds) != vs->serverds.width ||
ds_get_height(ds) != vs->serverds.height;
vs->serverds = *(ds->surface);
@@ -334,6 +356,16 @@
memset(vs->old_data, 42, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
}
+static void vnc_dpy_resize(DisplayState *ds)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs = vd->clients;
+ while (vs != NULL) {
+ vnc_resize(vs);
+ vs = vs->next;
+ }
+}
+
/* fastest code */
static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size)
{
@@ -599,10 +631,8 @@
}
}
-static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
- VncState *vs = ds->opaque;
-
vnc_update_client(vs);
vnc_write_u8(vs, 0); /* msg id */
@@ -614,6 +644,19 @@
vnc_flush(vs);
}
+static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs = vd->clients;
+ while (vs != NULL) {
+ if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
+ vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
+ else /* TODO */
+ vnc_update(vs, dst_x, dst_y, w, h);
+ vs = vs->next;
+ }
+}
+
static int find_dirty_height(VncState *vs, int y, int last_x, int x)
{
int h;
@@ -632,7 +675,6 @@
static void vnc_update_client(void *opaque)
{
VncState *vs = opaque;
-
if (vs->need_update && vs->csock != -1) {
int y;
uint8_t *row;
@@ -725,14 +767,6 @@
}
-static int vnc_listen_poll(void *opaque)
-{
- VncState *vs = opaque;
- if (vs->csock == -1)
- return 1;
- return 0;
-}
-
/* audio */
static void audio_capture_notify(void *opaque, audcnotification_e cmd)
{
@@ -817,19 +851,35 @@
VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);
qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
closesocket(vs->csock);
- vs->csock = -1;
- dcl->idle = 1;
- buffer_reset(&vs->input);
- buffer_reset(&vs->output);
- vs->need_update = 0;
+ qemu_del_timer(vs->timer);
+ qemu_free_timer(vs->timer);
+ if (vs->input.buffer) qemu_free(vs->input.buffer);
+ if (vs->output.buffer) qemu_free(vs->output.buffer);
#ifdef CONFIG_VNC_TLS
if (vs->tls_session) {
gnutls_deinit(vs->tls_session);
vs->tls_session = NULL;
}
- vs->wiremode = VNC_WIREMODE_CLEAR;
#endif /* CONFIG_VNC_TLS */
audio_del(vs);
+
+ VncState *p, *parent = NULL;
+ for (p = vs->vd->clients; p != NULL; p = p->next) {
+ if (p == vs) {
+ if (parent)
+ parent->next = p->next;
+ else
+ vs->vd->clients = p->next;
+ break;
+ }
+ parent = p;
+ }
+ if (!vs->vd->clients)
+ dcl->idle = 1;
+
+ qemu_free(vs->old_data);
+ qemu_free(vs);
+
return 0;
}
return ret;
@@ -1095,8 +1145,8 @@
static void press_key(VncState *vs, int keysym)
{
- kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) & 0x7f);
- kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80);
+ kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) & 0x7f);
+ kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) | 0x80);
}
static void do_key_event(VncState *vs, int down, int keycode, int sym)
@@ -1129,12 +1179,12 @@
break;
}
- if (keycode_is_keypad(vs->kbd_layout, keycode)) {
+ if (keycode_is_keypad(vs->vd->kbd_layout, keycode)) {
/* If the numlock state needs to change then simulate an additional
keypress before sending this one. This will happen if the user
toggles numlock away from the VNC window.
*/
- if (keysym_is_numlock(vs->kbd_layout, sym & 0xFFFF)) {
+ if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
if (!vs->modifiers_state[0x45]) {
vs->modifiers_state[0x45] = 1;
press_key(vs, 0xff7f);
@@ -1207,7 +1257,7 @@
if (sym >= 'A' && sym <= 'Z' && is_graphic_console())
sym = sym - 'A' + 'a';
- keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
+ keycode = keysym2scancode(vs->vd->kbd_layout, sym & 0xFFFF);
do_key_event(vs, down, keycode, sym);
}
@@ -1279,7 +1329,6 @@
vs->tight_compression = 9;
vs->tight_quality = 9;
vs->absolute = -1;
- dcl->dpy_copy = NULL;
for (i = n_encodings - 1; i >= 0; i--) {
enc = encodings[i];
@@ -1288,7 +1337,7 @@
vs->vnc_encoding = enc;
break;
case VNC_ENCODING_COPYRECT:
- dcl->dpy_copy = vnc_copy;
+ vs->features |= VNC_FEATURE_COPYRECT_MASK;
break;
case VNC_ENCODING_HEXTILE:
vs->features |= VNC_FEATURE_HEXTILE_MASK;
@@ -1432,17 +1481,15 @@
/* We don't have to do anything */
}
-static void vnc_colordepth(DisplayState *ds)
+static void vnc_colordepth(VncState *vs)
{
- struct VncState *vs = ds->opaque;
-
- if (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
+ if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
/* Sending a WMVi message to notify the client*/
vnc_write_u8(vs, 0); /* msg id */
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
- vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds),
- VNC_ENCODING_WMVi);
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
+ ds_get_height(vs->ds), VNC_ENCODING_WMVi);
pixel_format_message(vs);
vnc_flush(vs);
} else {
@@ -1626,7 +1673,7 @@
int i, j, pwlen;
unsigned char key[8];
- if (!vs->password || !vs->password[0]) {
+ if (!vs->vd->password || !vs->vd->password[0]) {
VNC_DEBUG("No password configured on server");
vnc_write_u32(vs, 1); /* Reject auth */
if (vs->minor >= 8) {
@@ -1642,9 +1689,9 @@
memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
/* Calculate the expected challenge response */
- pwlen = strlen(vs->password);
+ pwlen = strlen(vs->vd->password);
for (i=0; i<sizeof(key); i++)
- key[i] = i<pwlen ? vs->password[i] : 0;
+ key[i] = i<pwlen ? vs->vd->password[i] : 0;
deskey(key, EN0);
for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
des(response+j, response+j);
@@ -1733,15 +1780,15 @@
gnutls_certificate_credentials_t x509_cred;
int ret;
- if (!vs->x509cacert) {
+ if (!vs->vd->x509cacert) {
VNC_DEBUG("No CA x509 certificate specified\n");
return NULL;
}
- if (!vs->x509cert) {
+ if (!vs->vd->x509cert) {
VNC_DEBUG("No server x509 certificate specified\n");
return NULL;
}
- if (!vs->x509key) {
+ if (!vs->vd->x509key) {
VNC_DEBUG("No server private key specified\n");
return NULL;
}
@@ -1751,7 +1798,7 @@
return NULL;
}
if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
- vs->x509cacert,
+ vs->vd->x509cacert,
GNUTLS_X509_FMT_PEM)) < 0) {
VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials(x509_cred);
@@ -1759,17 +1806,17 @@
}
if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
- vs->x509cert,
- vs->x509key,
+ vs->vd->x509cert,
+ vs->vd->x509key,
GNUTLS_X509_FMT_PEM)) < 0) {
VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials(x509_cred);
return NULL;
}
- if (vs->x509cacrl) {
+ if (vs->vd->x509cacrl) {
if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
- vs->x509cacrl,
+ vs->vd->x509cacrl,
GNUTLS_X509_FMT_PEM)) < 0) {
VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
gnutls_certificate_free_credentials(x509_cred);
@@ -1863,7 +1910,7 @@
static int start_auth_vencrypt_subauth(VncState *vs)
{
- switch (vs->subauth) {
+ switch (vs->vd->subauth) {
case VNC_AUTH_VENCRYPT_TLSNONE:
case VNC_AUTH_VENCRYPT_X509NONE:
VNC_DEBUG("Accept TLS auth none\n");
@@ -1877,7 +1924,7 @@
return start_auth_vnc(vs);
default: /* Should not be possible, but just in case */
- VNC_DEBUG("Reject auth %d\n", vs->auth);
+ VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
vnc_write_u8(vs, 1);
if (vs->minor >= 8) {
static const char err[] = "Unsupported authentication type";
@@ -1909,7 +1956,7 @@
return -1;
}
- if (vs->x509verify) {
+ if (vs->vd->x509verify) {
if (vnc_validate_certificate(vs) < 0) {
VNC_DEBUG("Client verification failed\n");
vnc_client_error(vs);
@@ -1934,9 +1981,9 @@
}
#define NEED_X509_AUTH(vs) \
- ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
+ ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
static int vnc_start_tls(struct VncState *vs) {
@@ -2000,7 +2047,7 @@
vnc_client_error(vs);
return -1;
}
- if (vs->x509verify) {
+ if (vs->vd->x509verify) {
VNC_DEBUG("Requesting a client certificate\n");
gnutls_certificate_server_set_request (vs->tls_session, GNUTLS_CERT_REQUEST);
}
@@ -2035,7 +2082,7 @@
{
int auth = read_u32(data, 0);
- if (auth != vs->subauth) {
+ if (auth != vs->vd->subauth) {
VNC_DEBUG("Rejecting auth %d\n", auth);
vnc_write_u8(vs, 0); /* Reject auth */
vnc_flush(vs);
@@ -2070,10 +2117,10 @@
vnc_flush(vs);
vnc_client_error(vs);
} else {
- VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
+ VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
vnc_write_u8(vs, 0); /* Accept version */
vnc_write_u8(vs, 1); /* Number of sub-auths */
- vnc_write_u32(vs, vs->subauth); /* The supported auth */
+ vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
vnc_flush(vs);
vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
}
@@ -2095,7 +2142,7 @@
{
/* We only advertise 1 auth scheme at a time, so client
* must pick the one we sent. Verify this */
- if (data[0] != vs->auth) { /* Reject auth */
+ if (data[0] != vs->vd->auth) { /* Reject auth */
VNC_DEBUG("Reject auth %d\n", (int)data[0]);
vnc_write_u32(vs, 1);
if (vs->minor >= 8) {
@@ -2106,7 +2153,7 @@
vnc_client_error(vs);
} else { /* Accept requested auth */
VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
- switch (vs->auth) {
+ switch (vs->vd->auth) {
case VNC_AUTH_NONE:
VNC_DEBUG("Accept auth none\n");
if (vs->minor >= 8) {
@@ -2127,7 +2174,7 @@
#endif /* CONFIG_VNC_TLS */
default: /* Should not be possible, but just in case */
- VNC_DEBUG("Reject auth %d\n", vs->auth);
+ VNC_DEBUG("Reject auth %d\n", vs->vd->auth);
vnc_write_u8(vs, 1);
if (vs->minor >= 8) {
static const char err[] = "Authentication failed";
@@ -2172,26 +2219,26 @@
vs->minor = 3;
if (vs->minor == 3) {
- if (vs->auth == VNC_AUTH_NONE) {
+ if (vs->vd->auth == VNC_AUTH_NONE) {
VNC_DEBUG("Tell client auth none\n");
- vnc_write_u32(vs, vs->auth);
+ vnc_write_u32(vs, vs->vd->auth);
vnc_flush(vs);
vnc_read_when(vs, protocol_client_init, 1);
- } else if (vs->auth == VNC_AUTH_VNC) {
+ } else if (vs->vd->auth == VNC_AUTH_VNC) {
VNC_DEBUG("Tell client VNC auth\n");
- vnc_write_u32(vs, vs->auth);
+ vnc_write_u32(vs, vs->vd->auth);
vnc_flush(vs);
start_auth_vnc(vs);
} else {
- VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
+ VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth);
vnc_write_u32(vs, VNC_AUTH_INVALID);
vnc_flush(vs);
vnc_client_error(vs);
}
} else {
- VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
+ VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
vnc_write_u8(vs, 1); /* num auth */
- vnc_write_u8(vs, vs->auth);
+ vnc_write_u8(vs, vs->vd->auth);
vnc_read_when(vs, protocol_client_auth, 1);
vnc_flush(vs);
}
@@ -2199,55 +2246,67 @@
return 0;
}
-static void vnc_connect(VncState *vs)
+static void vnc_connect(VncDisplay *vd, int csock)
{
- VNC_DEBUG("New client on socket %d\n", vs->csock);
+ VncState *vs = qemu_mallocz(sizeof(VncState));
+ vs->csock = csock;
+
+ VNC_DEBUG("New client on socket %d\n", csock);
dcl->idle = 0;
socket_set_nonblock(vs->csock);
qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+
+ vs->vd = vd;
+ vs->ds = vd->ds;
+ vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+ vs->last_x = -1;
+ vs->last_y = -1;
+
+ vs->as.freq = 44100;
+ vs->as.nchannels = 2;
+ vs->as.fmt = AUD_FMT_S16;
+ vs->as.endianness = 0;
+
+ vnc_resize(vs);
vnc_write(vs, "RFB 003.008\n", 12);
vnc_flush(vs);
vnc_read_when(vs, protocol_version, 12);
memset(vs->old_data, 0, ds_get_linesize(vs->ds) * ds_get_height(vs->ds));
memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
- vs->features = 0;
- dcl->dpy_copy = NULL;
vnc_update_client(vs);
reset_keys(vs);
+
+ vs->next = vd->clients;
+ vd->clients = vs;
}
static void vnc_listen_read(void *opaque)
{
- VncState *vs = opaque;
+ VncDisplay *vs = opaque;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
/* Catch-up */
vga_hw_update();
- vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
- if (vs->csock != -1) {
- vnc_connect(vs);
+ int csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
+ if (csock != -1) {
+ vnc_connect(vs, csock);
}
}
void vnc_display_init(DisplayState *ds)
{
- VncState *vs;
+ VncDisplay *vs;
vs = qemu_mallocz(sizeof(VncState));
dcl = qemu_mallocz(sizeof(DisplayChangeListener));
ds->opaque = vs;
dcl->idle = 1;
- vnc_state = vs;
- vs->display = NULL;
- vs->password = NULL;
+ vnc_display = vs;
vs->lsock = -1;
- vs->csock = -1;
- vs->last_x = -1;
- vs->last_y = -1;
vs->ds = ds;
@@ -2259,22 +2318,15 @@
if (!vs->kbd_layout)
exit(1);
- vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
-
+ dcl->dpy_copy = vnc_dpy_copy;
dcl->dpy_update = vnc_dpy_update;
dcl->dpy_resize = vnc_dpy_resize;
dcl->dpy_setdata = vnc_dpy_setdata;
- dcl->dpy_refresh = NULL;
register_displaychangelistener(ds, dcl);
-
- vs->as.freq = 44100;
- vs->as.nchannels = 2;
- vs->as.fmt = AUD_FMT_S16;
- vs->as.endianness = 0;
}
#ifdef CONFIG_VNC_TLS
-static int vnc_set_x509_credential(VncState *vs,
+static int vnc_set_x509_credential(VncDisplay *vs,
const char *certdir,
const char *filename,
char **cred,
@@ -2305,7 +2357,7 @@
return 0;
}
-static int vnc_set_x509_credential_dir(VncState *vs,
+static int vnc_set_x509_credential_dir(VncDisplay *vs,
const char *certdir)
{
if (vnc_set_x509_credential(vs, certdir, X509_CA_CERT_FILE, &vs->x509cacert, 0) < 0)
@@ -2331,7 +2383,7 @@
void vnc_display_close(DisplayState *ds)
{
- VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
if (!vs)
return;
@@ -2344,32 +2396,16 @@
close(vs->lsock);
vs->lsock = -1;
}
- if (vs->csock != -1) {
- qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
- closesocket(vs->csock);
- vs->csock = -1;
- buffer_reset(&vs->input);
- buffer_reset(&vs->output);
- vs->need_update = 0;
-#ifdef CONFIG_VNC_TLS
- if (vs->tls_session) {
- gnutls_deinit(vs->tls_session);
- vs->tls_session = NULL;
- }
- vs->wiremode = VNC_WIREMODE_CLEAR;
-#endif /* CONFIG_VNC_TLS */
- }
vs->auth = VNC_AUTH_INVALID;
#ifdef CONFIG_VNC_TLS
vs->subauth = VNC_AUTH_INVALID;
vs->x509verify = 0;
#endif
- audio_del(vs);
}
int vnc_display_password(DisplayState *ds, const char *password)
{
- VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
if (vs->password) {
qemu_free(vs->password);
@@ -2385,7 +2421,7 @@
int vnc_display_open(DisplayState *ds, const char *display)
{
- VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
const char *options;
int password = 0;
int reverse = 0;
@@ -2394,7 +2430,7 @@
int tls = 0, x509 = 0;
#endif
- if (!vnc_state)
+ if (!vnc_display)
return -1;
vnc_display_close(ds);
if (strcmp(display, "none") == 0)
@@ -2499,9 +2535,9 @@
vs->display = NULL;
return -1;
} else {
- vs->csock = vs->lsock;
+ int csock = vs->lsock;
vs->lsock = -1;
- vnc_connect(vs);
+ vnc_connect(vs, csock);
}
return 0;
@@ -2523,6 +2559,5 @@
vs->display = dpy;
}
}
-
- return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
+ return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
}
Index: vnc.h
===================================================================
--- vnc.h (revision 6618)
+++ vnc.h (working copy)
@@ -101,6 +101,7 @@
#define VNC_FEATURE_WMVI 3
#define VNC_FEATURE_TIGHT 4
#define VNC_FEATURE_ZLIB 5
+#define VNC_FEATURE_COPYRECT 6
#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE)
#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE)
@@ -108,5 +109,6 @@
#define VNC_FEATURE_WMVI_MASK (1 << VNC_FEATURE_WMVI)
#define VNC_FEATURE_TIGHT_MASK (1 << VNC_FEATURE_TIGHT)
#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB)
+#define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT)
#endif /* __VNCTIGHT_H */
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-13 19:49 [Qemu-devel] [PATCH] Support multiple VNC clients Brian Kress
2009-02-14 10:07 ` malc
2009-02-14 22:07 ` Anthony Liguori
@ 2009-02-15 11:35 ` Daniel P. Berrange
2009-02-15 21:47 ` Itamar Heim
2 siblings, 1 reply; 10+ messages in thread
From: Daniel P. Berrange @ 2009-02-15 11:35 UTC (permalink / raw)
To: qemu-devel
On Fri, Feb 13, 2009 at 02:49:31PM -0500, Brian Kress wrote:
> Attached is a patch that allows qemu to support multiple vnc clients.
> Each client can have their own
> VNC encodings and VNC state.
>
> General idea:
>
> Change structure associated with a display from VncState to a new
> structure VncDisplay.
> Remove client specific fields from VncDisplay.
> Remove display specific fields from VncState.
> Maintain a linked list of VncStates per VncDisplay structure, update as
> necessary.
> When updates/resizes/copies come in from the hardware, dispatch to all
> clients.
>
>
> Patch is against current SVN (6618)
> Any ideas/suggestions/comments/flames appreciated.
Perhaps also add a -vnc command line flag to let us control policy for
multiple clients interactions ? eg some policies I think of
- Single client, new clients dropped
- Single client, original client dropped
- Many clients, first full access, others view only
- Many clients, all full access
Daniel
--
|: 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 :|
^ permalink raw reply [flat|nested] 10+ messages in thread
* RE: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-15 11:35 ` Daniel P. Berrange
@ 2009-02-15 21:47 ` Itamar Heim
2009-02-15 22:21 ` Brian Kress
0 siblings, 1 reply; 10+ messages in thread
From: Itamar Heim @ 2009-02-15 21:47 UTC (permalink / raw)
To: qemu-devel
> From: qemu-devel-bounces+iheim=redhat.com@nongnu.org [mailto:qemu-devel-
...
> Perhaps also add a -vnc command line flag to let us control policy for
> multiple clients interactions ? eg some policies I think of
>
> - Single client, new clients dropped
> - Single client, original client dropped
> - Many clients, first full access, others view only
> - Many clients, all full access
[IH] also - how does one specify the password for a specific vnc via the
monitor?
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-15 21:47 ` Itamar Heim
@ 2009-02-15 22:21 ` Brian Kress
2009-02-16 0:59 ` Anthony Liguori
0 siblings, 1 reply; 10+ messages in thread
From: Brian Kress @ 2009-02-15 22:21 UTC (permalink / raw)
To: qemu-devel
Itamar Heim wrote:
>> From: qemu-devel-bounces+iheim=redhat.com@nongnu.org [mailto:qemu-devel-
>>
> ...
>
>> Perhaps also add a -vnc command line flag to let us control policy for
>> multiple clients interactions ? eg some policies I think of
>>
>> - Single client, new clients dropped
>> - Single client, original client dropped
>> - Many clients, first full access, others view only
>> - Many clients, all full access
>>
> [IH] also - how does one specify the password for a specific vnc via the
> monitor?
>
The password (as well as all the other authentication parameters)
apply to the display, not
a specific client. Even with this patch you still only have one vnc
display, so you change the
password for that display the same way you did before this patch.
-Brian
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-15 22:21 ` Brian Kress
@ 2009-02-16 0:59 ` Anthony Liguori
2009-02-16 11:32 ` Stefano Stabellini
0 siblings, 1 reply; 10+ messages in thread
From: Anthony Liguori @ 2009-02-16 0:59 UTC (permalink / raw)
To: qemu-devel
Brian Kress wrote:
> Itamar Heim wrote:
>>> From: qemu-devel-bounces+iheim=redhat.com@nongnu.org
>>> [mailto:qemu-devel-
>>>
>> ...
>>
>>> Perhaps also add a -vnc command line flag to let us control policy for
>>> multiple clients interactions ? eg some policies I think of
>>>
>>> - Single client, new clients dropped
>>> - Single client, original client dropped
>>> - Many clients, first full access, others view only
>>> - Many clients, all full access
>>>
>> [IH] also - how does one specify the password for a specific vnc via the
>> monitor?
>>
> The password (as well as all the other authentication parameters)
> apply to the display, not
> a specific client. Even with this patch you still only have one vnc
> display, so you change the
> password for that display the same way you did before this patch.
I'm inclined to take this patch as-is. We can follow up with additional
features.
Regards,
Anthony Liguori
>
> -Brian
>
>
>
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-16 0:59 ` Anthony Liguori
@ 2009-02-16 11:32 ` Stefano Stabellini
0 siblings, 0 replies; 10+ messages in thread
From: Stefano Stabellini @ 2009-02-16 11:32 UTC (permalink / raw)
To: qemu-devel@nongnu.org
Anthony Liguori wrote:
> I'm inclined to take this patch as-is. We can follow up with additional
> features.
>
I also think this patch is a good base to start.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Qemu-devel] [PATCH] Support multiple VNC clients
2009-02-15 1:13 ` Brian Kress
@ 2009-02-16 14:59 ` Anthony Liguori
0 siblings, 0 replies; 10+ messages in thread
From: Anthony Liguori @ 2009-02-16 14:59 UTC (permalink / raw)
To: qemu-devel
Brian Kress wrote:
> Anthony Liguori wrote:
>> Brian Kress wrote:
>>> Attached is a patch that allows qemu to support multiple vnc
>>> clients. Each client can have their own
>>> VNC encodings and VNC state.
>>>
>>> General idea:
>>>
>>> Change structure associated with a display from VncState to a new
>>> structure VncDisplay. Remove client specific fields from VncDisplay.
>>> Remove display specific fields from VncState.
>>> Maintain a linked list of VncStates per VncDisplay structure, update
>>> as necessary.
>>> When updates/resizes/copies come in from the hardware, dispatch to
>>> all clients.
>>>
>>>
>>> Patch is against current SVN (6618)
>>> Any ideas/suggestions/comments/flames appreciated.
>>
>> Can you add a Signed-off-by line?
>>
>> The patch looks good to me.
>
> Attached.
Applied. Thanks.
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2009-02-16 15:00 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-02-13 19:49 [Qemu-devel] [PATCH] Support multiple VNC clients Brian Kress
2009-02-14 10:07 ` malc
2009-02-14 22:07 ` Anthony Liguori
2009-02-15 1:13 ` Brian Kress
2009-02-16 14:59 ` Anthony Liguori
2009-02-15 11:35 ` Daniel P. Berrange
2009-02-15 21:47 ` Itamar Heim
2009-02-15 22:21 ` Brian Kress
2009-02-16 0:59 ` Anthony Liguori
2009-02-16 11:32 ` Stefano Stabellini
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).