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 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'ui/vnc.c') diff --git a/ui/vnc.c b/ui/vnc.c index 810582b..83a9b15 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; + } } } -- cgit v1.1