From a78db5aeb230fa90fa478ad3a747a00119df985e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 17 Feb 2012 12:53:55 +0100 Subject: [PATCH 02/11] vnc: implement shared flag handling. RH-Author: Gerd Hoffmann Message-id: <1329483235-24945-3-git-send-email-kraxel@redhat.com> Patchwork-id: 37414 O-Subject: [RHEL-6.3 qemu-kvm PATCH 2/2] vnc: implement shared flag handling. Bugzilla: 653779 RH-Acked-by: Alex Williamson RH-Acked-by: Laszlo Ersek RH-Acked-by: Markus Armbruster 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 (cherry picked from commit 8cf364898cfe4ae761f2253e91a040633d6f87be) Conflicts: qemu-options.hx ui/vnc.h vnc.c --- qemu-options.hx | 13 +++++++ vnc.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ vnc.h | 16 +++++++++ 3 files changed, 127 insertions(+), 0 deletions(-) Signed-off-by: Michal Novotny --- qemu-options.hx | 13 +++++++ vnc.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ vnc.h | 16 +++++++++ 3 files changed, 127 insertions(+), 0 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index 92f2e12..ca12455 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -782,6 +782,19 @@ empty, with a @code{deny} policy. Thus no one will be allowed to use the VNC server until the ACLs have been loaded. This can be achieved using the @code{acl} monitor command. +@item share=[allow-exclusive|force-shared|ignore] + +Set display sharing policy. 'allow-exclusive' allows clients to ask +for exclusive access. As suggested by the rfb spec this is +implemented by dropping other connections. Connecting multiple +clients in parallel requires all clients asking for a shared session +(vncviewer: -shared switch). This is the default. 'force-shared' +disables exclusive client access. Useful for shared desktop sessions, +where you don't want someone forgetting specify -shared disconnect +everybody else. 'ignore' completely ignores the shared flag and +allows everybody connect unconditionally. Doesn't conform to the rfb +spec but is traditional qemu behavior. + @end table ETEXI diff --git a/vnc.c b/vnc.c index 85ec4b2..daeffe8 100644 --- a/vnc.c +++ b/vnc.c @@ -49,6 +49,29 @@ static VncDisplay *vnc_display; /* needed for info vnc */ static DisplayChangeListener *dcl; +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) { @@ -1059,6 +1082,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; @@ -2067,8 +2091,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); @@ -2397,6 +2480,7 @@ static void vnc_connect(VncDisplay *vd, int csock) 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; @@ -2555,6 +2639,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, ','))) { @@ -2604,6 +2689,19 @@ int vnc_display_open(DisplayState *ds, const char *display) #endif } else if (strncmp(options, "acl", 3) == 0) { acl = 1; + } 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/vnc.h b/vnc.h index 2be435d..f2cbd7c 100644 --- a/vnc.h +++ b/vnc.h @@ -91,9 +91,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; @@ -132,6 +147,7 @@ struct VncState int last_y; int client_width; int client_height; + VncShareMode share_mode; uint32_t vnc_encoding; uint8_t tight_quality; -- 1.7.7.6