On Tue, Jan 20, 2009 at 11:08:56PM +0000, Daniel P. Berrange wrote:
This patch adds support for using the monitor interface to set the
VNC
password
(qemu) change vnc password
Password: ********
A minor tricky thing is that we can't just send the command and password
all in one go, we must wait for the 'Password' prompt before sending the
password.
When doing this I noticed that virsh dumpxml has no way to request a
secure XML dump (required to see the password element), nor did the
virsh edit command set the SECURE or INACTIVE flags when changing
the XML.
qemu_conf.c | 45 ++++++++++++-----------
qemu_driver.c | 112 ++++++++++++++++++++++++++++++++++++++++++++--------------
virsh.c | 30 ++++++++++-----
3 files changed, 131 insertions(+), 56 deletions(-)
This update also makes it possible to set a global default password
for all QEMU vms in /etc/libvirt/qemu.conf. This mirrors a similar
option XenD has. A per-VM specified password always takes priority
qemud/Makefile.am | 2
qemud/libvirtd_qemu.aug | 1
qemud/test_libvirtd.aug | 18 ++++++
qemud/test_libvirtd_qemu.aug | 33 +++++++-----
src/qemu.conf | 11 ++++
src/qemu_conf.c | 58 ++++++++++++++-------
src/qemu_conf.h | 1
src/qemu_driver.c | 116 +++++++++++++++++++++++++++++++++----------
src/uml_driver.c | 4 +
src/virsh.c | 30 +++++++----
10 files changed, 204 insertions(+), 70 deletions(-)
Daniel
diff --git a/qemud/Makefile.am b/qemud/Makefile.am
--- a/qemud/Makefile.am
+++ b/qemud/Makefile.am
@@ -246,6 +246,8 @@ libvirtd.init: libvirtd.init.in
check-local:
test -x '$(AUGPARSE)' \
&& '$(AUGPARSE)' -I $(srcdir) $(srcdir)/test_libvirtd.aug || :
+ test -x '$(AUGPARSE)' \
+ && '$(AUGPARSE)' -I $(srcdir) $(srcdir)/test_libvirtd_qemu.aug || :
else
diff --git a/qemud/libvirtd_qemu.aug b/qemud/libvirtd_qemu.aug
--- a/qemud/libvirtd_qemu.aug
+++ b/qemud/libvirtd_qemu.aug
@@ -26,6 +26,7 @@ module Libvirtd_qemu =
| bool_entry "vnc_tls"
| str_entry "vnc_tls_x509_cert_dir"
| bool_entry "vnc_tls_x509_verify"
+ | str_entry "vnc_password"
(* Each enty in the config is one of the following three ... *)
let entry = vnc_entry
diff --git a/qemud/test_libvirtd.aug b/qemud/test_libvirtd.aug
--- a/qemud/test_libvirtd.aug
+++ b/qemud/test_libvirtd.aug
@@ -259,6 +259,15 @@ max_requests = 20
# this should be a small fraction of the global max_requests
# and max_workers parameter
max_client_requests = 5
+
+# Logging level:
+log_level = 4
+
+# Logging outputs:
+log_outputs=\"4:stderr\"
+
+# Logging filters:
+log_filters=\"a\"
"
test Libvirtd.lns get conf =
@@ -525,3 +534,12 @@ max_client_requests = 5
{ "#comment" = "this should be a small fraction of the global
max_requests" }
{ "#comment" = "and max_workers parameter" }
{ "max_client_requests" = "5" }
+ { "#empty" }
+ { "#comment" = "Logging level:" }
+ { "log_level" = "4" }
+ { "#empty" }
+ { "#comment" = "Logging outputs:" }
+ { "log_outputs" = "4:stderr" }
+ { "#empty" }
+ { "#comment" = "Logging filters:" }
+ { "log_filters" = "a" }
diff --git a/qemud/test_libvirtd_qemu.aug b/qemud/test_libvirtd_qemu.aug
--- a/qemud/test_libvirtd_qemu.aug
+++ b/qemud/test_libvirtd_qemu.aug
@@ -50,14 +50,16 @@ vnc_tls_x509_cert_dir = \"/etc/pki/libvi
#
vnc_tls_x509_verify = 1
-# Logging level:
-log_level = 4
-# Logging outputs:
-log_outputs="4:stderr"
-
-# Logging filters:
-log_filters=""
+# The default VNC password. Only 8 letters are significant for
+# VNC passwords. This parameter is only used if the per-domain
+# XML config does not already provide a password. To allow
+# access without passwords, leave this commented out. An empty
+# string will still enable passwords, but be rejected by QEMU
+# effectively preventing any use of VNC. Obviously change this
+# example here before you set this
+#
+vnc_password = \"XYZ12345\"
"
test Libvirtd_qemu.lns get conf =
@@ -110,9 +112,14 @@ log_filters=""
{ "#comment" = "certificate signed by the CA in
/etc/pki/libvirt-vnc/ca-cert.pem" }
{ "#comment" = "" }
{ "vnc_tls_x509_verify" = "1" }
-{ "#comment" = "Logging level:" }
-{ "log_level" = "4" }
-{ "#comment" = "Logging outputs:" }
-{ "log_outputs" = "4:stderr" }
-{ "#comment" = "Logging filters" }
-{ "log_filters" = "" }
+{ "#empty" }
+{ "#empty" }
+{ "#comment" = "The default VNC password. Only 8 letters are significant
for" }
+{ "#comment" = "VNC passwords. This parameter is only used if the
per-domain" }
+{ "#comment" = "XML config does not already provide a password. To
allow" }
+{ "#comment" = "access without passwords, leave this commented out. An
empty" }
+{ "#comment" = "string will still enable passwords, but be rejected by
QEMU" }
+{ "#comment" = "effectively preventing any use of VNC. Obviously change
this" }
+{ "#comment" = "example here before you set this" }
+{ "#comment" = "" }
+{ "vnc_password" = "XYZ12345" }
diff --git a/src/qemu.conf b/src/qemu.conf
--- a/src/qemu.conf
+++ b/src/qemu.conf
@@ -47,3 +47,14 @@
# certificate signed by the CA in /etc/pki/libvirt-vnc/ca-cert.pem
#
# vnc_tls_x509_verify = 1
+
+
+# The default VNC password. Only 8 letters are significant for
+# VNC passwords. This parameter is only used if the per-domain
+# XML config does not already provide a password. To allow
+# access without passwords, leave this commented out. An empty
+# string will still enable passwords, but be rejected by QEMU
+# effectively preventing any use of VNC. Obviously change this
+# example here before you set this
+#
+# vnc_password = "XYZ12345"
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -129,6 +129,18 @@ int qemudLoadDriverConfig(struct qemud_d
}
}
+ p = virConfGetValue (conf, "vnc_password");
+ CHECK_TYPE ("vnc_password", VIR_CONF_STRING);
+ if (p && p->str) {
+ VIR_FREE(driver->vncPassword);
+ if (!(driver->vncPassword = strdup(p->str))) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_NO_MEMORY,
+ "%s", _("failed to allocate
vnc_listen"));
+ virConfFree(conf);
+ return -1;
+ }
+ }
+
virConfFree (conf);
return 0;
}
@@ -1138,37 +1150,43 @@ int qemudBuildCommandLine(virConnectPtr
if (vm->def->graphics &&
vm->def->graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
- char vncdisplay[PATH_MAX];
- int ret;
+ virBuffer opt = VIR_BUFFER_INITIALIZER;
+ char *optstr;
if (qemuCmdFlags & QEMUD_CMD_FLAG_VNC_COLON) {
- char options[PATH_MAX] = "";
+ if (vm->def->graphics->data.vnc.listenAddr)
+ virBufferAdd(&opt, vm->def->graphics->data.vnc.listenAddr,
-1);
+ else if (driver->vncListen)
+ virBufferAdd(&opt, driver->vncListen, -1);
+
+ virBufferVSprintf(&opt, ":%d",
+ vm->def->graphics->data.vnc.port - 5900);
+
+ if (vm->def->graphics->data.vnc.passwd ||
+ driver->vncPassword)
+ virBufferAddLit(&opt, ",password");
+
if (driver->vncTLS) {
- strcat(options, ",tls");
+ virBufferAddLit(&opt, ",tls");
if (driver->vncTLSx509verify) {
- strcat(options, ",x509verify=");
+ virBufferVSprintf(&opt, ",x509verify=%s",
+ driver->vncTLSx509certdir);
} else {
- strcat(options, ",x509=");
+ virBufferVSprintf(&opt, ",x509=%s",
+ driver->vncTLSx509certdir);
}
- strncat(options, driver->vncTLSx509certdir,
- sizeof(options) - (strlen(driver->vncTLSx509certdir)-1));
- options[sizeof(options)-1] = '\0';
}
- ret = snprintf(vncdisplay, sizeof(vncdisplay), "%s:%d%s",
- (vm->def->graphics->data.vnc.listenAddr ?
- vm->def->graphics->data.vnc.listenAddr :
- (driver->vncListen ? driver->vncListen :
"")),
- vm->def->graphics->data.vnc.port - 5900,
- options);
} else {
- ret = snprintf(vncdisplay, sizeof(vncdisplay), "%d",
- vm->def->graphics->data.vnc.port - 5900);
+ virBufferVSprintf(&opt, "%d",
+ vm->def->graphics->data.vnc.port - 5900);
}
- if (ret < 0 || ret >= (int)sizeof(vncdisplay))
- goto error;
+ if (virBufferError(&opt))
+ goto no_memory;
+
+ optstr = virBufferContentAndReset(&opt);
ADD_ARG_LIT("-vnc");
- ADD_ARG_LIT(vncdisplay);
+ ADD_ARG(optstr);
if (vm->def->graphics->data.vnc.keymap) {
ADD_ARG_LIT("-k");
ADD_ARG_LIT(vm->def->graphics->data.vnc.keymap);
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
--- a/src/qemu_conf.h
+++ b/src/qemu_conf.h
@@ -68,6 +68,7 @@ struct qemud_driver {
unsigned int vncTLSx509verify : 1;
char *vncTLSx509certdir;
char *vncListen;
+ char *vncPassword;
virCapsPtr caps;
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -74,6 +74,10 @@
/* For storing short-lived temporary files. */
#define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt"
+#define QEMU_CMD_PROMPT "\n(qemu) "
+#define QEMU_PASSWD_PROMPT "Password: "
+
+
static int qemudShutdown(void);
#define qemudLog(level, msg...) fprintf(stderr, msg)
@@ -138,9 +142,14 @@ static void qemudShutdownVMDaemon(virCon
static int qemudDomainGetMaxVcpus(virDomainPtr dom);
-static int qemudMonitorCommand (const virDomainObjPtr vm,
- const char *cmd,
- char **reply);
+static int qemudMonitorCommand(const virDomainObjPtr vm,
+ const char *cmd,
+ char **reply);
+static int qemudMonitorCommandExtra(const virDomainObjPtr vm,
+ const char *cmd,
+ const char *extra,
+ const char *extraPrompt,
+ char **reply);
static struct qemud_driver *qemu_driver = NULL;
@@ -583,6 +592,7 @@ qemudShutdown(void) {
VIR_FREE(qemu_driver->stateDir);
VIR_FREE(qemu_driver->vncTLSx509certdir);
VIR_FREE(qemu_driver->vncListen);
+ VIR_FREE(qemu_driver->vncPassword);
/* Free domain callback list */
virDomainEventCallbackListFree(qemu_driver->domainEventCallbacks);
@@ -1012,6 +1022,39 @@ qemudInitCpus(virConnectPtr conn,
}
+static int
+qemudInitPasswords(virConnectPtr conn,
+ struct qemud_driver *driver,
+ virDomainObjPtr vm) {
+ char *info = NULL;
+
+ /*
+ * NB: Might have more passwords to set in the future. eg a qcow
+ * disk decryption password, but there's no monitor command
+ * for that yet...
+ */
+
+ if (vm->def->graphics &&
+ vm->def->graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
+ vm->def->graphics->data.vnc.passwd) {
+
+ if (qemudMonitorCommandExtra(vm, "change vnc password",
+ vm->def->graphics->data.vnc.passwd ?
+ vm->def->graphics->data.vnc.passwd :
+ driver->vncPassword,
+ QEMU_PASSWD_PROMPT,
+ &info) < 0) {
+ qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("setting VNC password
failed"));
+ return -1;
+ }
+ VIR_FREE(info);
+ }
+
+ return 0;
+}
+
+
static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
int i;
@@ -1204,7 +1247,8 @@ static int qemudStartVMDaemon(virConnect
if (ret == 0) {
if ((qemudWaitForMonitor(conn, driver, vm, pos) < 0) ||
(qemudDetectVcpuPIDs(conn, vm) < 0) ||
- (qemudInitCpus(conn, vm, migrateFrom) < 0)) {
+ (qemudInitCpus(conn, vm, migrateFrom) < 0) ||
+ (qemudInitPasswords(conn, driver, vm) < 0)) {
qemudShutdownVMDaemon(conn, driver, vm);
return -1;
}
@@ -1314,12 +1358,15 @@ cleanup:
}
static int
-qemudMonitorCommand (const virDomainObjPtr vm,
- const char *cmd,
- char **reply) {
+qemudMonitorCommandExtra(const virDomainObjPtr vm,
+ const char *cmd,
+ const char *extra,
+ const char *extraPrompt,
+ char **reply) {
int size = 0;
char *buf = NULL;
size_t cmdlen = strlen(cmd);
+ size_t extralen = extra ? strlen(extra) : 0;
if (safewrite(vm->monitor, cmd, cmdlen) != cmdlen)
return -1;
@@ -1355,25 +1402,34 @@ qemudMonitorCommand (const virDomainObjP
}
/* Look for QEMU prompt to indicate completion */
- if (buf && ((tmp = strstr(buf, "\n(qemu) ")) != NULL)) {
- char *commptr = NULL, *nlptr = NULL;
-
- /* Preserve the newline */
- tmp[1] = '\0';
-
- /* The monitor doesn't dump clean output after we have written to
- * it. Every character we write dumps a bunch of useless stuff,
- * so the result looks like "cXcoXcomXcommXcommaXcommanXcommand"
- * Try to throw away everything before the first full command
- * occurence, and inbetween the command and the newline starting
- * the response
- */
- if ((commptr = strstr(buf, cmd)))
- memmove(buf, commptr, strlen(commptr)+1);
- if ((nlptr = strchr(buf, '\n')))
- memmove(buf+strlen(cmd), nlptr, strlen(nlptr)+1);
-
- break;
+ if (buf) {
+ if (extra) {
+ if (strstr(buf, extraPrompt) != NULL) {
+ if (safewrite(vm->monitor, extra, extralen) != extralen)
+ return -1;
+ if (safewrite(vm->monitor, "\r", 1) != 1)
+ return -1;
+ extra = NULL;
+ }
+ } else if ((tmp = strstr(buf, QEMU_CMD_PROMPT)) != NULL) {
+ char *commptr = NULL, *nlptr = NULL;
+ /* Preserve the newline */
+ tmp[1] = '\0';
+
+ /* The monitor doesn't dump clean output after we have written to
+ * it. Every character we write dumps a bunch of useless stuff,
+ * so the result looks like
"cXcoXcomXcommXcommaXcommanXcommand"
+ * Try to throw away everything before the first full command
+ * occurence, and inbetween the command and the newline starting
+ * the response
+ */
+ if ((commptr = strstr(buf, cmd)))
+ memmove(buf, commptr, strlen(commptr)+1);
+ if ((nlptr = strchr(buf, '\n')))
+ memmove(buf+strlen(cmd), nlptr, strlen(nlptr)+1);
+
+ break;
+ }
}
pollagain:
/* Need to wait for more data */
@@ -1403,6 +1459,14 @@ qemudMonitorCommand (const virDomainObjP
return -1;
}
+static int
+qemudMonitorCommand(const virDomainObjPtr vm,
+ const char *cmd,
+ char **reply) {
+ return qemudMonitorCommandExtra(vm, cmd, NULL, NULL, reply);
+}
+
+
/**
* qemudProbe:
*
diff --git a/src/uml_driver.c b/src/uml_driver.c
--- a/src/uml_driver.c
+++ b/src/uml_driver.c
@@ -324,6 +324,7 @@ umlStartup(void) {
/* Don't have a dom0 so start from 1 */
uml_driver->nextvmid = 1;
+ uml_driver->inotifyWatch = -1;
userdir = virGetUserDirectory(NULL, uid);
if (!userdir)
@@ -484,7 +485,8 @@ umlShutdown(void) {
return -1;
umlDriverLock(uml_driver);
- virEventRemoveHandle(uml_driver->inotifyWatch);
+ if (uml_driver->inotifyWatch != -1)
+ virEventRemoveHandle(uml_driver->inotifyWatch);
close(uml_driver->inotifyFD);
virCapabilitiesFree(uml_driver->caps);
diff --git a/src/virsh.c b/src/virsh.c
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -2079,6 +2079,8 @@ static const vshCmdInfo info_dumpxml[] =
static const vshCmdOptDef opts_dumpxml[] = {
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id
or uuid")},
+ {"inactive", VSH_OT_BOOL, 0, gettext_noop("show inactive defined
XML")},
+ {"secure", VSH_OT_BOOL, 0, gettext_noop("include security sensitive
data")},
{NULL, 0, 0, NULL}
};
@@ -2088,14 +2090,22 @@ cmdDumpXML(vshControl *ctl, const vshCmd
virDomainPtr dom;
int ret = TRUE;
char *dump;
-
- if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
- return FALSE;
-
- if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
- return FALSE;
-
- dump = virDomainGetXMLDesc(dom, 0);
+ int flags = 0;
+ int inactive = vshCommandOptBool(cmd, "inactive");
+ int secure = vshCommandOptBool(cmd, "secure");
+
+ if (inactive)
+ flags |= VIR_DOMAIN_XML_INACTIVE;
+ if(secure)
+ flags |= VIR_DOMAIN_XML_SECURE;
+
+ if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+ return FALSE;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return FALSE;
+
+ dump = virDomainGetXMLDesc(dom, flags);
if (dump != NULL) {
printf("%s", dump);
free(dump);
@@ -5374,7 +5384,7 @@ cmdEdit (vshControl *ctl, const vshCmd *
goto cleanup;
/* Get the XML configuration of the domain. */
- doc = virDomainGetXMLDesc (dom, 0);
+ doc = virDomainGetXMLDesc (dom, VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_INACTIVE);
if (!doc)
goto cleanup;
@@ -5404,7 +5414,7 @@ cmdEdit (vshControl *ctl, const vshCmd *
* it was being edited? This also catches problems such as us
* losing a connection or the domain going away.
*/
- doc_reread = virDomainGetXMLDesc (dom, 0);
+ doc_reread = virDomainGetXMLDesc (dom, VIR_DOMAIN_XML_SECURE |
VIR_DOMAIN_XML_INACTIVE);
if (!doc_reread)
goto cleanup;
--
|: Red Hat, Engineering, London -o-
http://people.redhat.com/berrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org -o-
http://ovirt.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|