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