[libvirt] PATCH: Support VNC password for QEMU guests

Daniel P. Berrange berrange at redhat.com
Tue Jan 20 23:08:56 UTC 2009


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(-)

Daniel

diff --git a/src/qemu_conf.c b/src/qemu_conf.c
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -1138,37 +1138,42 @@ 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)
+                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_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;
 
@@ -1012,6 +1021,36 @@ qemudInitCpus(virConnectPtr conn,
 }
 
 
+static int
+qemudInitPasswords(virConnectPtr conn,
+                   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,
+                                     QEMU_PASSWD_PROMPT,
+                                     &info) < 0) {
+            qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("resume operation failed"));
+            return -1;
+        }
+        VIR_FREE(info);
+    }
+
+    return 0;
+}
+
+
 static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
     int i;
 
@@ -1204,7 +1243,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, vm) < 0)) {
             qemudShutdownVMDaemon(conn, driver, vm);
             return -1;
         }
@@ -1314,12 +1354,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 +1398,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 +1455,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/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 :|




More information about the libvir-list mailing list