X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/348c32709fdbeb475dd072af49523cfdd75873f1..5e774eb3bd264c76484906f4bd0fb38e00b8090e:/ui/vnc.c diff --git a/ui/vnc.c b/ui/vnc.c index c9f2fed9c9..76a3273e0b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "vnc.h" #include "vnc-jobs.h" #include "trace.h" @@ -37,13 +38,13 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/types.h" #include "qmp-commands.h" -#include "qemu/osdep.h" #include "ui/input.h" #include "qapi-event.h" #include "crypto/hash.h" #include "crypto/tlscredsanon.h" #include "crypto/tlscredsx509.h" #include "qom/object_interfaces.h" +#include "qemu/cutils.h" #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 @@ -70,8 +71,8 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode) [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]); + fprintf(stderr, "%s/%p: %s -> %s\n", __func__, + vs->ioc, mn[vs->share_mode], mn[mode]); #endif switch (vs->share_mode) { @@ -105,108 +106,70 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode) } } -static char *addr_to_string(const char *format, - struct sockaddr_storage *sa, - socklen_t salen) { - char *addr; - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - int err; - size_t addrlen; - - if ((err = getnameinfo((struct sockaddr *)sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { - VNC_DEBUG("Cannot resolve address %d: %s\n", - err, gai_strerror(err)); - return NULL; - } - - /* Enough for the existing format + the 2 vars we're - * substituting in. */ - addrlen = strlen(format) + strlen(host) + strlen(serv); - addr = g_malloc(addrlen + 1); - snprintf(addr, addrlen, format, host, serv); - addr[addrlen] = '\0'; - - return addr; -} - - -char *vnc_socket_local_addr(const char *format, int fd) { - struct sockaddr_storage sa; - socklen_t salen; - - salen = sizeof(sa); - if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) - return NULL; - - return addr_to_string(format, &sa, salen); -} - -char *vnc_socket_remote_addr(const char *format, int fd) { - struct sockaddr_storage sa; - socklen_t salen; - - salen = sizeof(sa); - if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) - return NULL; - return addr_to_string(format, &sa, salen); -} - -static void vnc_init_basic_info(struct sockaddr_storage *sa, - socklen_t salen, +static void vnc_init_basic_info(SocketAddress *addr, VncBasicInfo *info, Error **errp) { - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - int err; + switch (addr->type) { + case SOCKET_ADDRESS_KIND_INET: + info->host = g_strdup(addr->u.inet.data->host); + info->service = g_strdup(addr->u.inet.data->port); + if (addr->u.inet.data->ipv6) { + info->family = NETWORK_ADDRESS_FAMILY_IPV6; + } else { + info->family = NETWORK_ADDRESS_FAMILY_IPV4; + } + break; - if ((err = getnameinfo((struct sockaddr *)sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { - error_setg(errp, "Cannot resolve address: %s", - gai_strerror(err)); - return; + case SOCKET_ADDRESS_KIND_UNIX: + info->host = g_strdup(""); + info->service = g_strdup(addr->u.q_unix.data->path); + info->family = NETWORK_ADDRESS_FAMILY_UNIX; + break; + + default: + error_setg(errp, "Unsupported socket kind %d", + addr->type); + break; } - info->host = g_strdup(host); - info->service = g_strdup(serv); - info->family = inet_netfamily(sa->ss_family); + return; } -static void vnc_init_basic_info_from_server_addr(int fd, VncBasicInfo *info, +static void vnc_init_basic_info_from_server_addr(QIOChannelSocket *ioc, + VncBasicInfo *info, Error **errp) { - struct sockaddr_storage sa; - socklen_t salen; + SocketAddress *addr = NULL; - salen = sizeof(sa); - if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) { - error_setg_errno(errp, errno, "getsockname failed"); + if (!ioc) { + error_setg(errp, "No listener socket available"); return; } - vnc_init_basic_info(&sa, salen, info, errp); + addr = qio_channel_socket_get_local_address(ioc, errp); + if (!addr) { + return; + } + + vnc_init_basic_info(addr, info, errp); + qapi_free_SocketAddress(addr); } -static void vnc_init_basic_info_from_remote_addr(int fd, VncBasicInfo *info, +static void vnc_init_basic_info_from_remote_addr(QIOChannelSocket *ioc, + VncBasicInfo *info, Error **errp) { - struct sockaddr_storage sa; - socklen_t salen; + SocketAddress *addr = NULL; - salen = sizeof(sa); - if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) { - error_setg_errno(errp, errno, "getpeername failed"); + addr = qio_channel_socket_get_remote_address(ioc, errp); + if (!addr) { return; } - vnc_init_basic_info(&sa, salen, info, errp); + vnc_init_basic_info(addr, info, errp); + qapi_free_SocketAddress(addr); } static const char *vnc_auth_name(VncDisplay *vd) { @@ -261,7 +224,7 @@ static VncServerInfo *vnc_server_info_get(VncDisplay *vd) VncServerInfo *info; Error *err = NULL; - info = g_malloc(sizeof(*info)); + info = g_malloc0(sizeof(*info)); vnc_init_basic_info_from_server_addr(vd->lsock, qapi_VncServerInfo_base(info), &err); info->has_auth = true; @@ -300,7 +263,7 @@ static void vnc_client_cache_addr(VncState *client) Error *err = NULL; client->info = g_malloc0(sizeof(*client->info)); - vnc_init_basic_info_from_remote_addr(client->csock, + vnc_init_basic_info_from_remote_addr(client->sioc, qapi_VncClientInfo_base(client->info), &err); if (err) { @@ -343,27 +306,20 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event) static VncClientInfo *qmp_query_vnc_client(const VncState *client) { - struct sockaddr_storage sa; - socklen_t salen = sizeof(sa); - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; VncClientInfo *info; + Error *err = NULL; - if (getpeername(client->csock, (struct sockaddr *)&sa, &salen) < 0) { - return NULL; - } + info = g_malloc0(sizeof(*info)); - if (getnameinfo((struct sockaddr *)&sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV) < 0) { + vnc_init_basic_info_from_remote_addr(client->sioc, + qapi_VncClientInfo_base(info), + &err); + if (err) { + error_free(err); + qapi_free_VncClientInfo(info); return NULL; } - info = g_malloc0(sizeof(*info)); - info->host = g_strdup(host); - info->service = g_strdup(serv); - info->family = inet_netfamily(sa.ss_family); info->websocket = client->websocket; if (client->tls) { @@ -413,81 +369,89 @@ VncInfo *qmp_query_vnc(Error **errp) { VncInfo *info = g_malloc0(sizeof(*info)); VncDisplay *vd = vnc_display_find(NULL); + SocketAddress *addr = NULL; if (vd == NULL || !vd->enabled) { info->enabled = false; } else { - struct sockaddr_storage sa; - socklen_t salen = sizeof(sa); - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - info->enabled = true; /* for compatibility with the original command */ info->has_clients = true; info->clients = qmp_query_client_list(vd); - if (vd->lsock == -1) { + if (vd->lsock == NULL) { return info; } - if (getsockname(vd->lsock, (struct sockaddr *)&sa, - &salen) == -1) { - error_setg(errp, QERR_UNDEFINED_ERROR); + addr = qio_channel_socket_get_local_address(vd->lsock, errp); + if (!addr) { goto out_error; } - if (getnameinfo((struct sockaddr *)&sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV) < 0) { - error_setg(errp, QERR_UNDEFINED_ERROR); + switch (addr->type) { + case SOCKET_ADDRESS_KIND_INET: + info->host = g_strdup(addr->u.inet.data->host); + info->service = g_strdup(addr->u.inet.data->port); + if (addr->u.inet.data->ipv6) { + info->family = NETWORK_ADDRESS_FAMILY_IPV6; + } else { + info->family = NETWORK_ADDRESS_FAMILY_IPV4; + } + break; + + case SOCKET_ADDRESS_KIND_UNIX: + info->host = g_strdup(""); + info->service = g_strdup(addr->u.q_unix.data->path); + info->family = NETWORK_ADDRESS_FAMILY_UNIX; + break; + + default: + error_setg(errp, "Unsupported socket kind %d", + addr->type); 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 = inet_netfamily(sa.ss_family); info->has_auth = true; info->auth = g_strdup(vnc_auth_name(vd)); } + qapi_free_SocketAddress(addr); return info; out_error: + qapi_free_SocketAddress(addr); qapi_free_VncInfo(info); return NULL; } -static VncBasicInfoList *qmp_query_server_entry(int socket, +static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc, bool websocket, VncBasicInfoList *prev) { VncBasicInfoList *list; VncBasicInfo *info; - struct sockaddr_storage sa; - socklen_t salen = sizeof(sa); - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - - if (getsockname(socket, (struct sockaddr *)&sa, &salen) < 0 || - getnameinfo((struct sockaddr *)&sa, salen, - host, sizeof(host), serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV) < 0) { + Error *err = NULL; + SocketAddress *addr; + + addr = qio_channel_socket_get_local_address(ioc, &err); + if (!addr) { + error_free(err); return prev; } info = g_new0(VncBasicInfo, 1); - info->host = g_strdup(host); - info->service = g_strdup(serv); - info->family = inet_netfamily(sa.ss_family); + vnc_init_basic_info(addr, info, &err); + qapi_free_SocketAddress(addr); + if (err) { + qapi_free_VncBasicInfo(info); + error_free(err); + return prev; + } info->websocket = websocket; list = g_new0(VncBasicInfoList, 1); @@ -581,13 +545,13 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) info->has_display = true; info->display = g_strdup(dev->id); } - if (vd->lsock != -1) { - info->server = qmp_query_server_entry(vd->lsock, false, - info->server); + if (vd->lsock != NULL) { + info->server = qmp_query_server_entry( + vd->lsock, false, info->server); } - if (vd->lwebsock != -1) { - info->server = qmp_query_server_entry(vd->lwebsock, true, - info->server); + if (vd->lwebsock != NULL) { + info->server = qmp_query_server_entry( + vd->lwebsock, true, info->server); } item = g_new0(VncInfo2List, 1); @@ -673,7 +637,7 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, static void vnc_desktop_resize(VncState *vs) { - if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { + if (vs->ioc == NULL || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { return; } if (vs->client_width == pixman_image_get_width(vs->vd->server) && @@ -728,6 +692,8 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y) static void vnc_update_server_surface(VncDisplay *vd) { + int width, height; + qemu_pixman_image_unref(vd->server); vd->server = NULL; @@ -735,10 +701,15 @@ static void vnc_update_server_surface(VncDisplay *vd) return; } + width = vnc_width(vd); + height = vnc_height(vd); vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT, - vnc_width(vd), - vnc_height(vd), + width, height, NULL, 0); + + memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty)); + vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, + width, height); } static void vnc_dpy_switch(DisplayChangeListener *dcl, @@ -746,7 +717,6 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, { VncDisplay *vd = container_of(dcl, VncDisplay, dcl); VncState *vs; - int width, height; vnc_abort_display_jobs(vd); vd->ds = surface; @@ -758,11 +728,6 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, qemu_pixman_image_unref(vd->guest.fb); vd->guest.fb = pixman_image_ref(surface->image); vd->guest.format = surface->format; - width = vnc_width(vd); - height = vnc_height(vd); - memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty)); - vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, - width, height); QTAILQ_FOREACH(vs, &vd->clients, next) { vnc_colordepth(vs); @@ -772,7 +737,8 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, } memset(vs->dirty, 0x00, sizeof(vs->dirty)); vnc_set_area_dirty(vs->dirty, vd, 0, 0, - width, height); + vnc_width(vd), + vnc_height(vd)); } } @@ -931,6 +897,11 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl, int i, x, y, pitch, inc, w_lim, s; int cmp_bytes; + if (!vd->server) { + /* no client connected */ + return; + } + vnc_refresh_server_surface(vd); QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) { if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) { @@ -940,6 +911,10 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl, } } + if (!vd->server) { + /* no client connected */ + return; + } /* do bitblit op on the local surface too */ pitch = vnc_server_fb_stride(vd); src_row = vnc_server_fb_ptr(vd, src_x, src_y); @@ -1060,8 +1035,13 @@ static int find_and_clear_dirty_height(VncState *vs, static int vnc_update_client(VncState *vs, int has_dirty, bool sync) { + if (vs->disconnecting) { + vnc_disconnect_finish(vs); + return 0; + } + vs->has_dirty += has_dirty; - if (vs->need_update && vs->csock != -1) { + if (vs->need_update && !vs->disconnecting) { VncDisplay *vd = vs->vd; VncJob *job; int y; @@ -1125,7 +1105,7 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync) return n; } - if (vs->csock == -1) { + if (vs->disconnecting) { vnc_disconnect_finish(vs); } else if (sync) { vnc_jobs_join(vs); @@ -1207,12 +1187,15 @@ static void audio_del(VncState *vs) static void vnc_disconnect_start(VncState *vs) { - if (vs->csock == -1) + if (vs->disconnecting) { return; + } vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED); - qemu_set_fd_handler(vs->csock, NULL, NULL, NULL); - closesocket(vs->csock); - vs->csock = -1; + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + qio_channel_close(vs->ioc, NULL); + vs->disconnecting = TRUE; } void vnc_disconnect_finish(VncState *vs) @@ -1226,8 +1209,6 @@ void vnc_disconnect_finish(VncState *vs) buffer_free(&vs->input); buffer_free(&vs->output); - buffer_free(&vs->ws_input); - buffer_free(&vs->ws_output); qapi_free_VncClientInfo(vs->info); @@ -1235,7 +1216,6 @@ void vnc_disconnect_finish(VncState *vs) vnc_tight_clear(vs); vnc_zrle_clear(vs); - qcrypto_tls_session_free(vs->tls); #ifdef CONFIG_VNC_SASL vnc_sasl_client_cleanup(vs); #endif /* CONFIG_VNC_SASL */ @@ -1265,29 +1245,29 @@ void vnc_disconnect_finish(VncState *vs) g_free(vs->lossy_rect[i]); } g_free(vs->lossy_rect); + + object_unref(OBJECT(vs->ioc)); + vs->ioc = NULL; + object_unref(OBJECT(vs->sioc)); + vs->sioc = NULL; g_free(vs); } -ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno) +ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp) { - if (ret == 0 || ret == -1) { - if (ret == -1) { - switch (last_errno) { - case EINTR: - case EAGAIN: -#ifdef _WIN32 - case WSAEWOULDBLOCK: -#endif - return 0; - default: - break; - } + if (ret <= 0) { + if (ret == 0) { + VNC_DEBUG("Closing down client sock: EOF\n"); + } else if (ret != QIO_CHANNEL_ERR_BLOCK) { + VNC_DEBUG("Closing down client sock: ret %d (%s)\n", + ret, errp ? error_get_pretty(*errp) : "Unknown"); } - VNC_DEBUG("Closing down client sock: ret %zd, errno %d\n", - ret, ret < 0 ? last_errno : 0); vnc_disconnect_start(vs); - + if (errp) { + error_free(*errp); + *errp = NULL; + } return 0; } return ret; @@ -1301,40 +1281,6 @@ void vnc_client_error(VncState *vs) } -ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque) -{ - VncState *vs = opaque; - ssize_t ret; - - retry: - ret = qemu_recv(vs->csock, buf, len, 0); - if (ret < 0) { - if (errno == EINTR) { - goto retry; - } - return -1; - } - return ret; -} - - -ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque) -{ - VncState *vs = opaque; - ssize_t ret; - - retry: - ret = send(vs->csock, buf, len, 0); - if (ret < 0) { - if (errno == EINTR) { - goto retry; - } - return -1; - } - return ret; -} - - /* * Called to write a chunk of data to the client socket. The data may * be the raw data, or may have already been encoded by SASL. @@ -1352,21 +1298,12 @@ ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque) */ ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) { + Error *err = NULL; ssize_t ret; - int err = 0; - if (vs->tls) { - ret = qcrypto_tls_session_write(vs->tls, (const char *)data, datalen); - if (ret < 0) { - err = errno; - } - } else { - ret = send(vs->csock, (const void *)data, datalen, 0); - if (ret < 0) { - err = socket_error(); - } - } + ret = qio_channel_write( + vs->ioc, (const char *)data, datalen, &err); VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, err); + return vnc_client_io_error(vs, ret, &err); } @@ -1404,7 +1341,11 @@ static ssize_t vnc_client_write_plain(VncState *vs) buffer_advance(&vs->output, ret); if (vs->output.offset == 0) { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } return ret; @@ -1416,10 +1357,8 @@ static ssize_t vnc_client_write_plain(VncState *vs) * the client socket. Will delegate actual work according to whether * SASL SSF layers are enabled (thus requiring encryption calls) */ -static void vnc_client_write_locked(void *opaque) +static void vnc_client_write_locked(VncState *vs) { - VncState *vs = opaque; - #ifdef CONFIG_VNC_SASL if (vs->sasl.conn && vs->sasl.runSSF && @@ -1428,23 +1367,22 @@ static void vnc_client_write_locked(void *opaque) } else #endif /* CONFIG_VNC_SASL */ { - if (vs->encode_ws) { - vnc_client_write_ws(vs); - } else { - vnc_client_write_plain(vs); - } + vnc_client_write_plain(vs); } } -void vnc_client_write(void *opaque) +static void vnc_client_write(VncState *vs) { - VncState *vs = opaque; vnc_lock_output(vs); - if (vs->output.offset || vs->ws_output.offset) { - vnc_client_write_locked(opaque); - } else if (vs->csock != -1) { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); + if (vs->output.offset) { + vnc_client_write_locked(vs); + } else if (vs->ioc != NULL) { + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } vnc_unlock_output(vs); } @@ -1474,20 +1412,11 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) { ssize_t ret; - int err = -1; - if (vs->tls) { - ret = qcrypto_tls_session_read(vs->tls, (char *)data, datalen); - if (ret < 0) { - err = errno; - } - } else { - ret = qemu_recv(vs->csock, data, datalen, 0); - if (ret < 0) { - err = socket_error(); - } - } + Error *err = NULL; + ret = qio_channel_read( + vs->ioc, (char *)data, datalen, &err); VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, err); + return vnc_client_io_error(vs, ret, &err); } @@ -1523,10 +1452,10 @@ static void vnc_jobs_bh(void *opaque) * First function called whenever there is more data to be read from * the client socket. Will delegate actual work according to whether * SASL SSF layers are enabled (thus requiring decryption calls) + * Returns 0 on success, -1 if client disconnected */ -void vnc_client_read(void *opaque) +static int vnc_client_read(VncState *vs) { - VncState *vs = opaque; ssize_t ret; #ifdef CONFIG_VNC_SASL @@ -1534,22 +1463,13 @@ void vnc_client_read(void *opaque) ret = vnc_client_read_sasl(vs); else #endif /* CONFIG_VNC_SASL */ - if (vs->encode_ws) { - ret = vnc_client_read_ws(vs); - if (ret == -1) { - vnc_disconnect_start(vs); - return; - } else if (ret == -2) { - vnc_client_error(vs); - return; - } - } else { - ret = vnc_client_read_plain(vs); - } + ret = vnc_client_read_plain(vs); if (!ret) { - if (vs->csock == -1) + if (vs->disconnecting) { vnc_disconnect_finish(vs); - return; + return -1; + } + return 0; } while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { @@ -1557,9 +1477,9 @@ void vnc_client_read(void *opaque) int ret; ret = vs->read_handler(vs, vs->input.buffer, len); - if (vs->csock == -1) { + if (vs->disconnecting) { vnc_disconnect_finish(vs); - return; + return -1; } if (!ret) { @@ -1568,14 +1488,35 @@ void vnc_client_read(void *opaque) vs->read_handler_expect = ret; } } + return 0; } +gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, + GIOCondition condition, void *opaque) +{ + VncState *vs = opaque; + if (condition & G_IO_IN) { + if (vnc_client_read(vs) < 0) { + return TRUE; + } + } + if (condition & G_IO_OUT) { + vnc_client_write(vs); + } + return TRUE; +} + + void vnc_write(VncState *vs, const void *data, size_t len) { buffer_reserve(&vs->output, len); - if (vs->csock != -1 && buffer_empty(&vs->output)) { - qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); + if (vs->ioc != NULL && buffer_empty(&vs->output)) { + if (vs->ioc_tag) { + g_source_remove(vs->ioc_tag); + } + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL); } buffer_append(&vs->output, data, len); @@ -1616,8 +1557,7 @@ void vnc_write_u8(VncState *vs, uint8_t value) void vnc_flush(VncState *vs) { vnc_lock_output(vs); - if (vs->csock != -1 && (vs->output.offset || - vs->ws_output.offset)) { + if (vs->ioc != NULL && vs->output.offset) { vnc_client_write_locked(vs); } vnc_unlock_output(vs); @@ -1671,7 +1611,7 @@ static void check_pointer_type_change(Notifier *notifier, void *data) static void pointer_event(VncState *vs, int button_mask, int x, int y) { - static uint32_t bmap[INPUT_BUTTON_MAX] = { + static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = 0x01, [INPUT_BUTTON_MIDDLE] = 0x02, [INPUT_BUTTON_RIGHT] = 0x04, @@ -1710,6 +1650,7 @@ static void reset_keys(VncState *vs) for(i = 0; i < 256; i++) { if (vs->modifiers_state[i]) { qemu_input_event_send_key_number(vs->vd->dcl.con, i, false); + qemu_input_event_send_key_delay(vs->vd->key_delay_ms); vs->modifiers_state[i] = 0; } } @@ -1719,9 +1660,9 @@ static void press_key(VncState *vs, int keysym) { int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK; qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true); - qemu_input_event_send_key_delay(0); + qemu_input_event_send_key_delay(vs->vd->key_delay_ms); qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); - qemu_input_event_send_key_delay(0); + qemu_input_event_send_key_delay(vs->vd->key_delay_ms); } static int current_led_state(VncState *vs) @@ -1873,6 +1814,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) if (qemu_console_is_graphic(NULL)) { qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down); + qemu_input_event_send_key_delay(vs->vd->key_delay_ms); } else { bool numlock = vs->modifiers_state[0x45]; bool control = (vs->modifiers_state[0x1d] || @@ -1994,6 +1936,7 @@ static void vnc_release_modifiers(VncState *vs) continue; } qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); + qemu_input_event_send_key_delay(vs->vd->key_delay_ms); } } @@ -2128,6 +2071,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) break; case VNC_ENCODING_RICH_CURSOR: vs->features |= VNC_FEATURE_RICH_CURSOR_MASK; + if (vs->vd->cursor) { + vnc_cursor_define(vs); + } break; case VNC_ENCODING_EXT_KEY_EVENT: send_ext_key_event_ack(vs); @@ -2172,15 +2118,38 @@ static void set_pixel_conversion(VncState *vs) } } -static void set_pixel_format(VncState *vs, - int bits_per_pixel, int depth, +static void send_color_map(VncState *vs) +{ + int i; + + vnc_write_u8(vs, VNC_MSG_SERVER_SET_COLOUR_MAP_ENTRIES); + vnc_write_u8(vs, 0); /* padding */ + vnc_write_u16(vs, 0); /* first color */ + vnc_write_u16(vs, 256); /* # of colors */ + + for (i = 0; i < 256; i++) { + PixelFormat *pf = &vs->client_pf; + + vnc_write_u16(vs, (((i >> pf->rshift) & pf->rmax) << (16 - pf->rbits))); + vnc_write_u16(vs, (((i >> pf->gshift) & pf->gmax) << (16 - pf->gbits))); + vnc_write_u16(vs, (((i >> pf->bshift) & pf->bmax) << (16 - pf->bbits))); + } +} + +static void set_pixel_format(VncState *vs, int bits_per_pixel, int big_endian_flag, int true_color_flag, int red_max, int green_max, int blue_max, int red_shift, int green_shift, int blue_shift) { if (!true_color_flag) { - vnc_client_error(vs); - return; + /* Expose a reasonable default 256 color map */ + bits_per_pixel = 8; + red_max = 7; + green_max = 7; + blue_max = 3; + red_shift = 0; + green_shift = 3; + blue_shift = 6; } switch (bits_per_pixel) { @@ -2193,15 +2162,15 @@ static void set_pixel_format(VncState *vs, return; } - vs->client_pf.rmax = red_max; + vs->client_pf.rmax = red_max ? red_max : 0xFF; 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.gmax = green_max ? green_max : 0xFF; 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.bmax = blue_max ? blue_max : 0xFF; vs->client_pf.bbits = hweight_long(blue_max); vs->client_pf.bshift = blue_shift; vs->client_pf.bmask = blue_max << blue_shift; @@ -2210,6 +2179,10 @@ static void set_pixel_format(VncState *vs, vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; vs->client_be = big_endian_flag; + if (!true_color_flag) { + send_color_map(vs); + } + set_pixel_conversion(vs); graphic_hw_invalidate(vs->vd->dcl.con); @@ -2277,7 +2250,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 20; - set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5), + set_pixel_format(vs, read_u8(data, 4), read_u8(data, 6), read_u8(data, 7), read_u16(data, 8), read_u16(data, 10), read_u16(data, 12), read_u8(data, 14), @@ -3001,34 +2974,35 @@ static void vnc_refresh(DisplayChangeListener *dcl) } } -static void vnc_connect(VncDisplay *vd, int csock, +static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, bool skipauth, bool websocket) { VncState *vs = g_new0(VncState, 1); int i; - vs->csock = csock; + vs->sioc = sioc; + object_ref(OBJECT(vs->sioc)); + vs->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(vs->ioc)); vs->vd = vd; - buffer_init(&vs->input, "vnc-input/%d", csock); - buffer_init(&vs->output, "vnc-output/%d", csock); - buffer_init(&vs->ws_input, "vnc-ws_input/%d", csock); - buffer_init(&vs->ws_output, "vnc-ws_output/%d", csock); - buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%d", csock); + buffer_init(&vs->input, "vnc-input/%p", sioc); + buffer_init(&vs->output, "vnc-output/%p", sioc); + buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc); - buffer_init(&vs->tight.tight, "vnc-tight/%d", csock); - buffer_init(&vs->tight.zlib, "vnc-tight-zlib/%d", csock); - buffer_init(&vs->tight.gradient, "vnc-tight-gradient/%d", csock); + buffer_init(&vs->tight.tight, "vnc-tight/%p", sioc); + buffer_init(&vs->tight.zlib, "vnc-tight-zlib/%p", sioc); + buffer_init(&vs->tight.gradient, "vnc-tight-gradient/%p", sioc); #ifdef CONFIG_VNC_JPEG - buffer_init(&vs->tight.jpeg, "vnc-tight-jpeg/%d", csock); + buffer_init(&vs->tight.jpeg, "vnc-tight-jpeg/%p", sioc); #endif #ifdef CONFIG_VNC_PNG - buffer_init(&vs->tight.png, "vnc-tight-png/%d", csock); + buffer_init(&vs->tight.png, "vnc-tight-png/%p", sioc); #endif - buffer_init(&vs->zlib.zlib, "vnc-zlib/%d", csock); - buffer_init(&vs->zrle.zrle, "vnc-zrle/%d", csock); - buffer_init(&vs->zrle.fb, "vnc-zrle-fb/%d", csock); - buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%d", csock); + buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc); + buffer_init(&vs->zrle.zrle, "vnc-zrle/%p", sioc); + buffer_init(&vs->zrle.fb, "vnc-zrle-fb/%p", sioc); + buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%p", sioc); if (skipauth) { vs->auth = VNC_AUTH_NONE; @@ -3042,27 +3016,29 @@ static void vnc_connect(VncDisplay *vd, int csock, vs->subauth = vd->subauth; } } - VNC_DEBUG("Client sock=%d ws=%d auth=%d subauth=%d\n", - csock, websocket, vs->auth, vs->subauth); + VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n", + sioc, websocket, vs->auth, vs->subauth); 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_new0(uint8_t, VNC_STAT_COLS); } - VNC_DEBUG("New client on socket %d\n", csock); + VNC_DEBUG("New client on socket %p\n", vs->sioc); update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); - qemu_set_nonblock(vs->csock); + qio_channel_set_blocking(vs->ioc, false, NULL); if (websocket) { vs->websocket = 1; if (vd->ws_tls) { - qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs); + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL); } else { - qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL); } - } else - { - qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); + } else { + vs->ioc_tag = qio_channel_add_watch( + vs->ioc, G_IO_IN, vnc_client_io, vs, NULL); } vnc_client_cache_addr(vs); @@ -3120,35 +3096,28 @@ void vnc_init_state(VncState *vs) /* vs might be free()ed here */ } -static void vnc_listen_read(void *opaque, bool websocket) +static gboolean vnc_listen_io(QIOChannel *ioc, + GIOCondition condition, + void *opaque) { VncDisplay *vs = opaque; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - int csock; + QIOChannelSocket *sioc = NULL; + Error *err = NULL; /* Catch-up */ graphic_hw_update(vs->dcl.con); - if (websocket) { - csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); + sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err); + if (sioc != NULL) { + qio_channel_set_delay(QIO_CHANNEL(sioc), false); + vnc_connect(vs, sioc, false, + ioc != QIO_CHANNEL(vs->lsock)); + object_unref(OBJECT(sioc)); } else { - csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); - } - - if (csock != -1) { - socket_set_nodelay(csock); - vnc_connect(vs, csock, false, websocket); + /* client probably closed connection before we got there */ + error_free(err); } -} -static void vnc_listen_regular_read(void *opaque) -{ - vnc_listen_read(opaque, false); -} - -static void vnc_listen_websocket_read(void *opaque) -{ - vnc_listen_read(opaque, true); + return TRUE; } static const DisplayChangeListenerOps dcl_ops = { @@ -3174,9 +3143,6 @@ void vnc_display_init(const char *id) vs->id = strdup(id); QTAILQ_INSERT_TAIL(&vnc_displays, vs, next); - vs->lsock = -1; - vs->lwebsock = -1; - QTAILQ_INIT(&vs->clients); vs->expires = TIME_MAX; @@ -3190,6 +3156,9 @@ void vnc_display_init(const char *id) if (!vs->kbd_layout) exit(1); + vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; + vs->connections_limit = 32; + qemu_mutex_init(&vs->mutex); vnc_start_worker_thread(); @@ -3204,21 +3173,26 @@ static void vnc_display_close(VncDisplay *vs) return; vs->enabled = false; vs->is_unix = false; - if (vs->lsock != -1) { - qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL); - close(vs->lsock); - vs->lsock = -1; + if (vs->lsock != NULL) { + if (vs->lsock_tag) { + g_source_remove(vs->lsock_tag); + } + object_unref(OBJECT(vs->lsock)); + vs->lsock = NULL; } vs->ws_enabled = false; - if (vs->lwebsock != -1) { - qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL); - close(vs->lwebsock); - vs->lwebsock = -1; + if (vs->lwebsock != NULL) { + if (vs->lwebsock_tag) { + g_source_remove(vs->lwebsock_tag); + } + object_unref(OBJECT(vs->lwebsock)); + vs->lwebsock = NULL; } vs->auth = VNC_AUTH_INVALID; vs->subauth = VNC_AUTH_INVALID; if (vs->tlscreds) { object_unparent(OBJECT(vs->tlscreds)); + vs->tlscreds = NULL; } g_free(vs->tlsaclname); vs->tlsaclname = NULL; @@ -3233,7 +3207,7 @@ int vnc_display_password(const char *id, const char *password) } if (vs->auth == VNC_AUTH_NONE) { error_printf_unless_qmp("If you want use passwords please enable " - "password auth using '-vnc ${dpy},password'."); + "password auth using '-vnc ${dpy},password'.\n"); return -EINVAL; } @@ -3255,12 +3229,24 @@ int vnc_display_pw_expire(const char *id, time_t expires) return 0; } -char *vnc_display_local_addr(const char *id) +static void vnc_display_print_local_addr(VncDisplay *vs) { - VncDisplay *vs = vnc_display_find(id); + SocketAddress *addr; + Error *err = NULL; - assert(vs); - return vnc_socket_local_addr("%s:%s", vs->lsock); + addr = qio_channel_socket_get_local_address(vs->lsock, &err); + if (!addr) { + return; + } + + if (addr->type != SOCKET_ADDRESS_KIND_INET) { + qapi_free_SocketAddress(addr); + return; + } + error_printf_unless_qmp("VNC server running on %s:%s\n", + addr->u.inet.data->host, + addr->u.inet.data->port); + qapi_free_SocketAddress(addr); } static QemuOptsList qemu_vnc_opts = { @@ -3311,6 +3297,9 @@ static QemuOptsList qemu_vnc_opts = { },{ .name = "lock-key-sync", .type = QEMU_OPT_BOOL, + },{ + .name = "key-delay-ms", + .type = QEMU_OPT_NUMBER, },{ .name = "sasl", .type = QEMU_OPT_BOOL, @@ -3543,12 +3532,14 @@ void vnc_display_open(const char *id, Error **errp) const char *vnc; char *h; const char *credid; + int show_vnc_port = 0; bool sasl = false; #ifdef CONFIG_VNC_SASL int saslErr; #endif int acl = 0; int lock_key_sync = 1; + int key_delay_ms; if (!vs) { error_setg(errp, "VNC display not active"); @@ -3570,8 +3561,10 @@ void vnc_display_open(const char *id, Error **errp) const char *websocket = qemu_opt_get(opts, "websocket"); int to = qemu_opt_get_number(opts, "to", 0); - bool has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false); - bool has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false); + bool has_ipv4 = qemu_opt_get(opts, "ipv4"); + bool has_ipv6 = qemu_opt_get(opts, "ipv6"); + bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false); + bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false); saddr = g_new0(SocketAddress, 1); if (websocket) { @@ -3587,8 +3580,8 @@ void vnc_display_open(const char *id, Error **errp) if (strncmp(vnc, "unix:", 5) == 0) { saddr->type = SOCKET_ADDRESS_KIND_UNIX; - saddr->u.q_unix = g_new0(UnixSocketAddress, 1); - saddr->u.q_unix->path = g_strdup(vnc + 5); + saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1); + saddr->u.q_unix.data->path = g_strdup(vnc + 5); if (vs->ws_enabled) { error_setg(errp, "UNIX sockets not supported with websock"); @@ -3596,12 +3589,13 @@ void vnc_display_open(const char *id, Error **errp) } } else { unsigned long long baseport; + InetSocketAddress *inet; saddr->type = SOCKET_ADDRESS_KIND_INET; - saddr->u.inet = g_new0(InetSocketAddress, 1); + inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1); if (vnc[0] == '[' && vnc[hlen - 1] == ']') { - saddr->u.inet->host = g_strndup(vnc + 1, hlen - 2); + inet->host = g_strndup(vnc + 1, hlen - 2); } else { - saddr->u.inet->host = g_strndup(vnc, hlen); + inet->host = g_strndup(vnc, hlen); } if (parse_uint_full(h + 1, &baseport, 10) < 0) { error_setg(errp, "can't convert to a number: %s", h + 1); @@ -3612,28 +3606,33 @@ void vnc_display_open(const char *id, Error **errp) error_setg(errp, "port %s out of range", h + 1); goto fail; } - saddr->u.inet->port = g_strdup_printf( + inet->port = g_strdup_printf( "%d", (int)baseport + 5900); if (to) { - saddr->u.inet->has_to = true; - saddr->u.inet->to = to + 5900; + inet->has_to = true; + inet->to = to + 5900; + show_vnc_port = 1; } - saddr->u.inet->ipv4 = saddr->u.inet->has_ipv4 = has_ipv4; - saddr->u.inet->ipv6 = saddr->u.inet->has_ipv6 = has_ipv6; + inet->ipv4 = ipv4; + inet->has_ipv4 = has_ipv4; + inet->ipv6 = ipv6; + inet->has_ipv6 = has_ipv6; if (vs->ws_enabled) { wsaddr->type = SOCKET_ADDRESS_KIND_INET; - wsaddr->u.inet = g_new0(InetSocketAddress, 1); - wsaddr->u.inet->host = g_strdup(saddr->u.inet->host); - wsaddr->u.inet->port = g_strdup(websocket); + inet = wsaddr->u.inet.data = g_new0(InetSocketAddress, 1); + inet->host = g_strdup(saddr->u.inet.data->host); + inet->port = g_strdup(websocket); if (to) { - wsaddr->u.inet->has_to = true; - wsaddr->u.inet->to = to; + inet->has_to = true; + inet->to = to; } - wsaddr->u.inet->ipv4 = wsaddr->u.inet->has_ipv4 = has_ipv4; - wsaddr->u.inet->ipv6 = wsaddr->u.inet->has_ipv6 = has_ipv6; + inet->ipv4 = ipv4; + inet->has_ipv4 = has_ipv4; + inet->ipv6 = ipv6; + inet->has_ipv6 = has_ipv6; } } } else { @@ -3660,6 +3659,7 @@ void vnc_display_open(const char *id, Error **errp) reverse = qemu_opt_get_bool(opts, "reverse", false); lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true); + key_delay_ms = qemu_opt_get_number(opts, "key-delay-ms", 1); sasl = qemu_opt_get_bool(opts, "sasl", false); #ifndef CONFIG_VNC_SASL if (sasl) { @@ -3674,7 +3674,7 @@ void vnc_display_open(const char *id, Error **errp) qemu_opt_get(opts, "x509") || qemu_opt_get(opts, "x509verify")) { error_setg(errp, - "'credid' parameter is mutually exclusive with " + "'tls-creds' parameter is mutually exclusive with " "'tls', 'x509' and 'x509verify' parameters"); goto fail; } @@ -3764,7 +3764,7 @@ void vnc_display_open(const char *id, Error **errp) vs->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vs->id); } qemu_acl_init(vs->tlsaclname); - } + } #ifdef CONFIG_VNC_SASL if (acl && sasl) { char *aclname; @@ -3791,22 +3791,16 @@ void vnc_display_open(const char *id, Error **errp) } #endif vs->lock_key_sync = lock_key_sync; + vs->key_delay_ms = key_delay_ms; device_id = qemu_opt_get(opts, "display"); if (device_id) { - DeviceState *dev; int head = qemu_opt_get_number(opts, "head", 0); + Error *err = NULL; - dev = qdev_find_recursive(sysbus_get_default(), device_id); - if (dev == NULL) { - error_setg(errp, "Device '%s' not found", device_id); - goto fail; - } - - con = qemu_console_lookup_by_device(dev, head); - if (con == NULL) { - error_setg(errp, "Device %s is not bound to a QemuConsole", - device_id); + con = qemu_console_lookup_by_device_name(device_id, head, &err); + if (err) { + error_propagate(errp, err); goto fail; } } else { @@ -3821,44 +3815,52 @@ void vnc_display_open(const char *id, Error **errp) if (reverse) { /* connect to viewer */ - int csock; - vs->lsock = -1; - vs->lwebsock = -1; + QIOChannelSocket *sioc = NULL; + vs->lsock = NULL; + vs->lwebsock = NULL; if (vs->ws_enabled) { error_setg(errp, "Cannot use websockets in reverse mode"); goto fail; } - csock = socket_connect(saddr, errp, NULL, NULL); - if (csock < 0) { + vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; + sioc = qio_channel_socket_new(); + if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) { goto fail; } - vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; - vnc_connect(vs, csock, false, false); + vnc_connect(vs, sioc, false, false); + object_unref(OBJECT(sioc)); } else { - /* listen for connects */ - vs->lsock = socket_listen(saddr, errp); - if (vs->lsock < 0) { + vs->lsock = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(vs->lsock, saddr, errp) < 0) { goto fail; } vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX; + vs->enabled = true; + if (vs->ws_enabled) { - vs->lwebsock = socket_listen(wsaddr, errp); - if (vs->lwebsock < 0) { - if (vs->lsock != -1) { - close(vs->lsock); - vs->lsock = -1; - } + vs->lwebsock = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(vs->lwebsock, + wsaddr, errp) < 0) { + object_unref(OBJECT(vs->lsock)); + vs->lsock = NULL; goto fail; } } - vs->enabled = true; - qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs); + + vs->lsock_tag = qio_channel_add_watch( + QIO_CHANNEL(vs->lsock), + G_IO_IN, vnc_listen_io, vs, NULL); if (vs->ws_enabled) { - qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read, - NULL, vs); + vs->lwebsock_tag = qio_channel_add_watch( + QIO_CHANNEL(vs->lwebsock), + G_IO_IN, vnc_listen_io, vs, NULL); } } + if (show_vnc_port) { + vnc_display_print_local_addr(vs); + } + qapi_free_SocketAddress(saddr); qapi_free_SocketAddress(wsaddr); return; @@ -3873,11 +3875,17 @@ fail: void vnc_display_add_client(const char *id, int csock, bool skipauth) { VncDisplay *vs = vnc_display_find(id); + QIOChannelSocket *sioc; if (!vs) { return; } - vnc_connect(vs, csock, skipauth, false); + + sioc = qio_channel_socket_new_fd(csock, NULL); + if (sioc) { + vnc_connect(vs, sioc, skipauth, false); + object_unref(OBJECT(sioc)); + } } static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) @@ -3920,9 +3928,7 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) vnc_display_init(id); vnc_display_open(id, &local_err); if (local_err != NULL) { - error_report("Failed to start VNC server: %s", - error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "Failed to start VNC server: "); exit(1); } return 0; @@ -3932,4 +3938,4 @@ static void vnc_register_config(void) { qemu_add_opts(&qemu_vnc_opts); } -machine_init(vnc_register_config); +opts_init(vnc_register_config);