From ce3e14175ea36d851aede808fc8891313b91ec27 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 14 Jun 2010 12:28:23 +0200 Subject: Fix vnc memory corruption with width = 1400 vnc assumes that the screen width is a multiple of 16 in several places. If this is not the case vnc will overrun buffers, corrupt memory, make qemu crash. This is the minimum fix for this bug. It makes sure we don't overrun the scanline, thereby fixing the segfault. The rendering is *not* correct though, there is a black border at the right side of the screen, 8 pixels wide because 1400 % 16 == 8. Signed-off-by: Gerd Hoffmann --- ui/vnc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/vnc.c b/ui/vnc.c index 16b79ec423..5752bf8740 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2445,7 +2445,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd) guest_ptr = guest_row; server_ptr = server_row; - for (x = 0; x < vd->guest.ds->width; + for (x = 0; x + 15 < vd->guest.ds->width; x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) { if (!test_and_clear_bit((x / 16), vd->guest.dirty[y])) continue; -- cgit v1.2.1 From e26437c2d4a7f6cbbc0bbd51b08a2dcce84bb93b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 8 Nov 2011 10:02:16 +0100 Subject: vnc: fix ctrl key in vnc terminal emulation Make the control keys for terminals on the vnc display (i.e. qemu -vnc :0 -serial vc) work. Makes the terminals alot more usable as typing Ctrl-C in your serial console actually has the desired effect ;) Signed-off-by: Gerd Hoffmann --- ui/vnc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'ui') diff --git a/ui/vnc.c b/ui/vnc.c index 5752bf8740..810582b13a 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1552,9 +1552,11 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) else kbd_put_keycode(keycode | SCANCODE_UP); } else { + bool numlock = vs->modifiers_state[0x45]; + bool control = (vs->modifiers_state[0x1d] || + vs->modifiers_state[0x9d]); /* QEMU console emulation */ if (down) { - int numlock = vs->modifiers_state[0x45]; switch (keycode) { case 0x2a: /* Left Shift */ case 0x36: /* Right Shift */ @@ -1642,7 +1644,11 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) break; default: - kbd_put_keysym(sym); + if (control) { + kbd_put_keysym(sym & 0x1f); + } else { + kbd_put_keysym(sym); + } break; } } -- cgit v1.2.1 From 8cf364898cfe4ae761f2253e91a040633d6f87be Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 Nov 2011 18:10:49 +0100 Subject: vnc: implement shared flag handling. VNC clients send a shared flag in the client init message. Up to now qemu completely ignores this. This patch implements shared flag handling. It comes with three policies: By default qemu behaves as one would expect: Asking for a exclusive access grants exclusive access to the client connecting. There is also a desktop sharing mode which disallows exclusive connects (so one forgetting -shared wouldn't drop everybody else) and a compatibility mode which mimics the traditional (but non-conforming) qemu behavior. Signed-off-by: Gerd Hoffmann --- ui/vnc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui/vnc.h | 16 +++++++++++ 2 files changed, 114 insertions(+) (limited to 'ui') diff --git a/ui/vnc.c b/ui/vnc.c index 810582b13a..83a9b15c50 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -47,6 +47,29 @@ static DisplayChangeListener *dcl; static int vnc_cursor_define(VncState *vs); +static void vnc_set_share_mode(VncState *vs, VncShareMode mode) +{ +#ifdef _VNC_DEBUG + static const char *mn[] = { + [0] = "undefined", + [VNC_SHARE_MODE_CONNECTING] = "connecting", + [VNC_SHARE_MODE_SHARED] = "shared", + [VNC_SHARE_MODE_EXCLUSIVE] = "exclusive", + [VNC_SHARE_MODE_DISCONNECTED] = "disconnected", + }; + fprintf(stderr, "%s/%d: %s -> %s\n", __func__, + vs->csock, mn[vs->share_mode], mn[mode]); +#endif + + if (vs->share_mode == VNC_SHARE_MODE_EXCLUSIVE) { + vs->vd->num_exclusive--; + } + vs->share_mode = mode; + if (vs->share_mode == VNC_SHARE_MODE_EXCLUSIVE) { + vs->vd->num_exclusive++; + } +} + static char *addr_to_string(const char *format, struct sockaddr_storage *sa, socklen_t salen) { @@ -997,6 +1020,7 @@ static void vnc_disconnect_start(VncState *vs) { if (vs->csock == -1) return; + vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED); qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); closesocket(vs->csock); vs->csock = -1; @@ -2054,8 +2078,67 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) { char buf[1024]; + VncShareMode mode; int size; + mode = data[0] ? VNC_SHARE_MODE_SHARED : VNC_SHARE_MODE_EXCLUSIVE; + switch (vs->vd->share_policy) { + case VNC_SHARE_POLICY_IGNORE: + /* + * Ignore the shared flag. Nothing to do here. + * + * Doesn't conform to the rfb spec but is traditional qemu + * behavior, thus left here as option for compatibility + * reasons. + */ + break; + case VNC_SHARE_POLICY_ALLOW_EXCLUSIVE: + /* + * Policy: Allow clients ask for exclusive access. + * + * Implementation: When a client asks for exclusive access, + * disconnect all others. Shared connects are allowed as long + * as no exclusive connection exists. + * + * This is how the rfb spec suggests to handle the shared flag. + */ + if (mode == VNC_SHARE_MODE_EXCLUSIVE) { + VncState *client; + QTAILQ_FOREACH(client, &vs->vd->clients, next) { + if (vs == client) { + continue; + } + if (client->share_mode != VNC_SHARE_MODE_EXCLUSIVE && + client->share_mode != VNC_SHARE_MODE_SHARED) { + continue; + } + vnc_disconnect_start(client); + } + } + if (mode == VNC_SHARE_MODE_SHARED) { + if (vs->vd->num_exclusive > 0) { + vnc_disconnect_start(vs); + return 0; + } + } + break; + case VNC_SHARE_POLICY_FORCE_SHARED: + /* + * Policy: Shared connects only. + * Implementation: Disallow clients asking for exclusive access. + * + * Useful for shared desktop sessions where you don't want + * someone forgetting to say -shared when running the vnc + * client disconnect everybody else. + */ + if (mode == VNC_SHARE_MODE_EXCLUSIVE) { + vnc_disconnect_start(vs); + return 0; + } + break; + } + vnc_set_share_mode(vs, mode); + vs->client_width = ds_get_width(vs->ds); vs->client_height = ds_get_height(vs->ds); vnc_write_u16(vs, vs->client_width); @@ -2562,6 +2645,7 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth) vnc_client_cache_addr(vs); vnc_qmp_event(vs, QEVENT_VNC_CONNECTED); + vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); vs->vd = vd; vs->ds = vd->ds; @@ -2755,6 +2839,7 @@ int vnc_display_open(DisplayState *ds, const char *display) if (!(vs->display = strdup(display))) return -1; + vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; options = display; while ((options = strchr(options, ','))) { @@ -2810,6 +2895,19 @@ int vnc_display_open(DisplayState *ds, const char *display) vs->lossy = true; } else if (strncmp(options, "non-adapative", 13) == 0) { vs->non_adaptive = true; + } else if (strncmp(options, "share=", 6) == 0) { + if (strncmp(options+6, "ignore", 6) == 0) { + vs->share_policy = VNC_SHARE_POLICY_IGNORE; + } else if (strncmp(options+6, "allow-exclusive", 15) == 0) { + vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; + } else if (strncmp(options+6, "force-shared", 12) == 0) { + vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED; + } else { + fprintf(stderr, "unknown vnc share= option\n"); + g_free(vs->display); + vs->display = NULL; + return -1; + } } } diff --git a/ui/vnc.h b/ui/vnc.h index 66689f1d60..0bd1fc6d23 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -122,9 +122,24 @@ struct VncSurface DisplaySurface *ds; }; +typedef enum VncShareMode { + VNC_SHARE_MODE_CONNECTING = 1, + VNC_SHARE_MODE_SHARED, + VNC_SHARE_MODE_EXCLUSIVE, + VNC_SHARE_MODE_DISCONNECTED, +} VncShareMode; + +typedef enum VncSharePolicy { + VNC_SHARE_POLICY_IGNORE = 1, + VNC_SHARE_POLICY_ALLOW_EXCLUSIVE, + VNC_SHARE_POLICY_FORCE_SHARED, +} VncSharePolicy; + struct VncDisplay { QTAILQ_HEAD(, VncState) clients; + int num_exclusive; + VncSharePolicy share_policy; QEMUTimer *timer; int timer_interval; int lsock; @@ -250,6 +265,7 @@ struct VncState int last_y; int client_width; int client_height; + VncShareMode share_mode; uint32_t vnc_encoding; -- cgit v1.2.1 From 7bc9318bfb68b2d773449a55d4fa800d0fdb0918 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 8 Feb 2012 13:18:37 +0100 Subject: vnc: lift modifier keys on client disconnect. For any modifier key (shift, ctrl, alt) still pressed on disconnect inject a key-up event into the guest. The vnc client is gone, it will not do that, so qemu has to do it instead. Without this keys will get stuck, making the guest act in weird ways after reconnecting. Reproducer: exit vnc client via Alt-F4, guest continues to see the pressed alt key and will not react to key events in any useful way until you tap the alt key once to unstuck it. Signed-off-by: Gerd Hoffmann --- ui/vnc.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'ui') diff --git a/ui/vnc.c b/ui/vnc.c index 83a9b15c50..02b71bcd2c 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -46,6 +46,7 @@ static VncDisplay *vnc_display; /* needed for info vnc */ static DisplayChangeListener *dcl; static int vnc_cursor_define(VncState *vs); +static void vnc_release_modifiers(VncState *vs); static void vnc_set_share_mode(VncState *vs, VncShareMode mode) { @@ -1051,6 +1052,7 @@ static void vnc_disconnect_finish(VncState *vs) vnc_sasl_client_cleanup(vs); #endif /* CONFIG_VNC_SASL */ audio_del(vs); + vnc_release_modifiers(vs); QTAILQ_REMOVE(&vs->vd->clients, vs, next); @@ -1679,6 +1681,29 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } } +static void vnc_release_modifiers(VncState *vs) +{ + static const int keycodes[] = { + /* shift, control, alt keys, both left & right */ + 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, + }; + int i, keycode; + + if (!is_graphic_console()) { + return; + } + for (i = 0; i < ARRAY_SIZE(keycodes); i++) { + keycode = keycodes[i]; + if (!vs->modifiers_state[keycode]) { + continue; + } + if (keycode & SCANCODE_GREY) { + kbd_put_keycode(SCANCODE_EMUL0); + } + kbd_put_keycode(keycode | SCANCODE_UP); + } +} + static void key_event(VncState *vs, int down, uint32_t sym) { int keycode; -- cgit v1.2.1