Skip to content

Commit

Permalink
vnc: switch to QemuOpts, allow multiple servers
Browse files Browse the repository at this point in the history
This patch switches vnc over to QemuOpts, and it (more or less
as side effect) allows multiple vnc server instances.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
  • Loading branch information
kraxel committed Jan 22, 2015
1 parent c849640 commit 4db1462
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 132 deletions.
4 changes: 3 additions & 1 deletion include/ui/console.h
Expand Up @@ -332,12 +332,14 @@ void cocoa_display_init(DisplayState *ds, int full_screen);

/* vnc.c */
void vnc_display_init(const char *id);
void vnc_display_open(const char *id, const char *display, Error **errp);
void vnc_display_open(const char *id, Error **errp);
void vnc_display_add_client(const char *id, int csock, bool skipauth);
char *vnc_display_local_addr(const char *id);
#ifdef CONFIG_VNC
int vnc_display_password(const char *id, const char *password);
int vnc_display_pw_expire(const char *id, time_t expires);
QemuOpts *vnc_parse_func(const char *str);
int vnc_init_func(QemuOpts *opts, void *opaque);
#else
static inline int vnc_display_password(const char *id, const char *password)
{
Expand Down
15 changes: 14 additions & 1 deletion qmp.c
Expand Up @@ -368,7 +368,20 @@ void qmp_change_vnc_password(const char *password, Error **errp)

static void qmp_change_vnc_listen(const char *target, Error **errp)
{
vnc_display_open(NULL, target, errp);
QemuOptsList *olist = qemu_find_opts("vnc");
QemuOpts *opts;

if (strstr(target, "id=")) {
error_setg(errp, "id not supported");
return;
}

opts = qemu_opts_find(olist, "default");
if (opts) {
qemu_opts_del(opts);
}
opts = vnc_parse_func(target);
vnc_display_open("default", errp);
}

static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
Expand Down
270 changes: 169 additions & 101 deletions ui/vnc.c
Expand Up @@ -31,6 +31,7 @@
#include "qemu/sockets.h"
#include "qemu/timer.h"
#include "qemu/acl.h"
#include "qemu/config-file.h"
#include "qapi/qmp/types.h"
#include "qmp-commands.h"
#include "qemu/osdep.h"
Expand Down Expand Up @@ -2970,7 +2971,12 @@ static const DisplayChangeListenerOps dcl_ops = {

void vnc_display_init(const char *id)
{
VncDisplay *vs = g_malloc0(sizeof(*vs));
VncDisplay *vs;

if (vnc_display_find(id) != NULL) {
return;
}
vs = g_malloc0(sizeof(*vs));

vs->id = strdup(id);
QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
Expand Down Expand Up @@ -3066,14 +3072,65 @@ char *vnc_display_local_addr(const char *id)
return vnc_socket_local_addr("%s:%s", vs->lsock);
}

void vnc_display_open(const char *id, const char *display, Error **errp)
static QemuOptsList qemu_vnc_opts = {
.name = "vnc",
.head = QTAILQ_HEAD_INITIALIZER(qemu_vnc_opts.head),
.implied_opt_name = "vnc",
.desc = {
{
.name = "vnc",
.type = QEMU_OPT_STRING,
},{
.name = "websocket",
.type = QEMU_OPT_STRING,
},{
.name = "x509",
.type = QEMU_OPT_STRING,
},{
.name = "share",
.type = QEMU_OPT_STRING,
},{
.name = "password",
.type = QEMU_OPT_BOOL,
},{
.name = "reverse",
.type = QEMU_OPT_BOOL,
},{
.name = "lock-key-sync",
.type = QEMU_OPT_BOOL,
},{
.name = "sasl",
.type = QEMU_OPT_BOOL,
},{
.name = "tls",
.type = QEMU_OPT_BOOL,
},{
.name = "x509verify",
.type = QEMU_OPT_BOOL,
},{
.name = "acl",
.type = QEMU_OPT_BOOL,
},{
.name = "lossy",
.type = QEMU_OPT_BOOL,
},{
.name = "non-adaptive",
.type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},
};

void vnc_display_open(const char *id, Error **errp)
{
VncDisplay *vs = vnc_display_find(id);
const char *options;
QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
const char *display, *websocket, *share;
int password = 0;
int reverse = 0;
#ifdef CONFIG_VNC_TLS
int tls = 0, x509 = 0;
const char *path;
#endif
#ifdef CONFIG_VNC_SASL
int sasl = 0;
Expand All @@ -3089,115 +3146,86 @@ void vnc_display_open(const char *id, const char *display, Error **errp)
return;
}
vnc_display_close(vs);
if (strcmp(display, "none") == 0)
return;

if (!opts) {
return;
}
display = qemu_opt_get(opts, "vnc");
if (!display || strcmp(display, "none") == 0) {
return;
}
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", 16) == 0) {
lock_key_sync = 0;

password = qemu_opt_get_bool(opts, "password", false);
if (password && 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;
}

reverse = qemu_opt_get_bool(opts, "reverse", false);
lock_key_sync = qemu_opt_get_bool(opts, "lock-key-sync", true);
#ifdef CONFIG_VNC_SASL
} else if (strncmp(options, "sasl", 4) == 0) {
sasl = 1; /* Require SASL auth */
sasl = qemu_opt_get_bool(opts, "sasl", false);
#endif
#ifdef CONFIG_VNC_WS
} else if (strncmp(options, "websocket", 9) == 0) {
char *start, *end;
vs->websocket = 1;

/* Check for 'websocket=<port>' */
start = strchr(options, '=');
end = strchr(options, ',');
if (start && (!end || (start < end))) {
int len = end ? end-(start+1) : strlen(start+1);
if (len < 6) {
/* extract the host specification from display */
char *host = NULL, *port = NULL, *host_end = NULL;
port = g_strndup(start + 1, len);

/* ipv6 hosts have colons */
end = strchr(display, ',');
host_end = g_strrstr_len(display, end - display, ":");

if (host_end) {
host = g_strndup(display, host_end - display + 1);
} else {
host = g_strndup(":", 1);
}
vs->ws_display = g_strconcat(host, port, NULL);
g_free(host);
g_free(port);
}
}
#endif /* CONFIG_VNC_WS */
#ifdef CONFIG_VNC_TLS
} else if (strncmp(options, "tls", 3) == 0) {
tls = 1; /* Require TLS */
} else if (strncmp(options, "x509", 4) == 0) {
char *start, *end;
x509 = 1; /* Require x509 certificates */
if (strncmp(options, "x509verify", 10) == 0)
vs->tls.x509verify = 1; /* ...and verify client certs */

/* Now check for 'x509=/some/path' postfix
* and use that to setup x509 certificate/key paths */
start = strchr(options, '=');
end = strchr(options, ',');
if (start && (!end || (start < end))) {
int len = end ? end-(start+1) : strlen(start+1);
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) {
error_setg(errp, "Failed to find x509 certificates/keys in %s", path);
g_free(path);
goto fail;
}
g_free(path);
} else {
error_setg(errp, "No certificate path provided");
goto fail;
}
tls = qemu_opt_get_bool(opts, "tls", false);
path = qemu_opt_get(opts, "x509");
if (path) {
x509 = 1;
vs->tls.x509verify = qemu_opt_get_bool(opts, "x509verify", false);
if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
error_setg(errp, "Failed to find x509 certificates/keys in %s",
path);
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) {
#ifdef CONFIG_VNC_JPEG
vs->lossy = true;
acl = qemu_opt_get_bool(opts, "acl", false);
#endif
} else if (strncmp(options, "non-adaptive", 12) == 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;
}

share = qemu_opt_get(opts, "share");
if (share) {
if (strcmp(share, "ignore") == 0) {
vs->share_policy = VNC_SHARE_POLICY_IGNORE;
} else if (strcmp(share, "allow-exclusive") == 0) {
vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
} else if (strcmp(share, "force-shared") == 0) {
vs->share_policy = VNC_SHARE_POLICY_FORCE_SHARED;
} else {
error_setg(errp, "unknown vnc share= option");
goto fail;
}
} else {
vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
}

#ifdef CONFIG_VNC_WS
websocket = qemu_opt_get(opts, "websocket");
if (websocket) {
/* extract the host specification from display */
char *host = NULL, *host_end = NULL;
vs->websocket = 1;

/* ipv6 hosts have colons */
host_end = strrchr(display, ':');
if (host_end) {
host = g_strndup(display, host_end - display + 1);
} else {
host = g_strdup(":");
}
vs->ws_display = g_strconcat(host, websocket, NULL);
g_free(host);
}
#endif /* CONFIG_VNC_WS */

#ifdef CONFIG_VNC_JPEG
vs->lossy = qemu_opt_get_bool(opts, "lossy", false);
#endif
vs->non_adaptive = qemu_opt_get_bool(opts, "non-adaptive", false);
/* adaptive updates are only used with tight encoding and
* if lossy updates are enabled so we can disable all the
* calculations otherwise */
Expand Down Expand Up @@ -3408,3 +3436,43 @@ void vnc_display_add_client(const char *id, int csock, bool skipauth)
}
vnc_connect(vs, csock, skipauth, false);
}

QemuOpts *vnc_parse_func(const char *str)
{
return qemu_opts_parse(qemu_find_opts("vnc"), str, 1);
}

int vnc_init_func(QemuOpts *opts, void *opaque)
{
Error *local_err = NULL;
QemuOptsList *olist = qemu_find_opts("vnc");
char *id = (char *)qemu_opts_id(opts);

if (!id) {
/* auto-assign id if not present */
int i = 2;
id = g_strdup("default");
while (qemu_opts_find(olist, id)) {
g_free(id);
id = g_strdup_printf("vnc%d", i++);
}
qemu_opts_set_id(opts, id);
}

vnc_display_init(id);
vnc_display_open(id, &local_err);
if (local_err != NULL) {
error_report("Failed to start VNC server on `%s': %s",
qemu_opt_get(opts, "display"),
error_get_pretty(local_err));
error_free(local_err);
exit(1);
}
return 0;
}

static void vnc_register_config(void)
{
qemu_add_opts(&qemu_vnc_opts);
}
machine_init(vnc_register_config);

0 comments on commit 4db1462

Please sign in to comment.