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 <kraxel@redhat.com>
This commit is contained in:
Gerd Hoffmann 2011-11-24 18:10:49 +01:00
parent e26437c2d4
commit 8cf364898c
3 changed files with 127 additions and 0 deletions

View File

@ -1095,6 +1095,19 @@ This can be really helpful to save bandwidth when playing videos. Disabling
adaptive encodings allows to restore the original static behavior of encodings adaptive encodings allows to restore the original static behavior of encodings
like Tight. like Tight.
@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 @end table
ETEXI ETEXI

View File

@ -47,6 +47,29 @@ static DisplayChangeListener *dcl;
static int vnc_cursor_define(VncState *vs); 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, static char *addr_to_string(const char *format,
struct sockaddr_storage *sa, struct sockaddr_storage *sa,
socklen_t salen) { socklen_t salen) {
@ -997,6 +1020,7 @@ static void vnc_disconnect_start(VncState *vs)
{ {
if (vs->csock == -1) if (vs->csock == -1)
return; return;
vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
closesocket(vs->csock); closesocket(vs->csock);
vs->csock = -1; 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) static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
{ {
char buf[1024]; char buf[1024];
VncShareMode mode;
int size; 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_width = ds_get_width(vs->ds);
vs->client_height = ds_get_height(vs->ds); vs->client_height = ds_get_height(vs->ds);
vnc_write_u16(vs, vs->client_width); 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_client_cache_addr(vs);
vnc_qmp_event(vs, QEVENT_VNC_CONNECTED); vnc_qmp_event(vs, QEVENT_VNC_CONNECTED);
vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING);
vs->vd = vd; vs->vd = vd;
vs->ds = vd->ds; vs->ds = vd->ds;
@ -2755,6 +2839,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
if (!(vs->display = strdup(display))) if (!(vs->display = strdup(display)))
return -1; return -1;
vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
options = display; options = display;
while ((options = strchr(options, ','))) { while ((options = strchr(options, ','))) {
@ -2810,6 +2895,19 @@ int vnc_display_open(DisplayState *ds, const char *display)
vs->lossy = true; vs->lossy = true;
} else if (strncmp(options, "non-adapative", 13) == 0) { } else if (strncmp(options, "non-adapative", 13) == 0) {
vs->non_adaptive = true; 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;
}
} }
} }

View File

@ -122,9 +122,24 @@ struct VncSurface
DisplaySurface *ds; 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 struct VncDisplay
{ {
QTAILQ_HEAD(, VncState) clients; QTAILQ_HEAD(, VncState) clients;
int num_exclusive;
VncSharePolicy share_policy;
QEMUTimer *timer; QEMUTimer *timer;
int timer_interval; int timer_interval;
int lsock; int lsock;
@ -250,6 +265,7 @@ struct VncState
int last_y; int last_y;
int client_width; int client_width;
int client_height; int client_height;
VncShareMode share_mode;
uint32_t vnc_encoding; uint32_t vnc_encoding;