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@xxxxxxxxxx> --- 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@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list