vnc: add support for multiple listening sockets.
vnc: misc fixes and cleanups. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJYnJyOAAoJEEy22O7T6HE4JZAQAKLalr4Xxm677CPCLG3juTDs pheRub7CTULxEp/1EonaOg8zrlMJxQw0Xhenmqv9I9PF6mMCXjJnSokqzqM+sQOV TuLq8f62IbKK5F7iGLX6C/X+7MRQW/W/xqa7dpAHgttgt/KeuFOpj8H6ugHgxbrb aXkB2lCnzvjFv/OkZ1ZM8ZjN/tiuAkUJtN4DNKyXvjwP3rhPGKmQMicUf0IQxM1q 54i2xdW85UtGIkBW5PFh/inj7Ba6PIYH3BoIX4qNhMOMrgwlXb6dovtvHgUo6jb0 C0CQoo8kEntq4YBiy7qK+WMfOsNoyq+H8kVXbNgO0XJo4lraa72lkH7rnSMaIJIB aGorHg6gNq3ehVb8MApp4nt/mi830zSYxmI+Ck8Q0P2tYc8Tx6v7fhHZCvzPiMhk l/FQ9kuhE7HSIFZ+RgGTHdq21O72xAb+LBtjhJ2EpBYz5GdaPKVqmjzcxvSVmz7J rH1jIgbQQMxppdL6xIdKcznBVPouZkQsdxwmh0kldpn1cUYzuB94BaQzU7kPkE3n 1tdo0jWxJfcWqaNH7SLRyVjPeY+232QVez/VjFJF57SStmYKdhHrKSOj/EQe9iYU 67TVF1lwUtMJnT0b4J1ubNQNELoxt5Pm9gawBQFC8XoJ7qRKQq+NbhUGRgcC7nBR 2ZDjZKyM/V0rxRT6wjZV =nM13 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/pull-ui-20170209-2' into staging vnc: add support for multiple listening sockets. vnc: misc fixes and cleanups. # gpg: Signature made Thu 09 Feb 2017 16:45:02 GMT # gpg: using RSA key 0x4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * remotes/kraxel/tags/pull-ui-20170209-2: ui: add ability to specify multiple VNC listen addresses util: add iterators for QemuOpts values ui: let VNC server listen on all resolved IP addresses ui: extract code to connect/listen from vnc_display_open ui: refactor code for populating SocketAddress from vnc_display_open ui: refactor VncDisplay to allow multiple listening sockets ui: fix reporting of VNC auth in query-vnc-servers ui: fix regression handling bare 'websocket' option to -vnc vnc: do not disconnect on EAGAIN ui/vnc: Drop unused vnc_has_job() and vnc_jobs_clear() Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
8b1897725d
@ -100,6 +100,15 @@ typedef int (*qemu_opt_loopfunc)(void *opaque,
|
|||||||
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
|
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
QemuOpts *opts;
|
||||||
|
QemuOpt *opt;
|
||||||
|
const char *name;
|
||||||
|
} QemuOptsIter;
|
||||||
|
|
||||||
|
void qemu_opt_iter_init(QemuOptsIter *iter, QemuOpts *opts, const char *name);
|
||||||
|
const char *qemu_opt_iter_next(QemuOptsIter *iter);
|
||||||
|
|
||||||
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
|
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
|
||||||
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
|
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
|
||||||
int fail_if_exists, Error **errp);
|
int fail_if_exists, Error **errp);
|
||||||
|
@ -1506,7 +1506,8 @@
|
|||||||
#
|
#
|
||||||
# The network connection information for server
|
# The network connection information for server
|
||||||
#
|
#
|
||||||
# @auth: #optional authentication method
|
# @auth: #optional authentication method used for
|
||||||
|
# the plain (non-websocket) VNC server
|
||||||
#
|
#
|
||||||
# Since: 2.1
|
# Since: 2.1
|
||||||
##
|
##
|
||||||
@ -1597,6 +1598,25 @@
|
|||||||
'tls-plain', 'x509-plain',
|
'tls-plain', 'x509-plain',
|
||||||
'tls-sasl', 'x509-sasl' ] }
|
'tls-sasl', 'x509-sasl' ] }
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# @VncServerInfo2:
|
||||||
|
#
|
||||||
|
# The network connection information for server
|
||||||
|
#
|
||||||
|
# @auth: The current authentication type used by the servers
|
||||||
|
#
|
||||||
|
# @vencrypt: #optional The vencrypt sub authentication type used by the
|
||||||
|
# servers, only specified in case auth == vencrypt.
|
||||||
|
#
|
||||||
|
# Since: 2.9
|
||||||
|
##
|
||||||
|
{ 'struct': 'VncServerInfo2',
|
||||||
|
'base': 'VncBasicInfo',
|
||||||
|
'data': { 'auth' : 'VncPrimaryAuth',
|
||||||
|
'*vencrypt' : 'VncVencryptSubAuth' } }
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# @VncInfo2:
|
# @VncInfo2:
|
||||||
#
|
#
|
||||||
@ -1612,9 +1632,9 @@
|
|||||||
# @clients: A list of @VncClientInfo of all currently connected clients.
|
# @clients: A list of @VncClientInfo of all currently connected clients.
|
||||||
# The list can be empty, for obvious reasons.
|
# The list can be empty, for obvious reasons.
|
||||||
#
|
#
|
||||||
# @auth: The current authentication type used by the server
|
# @auth: The current authentication type used by the non-websockets servers
|
||||||
#
|
#
|
||||||
# @vencrypt: #optional The vencrypt sub authentication type used by the server,
|
# @vencrypt: #optional The vencrypt authentication type used by the servers,
|
||||||
# only specified in case auth == vencrypt.
|
# only specified in case auth == vencrypt.
|
||||||
#
|
#
|
||||||
# @display: #optional The display device the vnc server is linked to.
|
# @display: #optional The display device the vnc server is linked to.
|
||||||
@ -1623,7 +1643,7 @@
|
|||||||
##
|
##
|
||||||
{ 'struct': 'VncInfo2',
|
{ 'struct': 'VncInfo2',
|
||||||
'data': { 'id' : 'str',
|
'data': { 'id' : 'str',
|
||||||
'server' : ['VncBasicInfo'],
|
'server' : ['VncServerInfo2'],
|
||||||
'clients' : ['VncClientInfo'],
|
'clients' : ['VncClientInfo'],
|
||||||
'auth' : 'VncPrimaryAuth',
|
'auth' : 'VncPrimaryAuth',
|
||||||
'*vencrypt' : 'VncVencryptSubAuth',
|
'*vencrypt' : 'VncVencryptSubAuth',
|
||||||
|
@ -1296,10 +1296,14 @@ is a TCP port number, not a display number.
|
|||||||
@item websocket
|
@item websocket
|
||||||
|
|
||||||
Opens an additional TCP listening port dedicated to VNC Websocket connections.
|
Opens an additional TCP listening port dedicated to VNC Websocket connections.
|
||||||
By definition the Websocket port is 5700+@var{display}. If @var{host} is
|
If a bare @var{websocket} option is given, the Websocket port is
|
||||||
specified connections will only be allowed from this host.
|
5700+@var{display}. An alternative port can be specified with the
|
||||||
As an alternative the Websocket port could be specified by using
|
syntax @code{websocket}=@var{port}.
|
||||||
@code{websocket}=@var{port}.
|
|
||||||
|
If @var{host} is specified connections will only be allowed from this host.
|
||||||
|
It is possible to control the websocket listen address independently, using
|
||||||
|
the syntax @code{websocket}=@var{host}:@var{port}.
|
||||||
|
|
||||||
If no TLS credentials are provided, the websocket connection runs in
|
If no TLS credentials are provided, the websocket connection runs in
|
||||||
unencrypted mode. If TLS credentials are provided, the websocket connection
|
unencrypted mode. If TLS credentials are provided, the websocket connection
|
||||||
requires encrypted client connections.
|
requires encrypted client connections.
|
||||||
|
@ -128,29 +128,6 @@ static bool vnc_has_job_locked(VncState *vs)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool vnc_has_job(VncState *vs)
|
|
||||||
{
|
|
||||||
bool ret;
|
|
||||||
|
|
||||||
vnc_lock_queue(queue);
|
|
||||||
ret = vnc_has_job_locked(vs);
|
|
||||||
vnc_unlock_queue(queue);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vnc_jobs_clear(VncState *vs)
|
|
||||||
{
|
|
||||||
VncJob *job, *tmp;
|
|
||||||
|
|
||||||
vnc_lock_queue(queue);
|
|
||||||
QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) {
|
|
||||||
if (job->vs == vs || !vs) {
|
|
||||||
QTAILQ_REMOVE(&queue->jobs, job, next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vnc_unlock_queue(queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void vnc_jobs_join(VncState *vs)
|
void vnc_jobs_join(VncState *vs)
|
||||||
{
|
{
|
||||||
vnc_lock_queue(queue);
|
vnc_lock_queue(queue);
|
||||||
|
@ -34,8 +34,6 @@
|
|||||||
VncJob *vnc_job_new(VncState *vs);
|
VncJob *vnc_job_new(VncState *vs);
|
||||||
int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h);
|
int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h);
|
||||||
void vnc_job_push(VncJob *job);
|
void vnc_job_push(VncJob *job);
|
||||||
bool vnc_has_job(VncState *vs);
|
|
||||||
void vnc_jobs_clear(VncState *vs);
|
|
||||||
void vnc_jobs_join(VncState *vs);
|
void vnc_jobs_join(VncState *vs);
|
||||||
|
|
||||||
void vnc_jobs_consume_buffer(VncState *vs);
|
void vnc_jobs_consume_buffer(VncState *vs);
|
||||||
|
658
ui/vnc.c
658
ui/vnc.c
@ -45,6 +45,7 @@
|
|||||||
#include "crypto/tlscredsx509.h"
|
#include "crypto/tlscredsx509.h"
|
||||||
#include "qom/object_interfaces.h"
|
#include "qom/object_interfaces.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
|
#include "io/dns-resolver.h"
|
||||||
|
|
||||||
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
|
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
|
||||||
#define VNC_REFRESH_INTERVAL_INC 50
|
#define VNC_REFRESH_INTERVAL_INC 50
|
||||||
@ -224,8 +225,12 @@ static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
|
|||||||
VncServerInfo *info;
|
VncServerInfo *info;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
|
if (!vd->nlsock) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
info = g_malloc0(sizeof(*info));
|
info = g_malloc0(sizeof(*info));
|
||||||
vnc_init_basic_info_from_server_addr(vd->lsock,
|
vnc_init_basic_info_from_server_addr(vd->lsock[0],
|
||||||
qapi_VncServerInfo_base(info), &err);
|
qapi_VncServerInfo_base(info), &err);
|
||||||
info->has_auth = true;
|
info->has_auth = true;
|
||||||
info->auth = g_strdup(vnc_auth_name(vd));
|
info->auth = g_strdup(vnc_auth_name(vd));
|
||||||
@ -371,7 +376,7 @@ VncInfo *qmp_query_vnc(Error **errp)
|
|||||||
VncDisplay *vd = vnc_display_find(NULL);
|
VncDisplay *vd = vnc_display_find(NULL);
|
||||||
SocketAddress *addr = NULL;
|
SocketAddress *addr = NULL;
|
||||||
|
|
||||||
if (vd == NULL || !vd->lsock) {
|
if (vd == NULL || !vd->nlsock) {
|
||||||
info->enabled = false;
|
info->enabled = false;
|
||||||
} else {
|
} else {
|
||||||
info->enabled = true;
|
info->enabled = true;
|
||||||
@ -384,7 +389,7 @@ VncInfo *qmp_query_vnc(Error **errp)
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = qio_channel_socket_get_local_address(vd->lsock, errp);
|
addr = qio_channel_socket_get_local_address(vd->lsock[0], errp);
|
||||||
if (!addr) {
|
if (!addr) {
|
||||||
goto out_error;
|
goto out_error;
|
||||||
}
|
}
|
||||||
@ -429,12 +434,20 @@ out_error:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc,
|
|
||||||
bool websocket,
|
static void qmp_query_auth(int auth, int subauth,
|
||||||
VncBasicInfoList *prev)
|
VncPrimaryAuth *qmp_auth,
|
||||||
|
VncVencryptSubAuth *qmp_vencrypt,
|
||||||
|
bool *qmp_has_vencrypt);
|
||||||
|
|
||||||
|
static VncServerInfo2List *qmp_query_server_entry(QIOChannelSocket *ioc,
|
||||||
|
bool websocket,
|
||||||
|
int auth,
|
||||||
|
int subauth,
|
||||||
|
VncServerInfo2List *prev)
|
||||||
{
|
{
|
||||||
VncBasicInfoList *list;
|
VncServerInfo2List *list;
|
||||||
VncBasicInfo *info;
|
VncServerInfo2 *info;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
SocketAddress *addr;
|
SocketAddress *addr;
|
||||||
|
|
||||||
@ -444,85 +457,91 @@ static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc,
|
|||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
info = g_new0(VncBasicInfo, 1);
|
info = g_new0(VncServerInfo2, 1);
|
||||||
vnc_init_basic_info(addr, info, &err);
|
vnc_init_basic_info(addr, qapi_VncServerInfo2_base(info), &err);
|
||||||
qapi_free_SocketAddress(addr);
|
qapi_free_SocketAddress(addr);
|
||||||
if (err) {
|
if (err) {
|
||||||
qapi_free_VncBasicInfo(info);
|
qapi_free_VncServerInfo2(info);
|
||||||
error_free(err);
|
error_free(err);
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
info->websocket = websocket;
|
info->websocket = websocket;
|
||||||
|
|
||||||
list = g_new0(VncBasicInfoList, 1);
|
qmp_query_auth(auth, subauth, &info->auth,
|
||||||
|
&info->vencrypt, &info->has_vencrypt);
|
||||||
|
|
||||||
|
list = g_new0(VncServerInfo2List, 1);
|
||||||
list->value = info;
|
list->value = info;
|
||||||
list->next = prev;
|
list->next = prev;
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
|
static void qmp_query_auth(int auth, int subauth,
|
||||||
|
VncPrimaryAuth *qmp_auth,
|
||||||
|
VncVencryptSubAuth *qmp_vencrypt,
|
||||||
|
bool *qmp_has_vencrypt)
|
||||||
{
|
{
|
||||||
switch (vd->auth) {
|
switch (auth) {
|
||||||
case VNC_AUTH_VNC:
|
case VNC_AUTH_VNC:
|
||||||
info->auth = VNC_PRIMARY_AUTH_VNC;
|
*qmp_auth = VNC_PRIMARY_AUTH_VNC;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_RA2:
|
case VNC_AUTH_RA2:
|
||||||
info->auth = VNC_PRIMARY_AUTH_RA2;
|
*qmp_auth = VNC_PRIMARY_AUTH_RA2;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_RA2NE:
|
case VNC_AUTH_RA2NE:
|
||||||
info->auth = VNC_PRIMARY_AUTH_RA2NE;
|
*qmp_auth = VNC_PRIMARY_AUTH_RA2NE;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_TIGHT:
|
case VNC_AUTH_TIGHT:
|
||||||
info->auth = VNC_PRIMARY_AUTH_TIGHT;
|
*qmp_auth = VNC_PRIMARY_AUTH_TIGHT;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_ULTRA:
|
case VNC_AUTH_ULTRA:
|
||||||
info->auth = VNC_PRIMARY_AUTH_ULTRA;
|
*qmp_auth = VNC_PRIMARY_AUTH_ULTRA;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_TLS:
|
case VNC_AUTH_TLS:
|
||||||
info->auth = VNC_PRIMARY_AUTH_TLS;
|
*qmp_auth = VNC_PRIMARY_AUTH_TLS;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT:
|
case VNC_AUTH_VENCRYPT:
|
||||||
info->auth = VNC_PRIMARY_AUTH_VENCRYPT;
|
*qmp_auth = VNC_PRIMARY_AUTH_VENCRYPT;
|
||||||
info->has_vencrypt = true;
|
*qmp_has_vencrypt = true;
|
||||||
switch (vd->subauth) {
|
switch (subauth) {
|
||||||
case VNC_AUTH_VENCRYPT_PLAIN:
|
case VNC_AUTH_VENCRYPT_PLAIN:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_PLAIN;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT_TLSNONE:
|
case VNC_AUTH_VENCRYPT_TLSNONE:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_NONE;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT_TLSVNC:
|
case VNC_AUTH_VENCRYPT_TLSVNC:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_VNC;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT_TLSPLAIN:
|
case VNC_AUTH_VENCRYPT_TLSPLAIN:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_PLAIN;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT_X509NONE:
|
case VNC_AUTH_VENCRYPT_X509NONE:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_NONE;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT_X509VNC:
|
case VNC_AUTH_VENCRYPT_X509VNC:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_VNC;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT_X509PLAIN:
|
case VNC_AUTH_VENCRYPT_X509PLAIN:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_PLAIN;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT_TLSSASL:
|
case VNC_AUTH_VENCRYPT_TLSSASL:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_TLS_SASL;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_VENCRYPT_X509SASL:
|
case VNC_AUTH_VENCRYPT_X509SASL:
|
||||||
info->vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL;
|
*qmp_vencrypt = VNC_VENCRYPT_SUB_AUTH_X509_SASL;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
info->has_vencrypt = false;
|
*qmp_has_vencrypt = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_SASL:
|
case VNC_AUTH_SASL:
|
||||||
info->auth = VNC_PRIMARY_AUTH_SASL;
|
*qmp_auth = VNC_PRIMARY_AUTH_SASL;
|
||||||
break;
|
break;
|
||||||
case VNC_AUTH_NONE:
|
case VNC_AUTH_NONE:
|
||||||
default:
|
default:
|
||||||
info->auth = VNC_PRIMARY_AUTH_NONE;
|
*qmp_auth = VNC_PRIMARY_AUTH_NONE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -533,25 +552,28 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
|
|||||||
VncInfo2 *info;
|
VncInfo2 *info;
|
||||||
VncDisplay *vd;
|
VncDisplay *vd;
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
QTAILQ_FOREACH(vd, &vnc_displays, next) {
|
QTAILQ_FOREACH(vd, &vnc_displays, next) {
|
||||||
info = g_new0(VncInfo2, 1);
|
info = g_new0(VncInfo2, 1);
|
||||||
info->id = g_strdup(vd->id);
|
info->id = g_strdup(vd->id);
|
||||||
info->clients = qmp_query_client_list(vd);
|
info->clients = qmp_query_client_list(vd);
|
||||||
qmp_query_auth(vd, info);
|
qmp_query_auth(vd->auth, vd->subauth, &info->auth,
|
||||||
|
&info->vencrypt, &info->has_vencrypt);
|
||||||
if (vd->dcl.con) {
|
if (vd->dcl.con) {
|
||||||
dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con),
|
dev = DEVICE(object_property_get_link(OBJECT(vd->dcl.con),
|
||||||
"device", NULL));
|
"device", NULL));
|
||||||
info->has_display = true;
|
info->has_display = true;
|
||||||
info->display = g_strdup(dev->id);
|
info->display = g_strdup(dev->id);
|
||||||
}
|
}
|
||||||
if (vd->lsock != NULL) {
|
for (i = 0; i < vd->nlsock; i++) {
|
||||||
info->server = qmp_query_server_entry(
|
info->server = qmp_query_server_entry(
|
||||||
vd->lsock, false, info->server);
|
vd->lsock[i], false, vd->auth, vd->subauth, info->server);
|
||||||
}
|
}
|
||||||
if (vd->lwebsock != NULL) {
|
for (i = 0; i < vd->nlwebsock; i++) {
|
||||||
info->server = qmp_query_server_entry(
|
info->server = qmp_query_server_entry(
|
||||||
vd->lwebsock, true, info->server);
|
vd->lwebsock[i], true, vd->ws_auth,
|
||||||
|
vd->ws_subauth, info->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
item = g_new0(VncInfo2List, 1);
|
item = g_new0(VncInfo2List, 1);
|
||||||
@ -1256,12 +1278,13 @@ ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp)
|
|||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
VNC_DEBUG("Closing down client sock: EOF\n");
|
VNC_DEBUG("Closing down client sock: EOF\n");
|
||||||
|
vnc_disconnect_start(vs);
|
||||||
} else if (ret != QIO_CHANNEL_ERR_BLOCK) {
|
} else if (ret != QIO_CHANNEL_ERR_BLOCK) {
|
||||||
VNC_DEBUG("Closing down client sock: ret %zd (%s)\n",
|
VNC_DEBUG("Closing down client sock: ret %zd (%s)\n",
|
||||||
ret, errp ? error_get_pretty(*errp) : "Unknown");
|
ret, errp ? error_get_pretty(*errp) : "Unknown");
|
||||||
|
vnc_disconnect_start(vs);
|
||||||
}
|
}
|
||||||
|
|
||||||
vnc_disconnect_start(vs);
|
|
||||||
if (errp) {
|
if (errp) {
|
||||||
error_free(*errp);
|
error_free(*errp);
|
||||||
*errp = NULL;
|
*errp = NULL;
|
||||||
@ -3069,15 +3092,22 @@ static gboolean vnc_listen_io(QIOChannel *ioc,
|
|||||||
VncDisplay *vd = opaque;
|
VncDisplay *vd = opaque;
|
||||||
QIOChannelSocket *sioc = NULL;
|
QIOChannelSocket *sioc = NULL;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
bool isWebsock = false;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < vd->nlwebsock; i++) {
|
||||||
|
if (ioc == QIO_CHANNEL(vd->lwebsock[i])) {
|
||||||
|
isWebsock = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err);
|
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err);
|
||||||
if (sioc != NULL) {
|
if (sioc != NULL) {
|
||||||
qio_channel_set_name(QIO_CHANNEL(sioc),
|
qio_channel_set_name(QIO_CHANNEL(sioc),
|
||||||
ioc != QIO_CHANNEL(vd->lsock) ?
|
isWebsock ? "vnc-ws-server" : "vnc-server");
|
||||||
"vnc-ws-server" : "vnc-server");
|
|
||||||
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
|
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
|
||||||
vnc_connect(vd, sioc, false,
|
vnc_connect(vd, sioc, false, isWebsock);
|
||||||
ioc != QIO_CHANNEL(vd->lsock));
|
|
||||||
object_unref(OBJECT(sioc));
|
object_unref(OBJECT(sioc));
|
||||||
} else {
|
} else {
|
||||||
/* client probably closed connection before we got there */
|
/* client probably closed connection before we got there */
|
||||||
@ -3137,24 +3167,33 @@ void vnc_display_init(const char *id)
|
|||||||
|
|
||||||
static void vnc_display_close(VncDisplay *vd)
|
static void vnc_display_close(VncDisplay *vd)
|
||||||
{
|
{
|
||||||
|
size_t i;
|
||||||
if (!vd) {
|
if (!vd) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
vd->is_unix = false;
|
vd->is_unix = false;
|
||||||
if (vd->lsock != NULL) {
|
for (i = 0; i < vd->nlsock; i++) {
|
||||||
if (vd->lsock_tag) {
|
if (vd->lsock_tag[i]) {
|
||||||
g_source_remove(vd->lsock_tag);
|
g_source_remove(vd->lsock_tag[i]);
|
||||||
}
|
}
|
||||||
object_unref(OBJECT(vd->lsock));
|
object_unref(OBJECT(vd->lsock[i]));
|
||||||
vd->lsock = NULL;
|
|
||||||
}
|
}
|
||||||
if (vd->lwebsock != NULL) {
|
g_free(vd->lsock);
|
||||||
if (vd->lwebsock_tag) {
|
g_free(vd->lsock_tag);
|
||||||
g_source_remove(vd->lwebsock_tag);
|
vd->lsock = NULL;
|
||||||
|
vd->nlsock = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < vd->nlwebsock; i++) {
|
||||||
|
if (vd->lwebsock_tag[i]) {
|
||||||
|
g_source_remove(vd->lwebsock_tag[i]);
|
||||||
}
|
}
|
||||||
object_unref(OBJECT(vd->lwebsock));
|
object_unref(OBJECT(vd->lwebsock[i]));
|
||||||
vd->lwebsock = NULL;
|
|
||||||
}
|
}
|
||||||
|
g_free(vd->lwebsock);
|
||||||
|
g_free(vd->lwebsock_tag);
|
||||||
|
vd->lwebsock = NULL;
|
||||||
|
vd->nlwebsock = 0;
|
||||||
|
|
||||||
vd->auth = VNC_AUTH_INVALID;
|
vd->auth = VNC_AUTH_INVALID;
|
||||||
vd->subauth = VNC_AUTH_INVALID;
|
vd->subauth = VNC_AUTH_INVALID;
|
||||||
if (vd->tlscreds) {
|
if (vd->tlscreds) {
|
||||||
@ -3204,7 +3243,11 @@ static void vnc_display_print_local_addr(VncDisplay *vd)
|
|||||||
SocketAddress *addr;
|
SocketAddress *addr;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
addr = qio_channel_socket_get_local_address(vd->lsock, &err);
|
if (!vd->nlsock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = qio_channel_socket_get_local_address(vd->lsock[0], &err);
|
||||||
if (!addr) {
|
if (!addr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3453,19 +3496,364 @@ vnc_display_create_creds(bool x509,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int vnc_display_get_address(const char *addrstr,
|
||||||
|
bool websocket,
|
||||||
|
int displaynum,
|
||||||
|
int to,
|
||||||
|
bool has_ipv4,
|
||||||
|
bool has_ipv6,
|
||||||
|
bool ipv4,
|
||||||
|
bool ipv6,
|
||||||
|
SocketAddress **retaddr,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
SocketAddress *addr = NULL;
|
||||||
|
|
||||||
|
addr = g_new0(SocketAddress, 1);
|
||||||
|
|
||||||
|
if (strncmp(addrstr, "unix:", 5) == 0) {
|
||||||
|
addr->type = SOCKET_ADDRESS_KIND_UNIX;
|
||||||
|
addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
|
||||||
|
addr->u.q_unix.data->path = g_strdup(addrstr + 5);
|
||||||
|
|
||||||
|
if (websocket) {
|
||||||
|
error_setg(errp, "UNIX sockets not supported with websock");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to) {
|
||||||
|
error_setg(errp, "Port range not support with UNIX socket");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
const char *port;
|
||||||
|
size_t hostlen;
|
||||||
|
unsigned long long baseport = 0;
|
||||||
|
InetSocketAddress *inet;
|
||||||
|
|
||||||
|
port = strrchr(addrstr, ':');
|
||||||
|
if (!port) {
|
||||||
|
if (websocket) {
|
||||||
|
hostlen = 0;
|
||||||
|
port = addrstr;
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "no vnc port specified");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hostlen = port - addrstr;
|
||||||
|
port++;
|
||||||
|
if (*port == '\0') {
|
||||||
|
error_setg(errp, "vnc port cannot be empty");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr->type = SOCKET_ADDRESS_KIND_INET;
|
||||||
|
inet = addr->u.inet.data = g_new0(InetSocketAddress, 1);
|
||||||
|
if (addrstr[0] == '[' && addrstr[hostlen - 1] == ']') {
|
||||||
|
inet->host = g_strndup(addrstr + 1, hostlen - 2);
|
||||||
|
} else {
|
||||||
|
inet->host = g_strndup(addrstr, hostlen);
|
||||||
|
}
|
||||||
|
/* plain VNC port is just an offset, for websocket
|
||||||
|
* port is absolute */
|
||||||
|
if (websocket) {
|
||||||
|
if (g_str_equal(addrstr, "") ||
|
||||||
|
g_str_equal(addrstr, "on")) {
|
||||||
|
if (displaynum == -1) {
|
||||||
|
error_setg(errp, "explicit websocket port is required");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
inet->port = g_strdup_printf(
|
||||||
|
"%d", displaynum + 5700);
|
||||||
|
if (to) {
|
||||||
|
inet->has_to = true;
|
||||||
|
inet->to = to + 5700;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inet->port = g_strdup(port);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (parse_uint_full(port, &baseport, 10) < 0) {
|
||||||
|
error_setg(errp, "can't convert to a number: %s", port);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (baseport > 65535 ||
|
||||||
|
baseport + 5900 > 65535) {
|
||||||
|
error_setg(errp, "port %s out of range", port);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
inet->port = g_strdup_printf(
|
||||||
|
"%d", (int)baseport + 5900);
|
||||||
|
|
||||||
|
if (to) {
|
||||||
|
inet->has_to = true;
|
||||||
|
inet->to = to + 5900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inet->ipv4 = ipv4;
|
||||||
|
inet->has_ipv4 = has_ipv4;
|
||||||
|
inet->ipv6 = ipv6;
|
||||||
|
inet->has_ipv6 = has_ipv6;
|
||||||
|
|
||||||
|
ret = baseport;
|
||||||
|
}
|
||||||
|
|
||||||
|
*retaddr = addr;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (ret < 0) {
|
||||||
|
qapi_free_SocketAddress(addr);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vnc_display_get_addresses(QemuOpts *opts,
|
||||||
|
SocketAddress ***retsaddr,
|
||||||
|
size_t *retnsaddr,
|
||||||
|
SocketAddress ***retwsaddr,
|
||||||
|
size_t *retnwsaddr,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
SocketAddress *saddr = NULL;
|
||||||
|
SocketAddress *wsaddr = NULL;
|
||||||
|
QemuOptsIter addriter;
|
||||||
|
const char *addr;
|
||||||
|
int to = qemu_opt_get_number(opts, "to", 0);
|
||||||
|
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);
|
||||||
|
size_t i;
|
||||||
|
int displaynum = -1;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
*retsaddr = NULL;
|
||||||
|
*retnsaddr = 0;
|
||||||
|
*retwsaddr = NULL;
|
||||||
|
*retnwsaddr = 0;
|
||||||
|
|
||||||
|
addr = qemu_opt_get(opts, "vnc");
|
||||||
|
if (addr == NULL || g_str_equal(addr, "none")) {
|
||||||
|
ret = 0;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (qemu_opt_get(opts, "websocket") &&
|
||||||
|
!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
|
||||||
|
error_setg(errp,
|
||||||
|
"SHA1 hash support is required for websockets");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_opt_iter_init(&addriter, opts, "vnc");
|
||||||
|
while ((addr = qemu_opt_iter_next(&addriter)) != NULL) {
|
||||||
|
int rv;
|
||||||
|
rv = vnc_display_get_address(addr, false, 0, to,
|
||||||
|
has_ipv4, has_ipv6,
|
||||||
|
ipv4, ipv6,
|
||||||
|
&saddr, errp);
|
||||||
|
if (rv < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
/* Historical compat - first listen address can be used
|
||||||
|
* to set the default websocket port
|
||||||
|
*/
|
||||||
|
if (displaynum == -1) {
|
||||||
|
displaynum = rv;
|
||||||
|
}
|
||||||
|
*retsaddr = g_renew(SocketAddress *, *retsaddr, *retnsaddr + 1);
|
||||||
|
(*retsaddr)[(*retnsaddr)++] = saddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we had multiple primary displays, we don't do defaults
|
||||||
|
* for websocket, and require explicit config instead. */
|
||||||
|
if (*retnsaddr > 1) {
|
||||||
|
displaynum = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_opt_iter_init(&addriter, opts, "websocket");
|
||||||
|
while ((addr = qemu_opt_iter_next(&addriter)) != NULL) {
|
||||||
|
if (vnc_display_get_address(addr, true, displaynum, to,
|
||||||
|
has_ipv4, has_ipv6,
|
||||||
|
ipv4, ipv6,
|
||||||
|
&wsaddr, errp) < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Historical compat - if only a single listen address was
|
||||||
|
* provided, then this is used to set the default listen
|
||||||
|
* address for websocket too
|
||||||
|
*/
|
||||||
|
if (*retnsaddr == 1 &&
|
||||||
|
(*retsaddr)[0]->type == SOCKET_ADDRESS_KIND_INET &&
|
||||||
|
wsaddr->type == SOCKET_ADDRESS_KIND_INET &&
|
||||||
|
g_str_equal(wsaddr->u.inet.data->host, "") &&
|
||||||
|
!g_str_equal((*retsaddr)[0]->u.inet.data->host, "")) {
|
||||||
|
g_free(wsaddr->u.inet.data->host);
|
||||||
|
wsaddr->u.inet.data->host =
|
||||||
|
g_strdup((*retsaddr)[0]->u.inet.data->host);
|
||||||
|
}
|
||||||
|
|
||||||
|
*retwsaddr = g_renew(SocketAddress *, *retwsaddr, *retnwsaddr + 1);
|
||||||
|
(*retwsaddr)[(*retnwsaddr)++] = wsaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
cleanup:
|
||||||
|
if (ret < 0) {
|
||||||
|
for (i = 0; i < *retnsaddr; i++) {
|
||||||
|
qapi_free_SocketAddress((*retsaddr)[i]);
|
||||||
|
}
|
||||||
|
g_free(*retsaddr);
|
||||||
|
for (i = 0; i < *retnwsaddr; i++) {
|
||||||
|
qapi_free_SocketAddress((*retwsaddr)[i]);
|
||||||
|
}
|
||||||
|
g_free(*retwsaddr);
|
||||||
|
*retsaddr = *retwsaddr = NULL;
|
||||||
|
*retnsaddr = *retnwsaddr = 0;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vnc_display_connect(VncDisplay *vd,
|
||||||
|
SocketAddress **saddr,
|
||||||
|
size_t nsaddr,
|
||||||
|
SocketAddress **wsaddr,
|
||||||
|
size_t nwsaddr,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
/* connect to viewer */
|
||||||
|
QIOChannelSocket *sioc = NULL;
|
||||||
|
if (nwsaddr != 0) {
|
||||||
|
error_setg(errp, "Cannot use websockets in reverse mode");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (nsaddr != 1) {
|
||||||
|
error_setg(errp, "Expected a single address in reverse mode");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vd->is_unix = saddr[0]->type == SOCKET_ADDRESS_KIND_UNIX;
|
||||||
|
sioc = qio_channel_socket_new();
|
||||||
|
qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse");
|
||||||
|
if (qio_channel_socket_connect_sync(sioc, saddr[0], errp) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vnc_connect(vd, sioc, false, false);
|
||||||
|
object_unref(OBJECT(sioc));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int vnc_display_listen_addr(VncDisplay *vd,
|
||||||
|
SocketAddress *addr,
|
||||||
|
const char *name,
|
||||||
|
QIOChannelSocket ***lsock,
|
||||||
|
guint **lsock_tag,
|
||||||
|
size_t *nlsock,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QIODNSResolver *resolver = qio_dns_resolver_get_instance();
|
||||||
|
SocketAddress **rawaddrs = NULL;
|
||||||
|
size_t nrawaddrs = 0;
|
||||||
|
Error *listenerr = NULL;
|
||||||
|
bool listening = false;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (qio_dns_resolver_lookup_sync(resolver, addr, &nrawaddrs,
|
||||||
|
&rawaddrs, errp) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nrawaddrs; i++) {
|
||||||
|
QIOChannelSocket *sioc = qio_channel_socket_new();
|
||||||
|
|
||||||
|
qio_channel_set_name(QIO_CHANNEL(sioc), name);
|
||||||
|
if (qio_channel_socket_listen_sync(
|
||||||
|
sioc, rawaddrs[i], listenerr == NULL ? &listenerr : NULL) < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
listening = true;
|
||||||
|
(*nlsock)++;
|
||||||
|
*lsock = g_renew(QIOChannelSocket *, *lsock, *nlsock);
|
||||||
|
*lsock_tag = g_renew(guint, *lsock_tag, *nlsock);
|
||||||
|
|
||||||
|
(*lsock)[*nlsock - 1] = sioc;
|
||||||
|
(*lsock_tag)[*nlsock - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nrawaddrs; i++) {
|
||||||
|
qapi_free_SocketAddress(rawaddrs[i]);
|
||||||
|
}
|
||||||
|
g_free(rawaddrs);
|
||||||
|
|
||||||
|
if (listenerr) {
|
||||||
|
if (!listening) {
|
||||||
|
error_propagate(errp, listenerr);
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
error_free(listenerr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < *nlsock; i++) {
|
||||||
|
(*lsock_tag)[i] = qio_channel_add_watch(
|
||||||
|
QIO_CHANNEL((*lsock)[i]),
|
||||||
|
G_IO_IN, vnc_listen_io, vd, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int vnc_display_listen(VncDisplay *vd,
|
||||||
|
SocketAddress **saddr,
|
||||||
|
size_t nsaddr,
|
||||||
|
SocketAddress **wsaddr,
|
||||||
|
size_t nwsaddr,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < nsaddr; i++) {
|
||||||
|
if (vnc_display_listen_addr(vd, saddr[i],
|
||||||
|
"vnc-listen",
|
||||||
|
&vd->lsock,
|
||||||
|
&vd->lsock_tag,
|
||||||
|
&vd->nlsock,
|
||||||
|
errp) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < nwsaddr; i++) {
|
||||||
|
if (vnc_display_listen_addr(vd, wsaddr[i],
|
||||||
|
"vnc-ws-listen",
|
||||||
|
&vd->lwebsock,
|
||||||
|
&vd->lwebsock_tag,
|
||||||
|
&vd->nlwebsock,
|
||||||
|
errp) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void vnc_display_open(const char *id, Error **errp)
|
void vnc_display_open(const char *id, Error **errp)
|
||||||
{
|
{
|
||||||
VncDisplay *vd = vnc_display_find(id);
|
VncDisplay *vd = vnc_display_find(id);
|
||||||
QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
|
QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
|
||||||
SocketAddress *saddr = NULL, *wsaddr = NULL;
|
SocketAddress **saddr = NULL, **wsaddr = NULL;
|
||||||
|
size_t nsaddr, nwsaddr;
|
||||||
const char *share, *device_id;
|
const char *share, *device_id;
|
||||||
QemuConsole *con;
|
QemuConsole *con;
|
||||||
bool password = false;
|
bool password = false;
|
||||||
bool reverse = false;
|
bool reverse = false;
|
||||||
const char *vnc;
|
|
||||||
char *h;
|
|
||||||
const char *credid;
|
const char *credid;
|
||||||
int show_vnc_port = 0;
|
|
||||||
bool sasl = false;
|
bool sasl = false;
|
||||||
#ifdef CONFIG_VNC_SASL
|
#ifdef CONFIG_VNC_SASL
|
||||||
int saslErr;
|
int saslErr;
|
||||||
@ -3473,7 +3861,7 @@ void vnc_display_open(const char *id, Error **errp)
|
|||||||
int acl = 0;
|
int acl = 0;
|
||||||
int lock_key_sync = 1;
|
int lock_key_sync = 1;
|
||||||
int key_delay_ms;
|
int key_delay_ms;
|
||||||
bool ws_enabled = false;
|
size_t i;
|
||||||
|
|
||||||
if (!vd) {
|
if (!vd) {
|
||||||
error_setg(errp, "VNC display not active");
|
error_setg(errp, "VNC display not active");
|
||||||
@ -3484,94 +3872,14 @@ void vnc_display_open(const char *id, Error **errp)
|
|||||||
if (!opts) {
|
if (!opts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
vnc = qemu_opt_get(opts, "vnc");
|
|
||||||
if (!vnc || strcmp(vnc, "none") == 0) {
|
if (vnc_display_get_addresses(opts, &saddr, &nsaddr,
|
||||||
return;
|
&wsaddr, &nwsaddr, errp) < 0) {
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
h = strrchr(vnc, ':');
|
if (saddr == NULL) {
|
||||||
if (h) {
|
return;
|
||||||
size_t hlen = h - vnc;
|
|
||||||
|
|
||||||
const char *websocket = qemu_opt_get(opts, "websocket");
|
|
||||||
int to = qemu_opt_get_number(opts, "to", 0);
|
|
||||||
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) {
|
|
||||||
if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
|
|
||||||
error_setg(errp,
|
|
||||||
"SHA1 hash support is required for websockets");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
wsaddr = g_new0(SocketAddress, 1);
|
|
||||||
ws_enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(vnc, "unix:", 5) == 0) {
|
|
||||||
saddr->type = SOCKET_ADDRESS_KIND_UNIX;
|
|
||||||
saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
|
|
||||||
saddr->u.q_unix.data->path = g_strdup(vnc + 5);
|
|
||||||
|
|
||||||
if (ws_enabled) {
|
|
||||||
error_setg(errp, "UNIX sockets not supported with websock");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unsigned long long baseport;
|
|
||||||
InetSocketAddress *inet;
|
|
||||||
saddr->type = SOCKET_ADDRESS_KIND_INET;
|
|
||||||
inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
|
|
||||||
if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
|
|
||||||
inet->host = g_strndup(vnc + 1, hlen - 2);
|
|
||||||
} else {
|
|
||||||
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);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (baseport > 65535 ||
|
|
||||||
baseport + 5900 > 65535) {
|
|
||||||
error_setg(errp, "port %s out of range", h + 1);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
inet->port = g_strdup_printf(
|
|
||||||
"%d", (int)baseport + 5900);
|
|
||||||
|
|
||||||
if (to) {
|
|
||||||
inet->has_to = true;
|
|
||||||
inet->to = to + 5900;
|
|
||||||
show_vnc_port = 1;
|
|
||||||
}
|
|
||||||
inet->ipv4 = ipv4;
|
|
||||||
inet->has_ipv4 = has_ipv4;
|
|
||||||
inet->ipv6 = ipv6;
|
|
||||||
inet->has_ipv6 = has_ipv6;
|
|
||||||
|
|
||||||
if (ws_enabled) {
|
|
||||||
wsaddr->type = SOCKET_ADDRESS_KIND_INET;
|
|
||||||
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) {
|
|
||||||
inet->has_to = true;
|
|
||||||
inet->to = to;
|
|
||||||
}
|
|
||||||
inet->ipv4 = ipv4;
|
|
||||||
inet->has_ipv4 = has_ipv4;
|
|
||||||
inet->ipv6 = ipv6;
|
|
||||||
inet->has_ipv6 = has_ipv6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error_setg(errp, "no vnc port specified");
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
password = qemu_opt_get_bool(opts, "password", false);
|
password = qemu_opt_get_bool(opts, "password", false);
|
||||||
@ -3760,63 +4068,31 @@ void vnc_display_open(const char *id, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
/* connect to viewer */
|
if (vnc_display_connect(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) {
|
||||||
QIOChannelSocket *sioc = NULL;
|
|
||||||
vd->lsock = NULL;
|
|
||||||
vd->lwebsock = NULL;
|
|
||||||
if (ws_enabled) {
|
|
||||||
error_setg(errp, "Cannot use websockets in reverse mode");
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
|
|
||||||
sioc = qio_channel_socket_new();
|
|
||||||
qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse");
|
|
||||||
if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
vnc_connect(vd, sioc, false, false);
|
|
||||||
object_unref(OBJECT(sioc));
|
|
||||||
} else {
|
} else {
|
||||||
vd->lsock = qio_channel_socket_new();
|
if (vnc_display_listen(vd, saddr, nsaddr, wsaddr, nwsaddr, errp) < 0) {
|
||||||
qio_channel_set_name(QIO_CHANNEL(vd->lsock), "vnc-listen");
|
|
||||||
if (qio_channel_socket_listen_sync(vd->lsock, saddr, errp) < 0) {
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
|
|
||||||
|
|
||||||
if (ws_enabled) {
|
|
||||||
vd->lwebsock = qio_channel_socket_new();
|
|
||||||
qio_channel_set_name(QIO_CHANNEL(vd->lwebsock), "vnc-ws-listen");
|
|
||||||
if (qio_channel_socket_listen_sync(vd->lwebsock,
|
|
||||||
wsaddr, errp) < 0) {
|
|
||||||
object_unref(OBJECT(vd->lsock));
|
|
||||||
vd->lsock = NULL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vd->lsock_tag = qio_channel_add_watch(
|
|
||||||
QIO_CHANNEL(vd->lsock),
|
|
||||||
G_IO_IN, vnc_listen_io, vd, NULL);
|
|
||||||
if (ws_enabled) {
|
|
||||||
vd->lwebsock_tag = qio_channel_add_watch(
|
|
||||||
QIO_CHANNEL(vd->lwebsock),
|
|
||||||
G_IO_IN, vnc_listen_io, vd, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show_vnc_port) {
|
if (qemu_opt_get(opts, "to")) {
|
||||||
vnc_display_print_local_addr(vd);
|
vnc_display_print_local_addr(vd);
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_free_SocketAddress(saddr);
|
cleanup:
|
||||||
qapi_free_SocketAddress(wsaddr);
|
for (i = 0; i < nsaddr; i++) {
|
||||||
|
qapi_free_SocketAddress(saddr[i]);
|
||||||
|
}
|
||||||
|
for (i = 0; i < nwsaddr; i++) {
|
||||||
|
qapi_free_SocketAddress(wsaddr[i]);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
qapi_free_SocketAddress(saddr);
|
vnc_display_close(vd);
|
||||||
qapi_free_SocketAddress(wsaddr);
|
goto cleanup;
|
||||||
ws_enabled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vnc_display_add_client(const char *id, int csock, bool skipauth)
|
void vnc_display_add_client(const char *id, int csock, bool skipauth)
|
||||||
|
10
ui/vnc.h
10
ui/vnc.h
@ -146,10 +146,12 @@ struct VncDisplay
|
|||||||
int num_exclusive;
|
int num_exclusive;
|
||||||
int connections_limit;
|
int connections_limit;
|
||||||
VncSharePolicy share_policy;
|
VncSharePolicy share_policy;
|
||||||
QIOChannelSocket *lsock;
|
size_t nlsock;
|
||||||
guint lsock_tag;
|
QIOChannelSocket **lsock;
|
||||||
QIOChannelSocket *lwebsock;
|
guint *lsock_tag;
|
||||||
guint lwebsock_tag;
|
size_t nlwebsock;
|
||||||
|
QIOChannelSocket **lwebsock;
|
||||||
|
guint *lwebsock_tag;
|
||||||
DisplaySurface *ds;
|
DisplaySurface *ds;
|
||||||
DisplayChangeListener dcl;
|
DisplayChangeListener dcl;
|
||||||
kbd_layout_t *kbd_layout;
|
kbd_layout_t *kbd_layout;
|
||||||
|
@ -332,6 +332,25 @@ const char *qemu_opt_get(QemuOpts *opts, const char *name)
|
|||||||
return opt ? opt->str : NULL;
|
return opt ? opt->str : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qemu_opt_iter_init(QemuOptsIter *iter, QemuOpts *opts, const char *name)
|
||||||
|
{
|
||||||
|
iter->opts = opts;
|
||||||
|
iter->opt = QTAILQ_FIRST(&opts->head);
|
||||||
|
iter->name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *qemu_opt_iter_next(QemuOptsIter *iter)
|
||||||
|
{
|
||||||
|
QemuOpt *ret = iter->opt;
|
||||||
|
if (iter->name) {
|
||||||
|
while (ret && !g_str_equal(iter->name, ret->name)) {
|
||||||
|
ret = QTAILQ_NEXT(ret, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iter->opt = ret ? QTAILQ_NEXT(ret, next) : NULL;
|
||||||
|
return ret ? ret->str : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get a known option (or its default) and remove it from the list
|
/* Get a known option (or its default) and remove it from the list
|
||||||
* all in one action. Return a malloced string of the option value.
|
* all in one action. Return a malloced string of the option value.
|
||||||
* Result must be freed by caller with g_free().
|
* Result must be freed by caller with g_free().
|
||||||
|
Loading…
Reference in New Issue
Block a user