vnc: switch to QemuOpts, allow multiple servers
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>
This commit is contained in:
parent
c8496408b4
commit
4db14629c3
@ -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)
|
||||
{
|
||||
|
15
qmp.c
15
qmp.c
@ -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,
|
||||
|
236
ui/vnc.c
236
ui/vnc.c
@ -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"
|
||||
@ -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);
|
||||
@ -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;
|
||||
@ -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)
|
||||
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
||||
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);
|
||||
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);
|
||||
g_free(path);
|
||||
error_setg(errp, "Failed to find x509 certificates/keys in %s",
|
||||
path);
|
||||
goto fail;
|
||||
}
|
||||
g_free(path);
|
||||
} else {
|
||||
error_setg(errp, "No certificate path provided");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
|
||||
} else if (strncmp(options, "acl", 3) == 0) {
|
||||
acl = 1;
|
||||
acl = qemu_opt_get_bool(opts, "acl", false);
|
||||
#endif
|
||||
} else if (strncmp(options, "lossy", 5) == 0) {
|
||||
#ifdef CONFIG_VNC_JPEG
|
||||
vs->lossy = true;
|
||||
#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) {
|
||||
|
||||
share = qemu_opt_get(opts, "share");
|
||||
if (share) {
|
||||
if (strcmp(share, "ignore") == 0) {
|
||||
vs->share_policy = VNC_SHARE_POLICY_IGNORE;
|
||||
} else if (strncmp(options+6, "allow-exclusive", 15) == 0) {
|
||||
} else if (strcmp(share, "allow-exclusive") == 0) {
|
||||
vs->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE;
|
||||
} else if (strncmp(options+6, "force-shared", 12) == 0) {
|
||||
} 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 */
|
||||
@ -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);
|
||||
|
38
vl.c
38
vl.c
@ -158,9 +158,6 @@ int smp_cpus = 1;
|
||||
int max_cpus = 0;
|
||||
int smp_cores = 1;
|
||||
int smp_threads = 1;
|
||||
#ifdef CONFIG_VNC
|
||||
const char *vnc_display;
|
||||
#endif
|
||||
int acpi_enabled = 1;
|
||||
int no_hpet = 0;
|
||||
int fd_bootchk = 1;
|
||||
@ -2002,16 +1999,12 @@ static DisplayType select_display(const char *p)
|
||||
#endif
|
||||
} else if (strstart(p, "vnc", &opts)) {
|
||||
#ifdef CONFIG_VNC
|
||||
if (*opts == '=') {
|
||||
display_remote++;
|
||||
|
||||
if (*opts) {
|
||||
const char *nextopt;
|
||||
|
||||
if (strstart(opts, "=", &nextopt)) {
|
||||
vnc_display = nextopt;
|
||||
if (vnc_parse_func(opts+1) == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (!vnc_display) {
|
||||
} else {
|
||||
fprintf(stderr, "VNC requires a display argument vnc=<display>\n");
|
||||
exit(1);
|
||||
}
|
||||
@ -3479,7 +3472,9 @@ int main(int argc, char **argv, char **envp)
|
||||
case QEMU_OPTION_vnc:
|
||||
#ifdef CONFIG_VNC
|
||||
display_remote++;
|
||||
vnc_display = optarg;
|
||||
if (vnc_parse_func(optarg) == NULL) {
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
fprintf(stderr, "VNC support is disabled\n");
|
||||
exit(1);
|
||||
@ -3975,7 +3970,7 @@ int main(int argc, char **argv, char **envp)
|
||||
#elif defined(CONFIG_SDL) || defined(CONFIG_COCOA)
|
||||
display_type = DT_SDL;
|
||||
#elif defined(CONFIG_VNC)
|
||||
vnc_display = "localhost:0,to=99";
|
||||
vnc_parse_func("localhost:0,to=99,id=default");
|
||||
show_vnc_port = 1;
|
||||
#else
|
||||
display_type = DT_NONE;
|
||||
@ -4286,21 +4281,10 @@ int main(int argc, char **argv, char **envp)
|
||||
|
||||
#ifdef CONFIG_VNC
|
||||
/* init remote displays */
|
||||
if (vnc_display) {
|
||||
Error *local_err = NULL;
|
||||
const char *id = "default";
|
||||
vnc_display_init(id);
|
||||
vnc_display_open(id, vnc_display, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_report("Failed to start VNC server on `%s': %s",
|
||||
vnc_display, error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
qemu_opts_foreach(qemu_find_opts("vnc"), vnc_init_func, NULL, 0);
|
||||
if (show_vnc_port) {
|
||||
printf("VNC server running on `%s'\n", vnc_display_local_addr(id));
|
||||
}
|
||||
printf("VNC server running on `%s'\n",
|
||||
vnc_display_local_addr("default"));
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_SPICE
|
||||
|
Loading…
Reference in New Issue
Block a user