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
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
ETEXI

View File

@ -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;
}
}
}

View File

@ -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;