Implement the qemu hooks for XML namespace data.  This
allows us to specify a qemu XML namespace, and then
specify:

<qemu:commandline>
 <qemu:arg value='arg'/>
 <qemu:env name='name' value='value'/>
</qemu:commandline>

In the domain XML.

Changes since v1:
 - Change the <qemu:arg>arg</qemu:arg> XML to <qemu:arg value='arg'/> XML
 - Fix up some memory leaks in qemuDomainDefNamespaceParse
 - Rename num_extra and extra to num_args and args, respectively
 - Fixed up some error messages
 - Make sure to escape user-provided data in qemuDomainDefNamespaceFormatXML

Changes since v2:
 - Add checking to ensure environment variable names are valid
 - Invert the logic in qemuDomainDefNamespaceFormatXML to return early

Changes since v3:
 - Change strspn() to c_isalpha() check of first letter of environment variable

Signed-off-by: Chris Lalancette <clala...@redhat.com>
---
 src/qemu/qemu_conf.c   |   14 ++++
 src/qemu/qemu_conf.h   |   11 +++
 src/qemu/qemu_driver.c |  171 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 196 insertions(+), 0 deletions(-)

diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 988220b..151fa68 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -4766,6 +4766,20 @@ int qemudBuildCommandLine(virConnectPtr conn,
         ADD_ARG_LIT(current_snapshot->def->name);
     }
 
+    if (def->namespaceData) {
+        qemuDomainCmdlineDefPtr cmd;
+
+        cmd = def->namespaceData;
+        for (i = 0; i < cmd->num_args; i++)
+            ADD_ARG_LIT(cmd->args[i]);
+        for (i = 0; i < cmd->num_env; i++) {
+            if (cmd->env_value[i])
+                ADD_ENV_PAIR(cmd->env_name[i], cmd->env_value[i]);
+            else
+                ADD_ENV_PAIR(cmd->env_name[i], "");
+        }
+    }
+
     ADD_ARG(NULL);
     ADD_ENV(NULL);
 
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index ab5f158..821ed57 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -165,6 +165,17 @@ struct qemud_driver {
 typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet;
 typedef qemuDomainPCIAddressSet *qemuDomainPCIAddressSetPtr;
 
+typedef struct _qemuDomainCmdlineDef qemuDomainCmdlineDef;
+typedef qemuDomainCmdlineDef *qemuDomainCmdlineDefPtr;
+struct _qemuDomainCmdlineDef {
+    unsigned int num_args;
+    char **args;
+
+    unsigned int num_env;
+    char **env_name;
+    char **env_value;
+};
+
 /* Port numbers used for KVM migration. */
 # define QEMUD_MIGRATION_FIRST_PORT 49152
 # define QEMUD_MIGRATION_NUM_PORTS 64
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 487bfa3..2abdbc4 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -46,6 +46,8 @@
 #include <sys/ioctl.h>
 #include <sys/un.h>
 
+#include <libxml/xpathInternals.h>
+
 #include "virterror_internal.h"
 #include "logging.h"
 #include "datatypes.h"
@@ -84,6 +86,8 @@
 #define QEMU_VNC_PORT_MIN  5900
 #define QEMU_VNC_PORT_MAX  65535
 
+#define QEMU_NAMESPACE_HREF "http://libvirt.org/schemas/domain/qemu/1.0";
+
 /* Only 1 job is allowed at any time
  * A job includes *all* monitor commands, even those just querying
  * information, not merely actions */
@@ -531,6 +535,167 @@ static void qemuDomainObjExitMonitorWithDriver(struct 
qemud_driver *driver, virD
     }
 }
 
+static void qemuDomainDefNamespaceFree(void *nsdata)
+{
+    qemuDomainCmdlineDefPtr cmd = nsdata;
+    unsigned int i;
+
+    if (!cmd)
+        return;
+
+    for (i = 0; i < cmd->num_args; i++)
+        VIR_FREE(cmd->args[i]);
+    for (i = 0; i < cmd->num_env; i++) {
+        VIR_FREE(cmd->env_name[i]);
+        VIR_FREE(cmd->env_value[i]);
+    }
+    VIR_FREE(cmd->args);
+    VIR_FREE(cmd->env_name);
+    VIR_FREE(cmd->env_value);
+    VIR_FREE(cmd);
+}
+
+static int qemuDomainDefNamespaceParse(xmlDocPtr xml,
+                                       xmlNodePtr root,
+                                       xmlXPathContextPtr ctxt 
ATTRIBUTE_UNUSED,
+                                       void **data)
+{
+    qemuDomainCmdlineDefPtr cmd = NULL;
+    xmlNsPtr ns;
+    xmlNodePtr *nodes = NULL;
+    int n, i;
+
+    ns = xmlSearchNs(xml, root, BAD_CAST "qemu");
+    if (!ns)
+        /* this is fine; it just means there was no qemu namespace listed */
+        return 0;
+
+    if (STRNEQ((const char *)ns->href, QEMU_NAMESPACE_HREF)) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("Found namespace '%s' doesn't match expected '%s'"),
+                        ns->href, QEMU_NAMESPACE_HREF);
+        return -1;
+    }
+
+    if (xmlXPathRegisterNs(ctxt, ns->prefix, ns->href) < 0) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("Failed to register xml namespace '%s'"), ns->href);
+        return -1;
+    }
+
+    if (VIR_ALLOC(cmd) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    /* first handle the extra command-line arguments */
+    n = virXPathNodeSet("./qemu:commandline/qemu:arg", ctxt, &nodes);
+    if (n < 0)
+        /* virXPathNodeSet already set the error */
+        goto error;
+
+    if (n && VIR_ALLOC_N(cmd->args, n) < 0)
+        goto no_memory;
+
+    for (i = 0; i < n; i++) {
+        cmd->args[cmd->num_args] = virXMLPropString(nodes[i], "value");
+        if (cmd->args[cmd->num_args] == NULL) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            "%s", _("No qemu command-line argument 
specified"));
+            goto error;
+        }
+        cmd->num_args++;
+    }
+
+    VIR_FREE(nodes);
+
+    /* now handle the extra environment variables */
+    n = virXPathNodeSet("./qemu:commandline/qemu:env", ctxt, &nodes);
+    if (n < 0)
+        /* virXPathNodeSet already set the error */
+        goto error;
+
+    if (n && VIR_ALLOC_N(cmd->env_name, n) < 0)
+        goto no_memory;
+
+    if (n && VIR_ALLOC_N(cmd->env_value, n) < 0)
+        goto no_memory;
+
+    for (i = 0; i < n; i++) {
+        char *tmp;
+
+        tmp = virXMLPropString(nodes[i], "name");
+        if (tmp == NULL) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            "%s", _("No qemu environment name specified"));
+            goto error;
+        }
+        if (tmp[0] == '\0') {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            "%s", _("Empty qemu environment name specified"));
+            goto error;
+        }
+        if (!c_isalpha(tmp[0]) && tmp[0] != '_') {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            "%s", _("Invalid environment name, it must begin 
with a letter or underscore"));
+            goto error;
+        }
+        if (strspn(tmp, 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_") != 
strlen(tmp)) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            "%s", _("Invalid environment name, it must contain 
only alphanumerics and underscore"));
+            goto error;
+        }
+
+        cmd->env_name[cmd->num_env] = tmp;
+
+        cmd->env_value[cmd->num_env] = virXMLPropString(nodes[i], "value");
+        /* a NULL value for command is allowed, since it might be empty */
+        cmd->num_env++;
+    }
+
+    VIR_FREE(nodes);
+
+    *data = cmd;
+
+    return 0;
+
+no_memory:
+    virReportOOMError();
+
+error:
+    VIR_FREE(nodes);
+    qemuDomainDefNamespaceFree(cmd);
+    return -1;
+}
+
+static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf,
+                                           void *nsdata)
+{
+    qemuDomainCmdlineDefPtr cmd = nsdata;
+    unsigned int i;
+
+    if (!cmd->num_args && !cmd->num_env)
+        return 0;
+
+    virBufferAddLit(buf, "  <qemu:commandline>\n");
+    for (i = 0; i < cmd->num_args; i++)
+        virBufferEscapeString(buf, "    <qemu:arg value='%s'/>\n",
+                              cmd->args[i]);
+    for (i = 0; i < cmd->num_env; i++) {
+        virBufferVSprintf(buf, "    <qemu:env name='%s'", cmd->env_name[i]);
+        if (cmd->env_value[i])
+            virBufferEscapeString(buf, " value='%s'", cmd->env_value[i]);
+        virBufferAddLit(buf, "/>\n");
+    }
+    virBufferAddLit(buf, "  </qemu:commandline>\n");
+
+    return 0;
+}
+
+static const char *qemuDomainDefNamespaceHref(void)
+{
+    return "xmlns:qemu='" QEMU_NAMESPACE_HREF "'";
+}
 
 static int qemuCgroupControllerActive(struct qemud_driver *driver,
                                       int controller)
@@ -1367,6 +1532,12 @@ qemuCreateCapabilities(virCapsPtr oldcaps,
         goto err_exit;
     }
 
+    /* Domain Namespace XML parser hooks */
+    caps->ns.parse = qemuDomainDefNamespaceParse;
+    caps->ns.free = qemuDomainDefNamespaceFree;
+    caps->ns.format = qemuDomainDefNamespaceFormatXML;
+    caps->ns.href = qemuDomainDefNamespaceHref;
+
     /* Security driver data */
     if (driver->securityPrimaryDriver) {
         const char *doi, *model;
-- 
1.6.6.1

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to