From d34fd47b7d35a613ed95b0c0db6f5444da31fbc5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann <kraxel@redhat.com> Date: Thu, 16 Apr 2009 17:27:02 +0200 Subject: [PATCH 05/14] Support multiple VNC clients (re-added) On 04/16/09 14:31, Gerd Hoffmann wrote: > Hi, > > This is a backport of the multiple vnc client patch from upstream qemu. > > Fixes bug 490344. > > cheers, > Gerd Updated patch, one screen corruption bug fixed. cheers, Gerd >From 315865922b7a6259915ab13cc621bf2a4945b64e Mon Sep 17 00:00:00 2001 From: aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> Date: Mon, 16 Feb 2009 14:59:30 +0000 Subject: [PATCH] Support multiple VNC clients (Brian Kress) 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. Signed-off-by: Brian Kress <kressb@moose.net> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6621 c046a42c-6fe2-441c-8c8c-71466251a162 Backported required some non-trivial adaptions due to the DisplayState reorganization patches not being in the rhel-5 code base. [ehabkost: Added again now that the qemu_realloc() and displaystate initialization patches are being included too] Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> RH-Upstream-status: applied(qemu/trunk) Message-ID: <49E74E46.1060107@redhat.com> Depends: <20090518212210.GN2079@blackpad> Obsoletes: <49E72530.1000402@redhat.com> Acked-by: Eduardo Habkost <ehabkost@redhat.com> Acked-by: Zachary Amsden <zamsden@redhat.com> Acked-by: Juan Quintela <quintela@redhat.com> Bugzilla: 503793 --- qemu/vnc.c | 409 ++++++++++++++++++++++++++++++++++-------------------------- 1 files changed, 232 insertions(+), 177 deletions(-) diff --git a/qemu/vnc.c b/qemu/vnc.c index a455614..eca5d4d 100644 --- a/qemu/vnc.c +++ b/qemu/vnc.c @@ -117,12 +117,36 @@ enum { #endif /* CONFIG_VNC_TLS */ +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 + gcry_cipher_hd_t des_cipher; +}; + struct VncState { QEMUTimer *timer; - int lsock; int csock; DisplayState *ds; + VncDisplay *vd; int need_update; int width; int height; @@ -133,6 +157,7 @@ struct VncState int has_hextile; int has_pointer_type_change; int has_WMVi; + int has_copyrect; int absolute; int last_x; int last_y; @@ -140,18 +165,6 @@ struct VncState 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 @@ -161,7 +174,6 @@ struct VncState Buffer output; Buffer input; - kbd_layout_t *kbd_layout; /* current output mode information */ VncWritePixels *write_pixels; VncSendHextileTile *send_hextile_tile; @@ -178,21 +190,21 @@ struct VncState /* input */ uint8_t modifiers_state[256]; - gcry_cipher_hd_t des_cipher; + VncState *next; }; -static VncState *vnc_state; /* needed for info vnc */ +static VncDisplay *vnc_display; /* needed for info vnc */ 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"); @@ -215,7 +227,7 @@ static void vnc_flush(VncState *vs); static void vnc_update_client(void *opaque); static void vnc_client_read(void *opaque); -static void vnc_colordepth(DisplayState *ds, int depth); +static void vnc_colordepth(VncState *vs, int depth); static inline void vnc_set_bit(uint32_t *d, int k) { @@ -258,9 +270,8 @@ static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2, 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; @@ -282,6 +293,17 @@ static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) 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) { @@ -293,35 +315,23 @@ static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, vnc_write_s32(vs, encoding); } -static void vnc_dpy_resize(DisplayState *ds, int w, int h) +static void vnc_resize(VncState *vs, int w, int h) { - int size_changed; - VncState *vs = ds->opaque; - - ds->data = qemu_realloc(ds->data, w * h * vs->depth); vs->old_data = qemu_realloc(vs->old_data, w * h * vs->depth); - if (ds->data == NULL || vs->old_data == NULL) { + if (vs->old_data == NULL) { fprintf(stderr, "vnc: memory allocation failed\n"); exit(1); } - if (ds->depth != vs->depth * 8) { - ds->depth = vs->depth * 8; - console_color_init(ds); - } - size_changed = ds->width != w || ds->height != h; - ds->width = w; - ds->height = h; - ds->linesize = w * vs->depth; - if (size_changed) { - vs->width = ds->width; - vs->height = ds->height; + if (vs->width != w || vs->height != h) { + vs->width = w; + vs->height = h; if (vs->csock != -1 && vs->has_resize) { 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->width, ds->height, -223); + vnc_framebuffer_update(vs, 0, 0, vs->width, vs->height, -223); vnc_flush(vs); } } @@ -330,6 +340,26 @@ static void vnc_dpy_resize(DisplayState *ds, int w, int h) memset(vs->old_data, 42, ds_get_linesize(vs->ds) * ds_get_height(vs->ds)); } +static void vnc_dpy_resize(DisplayState *ds, int w, int h) +{ + VncDisplay *vd = ds->opaque; + VncState *vs = vd->clients; + + ds->width = w; + ds->height = h; + ds->linesize = w * ds->depth/8; + ds->data = qemu_realloc(ds->data, ds->linesize * ds->height); + if (ds->data == NULL) { + fprintf(stderr, "vnc: memory allocation failed\n"); + exit(1); + } + + while (vs != NULL) { + vnc_resize(vs, w, h); + vs = vs->next; + } +} + /* fastest code */ static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size) { @@ -494,38 +524,35 @@ static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) send_framebuffer_update_raw(vs, x, y, w, h); } -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_data(uint8_t *data, int pitch, int depth, + int src_x, int src_y, int dst_x, int dst_y, int w, int h) { int src, dst; uint8_t *src_row; uint8_t *dst_row; - char *old_row; int y = 0; - int pitch = ds_get_linesize(ds); - VncState *vs = ds->opaque; - - vnc_update_client(vs); + int p = pitch; if (dst_y > src_y) { y = h - 1; - pitch = -pitch; + p = -p; } - src = (ds_get_linesize(ds) * (src_y + y) + vs->depth * src_x); - dst = (ds_get_linesize(ds) * (dst_y + y) + vs->depth * dst_x); + src = (pitch * (src_y + y) + depth * src_x); + dst = (pitch * (dst_y + y) + depth * dst_x); - src_row = ds_get_data(ds) + src; - dst_row = ds_get_data(ds) + dst; - old_row = vs->old_data + dst; + src_row = data + src; + dst_row = data + dst; for (y = 0; y < h; y++) { - memmove(old_row, src_row, w * vs->depth); - memmove(dst_row, src_row, w * vs->depth); - src_row += pitch; - dst_row += pitch; - old_row += pitch; + memmove(dst_row, src_row, w * depth); + src_row += p; + dst_row += p; } +} +static void vnc_copy_msg(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h) +{ vnc_write_u8(vs, 0); /* msg id */ vnc_write_u8(vs, 0); vnc_write_u16(vs, 1); /* number of rects */ @@ -535,6 +562,29 @@ static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_ 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; + + for (vs = vd->clients; vs != NULL; vs = vs->next) { + if (vs->has_copyrect) + vnc_update_client(vs); + } + + vnc_copy_data(ds->data, ds->linesize, ds->depth / 8, + src_x, src_y, dst_x, dst_y, w, h); + for (vs = vd->clients; vs != NULL; vs = vs->next) { + if (vs->has_copyrect) { + vnc_copy_data((uint8_t*)vs->old_data, vs->width * vs->depth, vs->depth, + src_x, src_y, dst_x, dst_y, w, h); + vnc_copy_msg(vs, src_x, src_y, dst_x, dst_y, w, h); + } else { + vnc_update(vs, dst_x, dst_y, w, h); + } + } +} + static int find_dirty_height(VncState *vs, int y, int last_x, int x) { int h; @@ -646,14 +696,6 @@ static void vnc_update_client(void *opaque) } -static int vnc_listen_poll(void *opaque) -{ - VncState *vs = opaque; - if (vs->csock == -1) - return 1; - return 0; -} - static void buffer_reserve(Buffer *buffer, size_t len) { if ((buffer->capacity - buffer->offset) < len) { @@ -676,11 +718,6 @@ static uint8_t *buffer_end(Buffer *buffer) return buffer->buffer + buffer->offset; } -static void buffer_reset(Buffer *buffer) -{ - buffer->offset = 0; -} - static void buffer_append(Buffer *buffer, const void *data, size_t len) { memcpy(buffer->buffer + buffer->offset, data, len); @@ -771,21 +808,38 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno) 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; - vs->ds->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); - if (vs->display) - term_printf_async(VNC_ASYNC_EVENT, "VNC: Closing down connection %s\n", vs->display); + if (vs->vd->display) + term_printf_async(VNC_ASYNC_EVENT, "VNC: Closing down connection %s\n", vs->vd->display); + + 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) + vs->ds->idle = 1; + qemu_free(vs->old_data); + qemu_free(vs); + return 0; } return ret; @@ -1050,8 +1104,8 @@ static void reset_keys(VncState *vs) 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) @@ -1084,12 +1138,12 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) 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); @@ -1162,7 +1216,7 @@ static void key_event(VncState *vs, int down, uint32_t sym) 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); } @@ -1229,8 +1283,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->has_resize = 0; vs->has_pointer_type_change = 0; vs->has_WMVi = 0; + vs->has_copyrect = 0; vs->absolute = -1; - vs->ds->dpy_copy = NULL; for (i = n_encodings - 1; i >= 0; i--) { switch (encodings[i]) { @@ -1238,7 +1292,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vs->has_hextile = 0; break; case 1: /* CopyRect */ - vs->ds->dpy_copy = vnc_copy; + vs->has_copyrect = 1; break; case 5: /* Hextile */ vs->has_hextile = 1; @@ -1391,14 +1445,14 @@ static void pixel_format_message (VncState *vs) { vnc_write(vs, pad, 3); /* padding */ } -static void vnc_colordepth(DisplayState *ds, int depth) +static void vnc_colordepth(VncState *vs, int depth) { int host_big_endian_flag; - struct VncState *vs = ds->opaque; switch (depth) { case 24: - if (ds->depth == 32) return; + if (vs->ds->depth == 32) + return; depth = 32; break; case 15: @@ -1452,7 +1506,7 @@ static void vnc_colordepth(DisplayState *ds, int depth) 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->width, ds->height, 0x574D5669); + vnc_framebuffer_update(vs, 0, 0, vs->ds->width, vs->ds->height, 0x574D5669); pixel_format_message(vs); vnc_flush(vs); } else { @@ -1675,7 +1729,7 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) char key[8]; gcry_error_t e; - 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) { @@ -1688,7 +1742,7 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) return 0; } - if (gcry_cipher_reset(vs->des_cipher)) { + if (gcry_cipher_reset(vs->vd->des_cipher)) { VNC_DEBUG("f1\n"); goto fail; } @@ -1696,16 +1750,16 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) 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 ? bitrev(vs->password[i]) : 0; + key[i] = i<pwlen ? bitrev(vs->vd->password[i]) : 0; - if (gcry_cipher_setkey(vs->des_cipher, key, sizeof(key))) { + if (gcry_cipher_setkey(vs->vd->des_cipher, key, sizeof(key))) { VNC_DEBUG("f2\n"); goto fail; } for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8) { - if ( (e = gcry_cipher_encrypt(vs->des_cipher, response+j, 8, NULL, 0)) ) { + if ( (e = gcry_cipher_encrypt(vs->vd->des_cipher, response+j, 8, NULL, 0)) ) { VNC_DEBUG("f3: %s\n", gcry_strerror(e)); goto fail; } @@ -1807,15 +1861,15 @@ static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *v 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; } @@ -1825,7 +1879,7 @@ static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *v 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); @@ -1833,17 +1887,17 @@ static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *v } 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); @@ -1937,7 +1991,7 @@ static int vnc_validate_certificate(struct VncState *vs) 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"); @@ -1951,7 +2005,7 @@ static int start_auth_vencrypt_subauth(VncState *vs) 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"; @@ -1983,7 +2037,7 @@ static int vnc_continue_handshake(struct VncState *vs) { 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); @@ -2008,9 +2062,9 @@ static void vnc_handshake_io(void *opaque) { } #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) { @@ -2074,7 +2128,7 @@ static int vnc_start_tls(struct VncState *vs) { 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); } @@ -2109,7 +2163,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len { 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); @@ -2144,10 +2198,10 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len 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); } @@ -2169,7 +2223,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) { /* 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) { @@ -2180,7 +2234,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) 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) { @@ -2201,7 +2255,7 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) #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"; @@ -2246,26 +2300,26 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len) 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); } @@ -2273,58 +2327,86 @@ static int protocol_version(VncState *vs, uint8_t *version, size_t len) return 0; } -static void vnc_connect(VncState *vs) +static void vnc_connect(VncDisplay *vd, int csock) { + VncState *vs = qemu_mallocz(sizeof(VncState)); + vs->csock = csock; + VNC_DEBUG("New client on socket %d\n", vs->csock); - vs->ds->idle = 0; socket_set_nonblock(vs->csock); qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + + vs->vd = vd; + 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; + + vs->ds = vd->ds; + vs->ds->idle = 0; + if (vs->ds->depth != 32) { + /* + * We allways run with 32bpp internally. + * Easiest to handle with multiple clients, and + * this is what almost everybody uses anyway. + */ + vs->ds->depth = 32; + console_color_init(vs->ds); + } + vnc_colordepth(vs, vs->ds->depth); + if (vs->ds->width && vs->ds->height) { + vnc_resize(vs, vs->ds->width, vs->ds->height); + } else { + vnc_resize(vs, 640, 480); + } + 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->old_data, 42, ds_get_linesize(vs->ds) * ds_get_height(vs->ds)); memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); vs->has_resize = 0; vs->has_hextile = 0; vs->has_WMVi = 0; - vs->ds->dpy_copy = NULL; + vs->has_copyrect = 0; 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)); + vs = qemu_mallocz(sizeof(VncDisplay)); if (!vs) exit(1); ds->opaque = vs; ds->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; @@ -2336,20 +2418,10 @@ void vnc_display_init(DisplayState *ds) if (!vs->kbd_layout) exit(1); - vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs); - vs->ds->data = NULL; + vs->ds->dpy_copy = vnc_dpy_copy; vs->ds->dpy_update = vnc_dpy_update; vs->ds->dpy_resize = vnc_dpy_resize; - vs->ds->dpy_refresh = NULL; - - vnc_colordepth(vs->ds, 32); - vnc_dpy_resize(vs->ds, 640, 400); - - vs->as.freq = 44100; - vs->as.nchannels = 2; - vs->as.fmt = AUD_FMT_S16; - vs->as.endianness = 0; if (!gcry_check_version (GCRYPT_VERSION)) { fprintf(stderr, "libgcrypt initialization error\n"); @@ -2363,7 +2435,7 @@ void vnc_display_init(DisplayState *ds) } #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, @@ -2395,7 +2467,7 @@ static int vnc_set_x509_credential(VncState *vs, 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) @@ -2421,7 +2493,7 @@ static int vnc_set_x509_credential_dir(VncState *vs, void vnc_display_close(DisplayState *ds) { - VncState *vs = ds ? (VncState *)ds->opaque : vnc_state; + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; if (vs->display) { qemu_free(vs->display); @@ -2432,34 +2504,16 @@ void vnc_display_close(DisplayState *ds) 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); - if (vs->display) - term_printf_async(VNC_ASYNC_EVENT, "VNC: Closing down connection %s\n", vs->display); } int vnc_display_password(DisplayState *ds, const char *password) { - VncState *vs = ds ? (VncState *)ds->opaque : vnc_state; + VncDisplay *vs = ds ? ds->opaque : vnc_display; if (vs->password) { qemu_free(vs->password); @@ -2475,7 +2529,7 @@ int vnc_display_password(DisplayState *ds, const char *password) 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; @@ -2484,6 +2538,8 @@ int vnc_display_open(DisplayState *ds, const char *display) int tls = 0, x509 = 0; #endif + if (!vnc_display) + return -1; vnc_display_close(ds); if (strcmp(display, "none") == 0) return 0; @@ -2587,9 +2643,9 @@ int vnc_display_open(DisplayState *ds, const char *display) 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; @@ -2611,6 +2667,5 @@ int vnc_display_open(DisplayState *ds, const char *display) 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); } -- 1.6.3.rc4.29.g8146