*/
#include "vnc.h"
+#include "vnc-jobs.h"
#include "sysemu.h"
#include "qemu_socket.h"
#include "qemu-timer.h"
#include "acl.h"
#include "qemu-objects.h"
+#include "qmp-commands.h"
+#include "osdep.h"
#define VNC_REFRESH_INTERVAL_BASE 30
#define VNC_REFRESH_INTERVAL_INC 50
#define VNC_REFRESH_INTERVAL_MAX 2000
+static const struct timeval VNC_REFRESH_STATS = { 0, 500000 };
+static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
#include "vnc_keysym.h"
#include "d3des.h"
-#define count_bits(c, v) { \
- for (c = 0; v; v >>= 1) \
- { \
- c += v & 1; \
- } \
-}
-
-
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)
+{
+#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,
/* Enough for the existing format + the 2 vars we're
* substituting in. */
addrlen = strlen(format) + strlen(host) + strlen(serv);
- addr = qemu_malloc(addrlen + 1);
+ addr = g_malloc(addrlen + 1);
snprintf(addr, addrlen, format, host, serv);
addr[addrlen] = '\0';
static void vnc_client_cache_auth(VncState *client)
{
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
QDict *qdict;
+#endif
if (!client->info) {
return;
}
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
qdict = qobject_to_qdict(client->info);
+#endif
#ifdef CONFIG_VNC_TLS
if (client->tls.session &&
qobject_decref(data);
}
-static void info_vnc_iter(QObject *obj, void *opaque)
+static VncClientInfo *qmp_query_vnc_client(const VncState *client)
{
- QDict *client;
- Monitor *mon = opaque;
+ struct sockaddr_storage sa;
+ socklen_t salen = sizeof(sa);
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ VncClientInfo *info;
- client = qobject_to_qdict(obj);
- monitor_printf(mon, "Client:\n");
- monitor_printf(mon, " address: %s:%s\n",
- qdict_get_str(client, "host"),
- qdict_get_str(client, "service"));
+ if (getpeername(client->csock, (struct sockaddr *)&sa, &salen) < 0) {
+ return NULL;
+ }
+
+ if (getnameinfo((struct sockaddr *)&sa, salen,
+ host, sizeof(host),
+ serv, sizeof(serv),
+ NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+ return NULL;
+ }
+
+ info = g_malloc0(sizeof(*info));
+ info->host = g_strdup(host);
+ info->service = g_strdup(serv);
+ info->family = g_strdup(inet_strfamily(sa.ss_family));
#ifdef CONFIG_VNC_TLS
- monitor_printf(mon, " x509_dname: %s\n",
- qdict_haskey(client, "x509_dname") ?
- qdict_get_str(client, "x509_dname") : "none");
+ if (client->tls.session && client->tls.dname) {
+ info->has_x509_dname = true;
+ info->x509_dname = g_strdup(client->tls.dname);
+ }
#endif
#ifdef CONFIG_VNC_SASL
- monitor_printf(mon, " username: %s\n",
- qdict_haskey(client, "sasl_username") ?
- qdict_get_str(client, "sasl_username") : "none");
-#endif
-}
-
-void do_info_vnc_print(Monitor *mon, const QObject *data)
-{
- QDict *server;
- QList *clients;
-
- server = qobject_to_qdict(data);
- if (qdict_get_bool(server, "enabled") == 0) {
- monitor_printf(mon, "Server: disabled\n");
- return;
+ if (client->sasl.conn && client->sasl.username) {
+ info->has_sasl_username = true;
+ info->sasl_username = g_strdup(client->sasl.username);
}
+#endif
- monitor_printf(mon, "Server:\n");
- monitor_printf(mon, " address: %s:%s\n",
- qdict_get_str(server, "host"),
- qdict_get_str(server, "service"));
- monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth"));
-
- clients = qdict_get_qlist(server, "clients");
- if (qlist_empty(clients)) {
- monitor_printf(mon, "Client: none\n");
- } else {
- qlist_iter(clients, info_vnc_iter, mon);
- }
+ return info;
}
-void do_info_vnc(Monitor *mon, QObject **ret_data)
+VncInfo *qmp_query_vnc(Error **errp)
{
+ VncInfo *info = g_malloc0(sizeof(*info));
+
if (vnc_display == NULL || vnc_display->display == NULL) {
- *ret_data = qobject_from_jsonf("{ 'enabled': false }");
+ info->enabled = false;
} else {
- QList *clist;
+ VncClientInfoList *cur_item = NULL;
+ struct sockaddr_storage sa;
+ socklen_t salen = sizeof(sa);
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
VncState *client;
- clist = qlist_new();
+ info->enabled = true;
+
+ /* for compatibility with the original command */
+ info->has_clients = true;
+
QTAILQ_FOREACH(client, &vnc_display->clients, next) {
- if (client->info) {
- /* incref so that it's not freed by upper layers */
- qobject_incref(client->info);
- qlist_append_obj(clist, client->info);
+ VncClientInfoList *cinfo = g_malloc0(sizeof(*info));
+ cinfo->value = qmp_query_vnc_client(client);
+
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ info->clients = cur_item = cinfo;
+ } else {
+ cur_item->next = cinfo;
+ cur_item = cinfo;
}
}
- *ret_data = qobject_from_jsonf("{ 'enabled': true, 'clients': %p }",
- QOBJECT(clist));
- assert(*ret_data != NULL);
+ if (vnc_display->lsock == -1) {
+ return info;
+ }
+
+ if (getsockname(vnc_display->lsock, (struct sockaddr *)&sa,
+ &salen) == -1) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ goto out_error;
+ }
- if (vnc_server_info_put(qobject_to_qdict(*ret_data)) < 0) {
- qobject_decref(*ret_data);
- *ret_data = NULL;
+ if (getnameinfo((struct sockaddr *)&sa, salen,
+ host, sizeof(host),
+ serv, sizeof(serv),
+ NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ goto out_error;
}
+
+ info->has_host = true;
+ info->host = g_strdup(host);
+
+ info->has_service = true;
+ info->service = g_strdup(serv);
+
+ info->has_family = true;
+ info->family = g_strdup(inet_strfamily(sa.ss_family));
+
+ info->has_auth = true;
+ info->auth = g_strdup(vnc_auth_name(vnc_display));
}
+
+ return info;
+
+out_error:
+ qapi_free_VncInfo(info);
+ return NULL;
}
/* TODO
*/
static int vnc_update_client(VncState *vs, int has_dirty);
+static int vnc_update_client_sync(VncState *vs, int has_dirty);
static void vnc_disconnect_start(VncState *vs);
static void vnc_disconnect_finish(VncState *vs);
static void vnc_init_timer(VncDisplay *vd);
static void vnc_refresh(void *opaque);
static int vnc_refresh_server_surface(VncDisplay *vd);
-static inline void vnc_set_bit(uint32_t *d, int k)
-{
- d[k >> 5] |= 1 << (k & 0x1f);
-}
-
-static inline void vnc_clear_bit(uint32_t *d, int k)
-{
- d[k >> 5] &= ~(1 << (k & 0x1f));
-}
-
-static inline void vnc_set_bits(uint32_t *d, int n, int nb_words)
-{
- int j;
-
- j = 0;
- while (n >= 32) {
- d[j++] = -1;
- n -= 32;
- }
- if (n > 0)
- d[j++] = (1 << n) - 1;
- while (j < nb_words)
- d[j++] = 0;
-}
-
-static inline int vnc_get_bit(const uint32_t *d, int k)
-{
- return (d[k >> 5] >> (k & 0x1f)) & 1;
-}
-
-static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2,
- int nb_words)
-{
- int i;
- for(i = 0; i < nb_words; i++) {
- if ((d1[i] & d2[i]) != 0)
- return 1;
- }
- return 0;
-}
-
static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
{
int i;
VncDisplay *vd = ds->opaque;
struct VncSurface *s = &vd->guest;
+ int width = ds_get_width(ds);
+ int height = ds_get_height(ds);
h += y;
w += (x % 16);
x -= (x % 16);
- x = MIN(x, s->ds->width);
- y = MIN(y, s->ds->height);
- w = MIN(x + w, s->ds->width) - x;
- h = MIN(h, s->ds->height);
+ x = MIN(x, width);
+ y = MIN(y, height);
+ w = MIN(x + w, width) - x;
+ h = MIN(h, height);
for (; y < h; y++)
for (i = 0; i < w; i += 16)
- vnc_set_bit(s->dirty[y], (x + i) / 16);
+ set_bit((x + i) / 16, s->dirty[y]);
}
void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
{
if ((buffer->capacity - buffer->offset) < len) {
buffer->capacity += (len + 1024);
- buffer->buffer = qemu_realloc(buffer->buffer, buffer->capacity);
+ buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
if (buffer->buffer == NULL) {
fprintf(stderr, "vnc: out of memory\n");
exit(1);
}
}
-int buffer_empty(Buffer *buffer)
+static int buffer_empty(Buffer *buffer)
{
return buffer->offset == 0;
}
-uint8_t *buffer_end(Buffer *buffer)
+static uint8_t *buffer_end(Buffer *buffer)
{
return buffer->buffer + buffer->offset;
}
void buffer_free(Buffer *buffer)
{
- qemu_free(buffer->buffer);
+ g_free(buffer->buffer);
buffer->offset = 0;
buffer->capacity = 0;
buffer->buffer = NULL;
}
vs->client_width = ds_get_width(ds);
vs->client_height = ds_get_height(ds);
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
VNC_ENCODING_DESKTOPRESIZE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+ VncState *vs;
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = true;
+ vnc_unlock_output(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_jobs_join(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = false;
+ vnc_unlock_output(vs);
+ }
+}
+
+int vnc_server_fb_stride(VncDisplay *vd)
+{
+ return pixman_image_get_stride(vd->server);
+}
+
+void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y)
+{
+ uint8_t *ptr;
+
+ ptr = (uint8_t *)pixman_image_get_data(vd->server);
+ ptr += y * vnc_server_fb_stride(vd);
+ ptr += x * VNC_SERVER_FB_BYTES;
+ return ptr;
+}
+
static void vnc_dpy_resize(DisplayState *ds)
{
VncDisplay *vd = ds->opaque;
VncState *vs;
+ vnc_abort_display_jobs(vd);
+
/* server surface */
- if (!vd->server)
- vd->server = qemu_mallocz(sizeof(*vd->server));
- if (vd->server->data)
- qemu_free(vd->server->data);
- *(vd->server) = *(ds->surface);
- vd->server->data = qemu_mallocz(vd->server->linesize *
- vd->server->height);
+ qemu_pixman_image_unref(vd->server);
+ vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
+ ds_get_width(ds),
+ ds_get_height(ds),
+ NULL, 0);
/* guest surface */
- if (!vd->guest.ds)
- vd->guest.ds = qemu_mallocz(sizeof(*vd->guest.ds));
+#if 0 /* FIXME */
if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel)
console_color_init(ds);
- *(vd->guest.ds) = *(ds->surface);
+#endif
+ qemu_pixman_image_unref(vd->guest.fb);
+ vd->guest.fb = pixman_image_ref(ds->surface->image);
+ vd->guest.format = ds->surface->format;
memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty));
QTAILQ_FOREACH(vs, &vd->clients, next) {
}
/* fastest code */
-static void vnc_write_pixels_copy(VncState *vs, struct PixelFormat *pf,
+static void vnc_write_pixels_copy(VncState *vs,
void *pixels, int size)
{
vnc_write(vs, pixels, size);
void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
{
uint8_t r, g, b;
- VncDisplay *vd = vs->vd;
- r = ((((v & vd->server->pf.rmask) >> vd->server->pf.rshift) << vs->clientds.pf.rbits) >>
- vd->server->pf.rbits);
- g = ((((v & vd->server->pf.gmask) >> vd->server->pf.gshift) << vs->clientds.pf.gbits) >>
- vd->server->pf.gbits);
- b = ((((v & vd->server->pf.bmask) >> vd->server->pf.bshift) << vs->clientds.pf.bbits) >>
- vd->server->pf.bbits);
- v = (r << vs->clientds.pf.rshift) |
- (g << vs->clientds.pf.gshift) |
- (b << vs->clientds.pf.bshift);
- switch(vs->clientds.pf.bytes_per_pixel) {
+#if VNC_SERVER_FB_FORMAT == PIXMAN_FORMAT(32, PIXMAN_TYPE_ARGB, 0, 8, 8, 8)
+ r = (((v & 0x00ff0000) >> 16) << vs->client_pf.rbits) >> 8;
+ g = (((v & 0x0000ff00) >> 8) << vs->client_pf.gbits) >> 8;
+ b = (((v & 0x000000ff) >> 0) << vs->client_pf.bbits) >> 8;
+#else
+# error need some bits here if you change VNC_SERVER_FB_FORMAT
+#endif
+ v = (r << vs->client_pf.rshift) |
+ (g << vs->client_pf.gshift) |
+ (b << vs->client_pf.bshift);
+ switch (vs->client_pf.bytes_per_pixel) {
case 1:
buf[0] = v;
break;
case 2:
- if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
+ if (vs->client_be) {
buf[0] = v >> 8;
buf[1] = v;
} else {
break;
default:
case 4:
- if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
+ if (vs->client_be) {
buf[0] = v >> 24;
buf[1] = v >> 16;
buf[2] = v >> 8;
}
}
-static void vnc_write_pixels_generic(VncState *vs, struct PixelFormat *pf,
+static void vnc_write_pixels_generic(VncState *vs,
void *pixels1, int size)
{
uint8_t buf[4];
- if (pf->bytes_per_pixel == 4) {
+ if (VNC_SERVER_FB_BYTES == 4) {
uint32_t *pixels = pixels1;
int n, i;
n = size >> 2;
- for(i = 0; i < n; i++) {
- vnc_convert_pixel(vs, buf, pixels[i]);
- vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
- }
- } else if (pf->bytes_per_pixel == 2) {
- uint16_t *pixels = pixels1;
- int n, i;
- n = size >> 1;
- for(i = 0; i < n; i++) {
+ for (i = 0; i < n; i++) {
vnc_convert_pixel(vs, buf, pixels[i]);
- vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
+ vnc_write(vs, buf, vs->client_pf.bytes_per_pixel);
}
- } else if (pf->bytes_per_pixel == 1) {
- uint8_t *pixels = pixels1;
- int n, i;
- n = size;
- for(i = 0; i < n; i++) {
- vnc_convert_pixel(vs, buf, pixels[i]);
- vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
- }
- } else {
- fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n");
}
}
uint8_t *row;
VncDisplay *vd = vs->vd;
- row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
+ row = vnc_server_fb_ptr(vd, x, y);
for (i = 0; i < h; i++) {
- vs->write_pixels(vs, &vd->server->pf, row, w * ds_get_bytes_per_pixel(vs->ds));
- row += ds_get_linesize(vs->ds);
+ vs->write_pixels(vs, row, w * VNC_SERVER_FB_BYTES);
+ row += vnc_server_fb_stride(vd);
}
return 1;
}
-static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
{
int n = 0;
case VNC_ENCODING_TIGHT_PNG:
n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
break;
+ case VNC_ENCODING_ZRLE:
+ n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h);
+ break;
+ case VNC_ENCODING_ZYWRLE:
+ n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h);
+ break;
default:
vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
{
/* send bitblit op to the vnc client */
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
vnc_write_u16(vs, src_x);
vnc_write_u16(vs, src_y);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
VncState *vs, *vn;
uint8_t *src_row;
uint8_t *dst_row;
- int i,x,y,pitch,depth,inc,w_lim,s;
+ int i, x, y, pitch, inc, w_lim, s;
int cmp_bytes;
vnc_refresh_server_surface(vd);
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
vs->force_update = 1;
- vnc_update_client(vs, 1);
+ vnc_update_client_sync(vs, 1);
/* vs might be free()ed here */
}
}
/* do bitblit op on the local surface too */
- pitch = ds_get_linesize(vd->ds);
- depth = ds_get_bytes_per_pixel(vd->ds);
- src_row = vd->server->data + pitch * src_y + depth * src_x;
- dst_row = vd->server->data + pitch * dst_y + depth * dst_x;
+ pitch = vnc_server_fb_stride(vd);
+ src_row = vnc_server_fb_ptr(vd, src_x, src_y);
+ dst_row = vnc_server_fb_ptr(vd, dst_x, dst_y);
y = dst_y;
inc = 1;
if (dst_y > src_y) {
} else {
s = 16;
}
- cmp_bytes = s * depth;
+ cmp_bytes = s * VNC_SERVER_FB_BYTES;
if (memcmp(src_row, dst_row, cmp_bytes) == 0)
continue;
memmove(dst_row, src_row, cmp_bytes);
QTAILQ_FOREACH(vs, &vd->clients, next) {
if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
- vnc_set_bit(vs->dirty[y], ((x + dst_x) / 16));
+ set_bit(((x + dst_x) / 16), vs->dirty[y]);
}
}
}
- src_row += pitch - w * depth;
- dst_row += pitch - w * depth;
+ src_row += pitch - w * VNC_SERVER_FB_BYTES;
+ dst_row += pitch - w * VNC_SERVER_FB_BYTES;
y += inc;
}
}
}
-static void vnc_mouse_set(int x, int y, int visible)
+static void vnc_mouse_set(DisplayState *ds, int x, int y, int visible)
{
/* can we ask the client(s) to move the pointer ??? */
}
static int vnc_cursor_define(VncState *vs)
{
QEMUCursor *c = vs->vd->cursor;
- PixelFormat pf = qemu_default_pixelformat(32);
int isize;
if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0); /* padding */
vnc_write_u16(vs, 1); /* # of rects */
vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height,
VNC_ENCODING_RICH_CURSOR);
- isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel;
- vnc_write_pixels_generic(vs, &pf, c->data, isize);
+ isize = c->width * c->height * vs->client_pf.bytes_per_pixel;
+ vnc_write_pixels_generic(vs, c->data, isize);
vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
+ vnc_unlock_output(vs);
return 0;
}
return -1;
}
-static void vnc_dpy_cursor_define(QEMUCursor *c)
+static void vnc_dpy_cursor_define(DisplayState *ds, QEMUCursor *c)
{
VncDisplay *vd = vnc_display;
VncState *vs;
cursor_put(vd->cursor);
- qemu_free(vd->cursor_mask);
+ g_free(vd->cursor_mask);
vd->cursor = c;
cursor_get(vd->cursor);
vd->cursor_msize = cursor_get_mono_bpl(c) * c->height;
- vd->cursor_mask = qemu_mallocz(vd->cursor_msize);
+ vd->cursor_mask = g_malloc0(vd->cursor_msize);
cursor_get_mono_mask(c, 0, vd->cursor_mask);
QTAILQ_FOREACH(vs, &vd->clients, next) {
}
static int find_and_clear_dirty_height(struct VncState *vs,
- int y, int last_x, int x)
+ int y, int last_x, int x, int height)
{
int h;
- VncDisplay *vd = vs->vd;
- for (h = 1; h < (vd->server->height - y); h++) {
+ for (h = 1; h < (height - y); h++) {
int tmp_x;
- if (!vnc_get_bit(vs->dirty[y + h], last_x))
+ if (!test_bit(last_x, vs->dirty[y + h])) {
break;
- for (tmp_x = last_x; tmp_x < x; tmp_x++)
- vnc_clear_bit(vs->dirty[y + h], tmp_x);
+ }
+ for (tmp_x = last_x; tmp_x < x; tmp_x++) {
+ clear_bit(tmp_x, vs->dirty[y + h]);
+ }
}
return h;
}
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ int ret = vnc_update_client(vs, has_dirty);
+ vnc_jobs_join(vs);
+ return ret;
+}
+
static int vnc_update_client(VncState *vs, int has_dirty)
{
if (vs->need_update && vs->csock != -1) {
VncDisplay *vd = vs->vd;
+ VncJob *job;
int y;
- int n_rectangles;
- int saved_offset;
int width, height;
- int n;
+ int n = 0;
+
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
/* kernel send buffers are full -> drop frames to throttle */
* happening in parallel don't disturb us, the next pass will
* send them to the client.
*/
- n_rectangles = 0;
- vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
- vnc_write_u8(vs, 0);
- saved_offset = vs->output.offset;
- vnc_write_u16(vs, 0);
+ job = vnc_job_new(vs);
- width = MIN(vd->server->width, vs->client_width);
- height = MIN(vd->server->height, vs->client_height);
+ width = MIN(pixman_image_get_width(vd->server), vs->client_width);
+ height = MIN(pixman_image_get_height(vd->server), vs->client_height);
for (y = 0; y < height; y++) {
int x;
int last_x = -1;
for (x = 0; x < width / 16; x++) {
- if (vnc_get_bit(vs->dirty[y], x)) {
+ if (test_and_clear_bit(x, vs->dirty[y])) {
if (last_x == -1) {
last_x = x;
}
- vnc_clear_bit(vs->dirty[y], x);
} else {
if (last_x != -1) {
- int h = find_and_clear_dirty_height(vs, y, last_x, x);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+ int h = find_and_clear_dirty_height(vs, y, last_x, x,
+ height);
+
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
}
last_x = -1;
}
}
if (last_x != -1) {
- int h = find_and_clear_dirty_height(vs, y, last_x, x);
- n = send_framebuffer_update(vs, last_x * 16, y,
- (x - last_x) * 16, h);
- n_rectangles += n;
+ int h = find_and_clear_dirty_height(vs, y, last_x, x, height);
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
}
}
- vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
- vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
- vnc_flush(vs);
+
+ vnc_job_push(job);
vs->force_update = 0;
- return n_rectangles;
+ return n;
}
if (vs->csock == -1)
switch (cmd) {
case AUD_CNOTIFY_DISABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
case AUD_CNOTIFY_ENABLE:
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
+ vnc_unlock_output(vs);
vnc_flush(vs);
break;
}
{
VncState *vs = opaque;
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
vnc_write_u32(vs, size);
vnc_write(vs, buf, size);
+ vnc_unlock_output(vs);
vnc_flush(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;
static void vnc_disconnect_finish(VncState *vs)
{
+ int i;
+
+ vnc_jobs_join(vs); /* Wait encoding jobs */
+
+ vnc_lock_output(vs);
vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED);
buffer_free(&vs->input);
vnc_zlib_clear(vs);
vnc_tight_clear(vs);
+ vnc_zrle_clear(vs);
#ifdef CONFIG_VNC_TLS
vnc_tls_client_cleanup(vs);
vnc_sasl_client_cleanup(vs);
#endif /* CONFIG_VNC_SASL */
audio_del(vs);
+ vnc_release_modifiers(vs);
QTAILQ_REMOVE(&vs->vd->clients, vs, next);
vnc_remove_timer(vs->vd);
if (vs->vd->lock_key_sync)
qemu_remove_led_event_handler(vs->led);
- qemu_free(vs);
+ vnc_unlock_output(vs);
+
+ qemu_mutex_destroy(&vs->output_mutex);
+ qemu_bh_delete(vs->bh);
+ buffer_free(&vs->jobs_buffer);
+
+ for (i = 0; i < VNC_STAT_ROWS; ++i) {
+ g_free(vs->lossy_rect[i]);
+ }
+ g_free(vs->lossy_rect);
+ g_free(vs);
}
int vnc_client_io_error(VncState *vs, int ret, int last_errno)
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring encryption calls)
*/
-void vnc_client_write(void *opaque)
+static void vnc_client_write_locked(void *opaque)
{
VncState *vs = opaque;
vnc_client_write_plain(vs);
}
+void vnc_client_write(void *opaque)
+{
+ VncState *vs = opaque;
+
+ vnc_lock_output(vs);
+ if (vs->output.offset) {
+ vnc_client_write_locked(opaque);
+ } else if (vs->csock != -1) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+ vnc_unlock_output(vs);
+}
+
void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
{
vs->read_handler = func;
}
} else
#endif /* CONFIG_VNC_TLS */
- ret = recv(vs->csock, (void *)data, datalen, 0);
+ ret = qemu_recv(vs->csock, data, datalen, 0);
VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
return vnc_client_io_error(vs, ret, socket_error());
}
return ret;
}
+static void vnc_jobs_bh(void *opaque)
+{
+ VncState *vs = opaque;
+
+ vnc_jobs_consume_buffer(vs);
+}
/*
* First function called whenever there is more data to be read from
void vnc_flush(VncState *vs)
{
- if (vs->csock != -1 && vs->output.offset)
- vnc_client_write(vs);
+ vnc_lock_output(vs);
+ if (vs->csock != -1 && vs->output.offset) {
+ vnc_client_write_locked(vs);
+ }
+ vnc_unlock_output(vs);
}
-uint8_t read_u8(uint8_t *data, size_t offset)
+static uint8_t read_u8(uint8_t *data, size_t offset)
{
return data[offset];
}
-uint16_t read_u16(uint8_t *data, size_t offset)
+static uint16_t read_u16(uint8_t *data, size_t offset)
{
return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
}
-int32_t read_s32(uint8_t *data, size_t offset)
+static int32_t read_s32(uint8_t *data, size_t offset)
{
return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
(data[offset + 2] << 8) | data[offset + 3]);
{
}
-static void check_pointer_type_change(Notifier *notifier)
+static void check_pointer_type_change(Notifier *notifier, void *data)
{
VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
int absolute = kbd_mouse_is_absolute();
if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, absolute, 0,
ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_POINTER_TYPE_CHANGE);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
vs->absolute = absolute;
break;
}
- if (vs->vd->lock_key_sync &&
+ if (down && vs->vd->lock_key_sync &&
keycode_is_keypad(vs->vd->kbd_layout, keycode)) {
/* If the numlock state needs to change then simulate an additional
keypress before sending this one. This will happen if the user
}
}
- if (vs->vd->lock_key_sync &&
+ if (down && vs->vd->lock_key_sync &&
((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z'))) {
/* If the capslock state needs to change then simulate an additional
keypress before sending this one. This will happen if the user
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 */
break;
default:
- kbd_put_keysym(sym);
+ if (control) {
+ kbd_put_keysym(sym & 0x1f);
+ } else {
+ kbd_put_keysym(sym);
+ }
break;
}
}
}
}
+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;
int x_position, int y_position,
int w, int h)
{
+ int i;
+ const size_t width = ds_get_width(vs->ds) / 16;
+
if (y_position > ds_get_height(vs->ds))
y_position = ds_get_height(vs->ds);
if (y_position + h >= ds_get_height(vs->ds))
h = ds_get_height(vs->ds) - y_position;
- int i;
vs->need_update = 1;
if (!incremental) {
vs->force_update = 1;
for (i = 0; i < h; i++) {
- vnc_set_bits(vs->dirty[y_position + i],
- (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
+ bitmap_set(vs->dirty[y_position + i], 0, width);
+ bitmap_clear(vs->dirty[y_position + i], width,
+ VNC_DIRTY_BITS - width);
}
}
}
static void send_ext_key_event_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_EXT_KEY_EVENT);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
static void send_ext_audio_ack(VncState *vs)
{
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1);
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
VNC_ENCODING_AUDIO);
+ vnc_unlock_output(vs);
vnc_flush(vs);
}
/*
* Start from the end because the encodings are sent in order of preference.
- * This way the prefered encoding (first encoding defined in the array)
+ * This way the preferred encoding (first encoding defined in the array)
* will be set at the end of the loop.
*/
for (i = n_encodings - 1; i >= 0; i--) {
vs->features |= VNC_FEATURE_TIGHT_MASK;
vs->vnc_encoding = enc;
break;
+#ifdef CONFIG_VNC_PNG
case VNC_ENCODING_TIGHT_PNG:
vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
vs->vnc_encoding = enc;
break;
+#endif
case VNC_ENCODING_ZLIB:
vs->features |= VNC_FEATURE_ZLIB_MASK;
vs->vnc_encoding = enc;
break;
+ case VNC_ENCODING_ZRLE:
+ vs->features |= VNC_FEATURE_ZRLE_MASK;
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_ZYWRLE:
+ vs->features |= VNC_FEATURE_ZYWRLE_MASK;
+ vs->vnc_encoding = enc;
+ break;
case VNC_ENCODING_DESKTOPRESIZE:
vs->features |= VNC_FEATURE_RESIZE_MASK;
break;
vs->tight.compression = (enc & 0x0F);
break;
case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
- vs->tight.quality = (enc & 0x0F);
+ if (vs->vd->lossy) {
+ vs->tight.quality = (enc & 0x0F);
+ }
break;
default:
VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
}
}
vnc_desktop_resize(vs);
- check_pointer_type_change(&vs->mouse_mode_notifier);
+ check_pointer_type_change(&vs->mouse_mode_notifier, NULL);
}
static void set_pixel_conversion(VncState *vs)
{
- if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
- (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) &&
- !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) {
+ pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf);
+
+ if (fmt == VNC_SERVER_FB_FORMAT) {
vs->write_pixels = vnc_write_pixels_copy;
vnc_hextile_set_pixel_conversion(vs, 0);
} else {
return;
}
- vs->clientds = *(vs->vd->guest.ds);
- vs->clientds.pf.rmax = red_max;
- count_bits(vs->clientds.pf.rbits, red_max);
- vs->clientds.pf.rshift = red_shift;
- vs->clientds.pf.rmask = red_max << red_shift;
- vs->clientds.pf.gmax = green_max;
- count_bits(vs->clientds.pf.gbits, green_max);
- vs->clientds.pf.gshift = green_shift;
- vs->clientds.pf.gmask = green_max << green_shift;
- vs->clientds.pf.bmax = blue_max;
- count_bits(vs->clientds.pf.bbits, blue_max);
- vs->clientds.pf.bshift = blue_shift;
- vs->clientds.pf.bmask = blue_max << blue_shift;
- vs->clientds.pf.bits_per_pixel = bits_per_pixel;
- vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8;
- vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
- vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00;
+ vs->client_pf.rmax = red_max;
+ vs->client_pf.rbits = hweight_long(red_max);
+ vs->client_pf.rshift = red_shift;
+ vs->client_pf.rmask = red_max << red_shift;
+ vs->client_pf.gmax = green_max;
+ vs->client_pf.gbits = hweight_long(green_max);
+ vs->client_pf.gshift = green_shift;
+ vs->client_pf.gmask = green_max << green_shift;
+ vs->client_pf.bmax = blue_max;
+ vs->client_pf.bbits = hweight_long(blue_max);
+ vs->client_pf.bshift = blue_shift;
+ vs->client_pf.bmask = blue_max << blue_shift;
+ vs->client_pf.bits_per_pixel = bits_per_pixel;
+ vs->client_pf.bytes_per_pixel = bits_per_pixel / 8;
+ vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
+ vs->client_be = big_endian_flag;
set_pixel_conversion(vs);
static void pixel_format_message (VncState *vs) {
char pad[3] = { 0, 0, 0 };
- vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */
- vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */
+ vs->client_pf = qemu_default_pixelformat(32);
+
+ vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */
+ vnc_write_u8(vs, vs->client_pf.depth); /* depth */
#ifdef HOST_WORDS_BIGENDIAN
vnc_write_u8(vs, 1); /* big-endian-flag */
vnc_write_u8(vs, 0); /* big-endian-flag */
#endif
vnc_write_u8(vs, 1); /* true-color-flag */
- vnc_write_u16(vs, vs->ds->surface->pf.rmax); /* red-max */
- vnc_write_u16(vs, vs->ds->surface->pf.gmax); /* green-max */
- vnc_write_u16(vs, vs->ds->surface->pf.bmax); /* blue-max */
- vnc_write_u8(vs, vs->ds->surface->pf.rshift); /* red-shift */
- vnc_write_u8(vs, vs->ds->surface->pf.gshift); /* green-shift */
- vnc_write_u8(vs, vs->ds->surface->pf.bshift); /* blue-shift */
+ vnc_write_u16(vs, vs->client_pf.rmax); /* red-max */
+ vnc_write_u16(vs, vs->client_pf.gmax); /* green-max */
+ vnc_write_u16(vs, vs->client_pf.bmax); /* blue-max */
+ vnc_write_u8(vs, vs->client_pf.rshift); /* red-shift */
+ vnc_write_u8(vs, vs->client_pf.gshift); /* green-shift */
+ vnc_write_u8(vs, vs->client_pf.bshift); /* blue-shift */
+ vnc_write(vs, pad, 3); /* padding */
vnc_hextile_set_pixel_conversion(vs, 0);
-
- vs->clientds = *(vs->ds->surface);
- vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG;
vs->write_pixels = vnc_write_pixels_copy;
-
- vnc_write(vs, pad, 3); /* padding */
}
static void vnc_dpy_setdata(DisplayState *ds)
{
- /* We don't have to do anything */
+ VncDisplay *vd = ds->opaque;
+
+ qemu_pixman_image_unref(vd->guest.fb);
+ vd->guest.fb = pixman_image_ref(ds->surface->image);
+ vd->guest.format = ds->surface->format;
+ vnc_dpy_update(ds, 0, 0, ds_get_width(ds), ds_get_height(ds));
}
static void vnc_colordepth(VncState *vs)
{
if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
/* Sending a WMVi message to notify the client*/
+ vnc_lock_output(vs);
vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
vnc_write_u8(vs, 0);
vnc_write_u16(vs, 1); /* number of rects */
vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
ds_get_height(vs->ds), VNC_ENCODING_WMVi);
pixel_format_message(vs);
+ vnc_unlock_output(vs);
vnc_flush(vs);
} else {
set_pixel_conversion(vs);
if (data[0] > 3) {
vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
- if (!qemu_timer_expired(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval))
- qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval);
+ if (!qemu_timer_expired(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval))
+ qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval);
}
switch (data[0]) {
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);
unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
int i, j, pwlen;
unsigned char key[8];
+ time_t now = time(NULL);
- if (!vs->vd->password || !vs->vd->password[0]) {
+ if (!vs->vd->password) {
VNC_DEBUG("No password configured on server");
- vnc_write_u32(vs, 1); /* Reject auth */
- if (vs->minor >= 8) {
- static const char err[] = "Authentication failed";
- vnc_write_u32(vs, sizeof(err));
- vnc_write(vs, err, sizeof(err));
- }
- vnc_flush(vs);
- vnc_client_error(vs);
- return 0;
+ goto reject;
+ }
+ if (vs->vd->expires < now) {
+ VNC_DEBUG("Password is expired");
+ goto reject;
}
memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
/* Compare expected vs actual challenge response */
if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
- VNC_DEBUG("Client challenge reponse did not match\n");
- vnc_write_u32(vs, 1); /* Reject auth */
- if (vs->minor >= 8) {
- static const char err[] = "Authentication failed";
- vnc_write_u32(vs, sizeof(err));
- vnc_write(vs, err, sizeof(err));
- }
- vnc_flush(vs);
- vnc_client_error(vs);
+ VNC_DEBUG("Client challenge response did not match\n");
+ goto reject;
} else {
VNC_DEBUG("Accepting VNC challenge response\n");
vnc_write_u32(vs, 0); /* Accept auth */
start_client_init(vs);
}
return 0;
+
+reject:
+ vnc_write_u32(vs, 1); /* Reject auth */
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return 0;
}
void start_auth_vnc(VncState *vs)
{
/* We only advertise 1 auth scheme at a time, so client
* must pick the one we sent. Verify this */
- if (data[0] != vs->vd->auth) { /* Reject auth */
+ if (data[0] != vs->auth) { /* Reject auth */
VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]);
vnc_write_u32(vs, 1);
if (vs->minor >= 8) {
vnc_client_error(vs);
} else { /* Accept requested auth */
VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
- switch (vs->vd->auth) {
+ switch (vs->auth) {
case VNC_AUTH_NONE:
VNC_DEBUG("Accept auth none\n");
if (vs->minor >= 8) {
#ifdef CONFIG_VNC_TLS
case VNC_AUTH_VENCRYPT:
- VNC_DEBUG("Accept VeNCrypt auth\n");;
+ VNC_DEBUG("Accept VeNCrypt auth\n");
start_auth_vencrypt(vs);
break;
#endif /* CONFIG_VNC_TLS */
#endif /* CONFIG_VNC_SASL */
default: /* Should not be possible, but just in case */
- VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth);
+ VNC_DEBUG("Reject auth %d server code bug\n", vs->auth);
vnc_write_u8(vs, 1);
if (vs->minor >= 8) {
static const char err[] = "Authentication failed";
vs->minor = 3;
if (vs->minor == 3) {
- if (vs->vd->auth == VNC_AUTH_NONE) {
+ if (vs->auth == VNC_AUTH_NONE) {
VNC_DEBUG("Tell client auth none\n");
- vnc_write_u32(vs, vs->vd->auth);
+ vnc_write_u32(vs, vs->auth);
vnc_flush(vs);
start_client_init(vs);
- } else if (vs->vd->auth == VNC_AUTH_VNC) {
+ } else if (vs->auth == VNC_AUTH_VNC) {
VNC_DEBUG("Tell client VNC auth\n");
- vnc_write_u32(vs, vs->vd->auth);
+ vnc_write_u32(vs, vs->auth);
vnc_flush(vs);
start_auth_vnc(vs);
} else {
- VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth);
+ VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
vnc_write_u32(vs, VNC_AUTH_INVALID);
vnc_flush(vs);
vnc_client_error(vs);
}
} else {
- VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
+ VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
vnc_write_u8(vs, 1); /* num auth */
- vnc_write_u8(vs, vs->vd->auth);
+ vnc_write_u8(vs, vs->auth);
vnc_read_when(vs, protocol_client_auth, 1);
vnc_flush(vs);
}
return 0;
}
+static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y)
+{
+ struct VncSurface *vs = &vd->guest;
+
+ return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT];
+}
+
+void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h)
+{
+ int i, j;
+
+ w = (x + w) / VNC_STAT_RECT;
+ h = (y + h) / VNC_STAT_RECT;
+ x /= VNC_STAT_RECT;
+ y /= VNC_STAT_RECT;
+
+ for (j = y; j <= h; j++) {
+ for (i = x; i <= w; i++) {
+ vs->lossy_rect[j][i] = 1;
+ }
+ }
+}
+
+static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
+{
+ VncState *vs;
+ int sty = y / VNC_STAT_RECT;
+ int stx = x / VNC_STAT_RECT;
+ int has_dirty = 0;
+
+ y = y / VNC_STAT_RECT * VNC_STAT_RECT;
+ x = x / VNC_STAT_RECT * VNC_STAT_RECT;
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ int j;
+
+ /* kernel send buffers are full -> refresh later */
+ if (vs->output.offset) {
+ continue;
+ }
+
+ if (!vs->lossy_rect[sty][stx]) {
+ continue;
+ }
+
+ vs->lossy_rect[sty][stx] = 0;
+ for (j = 0; j < VNC_STAT_RECT; ++j) {
+ bitmap_set(vs->dirty[y + j], x / 16, VNC_STAT_RECT / 16);
+ }
+ has_dirty++;
+ }
+
+ return has_dirty;
+}
+
+static int vnc_update_stats(VncDisplay *vd, struct timeval * tv)
+{
+ int width = pixman_image_get_width(vd->guest.fb);
+ int height = pixman_image_get_height(vd->guest.fb);
+ int x, y;
+ struct timeval res;
+ int has_dirty = 0;
+
+ for (y = 0; y < height; y += VNC_STAT_RECT) {
+ for (x = 0; x < width; x += VNC_STAT_RECT) {
+ VncRectStat *rect = vnc_stat_rect(vd, x, y);
+
+ rect->updated = false;
+ }
+ }
+
+ qemu_timersub(tv, &VNC_REFRESH_STATS, &res);
+
+ if (timercmp(&vd->guest.last_freq_check, &res, >)) {
+ return has_dirty;
+ }
+ vd->guest.last_freq_check = *tv;
+
+ for (y = 0; y < height; y += VNC_STAT_RECT) {
+ for (x = 0; x < width; x += VNC_STAT_RECT) {
+ VncRectStat *rect= vnc_stat_rect(vd, x, y);
+ int count = ARRAY_SIZE(rect->times);
+ struct timeval min, max;
+
+ if (!timerisset(&rect->times[count - 1])) {
+ continue ;
+ }
+
+ max = rect->times[(rect->idx + count - 1) % count];
+ qemu_timersub(tv, &max, &res);
+
+ if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) {
+ rect->freq = 0;
+ has_dirty += vnc_refresh_lossy_rect(vd, x, y);
+ memset(rect->times, 0, sizeof (rect->times));
+ continue ;
+ }
+
+ min = rect->times[rect->idx];
+ max = rect->times[(rect->idx + count - 1) % count];
+ qemu_timersub(&max, &min, &res);
+
+ rect->freq = res.tv_sec + res.tv_usec / 1000000.;
+ rect->freq /= count;
+ rect->freq = 1. / rect->freq;
+ }
+ }
+ return has_dirty;
+}
+
+double vnc_update_freq(VncState *vs, int x, int y, int w, int h)
+{
+ int i, j;
+ double total = 0;
+ int num = 0;
+
+ x = (x / VNC_STAT_RECT) * VNC_STAT_RECT;
+ y = (y / VNC_STAT_RECT) * VNC_STAT_RECT;
+
+ for (j = y; j <= y + h; j += VNC_STAT_RECT) {
+ for (i = x; i <= x + w; i += VNC_STAT_RECT) {
+ total += vnc_stat_rect(vs->vd, i, j)->freq;
+ num++;
+ }
+ }
+
+ if (num) {
+ return total / num;
+ } else {
+ return 0;
+ }
+}
+
+static void vnc_rect_updated(VncDisplay *vd, int x, int y, struct timeval * tv)
+{
+ VncRectStat *rect;
+
+ rect = vnc_stat_rect(vd, x, y);
+ if (rect->updated) {
+ return ;
+ }
+ rect->times[rect->idx] = *tv;
+ rect->idx = (rect->idx + 1) % ARRAY_SIZE(rect->times);
+ rect->updated = true;
+}
+
static int vnc_refresh_server_surface(VncDisplay *vd)
{
+ int width = pixman_image_get_width(vd->guest.fb);
+ int height = pixman_image_get_height(vd->guest.fb);
int y;
uint8_t *guest_row;
uint8_t *server_row;
int cmp_bytes;
- uint32_t width_mask[VNC_DIRTY_WORDS];
VncState *vs;
int has_dirty = 0;
+ pixman_image_t *tmpbuf = NULL;
+
+ struct timeval tv = { 0, 0 };
+
+ if (!vd->non_adaptive) {
+ gettimeofday(&tv, NULL);
+ has_dirty = vnc_update_stats(vd, &tv);
+ }
/*
* Walk through the guest dirty map.
* Check and copy modified bits from guest to server surface.
* Update server dirty map.
*/
- vnc_set_bits(width_mask, (ds_get_width(vd->ds) / 16), VNC_DIRTY_WORDS);
- cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds);
- guest_row = vd->guest.ds->data;
- server_row = vd->server->data;
- for (y = 0; y < vd->guest.ds->height; y++) {
- if (vnc_and_bits(vd->guest.dirty[y], width_mask, VNC_DIRTY_WORDS)) {
+ cmp_bytes = 64;
+ if (cmp_bytes > vnc_server_fb_stride(vd)) {
+ cmp_bytes = vnc_server_fb_stride(vd);
+ }
+ if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
+ int width = pixman_image_get_width(vd->server);
+ tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
+ }
+ guest_row = (uint8_t *)pixman_image_get_data(vd->guest.fb);
+ server_row = (uint8_t *)pixman_image_get_data(vd->server);
+ for (y = 0; y < height; y++) {
+ if (!bitmap_empty(vd->guest.dirty[y], VNC_DIRTY_BITS)) {
int x;
uint8_t *guest_ptr;
uint8_t *server_ptr;
- guest_ptr = guest_row;
+ if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
+ qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, y);
+ guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf);
+ } else {
+ guest_ptr = guest_row;
+ }
server_ptr = server_row;
- for (x = 0; x < vd->guest.ds->width;
+ for (x = 0; x + 15 < width;
x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
- if (!vnc_get_bit(vd->guest.dirty[y], (x / 16)))
+ if (!test_and_clear_bit((x / 16), vd->guest.dirty[y]))
continue;
- vnc_clear_bit(vd->guest.dirty[y], (x / 16));
if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0)
continue;
memcpy(server_ptr, guest_ptr, cmp_bytes);
+ if (!vd->non_adaptive)
+ vnc_rect_updated(vd, x, y, &tv);
QTAILQ_FOREACH(vs, &vd->clients, next) {
- vnc_set_bit(vs->dirty[y], (x / 16));
+ set_bit((x / 16), vs->dirty[y]);
}
has_dirty++;
}
}
- guest_row += ds_get_linesize(vd->ds);
- server_row += ds_get_linesize(vd->ds);
+ guest_row += pixman_image_get_stride(vd->guest.fb);
+ server_row += pixman_image_get_stride(vd->server);
}
+ qemu_pixman_image_unref(tmpbuf);
return has_dirty;
}
vga_hw_update();
+ if (vnc_trylock_display(vd)) {
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) +
+ vd->timer_interval);
+ return;
+ }
+
has_dirty = vnc_refresh_server_surface(vd);
+ vnc_unlock_display(vd);
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
rects += vnc_update_client(vs, has_dirty);
/* vs might be free()ed here */
}
+
/* vd->timer could be NULL now if the last client disconnected,
* in this case don't update the timer */
if (vd->timer == NULL)
if (vd->timer_interval > VNC_REFRESH_INTERVAL_MAX)
vd->timer_interval = VNC_REFRESH_INTERVAL_MAX;
}
- qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval);
+ qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval);
}
static void vnc_init_timer(VncDisplay *vd)
{
vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
if (vd->timer == NULL && !QTAILQ_EMPTY(&vd->clients)) {
- vd->timer = qemu_new_timer(rt_clock, vnc_refresh, vd);
+ vd->timer = qemu_new_timer_ms(rt_clock, vnc_refresh, vd);
+ vnc_dpy_resize(vd->ds);
vnc_refresh(vd);
}
}
}
}
-static void vnc_connect(VncDisplay *vd, int csock)
+static void vnc_connect(VncDisplay *vd, int csock, int skipauth)
{
- VncState *vs = qemu_mallocz(sizeof(VncState));
+ VncState *vs = g_malloc0(sizeof(VncState));
+ int i;
+
vs->csock = csock;
+ if (skipauth) {
+ vs->auth = VNC_AUTH_NONE;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+#endif
+ } else {
+ vs->auth = vd->auth;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = vd->subauth;
+#endif
+ }
+
+ vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
+ for (i = 0; i < VNC_STAT_ROWS; ++i) {
+ vs->lossy_rect[i] = g_malloc0(VNC_STAT_COLS * sizeof (uint8_t));
+ }
+
VNC_DEBUG("New client on socket %d\n", csock);
dcl->idle = 0;
socket_set_nonblock(vs->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;
vs->as.fmt = AUD_FMT_S16;
vs->as.endianness = 0;
+ qemu_mutex_init(&vs->output_mutex);
+ vs->bh = qemu_bh_new(vnc_jobs_bh, vs);
+
QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
vga_hw_update();
int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
if (csock != -1) {
- vnc_connect(vs, csock);
+ vnc_connect(vs, csock, 0);
}
}
void vnc_display_init(DisplayState *ds)
{
- VncDisplay *vs = qemu_mallocz(sizeof(*vs));
+ VncDisplay *vs = g_malloc0(sizeof(*vs));
- dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+ dcl = g_malloc0(sizeof(DisplayChangeListener));
ds->opaque = vs;
dcl->idle = 1;
vs->ds = ds;
QTAILQ_INIT(&vs->clients);
+ vs->expires = TIME_MAX;
if (keyboard_layout)
vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
if (!vs->kbd_layout)
exit(1);
- dcl->dpy_copy = vnc_dpy_copy;
- dcl->dpy_update = vnc_dpy_update;
- dcl->dpy_resize = vnc_dpy_resize;
- dcl->dpy_setdata = vnc_dpy_setdata;
+ qemu_mutex_init(&vs->mutex);
+ vnc_start_worker_thread();
+
+ dcl->dpy_gfx_copy = vnc_dpy_copy;
+ dcl->dpy_gfx_update = vnc_dpy_update;
+ dcl->dpy_gfx_resize = vnc_dpy_resize;
+ dcl->dpy_gfx_setdata = vnc_dpy_setdata;
+ dcl->dpy_mouse_set = vnc_mouse_set;
+ dcl->dpy_cursor_define = vnc_dpy_cursor_define;
register_displaychangelistener(ds, dcl);
- ds->mouse_set = vnc_mouse_set;
- ds->cursor_define = vnc_dpy_cursor_define;
}
-void vnc_display_close(DisplayState *ds)
+static void vnc_display_close(DisplayState *ds)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
if (!vs)
return;
if (vs->display) {
- qemu_free(vs->display);
+ g_free(vs->display);
vs->display = NULL;
}
if (vs->lsock != -1) {
#endif
}
-int vnc_display_password(DisplayState *ds, const char *password)
+static int vnc_display_disable_login(DisplayState *ds)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
}
if (vs->password) {
- qemu_free(vs->password);
+ g_free(vs->password);
+ }
+
+ vs->password = NULL;
+ if (vs->auth == VNC_AUTH_NONE) {
+ vs->auth = VNC_AUTH_VNC;
+ }
+
+ return 0;
+}
+
+int vnc_display_password(DisplayState *ds, const char *password)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ if (!vs) {
+ return -EINVAL;
+ }
+
+ if (!password) {
+ /* This is not the intention of this interface but err on the side
+ of being safe */
+ return vnc_display_disable_login(ds);
+ }
+
+ if (vs->password) {
+ g_free(vs->password);
vs->password = NULL;
}
- if (password && password[0]) {
- if (!(vs->password = qemu_strdup(password)))
- return -1;
- if (vs->auth == VNC_AUTH_NONE) {
- vs->auth = VNC_AUTH_VNC;
- }
- } else {
- vs->auth = VNC_AUTH_NONE;
+ vs->password = g_strdup(password);
+ if (vs->auth == VNC_AUTH_NONE) {
+ vs->auth = VNC_AUTH_VNC;
}
return 0;
}
+int vnc_display_pw_expire(DisplayState *ds, time_t expires)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ if (!vs) {
+ return -EINVAL;
+ }
+
+ vs->expires = expires;
+ return 0;
+}
+
char *vnc_display_local_addr(DisplayState *ds)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
return vnc_socket_local_addr("%s:%s", vs->lsock);
}
-int vnc_display_open(DisplayState *ds, const char *display)
+void vnc_display_open(DisplayState *ds, const char *display, Error **errp)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
const char *options;
int sasl = 0;
int saslErr;
#endif
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
int acl = 0;
+#endif
int lock_key_sync = 1;
- if (!vnc_display)
- return -1;
+ if (!vnc_display) {
+ error_setg(errp, "VNC display not active");
+ return;
+ }
vnc_display_close(ds);
if (strcmp(display, "none") == 0)
- return 0;
+ return;
- if (!(vs->display = strdup(display)))
- return -1;
+ vs->display = g_strdup(display);
+ vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
options = display;
while ((options = strchr(options, ','))) {
options++;
if (strncmp(options, "password", 8) == 0) {
+ if (fips_get_state()) {
+ error_setg(errp,
+ "VNC password auth disabled due to FIPS mode, "
+ "consider using the VeNCrypt or SASL authentication "
+ "methods as an alternative");
+ goto fail;
+ }
password = 1; /* Require password auth */
} else if (strncmp(options, "reverse", 7) == 0) {
reverse = 1;
- } else if (strncmp(options, "no-lock-key-sync", 9) == 0) {
+ } else if (strncmp(options, "no-lock-key-sync", 16) == 0) {
lock_key_sync = 0;
#ifdef CONFIG_VNC_SASL
} else if (strncmp(options, "sasl", 4) == 0) {
end = strchr(options, ',');
if (start && (!end || (start < end))) {
int len = end ? end-(start+1) : strlen(start+1);
- char *path = qemu_strndup(start + 1, len);
+ char *path = g_strndup(start + 1, len);
VNC_DEBUG("Trying certificate path '%s'\n", path);
if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
- fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
- qemu_free(path);
- qemu_free(vs->display);
- vs->display = NULL;
- return -1;
+ error_setg(errp, "Failed to find x509 certificates/keys in %s", path);
+ g_free(path);
+ goto fail;
}
- qemu_free(path);
+ g_free(path);
} else {
- fprintf(stderr, "No certificate path provided\n");
- qemu_free(vs->display);
- vs->display = NULL;
- return -1;
+ error_setg(errp, "No certificate path provided");
+ goto fail;
}
#endif
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
} else if (strncmp(options, "acl", 3) == 0) {
acl = 1;
+#endif
} else if (strncmp(options, "lossy", 5) == 0) {
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 {
+ error_setg(errp, "unknown vnc share= option");
+ goto fail;
+ }
}
}
#ifdef CONFIG_VNC_SASL
if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
- fprintf(stderr, "Failed to initialize SASL auth %s",
- sasl_errstring(saslErr, NULL, NULL));
- free(vs->display);
- vs->display = NULL;
- return -1;
+ error_setg(errp, "Failed to initialize SASL auth: %s",
+ sasl_errstring(saslErr, NULL, NULL));
+ goto fail;
}
#endif
vs->lock_key_sync = lock_key_sync;
if (reverse) {
/* connect to viewer */
- if (strncmp(display, "unix:", 5) == 0)
- vs->lsock = unix_connect(display+5);
- else
- vs->lsock = inet_connect(display, SOCK_STREAM);
- if (-1 == vs->lsock) {
- free(vs->display);
- vs->display = NULL;
- return -1;
+ int csock;
+ vs->lsock = -1;
+ if (strncmp(display, "unix:", 5) == 0) {
+ csock = unix_connect(display+5, errp);
} else {
- int csock = vs->lsock;
- vs->lsock = -1;
- vnc_connect(vs, csock);
+ csock = inet_connect(display, errp);
}
- return 0;
-
+ if (csock < 0) {
+ goto fail;
+ }
+ vnc_connect(vs, csock, 0);
} else {
/* listen for connects */
char *dpy;
- dpy = qemu_malloc(256);
+ dpy = g_malloc(256);
if (strncmp(display, "unix:", 5) == 0) {
pstrcpy(dpy, 256, "unix:");
- vs->lsock = unix_listen(display+5, dpy+5, 256-5);
+ vs->lsock = unix_listen(display+5, dpy+5, 256-5, errp);
} else {
- vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900);
+ vs->lsock = inet_listen(display, dpy, 256,
+ SOCK_STREAM, 5900, errp);
}
- if (-1 == vs->lsock) {
- free(dpy);
- return -1;
- } else {
- free(vs->display);
- vs->display = dpy;
+ if (vs->lsock < 0) {
+ g_free(dpy);
+ goto fail;
}
+ g_free(vs->display);
+ vs->display = dpy;
+ qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
}
- return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
+ return;
+
+fail:
+ g_free(vs->display);
+ vs->display = NULL;
+}
+
+void vnc_display_add_client(DisplayState *ds, int csock, int skipauth)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ vnc_connect(vs, csock, skipauth);
}