diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 8423bb4da1..c70d33ae12 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4882,6 +4882,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 ca4559f5f0..8c17e26c4b 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -166,6 +166,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 c96788b4a9..9e4802777c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -46,6 +46,8 @@ #include #include +#include + #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 */ @@ -547,6 +551,168 @@ static void qemuDomainObjExitRemoteWithDriver(struct qemud_driver *driver, virDomainObjUnref(obj); } +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, + 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, " \n"); + for (i = 0; i < cmd->num_args; i++) + virBufferEscapeString(buf, " \n", + cmd->args[i]); + for (i = 0; i < cmd->num_env; i++) { + virBufferVSprintf(buf, " env_name[i]); + if (cmd->env_value[i]) + virBufferEscapeString(buf, " value='%s'", cmd->env_value[i]); + virBufferAddLit(buf, "/>\n"); + } + virBufferAddLit(buf, " \n"); + + return 0; +} + +static const char *qemuDomainDefNamespaceHref(void) +{ + return "xmlns:qemu='" QEMU_NAMESPACE_HREF "'"; +} + static int qemuCgroupControllerActive(struct qemud_driver *driver, int controller) { @@ -1396,6 +1562,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;