[libvirt] [PATCH 00/10]: Add arbitrary qemu command-line and monitor commands

As we discussed previously, here is the patch series to add the ability to specify arbitrary qemu command-line parameters and environment variables, and also give arbitrary monitor commands to a guest. Because these extra arguments have a good shot at confusing libvirt, the use of them is not supported, but left available for advanced users and developers. They are also in a separate library and have a separate on-the-wire protocol. There is one bug left that I have not yet been able to fix. Because of the complicated way that virsh parses command-line arguments, it is not possible to pass through spaces and quotes when using the qemu-monitor-command. Unfortunately, the qemu monitor commands (and in particular when using QMP) depend heavily on quoting and spacing, so using virsh to send through command-lines is difficult. I'll have to think about how to better resolve this issue, but it should not hold up the rest of the series. Thanks to Dan Berrange for his review already, and to DV for the Relax NG schema changes.

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/esx/esx_vi.h | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h index e0d731e..a2ed5a6 100644 --- a/src/esx/esx_vi.h +++ b/src/esx/esx_vi.h @@ -40,7 +40,7 @@ -#define ESX_VI__SOAP__REQUEST_HEADER \ +# define ESX_VI__SOAP__REQUEST_HEADER \ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" \ "<soapenv:Envelope\n" \ " xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n" \ @@ -51,13 +51,13 @@ -#define ESX_VI__SOAP__REQUEST_FOOTER \ +# define ESX_VI__SOAP__REQUEST_FOOTER \ "</soapenv:Body>\n" \ "</soapenv:Envelope>" -#define ESV_VI__XML_TAG__OPEN(_buffer, _element, _type) \ +# define ESV_VI__XML_TAG__OPEN(_buffer, _element, _type) \ do { \ virBufferAddLit(_buffer, "<"); \ virBufferAdd(_buffer, _element, -1); \ @@ -68,7 +68,7 @@ -#define ESV_VI__XML_TAG__CLOSE(_buffer, _element) \ +# define ESV_VI__XML_TAG__CLOSE(_buffer, _element) \ do { \ virBufferAddLit(_buffer, "</"); \ virBufferAdd(_buffer, _element, -1); \ -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/esx/esx_vi.h | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h index e0d731e..a2ed5a6 100644 --- a/src/esx/esx_vi.h +++ b/src/esx/esx_vi.h @@ -40,7 +40,7 @@
-#define ESX_VI__SOAP__REQUEST_HEADER \ +# define ESX_VI__SOAP__REQUEST_HEADER \
I independently applied this as obvious; you can drop it from your series. https://www.redhat.com/archives/libvir-list/2010-April/msg00906.html -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

2010/4/21 Eric Blake <eblake@redhat.com>:
On 04/21/2010 10:01 AM, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/esx/esx_vi.h | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h index e0d731e..a2ed5a6 100644 --- a/src/esx/esx_vi.h +++ b/src/esx/esx_vi.h @@ -40,7 +40,7 @@
-#define ESX_VI__SOAP__REQUEST_HEADER \ +# define ESX_VI__SOAP__REQUEST_HEADER \
I independently applied this as obvious; you can drop it from your series. https://www.redhat.com/archives/libvir-list/2010-April/msg00906.html
I installed cppi now, so this won't happen again. Matthias

On 04/21/2010 11:40 AM, Matthias Bolte wrote:
I independently applied this as obvious; you can drop it from your series. https://www.redhat.com/archives/libvir-list/2010-April/msg00906.html
I installed cppi now, so this won't happen again.
Now that cppi is in use by a few more developers, is it time to bite the bullet and update bootstrap.conf to require cppi as a prerequisite, or is that still too strong of a move? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 37 +++++++++++++++++++------------------ src/conf/domain_conf.h | 6 ------ 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2de838b..e4a2cac 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4504,24 +4504,9 @@ cleanup: } -virDomainObjPtr virDomainObjParseFile(virCapsPtr caps, - const char *filename) -{ - xmlDocPtr xml; - virDomainObjPtr obj = NULL; - - if ((xml = virXMLParseFile(filename))) { - obj = virDomainObjParseNode(caps, xml, xmlDocGetRootElement(xml)); - xmlFreeDoc(xml); - } - - return obj; -} - - -virDomainObjPtr virDomainObjParseNode(virCapsPtr caps, - xmlDocPtr xml, - xmlNodePtr root) +static virDomainObjPtr virDomainObjParseNode(virCapsPtr caps, + xmlDocPtr xml, + xmlNodePtr root) { xmlXPathContextPtr ctxt = NULL; virDomainObjPtr obj = NULL; @@ -4546,6 +4531,22 @@ cleanup: return obj; } + +static virDomainObjPtr virDomainObjParseFile(virCapsPtr caps, + const char *filename) +{ + xmlDocPtr xml; + virDomainObjPtr obj = NULL; + + if ((xml = virXMLParseFile(filename))) { + obj = virDomainObjParseNode(caps, xml, xmlDocGetRootElement(xml)); + xmlFreeDoc(xml); + } + + return obj; +} + + static int virDomainDefMaybeAddController(virDomainDefPtr def, int type, int idx) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 82f2d15..fafa0f5 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -974,12 +974,6 @@ virDomainDefPtr virDomainDefParseNode(virCapsPtr caps, xmlNodePtr root, int flags); -virDomainObjPtr virDomainObjParseFile(virCapsPtr caps, - const char *filename); -virDomainObjPtr virDomainObjParseNode(virCapsPtr caps, - xmlDocPtr xml, - xmlNodePtr root); - int virDomainDefAddImplicitControllers(virDomainDefPtr def); # endif -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 37 +++++++++++++++++++------------------ src/conf/domain_conf.h | 6 ------ 2 files changed, 19 insertions(+), 24 deletions(-)
ACK; the reorder is an obvious fallout to avoid an extra declaration. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Wed, Apr 21, 2010 at 12:01:16PM -0400, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 37 +++++++++++++++++++------------------ src/conf/domain_conf.h | 6 ------ 2 files changed, 19 insertions(+), 24 deletions(-)
What's the reason for making these static ? Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On 04/22/2010 08:03 AM, Daniel P. Berrange wrote:
On Wed, Apr 21, 2010 at 12:01:16PM -0400, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 37 +++++++++++++++++++------------------ src/conf/domain_conf.h | 6 ------ 2 files changed, 19 insertions(+), 24 deletions(-)
What's the reason for making these static ?
They aren't currently used outside of domain_conf.c, so I figured I would just restrict them to the file. It's not really required for the patchset, just a cleanup. -- Chris Lalancette

On Mon, Apr 26, 2010 at 12:07:17PM -0400, Chris Lalancette wrote:
On 04/22/2010 08:03 AM, Daniel P. Berrange wrote:
On Wed, Apr 21, 2010 at 12:01:16PM -0400, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 37 +++++++++++++++++++------------------ src/conf/domain_conf.h | 6 ------ 2 files changed, 19 insertions(+), 24 deletions(-)
What's the reason for making these static ?
They aren't currently used outside of domain_conf.c, so I figured I would just restrict them to the file. It's not really required for the patchset, just a cleanup.
Ok, I'd rather we leave them as is then. Usage of internal APIs changes over time, so its just code churn to switch between static & non-static every time usage happens to change. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

This patch adds namespace XML parsers to be hooked into the main domain parser. This allows for individual hypervisor drivers to add per-namespace XML into the main domain XML. Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/capabilities.c | 2 ++ src/conf/capabilities.h | 17 +++++++++++++++++ src/conf/domain_conf.c | 44 +++++++++++++++++++++++++++++++++++++------- src/conf/domain_conf.h | 3 +++ 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index dafd821..d4a6070 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -174,6 +174,8 @@ virCapabilitiesFree(virCapsPtr caps) { VIR_FREE(caps->host.secModel.doi); virCPUDefFree(caps->host.cpu); + VIR_FREE(caps->ns); + VIR_FREE(caps); } diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 0ed0166..95bc65c 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -112,6 +112,21 @@ struct _virCapsHost { virCPUDefPtr cpu; }; +typedef int (*virDomainDefNamespaceParse)(xmlDocPtr, xmlNodePtr, + xmlXPathContextPtr, void **); +typedef void (*virDomainDefNamespaceFree)(void *); +typedef int (*virDomainDefNamespaceXMLFormat)(virBufferPtr, void *); +typedef const char *(*virDomainDefNamespaceHref)(void); + +typedef struct _virDomainXMLNamespace virDomainXMLNamespace; +typedef virDomainXMLNamespace *virDomainXMLNamespacePtr; +struct _virDomainXMLNamespace { + virDomainDefNamespaceParse parse; + virDomainDefNamespaceFree free; + virDomainDefNamespaceXMLFormat format; + virDomainDefNamespaceHref href; +}; + typedef struct _virCaps virCaps; typedef virCaps* virCapsPtr; struct _virCaps { @@ -124,6 +139,8 @@ struct _virCaps { void (*privateDataFreeFunc)(void *); int (*privateDataXMLFormat)(virBufferPtr, void *); int (*privateDataXMLParse)(xmlXPathContextPtr, void *); + + virDomainXMLNamespacePtr ns; }; diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e4a2cac..a374206 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -728,6 +728,9 @@ void virDomainDefFree(virDomainDefPtr def) virCPUDefFree(def->cpu); + if (def->namespaceData && def->ns.free) + (def->ns.free)(def->namespaceData); + VIR_FREE(def); } @@ -3717,7 +3720,10 @@ static char *virDomainDefDefaultEmulator(virDomainDefPtr def, } static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, - xmlXPathContextPtr ctxt, int flags) + xmlDocPtr xml, + xmlNodePtr root, + xmlXPathContextPtr ctxt, + int flags) { xmlNodePtr *nodes = NULL, node = NULL; char *tmp = NULL; @@ -4366,6 +4372,22 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, goto error; } + /* callout to per-namespace parsers (in the individual drivers) */ + if (caps->ns) { + /* we have to make a copy of all of the callback pointers here since + * we won't have the virCaps structure available during free + */ + def->ns.parse = caps->ns->parse; + def->ns.free = caps->ns->free; + def->ns.format = caps->ns->format; + def->ns.href = caps->ns->href; + + if (def->ns.parse) { + if ((def->ns.parse)(xml, root, ctxt, &def->namespaceData) < 0) + goto error; + } + } + /* Auto-add any implied controllers which aren't present */ if (virDomainDefAddImplicitControllers(def) < 0) @@ -4386,6 +4408,7 @@ no_memory: static virDomainObjPtr virDomainObjParseXML(virCapsPtr caps, + xmlDocPtr xml, xmlXPathContextPtr ctxt) { char *tmp = NULL; @@ -4405,7 +4428,7 @@ static virDomainObjPtr virDomainObjParseXML(virCapsPtr caps, oldnode = ctxt->node; ctxt->node = config; - obj->def = virDomainDefParseXML(caps, ctxt, + obj->def = virDomainDefParseXML(caps, xml, config, ctxt, VIR_DOMAIN_XML_INTERNAL_STATUS); ctxt->node = oldnode; if (!obj->def) @@ -4496,7 +4519,7 @@ virDomainDefPtr virDomainDefParseNode(virCapsPtr caps, } ctxt->node = root; - def = virDomainDefParseXML(caps, ctxt, flags); + def = virDomainDefParseXML(caps, xml, root, ctxt, flags); cleanup: xmlXPathFreeContext(ctxt); @@ -4524,7 +4547,7 @@ static virDomainObjPtr virDomainObjParseNode(virCapsPtr caps, } ctxt->node = root; - obj = virDomainObjParseXML(caps, ctxt); + obj = virDomainObjParseXML(caps, xml, ctxt); cleanup: xmlXPathFreeContext(ctxt); @@ -5736,10 +5759,12 @@ char *virDomainDefFormat(virDomainDefPtr def, if (def->id == -1) flags |= VIR_DOMAIN_XML_INACTIVE; + virBufferVSprintf(&buf, "<domain type='%s'", type); if (!(flags & VIR_DOMAIN_XML_INACTIVE)) - virBufferVSprintf(&buf, "<domain type='%s' id='%d'>\n", type, def->id); - else - virBufferVSprintf(&buf, "<domain type='%s'>\n", type); + virBufferVSprintf(&buf, " id='%d'", def->id); + if (def->namespaceData && def->ns.href) + virBufferVSprintf(&buf, " %s", (def->ns.href)()); + virBufferAddLit(&buf, ">\n"); virBufferEscapeString(&buf, " <name>%s</name>\n", def->name); @@ -5996,6 +6021,11 @@ char *virDomainDefFormat(virDomainDefPtr def, } } + if (def->namespaceData && def->ns.format) { + if ((def->ns.format)(&buf, def->namespaceData) < 0) + goto cleanup; + } + virBufferAddLit(&buf, "</domain>\n"); if (virBufferError(&buf)) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index fafa0f5..80f4146 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -863,6 +863,9 @@ struct _virDomainDef { virSecurityLabelDef seclabel; virDomainWatchdogDefPtr watchdog; virCPUDefPtr cpu; + + void *namespaceData; + virDomainXMLNamespace ns; }; /* Guest VM runtime state */ -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
This patch adds namespace XML parsers to be hooked into the main domain parser. This allows for individual hypervisor drivers to add per-namespace XML into the main domain XML.
@@ -4366,6 +4372,22 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, goto error; }
+ /* callout to per-namespace parsers (in the individual drivers) */ + if (caps->ns) { + /* we have to make a copy of all of the callback pointers here since + * we won't have the virCaps structure available during free + */ + def->ns.parse = caps->ns->parse; + def->ns.free = caps->ns->free; + def->ns.format = caps->ns->format; + def->ns.href = caps->ns->href;
Can these four lines be simplified as: def->ns = *caps->ns
+ if (def->namespaceData && def->ns.href) + virBufferVSprintf(&buf, " %s", (def->ns.href)());
Are we guaranteed that def->namespaceData will be non-null if ns.parse succeeded, or should we call ns.href if present even if def->namespaceData is NULL?
+ if (def->namespaceData && def->ns.format) { + if ((def->ns.format)(&buf, def->namespaceData) < 0)
Likewise - can ns.format exist, but namespaceData validly be NULL after a successful ns.parse? If ns.parse is required to set namespaceData to a non-NULL value on success, then ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Wed, Apr 21, 2010 at 12:01:17PM -0400, Chris Lalancette wrote:
This patch adds namespace XML parsers to be hooked into the main domain parser. This allows for individual hypervisor drivers to add per-namespace XML into the main domain XML.
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/capabilities.c | 2 ++ src/conf/capabilities.h | 17 +++++++++++++++++ src/conf/domain_conf.c | 44 +++++++++++++++++++++++++++++++++++++------- src/conf/domain_conf.h | 3 +++ 4 files changed, 59 insertions(+), 7 deletions(-)
diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index dafd821..d4a6070 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -174,6 +174,8 @@ virCapabilitiesFree(virCapsPtr caps) { VIR_FREE(caps->host.secModel.doi); virCPUDefFree(caps->host.cpu);
+ VIR_FREE(caps->ns); + VIR_FREE(caps); }
I'm inclined to say caps->ns can just be a pointer to a statically declared table as we with for the main driver function tables, avoiding need to free it.
@@ -4366,6 +4372,22 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, goto error; }
+ /* callout to per-namespace parsers (in the individual drivers) */ + if (caps->ns) { + /* we have to make a copy of all of the callback pointers here since + * we won't have the virCaps structure available during free + */ + def->ns.parse = caps->ns->parse; + def->ns.free = caps->ns->free; + def->ns.format = caps->ns->format; + def->ns.href = caps->ns->href; + + if (def->ns.parse) { + if ((def->ns.parse)(xml, root, ctxt, &def->namespaceData) < 0) + goto error; + } + }
If we're using the static driver tables, we can just use a plain assignment here like 'def->ns = caps->ns" Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Implement the qemu hooks for XML namespace data. This allows us to specify a qemu XML namespace, and then specify: <qemu:commandline> <qemu:arg>arg</qemu:arg> <qemu:env name='name' value='value'/> </qemu:commandline> In the domain XML. Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/qemu/qemu_conf.c | 14 +++++ src/qemu/qemu_conf.h | 11 ++++ src/qemu/qemu_driver.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 0cbedf2..95843bf 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4618,6 +4618,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_extra; i++) + ADD_ARG_LIT(cmd->extra[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 b2820f0..ab0a4a4 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -158,6 +158,17 @@ struct qemud_driver { typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet; typedef qemuDomainPCIAddressSet *qemuDomainPCIAddressSetPtr; +typedef struct _qemuDomainCmdlineDef qemuDomainCmdlineDef; +typedef qemuDomainCmdlineDef *qemuDomainCmdlineDefPtr; +struct _qemuDomainCmdlineDef { + unsigned int num_extra; + char **extra; + + 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 5f4adfd..0b297a7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -47,6 +47,8 @@ #include <sys/ioctl.h> #include <sys/un.h> +#include <libxml/xpathInternals.h> + #ifdef __linux__ # include <sys/vfs.h> # ifndef NFS_SUPER_MAGIC @@ -88,6 +90,9 @@ #define VIR_FROM_THIS VIR_FROM_QEMU +#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 */ @@ -526,6 +531,144 @@ static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virD } } +static void qemuDomainDefNamespaceFree(void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + int i; + + if (!cmd) + return; + + for (i = 0; i < cmd->num_extra; i++) + VIR_FREE(cmd->extra[i]); + for (i = 0; i < cmd->num_env; i++) { + VIR_FREE(cmd->env_name[i]); + VIR_FREE(cmd->env_value[i]); + } + VIR_FREE(cmd->extra); + 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; + xmlNodePtr oldnode; + + 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->extra, n) < 0) { + virReportOOMError(); + goto error; + } + + for (i = 0; i < n; i++) { + oldnode = ctxt->node; + ctxt->node = nodes[i]; + cmd->extra[cmd->num_extra] = virXPathString("string(.)", ctxt); + if (cmd->extra[cmd->num_extra] == NULL) + goto error; + cmd->num_extra++; + ctxt->node = oldnode; + } + + /* 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) { + virReportOOMError(); + goto error; + } + + if (n && VIR_ALLOC_N(cmd->env_value, n) < 0) { + virReportOOMError(); + goto error; + } + + for (i = 0; i < n; i++) { + oldnode = ctxt->node; + ctxt->node = nodes[i]; + cmd->env_name[cmd->num_env] = virXPathString("string(./@name)", ctxt); + if (cmd->env_name[cmd->num_env] == NULL) + goto error; + cmd->env_value[cmd->num_env] = virXPathString("string(./@value)", ctxt); + /* a NULL value for command is allowed, since it might be empty */ + cmd->num_env++; + ctxt->node = oldnode; + } + + *data = cmd; + + return 0; + +error: + qemuDomainDefNamespaceFree(cmd); + return -1; +} + +static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + int i; + + if (cmd->num_extra || cmd->num_env) + virBufferAddLit(buf, " <qemu:commandline>\n"); + for (i = 0; i < cmd->num_extra; i++) + virBufferVSprintf(buf, " <qemu:arg>%s</qemu:arg>\n", cmd->extra[i]); + for (i = 0; i < cmd->num_env; i++) { + virBufferVSprintf(buf, " <qemu:env name='%s'", cmd->env_name[i]); + if (cmd->env_value[i]) + virBufferVSprintf(buf, " value='%s'", cmd->env_value[i]); + virBufferAddLit(buf, "/>\n"); + } + if (cmd->num_extra || cmd->num_env) + 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) @@ -1316,6 +1459,14 @@ qemuCreateCapabilities(virCapsPtr oldcaps, caps->privateDataXMLFormat = qemuDomainObjPrivateXMLFormat; caps->privateDataXMLParse = qemuDomainObjPrivateXMLParse; + /* Domain Namespace XML parser hooks */ + if (VIR_ALLOC(caps->ns) < 0) + goto no_memory; + + caps->ns->parse = qemuDomainDefNamespaceParse; + caps->ns->free = qemuDomainDefNamespaceFree; + caps->ns->format = qemuDomainDefNamespaceFormatXML; + caps->ns->href = qemuDomainDefNamespaceHref; /* Security driver data */ if (driver->securityPrimaryDriver) { -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
+static void qemuDomainDefNamespaceFree(void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + int i;
s/int/unsigned int/
+static int qemuDomainDefNamespaceParse(xmlDocPtr xml, + xmlNodePtr root, + xmlXPathContextPtr ctxt, + void **data) +{ + qemuDomainCmdlineDefPtr cmd = NULL; + xmlNsPtr ns; + xmlNodePtr *nodes = NULL; + int n, i;
n must be signed, but can i be unsigned?
+ +static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + int i;
unsigned
+ + if (cmd->num_extra || cmd->num_env) + virBufferAddLit(buf, " <qemu:commandline>\n"); + for (i = 0; i < cmd->num_extra; i++) + virBufferVSprintf(buf, " <qemu:arg>%s</qemu:arg>\n", cmd->extra[i]);
Is there any chance that cmd->extra[i] might contain content that needs escaping before it is valid as XML?
+ for (i = 0; i < cmd->num_env; i++) { + virBufferVSprintf(buf, " <qemu:env name='%s'", cmd->env_name[i]); + if (cmd->env_value[i]) + virBufferVSprintf(buf, " value='%s'", cmd->env_value[i]);
Likewise for env_value[i]? (I'm assuming that env_name[i] is immune, since it was parsed using virXPathString("string(./@name)",), which should have rejected strings not valid as environment variable names.) The approach looks correct, but I hesitate to give an ack without knowing for sure that the formatted XML is safe from arbitrary content in the extra arguments. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Wed, Apr 21, 2010 at 11:25:48AM -0600, Eric Blake wrote:
On 04/21/2010 10:01 AM, Chris Lalancette wrote:
+static void qemuDomainDefNamespaceFree(void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + int i;
s/int/unsigned int/
+static int qemuDomainDefNamespaceParse(xmlDocPtr xml, + xmlNodePtr root, + xmlXPathContextPtr ctxt, + void **data) +{ + qemuDomainCmdlineDefPtr cmd = NULL; + xmlNsPtr ns; + xmlNodePtr *nodes = NULL; + int n, i;
n must be signed, but can i be unsigned?
+ +static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + int i;
unsigned
+ + if (cmd->num_extra || cmd->num_env) + virBufferAddLit(buf, " <qemu:commandline>\n"); + for (i = 0; i < cmd->num_extra; i++) + virBufferVSprintf(buf, " <qemu:arg>%s</qemu:arg>\n", cmd->extra[i]);
Is there any chance that cmd->extra[i] might contain content that needs escaping before it is valid as XML?
Yes, this should use virBufferEscapeString() instead.
+ for (i = 0; i < cmd->num_env; i++) { + virBufferVSprintf(buf, " <qemu:env name='%s'", cmd->env_name[i]); + if (cmd->env_value[i]) + virBufferVSprintf(buf, " value='%s'", cmd->env_value[i]);
Likewise for env_value[i]? (I'm assuming that env_name[i] is immune, since it was parsed using virXPathString("string(./@name)",), which should have rejected strings not valid as environment variable names.)
Yep, likewise here Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Wed, Apr 21, 2010 at 12:01:18PM -0400, Chris Lalancette wrote:
Implement the qemu hooks for XML namespace data. This allows us to specify a qemu XML namespace, and then specify:
<qemu:commandline> <qemu:arg>arg</qemu:arg> <qemu:env name='name' value='value'/> </qemu:commandline>
Now I see it, it looks a little odd to use the element content for qemu:arg, while having an element attributes for qemu:env. Since changing qemu:env to use content isn't practical, I think we could do this instead <qemu:arg value='blah'/>
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index b2820f0..ab0a4a4 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -158,6 +158,17 @@ struct qemud_driver { typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet; typedef qemuDomainPCIAddressSet *qemuDomainPCIAddressSetPtr;
+typedef struct _qemuDomainCmdlineDef qemuDomainCmdlineDef; +typedef qemuDomainCmdlineDef *qemuDomainCmdlineDefPtr; +struct _qemuDomainCmdlineDef { + unsigned int num_extra; + char **extra;
Lets call these 'num_args' and 'args' instead.
+ + 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 5f4adfd..0b297a7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -47,6 +47,8 @@ #include <sys/ioctl.h> #include <sys/un.h>
+#include <libxml/xpathInternals.h> + #ifdef __linux__ # include <sys/vfs.h> # ifndef NFS_SUPER_MAGIC @@ -88,6 +90,9 @@
#define VIR_FROM_THIS VIR_FROM_QEMU
+#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 */ @@ -526,6 +531,144 @@ static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virD } }
+static void qemuDomainDefNamespaceFree(void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + int i; + + if (!cmd) + return; + + for (i = 0; i < cmd->num_extra; i++) + VIR_FREE(cmd->extra[i]); + for (i = 0; i < cmd->num_env; i++) { + VIR_FREE(cmd->env_name[i]); + VIR_FREE(cmd->env_value[i]); + } + VIR_FREE(cmd->extra); + 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; + xmlNodePtr oldnode; + + 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->extra, n) < 0) { + virReportOOMError(); + goto error; + } + + for (i = 0; i < n; i++) { + oldnode = ctxt->node; + ctxt->node = nodes[i]; + cmd->extra[cmd->num_extra] = virXPathString("string(.)", ctxt); + if (cmd->extra[cmd->num_extra] == NULL) + goto error; + cmd->num_extra++; + ctxt->node = oldnode; + } + + /* 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) { + virReportOOMError(); + goto error; + } + + if (n && VIR_ALLOC_N(cmd->env_value, n) < 0) { + virReportOOMError(); + goto error; + } + + for (i = 0; i < n; i++) { + oldnode = ctxt->node; + ctxt->node = nodes[i]; + cmd->env_name[cmd->num_env] = virXPathString("string(./@name)", ctxt); + if (cmd->env_name[cmd->num_env] == NULL) + goto error; + cmd->env_value[cmd->num_env] = virXPathString("string(./@value)", ctxt); + /* a NULL value for command is allowed, since it might be empty */ + cmd->num_env++; + ctxt->node = oldnode; + }
Using xpath for getting immediate attributes is somewhat overkill. You can just avoid touching ctxt at all, and use virXMLPropString(nodes[i], "name"); virXMLPropString(nodes[i], "value");
+static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + int i; + + if (cmd->num_extra || cmd->num_env) + virBufferAddLit(buf, " <qemu:commandline>\n"); + for (i = 0; i < cmd->num_extra; i++) + virBufferVSprintf(buf, " <qemu:arg>%s</qemu:arg>\n", cmd->extra[i]); + for (i = 0; i < cmd->num_env; i++) { + virBufferVSprintf(buf, " <qemu:env name='%s'", cmd->env_name[i]); + if (cmd->env_value[i]) + virBufferVSprintf(buf, " value='%s'", cmd->env_value[i]); + virBufferAddLit(buf, "/>\n"); + }
Should use escaping for all the attributes here
+ if (cmd->num_extra || cmd->num_env) + 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) @@ -1316,6 +1459,14 @@ qemuCreateCapabilities(virCapsPtr oldcaps, caps->privateDataXMLFormat = qemuDomainObjPrivateXMLFormat; caps->privateDataXMLParse = qemuDomainObjPrivateXMLParse;
+ /* Domain Namespace XML parser hooks */ + if (VIR_ALLOC(caps->ns) < 0) + goto no_memory; + + caps->ns->parse = qemuDomainDefNamespaceParse; + caps->ns->free = qemuDomainDefNamespaceFree; + caps->ns->format = qemuDomainDefNamespaceFormatXML; + caps->ns->href = qemuDomainDefNamespaceHref;
Could avoid needing to allocate this, by just statically declaring the callback table. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Now that we have the ability to specify arbitrary qemu command-line parameters in the XML, use it to handle unknown command-line parameters when doing a native-to-xml conversion. Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 13 +++++++++---- src/conf/domain_conf.h | 2 ++ src/libvirt_private.syms | 1 + src/qemu/qemu_conf.c | 27 +++++++++++++++++++++------ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a374206..3f13f32 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3719,6 +3719,14 @@ static char *virDomainDefDefaultEmulator(virDomainDefPtr def, return retemu; } +void virDomainDefAssignNamespace(virCapsPtr caps, virDomainDefPtr def) +{ + def->ns.parse = caps->ns->parse; + def->ns.free = caps->ns->free; + def->ns.format = caps->ns->format; + def->ns.href = caps->ns->href; +} + static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, xmlDocPtr xml, xmlNodePtr root, @@ -4377,10 +4385,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, /* we have to make a copy of all of the callback pointers here since * we won't have the virCaps structure available during free */ - def->ns.parse = caps->ns->parse; - def->ns.free = caps->ns->free; - def->ns.format = caps->ns->format; - def->ns.href = caps->ns->href; + virDomainDefAssignNamespace(caps, def); if (def->ns.parse) { if ((def->ns.parse)(xml, root, ctxt, &def->namespaceData) < 0) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 80f4146..dddfec4 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -979,6 +979,8 @@ virDomainDefPtr virDomainDefParseNode(virCapsPtr caps, int virDomainDefAddImplicitControllers(virDomainDefPtr def); +void virDomainDefAssignNamespace(virCapsPtr caps, virDomainDefPtr def); + # endif char *virDomainDefFormat(virDomainDefPtr def, int flags); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 7950bcd..ddc412d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -215,6 +215,7 @@ virDomainSnapshotObjUnref; virDomainSnapshotDefParseString; virDomainSnapshotDefFormat; virDomainSnapshotAssignDef; +virDomainDefAssignNamespace; # domain_event.h diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 95843bf..c4b55de 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -5704,6 +5704,7 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, const char **nics = NULL; int video = VIR_DOMAIN_VIDEO_TYPE_CIRRUS; int nvirtiodisk = 0; + qemuDomainCmdlineDefPtr cmd; if (!progargv[0]) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -5714,6 +5715,10 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, if (VIR_ALLOC(def) < 0) goto no_memory; + /* allocate the cmdlinedef up-front; if it's unused, we'll free it later */ + if (VIR_ALLOC(cmd) < 0) + goto no_memory; + virUUIDGenerate(def->uuid); def->id = -1; @@ -6138,12 +6143,15 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, } else if (STREQ(arg, "-S")) { /* ignore, always added by libvirt */ } else { - VIR_WARN(_("unknown QEMU argument '%s' during conversion"), arg); -#if 0 - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown argument '%s'"), arg); - goto error; -#endif + /* something we can't yet parse. Add it to the qemu namespace + * cmdline/environment advanced options and hope for the best + */ + if (VIR_REALLOC_N(cmd->extra, cmd->num_extra+1) < 0) + goto no_memory; + cmd->extra[cmd->num_extra] = strdup(arg); + if (cmd->extra[cmd->num_extra] == NULL) + goto no_memory; + cmd->num_extra++; } } @@ -6203,6 +6211,13 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, if (virDomainDefAddImplicitControllers(def) < 0) goto error; + if (cmd->num_extra || cmd->num_env) { + virDomainDefAssignNamespace(caps, def); + def->namespaceData = cmd; + } + else + VIR_FREE(cmd); + return def; no_memory: -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
Now that we have the ability to specify arbitrary qemu command-line parameters in the XML, use it to handle unknown command-line parameters when doing a native-to-xml conversion.
+++ b/src/conf/domain_conf.c @@ -3719,6 +3719,14 @@ static char *virDomainDefDefaultEmulator(virDomainDefPtr def, return retemu; }
+void virDomainDefAssignNamespace(virCapsPtr caps, virDomainDefPtr def) +{ + def->ns.parse = caps->ns->parse; + def->ns.free = caps->ns->free; + def->ns.format = caps->ns->format; + def->ns.href = caps->ns->href;
My comments for 03/10 still apply; can this be simplified? def->ns = *caps->ns;
@@ -6203,6 +6211,13 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, if (virDomainDefAddImplicitControllers(def) < 0) goto error;
+ if (cmd->num_extra || cmd->num_env) { + virDomainDefAssignNamespace(caps, def); + def->namespaceData = cmd; + } + else + VIR_FREE(cmd);
Per Jim's recent request to update the style guidelines, this should be rewritten as either: if (cond) { ... } else { VIR_FREE(cmd); } or: if (!(cond)) VIR_FREE(cmd); else { ... } ACK, once nits are resolved. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Wed, Apr 21, 2010 at 12:01:19PM -0400, Chris Lalancette wrote:
Now that we have the ability to specify arbitrary qemu command-line parameters in the XML, use it to handle unknown command-line parameters when doing a native-to-xml conversion.
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/conf/domain_conf.c | 13 +++++++++---- src/conf/domain_conf.h | 2 ++ src/libvirt_private.syms | 1 + src/qemu/qemu_conf.c | 27 +++++++++++++++++++++------ 4 files changed, 33 insertions(+), 10 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a374206..3f13f32 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3719,6 +3719,14 @@ static char *virDomainDefDefaultEmulator(virDomainDefPtr def, return retemu; }
+void virDomainDefAssignNamespace(virCapsPtr caps, virDomainDefPtr def) +{ + def->ns.parse = caps->ns->parse; + def->ns.free = caps->ns->free; + def->ns.format = caps->ns->format; + def->ns.href = caps->ns->href; +} + static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, xmlDocPtr xml, xmlNodePtr root, @@ -4377,10 +4385,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, /* we have to make a copy of all of the callback pointers here since * we won't have the virCaps structure available during free */ - def->ns.parse = caps->ns->parse; - def->ns.free = caps->ns->free; - def->ns.format = caps->ns->format; - def->ns.href = caps->ns->href; + virDomainDefAssignNamespace(caps, def);
This could mostly go away, if we just directly passed around the statically allocated driver table.
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 95843bf..c4b55de 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -5704,6 +5704,7 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, const char **nics = NULL; int video = VIR_DOMAIN_VIDEO_TYPE_CIRRUS; int nvirtiodisk = 0; + qemuDomainCmdlineDefPtr cmd;
if (!progargv[0]) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -5714,6 +5715,10 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, if (VIR_ALLOC(def) < 0) goto no_memory;
+ /* allocate the cmdlinedef up-front; if it's unused, we'll free it later */ + if (VIR_ALLOC(cmd) < 0) + goto no_memory; + virUUIDGenerate(def->uuid);
def->id = -1; @@ -6138,12 +6143,15 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, } else if (STREQ(arg, "-S")) { /* ignore, always added by libvirt */ } else { - VIR_WARN(_("unknown QEMU argument '%s' during conversion"), arg); -#if 0 - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown argument '%s'"), arg); - goto error; -#endif + /* something we can't yet parse. Add it to the qemu namespace + * cmdline/environment advanced options and hope for the best + */ + if (VIR_REALLOC_N(cmd->extra, cmd->num_extra+1) < 0) + goto no_memory; + cmd->extra[cmd->num_extra] = strdup(arg); + if (cmd->extra[cmd->num_extra] == NULL) + goto no_memory; + cmd->num_extra++; } }
IIRC, there are other places where we know about the arg, but don't know how to handle its values. We can deal with that later though.
@@ -6203,6 +6211,13 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, if (virDomainDefAddImplicitControllers(def) < 0) goto error;
+ if (cmd->num_extra || cmd->num_env) { + virDomainDefAssignNamespace(caps, def); + def->namespaceData = cmd; + } + else + VIR_FREE(cmd); + return def;
no_memory:
IIUC this is leaking 'cmd' in the no_memory: path ? Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Add the library entry point for the new virDomainQemuMonitorCommand() entry point. Because this is not part of the "normal" libvirt API, it gets it's own header file, library file, and will eventually get it's own over-the-wire protocol later in the series. Signed-off-by: Chris Lalancette <clalance@redhat.com> --- include/libvirt/Makefile.am | 1 + include/libvirt/libvirt-qemu.h | 30 ++++++++++++++ src/Makefile.am | 8 +++- src/libvirt-qemu.c | 88 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletions(-) create mode 100644 include/libvirt/libvirt-qemu.h create mode 100644 src/libvirt-qemu.c diff --git a/include/libvirt/Makefile.am b/include/libvirt/Makefile.am index 8589dc5..b2c2b76 100644 --- a/include/libvirt/Makefile.am +++ b/include/libvirt/Makefile.am @@ -3,6 +3,7 @@ virincdir = $(includedir)/libvirt virinc_HEADERS = libvirt.h \ + libvirt-qemu.h \ virterror.h install-exec-hook: diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h new file mode 100644 index 0000000..1170196 --- /dev/null +++ b/include/libvirt/libvirt-qemu.h @@ -0,0 +1,30 @@ +/* -*- c -*- + * libvirt_qemu.h: + * Summary: qemu specific interfaces + * Description: Provides the interfaces of the libvirt library to handle + * qemu specific methods + * + * Copy: Copyright (C) 2010 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Author: Chris Lalancette <clalance@redhat.com> + */ + +#ifndef __VIR_QEMU_H__ +# define __VIR_QEMU_H__ + +# include "libvirt.h" + +# ifdef __cplusplus +extern "C" { +# endif + +int virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags); + +# ifdef __cplusplus +} +# endif + +#endif /* __VIR_QEMU_H__ */ diff --git a/src/Makefile.am b/src/Makefile.am index 66dc349..c57a7b8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,7 +32,7 @@ if WITH_NETWORK UUID=$(shell uuidgen 2>/dev/null) endif -lib_LTLIBRARIES = libvirt.la +lib_LTLIBRARIES = libvirt.la libvirt-qemu.la moddir = $(libdir)/libvirt/drivers mod_LTLIBRARIES = @@ -947,6 +947,12 @@ libvirt_test_la_LIBADD = $(libvirt_la_LIBADD) libvirt_test_la_LDFLAGS = $(test_LDFLAGS) libvirt_test_la_CFLAGS = $(COVERAGE_CFLAGS) +libvirt_qemu_la_SOURCES = libvirt-qemu.c +libvirt_qemu_la_LDFLAGS = $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) +libvirt_qemu_la_CFLAGS = $(COVERAGE_CFLAGS) +libvirt_qemu_la_LIBADD = libvirt_util.la libvirt_driver_qemu.la \ + libvirt_driver_remote.la $(CYGWIN_EXTRA_LIBADD) \ + ../gnulib/lib/libgnu.la libexec_PROGRAMS = diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c new file mode 100644 index 0000000..e6a4cc4 --- /dev/null +++ b/src/libvirt-qemu.c @@ -0,0 +1,88 @@ +#include <config.h> + +#include "virterror_internal.h" +#include "logging.h" +#include "datatypes.h" +#include "libvirt/libvirt-qemu.h" + +/** + * virLibConnError: + * @conn: the connection if available + * @error: the error number + * @info: extra information string + * + * Handle an error at the connection level + */ +static void +virLibConnError(virConnectPtr conn, virErrorNumber error, const char *info) +{ + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = virErrorMsg(error, info); + virRaiseError(conn, NULL, NULL, VIR_FROM_NONE, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +/** + * virLibDomainError: + * @domain: the domain if available + * @error: the error number + * @info: extra information string + * + * Handle an error at the connection level + */ +static void +virLibDomainError(virDomainPtr domain, virErrorNumber error, + const char *info) +{ + virConnectPtr conn = NULL; + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = virErrorMsg(error, info); + if (error != VIR_ERR_INVALID_DOMAIN) { + conn = domain->conn; + } + virRaiseError(conn, domain, NULL, VIR_FROM_DOM, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +} + +int +virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags) +{ + virConnectPtr conn; + + DEBUG("domain=%p, cmd=%s, result=%p, flags=%u", domain, cmd, result, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + if (result == NULL) { + virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if (conn->flags & VIR_CONNECT_RO) { + virLibDomainError(domain, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
Add the library entry point for the new virDomainQemuMonitorCommand() entry point. Because this is not part of the "normal" libvirt API, it gets it's own header file, library file, and will eventually get it's own over-the-wire protocol later in the series.
Now we get into the realm of multiple .so in a single package, at which point switching over to the libtool versioning scheme makes more sense. In other words, it is now _very_ feasible that libvirt.so will change versions between libvirt 0.8.1 and 0.8.2, while libvirt-qemu.so stays stable between those two package releases. We need to revisit this thread: https://www.redhat.com/archives/libvir-list/2010-April/msg00226.html
+++ b/src/Makefile.am @@ -32,7 +32,7 @@ if WITH_NETWORK UUID=$(shell uuidgen 2>/dev/null) endif
-lib_LTLIBRARIES = libvirt.la +lib_LTLIBRARIES = libvirt.la libvirt-qemu.la
moddir = $(libdir)/libvirt/drivers mod_LTLIBRARIES = @@ -947,6 +947,12 @@ libvirt_test_la_LIBADD = $(libvirt_la_LIBADD) libvirt_test_la_LDFLAGS = $(test_LDFLAGS) libvirt_test_la_CFLAGS = $(COVERAGE_CFLAGS)
+libvirt_qemu_la_SOURCES = libvirt-qemu.c +libvirt_qemu_la_LDFLAGS = $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS)
Shouldn't there be a -version-info $(...) as part of the LDFLAGS, since this is a public rather than a convenience library? Which gets back to the question of whether $(LIBVIRT_VERSION_INFO) is the right thing to use.
+libvirt_qemu_la_CFLAGS = $(COVERAGE_CFLAGS) +libvirt_qemu_la_LIBADD = libvirt_util.la libvirt_driver_qemu.la \ + libvirt_driver_remote.la $(CYGWIN_EXTRA_LIBADD) \ + ../gnulib/lib/libgnu.la
I still haven't had a successful attempt of building libvirt on cygwin; I'm wondering if we have some cruft with all that $(CYGWIN_EXTRA_LDFLAGS), but that is independent of this patch.
+ +int +virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags) +{ + virConnectPtr conn; + + DEBUG("domain=%p, cmd=%s, result=%p, flags=%u", domain, cmd, result, flags);
Should we add a use of virCheckFlags here, to reject unknown flags? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Wed, Apr 21, 2010 at 12:01:20PM -0400, Chris Lalancette wrote:
Add the library entry point for the new virDomainQemuMonitorCommand() entry point. Because this is not part of the "normal" libvirt API, it gets it's own header file, library file, and will eventually get it's own over-the-wire protocol later in the series.
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- include/libvirt/Makefile.am | 1 + include/libvirt/libvirt-qemu.h | 30 ++++++++++++++ src/Makefile.am | 8 +++- src/libvirt-qemu.c | 88 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletions(-) create mode 100644 include/libvirt/libvirt-qemu.h create mode 100644 src/libvirt-qemu.c
diff --git a/include/libvirt/Makefile.am b/include/libvirt/Makefile.am index 8589dc5..b2c2b76 100644 --- a/include/libvirt/Makefile.am +++ b/include/libvirt/Makefile.am @@ -3,6 +3,7 @@ virincdir = $(includedir)/libvirt
virinc_HEADERS = libvirt.h \ + libvirt-qemu.h \ virterror.h
install-exec-hook: diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h new file mode 100644 index 0000000..1170196 --- /dev/null +++ b/include/libvirt/libvirt-qemu.h @@ -0,0 +1,30 @@ +/* -*- c -*- + * libvirt_qemu.h: + * Summary: qemu specific interfaces + * Description: Provides the interfaces of the libvirt library to handle + * qemu specific methods + * + * Copy: Copyright (C) 2010 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Author: Chris Lalancette <clalance@redhat.com> + */ + +#ifndef __VIR_QEMU_H__ +# define __VIR_QEMU_H__ + +# include "libvirt.h" + +# ifdef __cplusplus +extern "C" { +# endif + +int virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags); + +# ifdef __cplusplus +} +# endif + +#endif /* __VIR_QEMU_H__ */ diff --git a/src/Makefile.am b/src/Makefile.am index 66dc349..c57a7b8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,7 +32,7 @@ if WITH_NETWORK UUID=$(shell uuidgen 2>/dev/null) endif
-lib_LTLIBRARIES = libvirt.la +lib_LTLIBRARIES = libvirt.la libvirt-qemu.la
moddir = $(libdir)/libvirt/drivers mod_LTLIBRARIES = @@ -947,6 +947,12 @@ libvirt_test_la_LIBADD = $(libvirt_la_LIBADD) libvirt_test_la_LDFLAGS = $(test_LDFLAGS) libvirt_test_la_CFLAGS = $(COVERAGE_CFLAGS)
+libvirt_qemu_la_SOURCES = libvirt-qemu.c +libvirt_qemu_la_LDFLAGS = $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) +libvirt_qemu_la_CFLAGS = $(COVERAGE_CFLAGS) +libvirt_qemu_la_LIBADD = libvirt_util.la libvirt_driver_qemu.la \ + libvirt_driver_remote.la $(CYGWIN_EXTRA_LIBADD) \ + ../gnulib/lib/libgnu.la
This is still going to cause duplicate copies of the code to be statically linked in. If you just mention 'libvirt.la', then it should dynamically link to the libvirt.so with no duplication. You may need to export some more private symbols but that's not a big issue.
diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c new file mode 100644 index 0000000..e6a4cc4 --- /dev/null +++ b/src/libvirt-qemu.c @@ -0,0 +1,88 @@ +#include <config.h> + +#include "virterror_internal.h" +#include "logging.h" +#include "datatypes.h" +#include "libvirt/libvirt-qemu.h" + +/** + * virLibConnError: + * @conn: the connection if available + * @error: the error number + * @info: extra information string + * + * Handle an error at the connection level + */ +static void +virLibConnError(virConnectPtr conn, virErrorNumber error, const char *info) +{ + const char *errmsg; + + if (error == VIR_ERR_OK) + return; + + errmsg = virErrorMsg(error, info); + virRaiseError(conn, NULL, NULL, VIR_FROM_NONE, error, VIR_ERR_ERROR, + errmsg, info, NULL, 0, 0, errmsg, info); +}
I know this is just a cut+paste from libvirt.c, but it could be replaced with a macro #define virLibConnError(code, ...) \ virReportErrorHelper(NULL, VIR_FROM_NONE, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) which means source file/function/line info is preserved. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On 04/22/2010 08:27 AM, Daniel P. Berrange wrote:
--- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,7 +32,7 @@ if WITH_NETWORK UUID=$(shell uuidgen 2>/dev/null) endif
-lib_LTLIBRARIES = libvirt.la +lib_LTLIBRARIES = libvirt.la libvirt-qemu.la
moddir = $(libdir)/libvirt/drivers mod_LTLIBRARIES = @@ -947,6 +947,12 @@ libvirt_test_la_LIBADD = $(libvirt_la_LIBADD) libvirt_test_la_LDFLAGS = $(test_LDFLAGS) libvirt_test_la_CFLAGS = $(COVERAGE_CFLAGS)
+libvirt_qemu_la_SOURCES = libvirt-qemu.c +libvirt_qemu_la_LDFLAGS = $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) +libvirt_qemu_la_CFLAGS = $(COVERAGE_CFLAGS) +libvirt_qemu_la_LIBADD = libvirt_util.la libvirt_driver_qemu.la \ + libvirt_driver_remote.la $(CYGWIN_EXTRA_LIBADD) \ + ../gnulib/lib/libgnu.la
This is still going to cause duplicate copies of the code to be statically linked in. If you just mention 'libvirt.la', then it should dynamically link to the libvirt.so with no duplication. You may need to export some more private symbols but that's not a big issue.
I actually tried to do this, but I can't get it to work. It certainly might be my unfamiliarity with the auto* tools, but doing something like: diff --git a/src/Makefile.am b/src/Makefile.am index 3994f00..04aa203 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -989,11 +989,11 @@ libvirt_test_la_LDFLAGS = $(test_LDFLAGS) libvirt_test_la_CFLAGS = $(COVERAGE_CFLAGS) libvirt_qemu_la_SOURCES = libvirt-qemu.c -libvirt_qemu_la_LDFLAGS = $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) +libvirt_qemu_la_LDFLAGS = $(VERSION_SCRIPT_FLAGS)$(LIBVIRT_SYMBOL_FILE) \ + -version-info $(LIBVIRT_VERSION_INFO) \ + $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) libvirt_qemu_la_CFLAGS = $(COVERAGE_CFLAGS) -libvirt_qemu_la_LIBADD = libvirt_util.la libvirt_driver_qemu.la \ - libvirt_driver_remote.la $(CYGWIN_EXTRA_LIBADD) \ - ../gnulib/lib/libgnu.la +libvirt_qemu_la_LIBADD = libvirt.la libexec_PROGRAMS = diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 746327d..4b88a74 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -706,6 +706,7 @@ virReportSystemErrorFull; virReportOOMErrorFull; virStrerror; virSetError; +virDispatchError; # xml.h @@ -723,3 +724,11 @@ virXPathLongLong; virXPathULongLong; virXPathLongHex; virXPathULongHex; + + +# remote_driver.h +remoteQemuMonitorCommand; + + +# qemu_driver.h +qemuMonitorCommand; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b4db904..22d53f9 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -393,5 +393,8 @@ LIBVIRT_0.8.0 { virDomainSnapshotFree; } LIBVIRT_0.7.7; - +LIBVIRT_0.8.1 { + global: + virDomainQemuMonitorCommand; +} LIBVIRT_0.8.0; Causes the build to fail when linking virsh: CCLD virsh ../src/.libs/libvirt-qemu.so: undefined reference to `qemuMonitorCommand' collect2: ld returned 1 exit status Indeed, if I nm src/.libs/libvirt-qemu.so: [clalance@localhost libvirt (qemu_monitor)]$ nm src/.libs/libvirt-qemu.so | grep Monitor U qemuMonitorCommand U remoteQemuMonitorCommand@@LIBVIRT_PRIVATE_0.8.0 000010b0 T virDomainQemuMonitorCommand Any suggestions about what I'm doing wrong? -- Chris Lalancette

On Tue, Apr 27, 2010 at 04:16:15PM -0400, Chris Lalancette wrote:
On 04/22/2010 08:27 AM, Daniel P. Berrange wrote:
--- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,7 +32,7 @@ if WITH_NETWORK UUID=$(shell uuidgen 2>/dev/null) endif
-lib_LTLIBRARIES = libvirt.la +lib_LTLIBRARIES = libvirt.la libvirt-qemu.la
moddir = $(libdir)/libvirt/drivers mod_LTLIBRARIES = @@ -947,6 +947,12 @@ libvirt_test_la_LIBADD = $(libvirt_la_LIBADD) libvirt_test_la_LDFLAGS = $(test_LDFLAGS) libvirt_test_la_CFLAGS = $(COVERAGE_CFLAGS)
+libvirt_qemu_la_SOURCES = libvirt-qemu.c +libvirt_qemu_la_LDFLAGS = $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) +libvirt_qemu_la_CFLAGS = $(COVERAGE_CFLAGS) +libvirt_qemu_la_LIBADD = libvirt_util.la libvirt_driver_qemu.la \ + libvirt_driver_remote.la $(CYGWIN_EXTRA_LIBADD) \ + ../gnulib/lib/libgnu.la
This is still going to cause duplicate copies of the code to be statically linked in. If you just mention 'libvirt.la', then it should dynamically link to the libvirt.so with no duplication. You may need to export some more private symbols but that's not a big issue.
I actually tried to do this, but I can't get it to work. It certainly might be my unfamiliarity with the auto* tools, but doing something like:
diff --git a/src/Makefile.am b/src/Makefile.am index 3994f00..04aa203 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -989,11 +989,11 @@ libvirt_test_la_LDFLAGS = $(test_LDFLAGS) libvirt_test_la_CFLAGS = $(COVERAGE_CFLAGS)
libvirt_qemu_la_SOURCES = libvirt-qemu.c -libvirt_qemu_la_LDFLAGS = $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) +libvirt_qemu_la_LDFLAGS = $(VERSION_SCRIPT_FLAGS)$(LIBVIRT_SYMBOL_FILE) \ + -version-info $(LIBVIRT_VERSION_INFO) \ + $(CYGWIN_EXTRA_LDFLAGS) $(MINGW_EXTRA_LDFLAGS) libvirt_qemu_la_CFLAGS = $(COVERAGE_CFLAGS) -libvirt_qemu_la_LIBADD = libvirt_util.la libvirt_driver_qemu.la \ - libvirt_driver_remote.la $(CYGWIN_EXTRA_LIBADD) \ - ../gnulib/lib/libgnu.la +libvirt_qemu_la_LIBADD = libvirt.la
libexec_PROGRAMS =
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 746327d..4b88a74 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -706,6 +706,7 @@ virReportSystemErrorFull; virReportOOMErrorFull; virStrerror; virSetError; +virDispatchError;
# xml.h @@ -723,3 +724,11 @@ virXPathLongLong; virXPathULongLong; virXPathLongHex; virXPathULongHex; + + +# remote_driver.h +remoteQemuMonitorCommand; + + +# qemu_driver.h +qemuMonitorCommand; diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index b4db904..22d53f9 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -393,5 +393,8 @@ LIBVIRT_0.8.0 { virDomainSnapshotFree; } LIBVIRT_0.7.7;
- +LIBVIRT_0.8.1 { + global: + virDomainQemuMonitorCommand; +} LIBVIRT_0.8.0;
Causes the build to fail when linking virsh:
CCLD virsh ../src/.libs/libvirt-qemu.so: undefined reference to `qemuMonitorCommand' collect2: ld returned 1 exit status
Indeed, if I nm src/.libs/libvirt-qemu.so:
[clalance@localhost libvirt (qemu_monitor)]$ nm src/.libs/libvirt-qemu.so | grep Monitor U qemuMonitorCommand U remoteQemuMonitorCommand@@LIBVIRT_PRIVATE_0.8.0 000010b0 T virDomainQemuMonitorCommand
Any suggestions about what I'm doing wrong?
Have you re-ordered your patches ? There is a qemuMonitorCommand defined in src/qemu/qemu_monitor_text.c but that is static. The qemuMonitorCommand you actually want is not added till your next patch. So perhaps your old code was pullin in the wrong definition ? It'd be good to rename this to reflect the public API name and avoid the clash Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On 04/27/2010 04:25 PM, Daniel P. Berrange wrote:
Causes the build to fail when linking virsh:
CCLD virsh ../src/.libs/libvirt-qemu.so: undefined reference to `qemuMonitorCommand' collect2: ld returned 1 exit status
Indeed, if I nm src/.libs/libvirt-qemu.so:
[clalance@localhost libvirt (qemu_monitor)]$ nm src/.libs/libvirt-qemu.so | grep Monitor U qemuMonitorCommand U remoteQemuMonitorCommand@@LIBVIRT_PRIVATE_0.8.0 000010b0 T virDomainQemuMonitorCommand
Any suggestions about what I'm doing wrong?
Have you re-ordered your patches ? There is a qemuMonitorCommand defined in src/qemu/qemu_monitor_text.c but that is static. The qemuMonitorCommand you actually want is not added till your next patch. So perhaps your old code was pullin in the wrong definition ?
It'd be good to rename this to reflect the public API name and avoid the clash
I did not re-order my patches, but you are right, there is a name clash. I've now resolved that as you suggested (calling the external function qemuDomainMonitorCommand). That's not actually the problem, though. What's happening is that src/Makefile.am has this: if WITH_QEMU if WITH_DRIVER_MODULES mod_LTLIBRARIES += libvirt_driver_qemu.la else noinst_LTLIBRARIES += libvirt_driver_qemu.la # Stateful, so linked to daemon instead #libvirt_la_LIBADD += libvirt_driver_qemu.la i.e. libvirt_driver_qemu.la is not being added to libvirt.la, so that's why my linking step in virsh is failing (and *not* failing when linking libvirtd). So I think I need to manually specify libvirt_qemu.la in my libvirt_qemu_la_LIBADD to avoid this failure. -- Chris Lalancette

On Tue, Apr 27, 2010 at 05:36:40PM -0400, Chris Lalancette wrote:
On 04/27/2010 04:25 PM, Daniel P. Berrange wrote:
Causes the build to fail when linking virsh:
CCLD virsh ../src/.libs/libvirt-qemu.so: undefined reference to `qemuMonitorCommand' collect2: ld returned 1 exit status
Indeed, if I nm src/.libs/libvirt-qemu.so:
[clalance@localhost libvirt (qemu_monitor)]$ nm src/.libs/libvirt-qemu.so | grep Monitor U qemuMonitorCommand U remoteQemuMonitorCommand@@LIBVIRT_PRIVATE_0.8.0 000010b0 T virDomainQemuMonitorCommand
Any suggestions about what I'm doing wrong?
Have you re-ordered your patches ? There is a qemuMonitorCommand defined in src/qemu/qemu_monitor_text.c but that is static. The qemuMonitorCommand you actually want is not added till your next patch. So perhaps your old code was pullin in the wrong definition ?
It'd be good to rename this to reflect the public API name and avoid the clash
I did not re-order my patches, but you are right, there is a name clash. I've now resolved that as you suggested (calling the external function qemuDomainMonitorCommand).
That's not actually the problem, though. What's happening is that src/Makefile.am has this:
if WITH_QEMU if WITH_DRIVER_MODULES mod_LTLIBRARIES += libvirt_driver_qemu.la else noinst_LTLIBRARIES += libvirt_driver_qemu.la # Stateful, so linked to daemon instead #libvirt_la_LIBADD += libvirt_driver_qemu.la
i.e. libvirt_driver_qemu.la is not being added to libvirt.la, so that's why my linking step in virsh is failing (and *not* failing when linking libvirtd). So I think I need to manually specify libvirt_qemu.la in my libvirt_qemu_la_LIBADD to avoid this failure.
Ahh, damn. This is a result of my suggestion to you not to use the virDriver struct for dispatching these methods :-( It means that the main libvirt-qemu.c file gets a direct dependancy on the QEMU driver code, instead of indirectly via the driver table. So I'm afraid I was wrong & you should go back to using the driver table for dispatch Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

Implement the qemu driver's virDomainQemuMonitorCommand and hook it into the API entry point. Signed-off-by: Chris Lalancette <clalance@redhat.com> --- src/libvirt-qemu.c | 12 +++++++++++- src/qemu/qemu_driver.c | 38 ++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_driver.h | 3 +++ src/qemu/qemu_monitor.c | 13 +++++++++++++ src/qemu/qemu_monitor.h | 2 ++ src/qemu/qemu_monitor_json.c | 28 ++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ src/qemu/qemu_monitor_text.c | 21 +++++++++++++++++++++ src/qemu/qemu_monitor_text.h | 3 +++ 9 files changed, 123 insertions(+), 1 deletions(-) diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index e6a4cc4..a8aeebc 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -4,6 +4,7 @@ #include "logging.h" #include "datatypes.h" #include "libvirt/libvirt-qemu.h" +#include "qemu/qemu_driver.h" /** * virLibConnError: @@ -57,6 +58,7 @@ virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd, char **result, unsigned int flags) { virConnectPtr conn; + int ret; DEBUG("domain=%p, cmd=%s, result=%p, flags=%u", domain, cmd, result, flags); @@ -80,7 +82,15 @@ virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd, goto error; } - virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + if (STREQ(conn->driver->name, "QEMU")) + ret = qemuMonitorCommand(domain, cmd, result, flags); + else { + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + goto error; + } + if (ret < 0) + goto error; + return ret; error: virDispatchError(conn); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0b297a7..973a752 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -11418,6 +11418,44 @@ cleanup: return ret; } +int qemuMonitorCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + VIR_WARN(_("Qemu monitor command '%s' executed; libvirt results may be unpredictable!"), cmd); + + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, domain->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(domain->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorArbitraryCommand(priv->mon, cmd, result); + qemuDomainObjExitMonitorWithDriver(driver, vm); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); + return ret; +} + static virDriver qemuDriver = { VIR_DRV_QEMU, "QEMU", diff --git a/src/qemu/qemu_driver.h b/src/qemu/qemu_driver.h index 95b8bff..f061cba 100644 --- a/src/qemu/qemu_driver.h +++ b/src/qemu/qemu_driver.h @@ -49,4 +49,7 @@ int qemuRegister(void); +int qemuMonitorCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags); + #endif /* QEMUD_DRIVER_H */ diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c4f2725..da10d40 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1545,3 +1545,16 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name) ret = qemuMonitorTextDeleteSnapshot(mon, name); return ret; } + +int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply) +{ + int ret; + + DEBUG("mon=%p, cmd=%s, reply=%p", mon, cmd, reply); + + if (mon->json) + ret = qemuMonitorJSONArbitraryCommand(mon, cmd, reply); + else + ret = qemuMonitorTextArbitraryCommand(mon, cmd, reply); + return ret; +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 251233a..5ccbf12 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -351,4 +351,6 @@ int qemuMonitorCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply); + #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index cde9899..d80cb95 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2177,3 +2177,31 @@ int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name) virJSONValueFree(reply); return ret; } + +int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, + const char *cmd_str, + char **reply_str) +{ + virJSONValuePtr cmd = NULL; + virJSONValuePtr reply = NULL; + int ret = -1; + + cmd = virJSONValueFromString(cmd_str); + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + *reply_str = virJSONValueToString(reply); + if (!(*reply_str)) + goto cleanup; + + ret = 0; + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index f404c56..8de956d 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -182,4 +182,8 @@ int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, + const char *cmd_str, + char **reply_str); + #endif /* QEMU_MONITOR_JSON_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 6ad07b1..f4b289d 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -2450,3 +2450,24 @@ cleanup: VIR_FREE(reply); return ret; } + +int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, + char **reply) +{ + char *safecmd = NULL; + int ret; + + if (!(safecmd = qemuMonitorEscapeArg(cmd))) { + virReportOOMError(); + return -1; + } + + ret = qemuMonitorCommand(mon, safecmd, reply); + if (ret != 0) + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("failed to run cmd '%s'"), safecmd); + + VIR_FREE(safecmd); + + return ret; +} diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 3200660..4ee78ab 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -181,4 +181,7 @@ int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name); +int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, + char **reply); + #endif /* QEMU_MONITOR_TEXT_H */ -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
Implement the qemu driver's virDomainQemuMonitorCommand and hook it into the API entry point.
+int qemuMonitorCommand(virDomainPtr domain, const char *cmd, + char **result, unsigned int flags ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = domain->conn->privateData; + virDomainObjPtr vm = NULL; + int ret = -1; + qemuDomainObjPrivatePtr priv;
virCheckFlags() to reject unknown flags, rather than ignoring it. Other than this, it looks clean to me, so ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Since we are adding a new "per-hypervisor" protocol, we make it so that the qemu remote protocol uses a new PROTOCOL and PROGRAM number. This allows us to easily distinguish it from the normal REMOTE protocol. This necessitates changing the proc in remote_message_header from a "remote_procedure" to an "unsigned", which should be the same size (and thus preserve the on-wire protocol). Signed-off-by: Chris Lalancette <clalance@redhat.com> --- cfg.mk | 2 +- daemon/Makefile.am | 32 +++++++++--- daemon/dispatch.c | 43 +++++++++++----- daemon/libvirtd.h | 1 + daemon/qemu_dispatch_args.h | 5 ++ daemon/qemu_dispatch_prototypes.h | 12 ++++ daemon/qemu_dispatch_ret.h | 5 ++ daemon/qemu_dispatch_table.h | 14 +++++ daemon/remote.c | 45 ++++++++++++++++ daemon/remote.h | 8 +++ daemon/remote_generate_stubs.pl | 64 ++++++++++++++--------- src/Makefile.am | 37 +++++++++++++- src/libvirt-qemu.c | 3 + src/remote/qemu_protocol.c | 41 +++++++++++++++ src/remote/qemu_protocol.h | 57 ++++++++++++++++++++ src/remote/qemu_protocol.x | 46 ++++++++++++++++ src/remote/remote_driver.c | 103 +++++++++++++++++++++++++++---------- src/remote/remote_driver.h | 3 + src/remote/remote_protocol.c | 50 +++++++++++++++++- src/remote/remote_protocol.h | 2 +- src/remote/remote_protocol.x | 2 +- 21 files changed, 498 insertions(+), 77 deletions(-) create mode 100644 daemon/qemu_dispatch_args.h create mode 100644 daemon/qemu_dispatch_prototypes.h create mode 100644 daemon/qemu_dispatch_ret.h create mode 100644 daemon/qemu_dispatch_table.h create mode 100644 src/remote/qemu_protocol.c create mode 100644 src/remote/qemu_protocol.h create mode 100644 src/remote/qemu_protocol.x diff --git a/cfg.mk b/cfg.mk index 5d8103e..336082f 100644 --- a/cfg.mk +++ b/cfg.mk @@ -245,7 +245,7 @@ sc_prohibit_trailing_blank_lines: test $$found = 0 # Regex for grep -E that exempts generated files from style rules. -preprocessor_exempt = (remote_(driver|protocol)\.h)$$ +preprocessor_exempt = ((qemu|remote)_(driver|protocol)\.h)$$ # Enforce recommended preprocessor indentation style. sc_preprocessor_indentation: @if (cppi --version >/dev/null 2>&1); then \ diff --git a/daemon/Makefile.am b/daemon/Makefile.am index a82e9a9..089f1aa 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -10,7 +10,8 @@ DAEMON_SOURCES = \ remote_dispatch_table.h \ remote_dispatch_args.h \ remote_dispatch_ret.h \ - ../src/remote/remote_protocol.c + ../src/remote/remote_protocol.c \ + ../src/remote/qemu_protocol.c AVAHI_SOURCES = \ mdns.c mdns.h @@ -76,7 +77,7 @@ libvirtd_LDADD = \ $(SASL_LIBS) \ $(POLKIT_LIBS) -libvirtd_LDADD += ../src/libvirt_util.la +libvirtd_LDADD += ../src/libvirt_util.la ../src/libvirt-qemu.la if WITH_DRIVER_MODULES libvirtd_LDADD += ../src/libvirt_driver.la @@ -167,21 +168,38 @@ endif remote.c: remote_dispatch_prototypes.h \ remote_dispatch_table.h \ remote_dispatch_args.h \ - remote_dispatch_ret.h + remote_dispatch_ret.h \ + qemu_dispatch_prototypes.h \ + qemu_dispatch_table.h \ + qemu_dispatch_args.h \ + qemu_dispatch_ret.h REMOTE_PROTOCOL = $(top_srcdir)/src/remote/remote_protocol.x +QEMU_PROTOCOL = $(top_srcdir)/src/remote/qemu_protocol.x remote_dispatch_prototypes.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -p $(REMOTE_PROTOCOL) > $@ + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -p remote $(REMOTE_PROTOCOL) > $@ remote_dispatch_table.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -t $(REMOTE_PROTOCOL) > $@ + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -t remote $(REMOTE_PROTOCOL) > $@ remote_dispatch_args.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -a $(REMOTE_PROTOCOL) > $@ + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -a remote $(REMOTE_PROTOCOL) > $@ remote_dispatch_ret.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -r $(REMOTE_PROTOCOL) > $@ + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -r remote $(REMOTE_PROTOCOL) > $@ + +qemu_dispatch_prototypes.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL) + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -p qemu $(QEMU_PROTOCOL) > $@ + +qemu_dispatch_table.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL) + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -t qemu $(QEMU_PROTOCOL) > $@ + +qemu_dispatch_args.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL) + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -a qemu $(QEMU_PROTOCOL) > $@ + +qemu_dispatch_ret.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL) + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -r qemu $(QEMU_PROTOCOL) > $@ LOGROTATE_CONFS = libvirtd.qemu.logrotate libvirtd.lxc.logrotate \ libvirtd.uml.logrotate diff --git a/daemon/dispatch.c b/daemon/dispatch.c index 8f55eaa..cfcb18b 100644 --- a/daemon/dispatch.c +++ b/daemon/dispatch.c @@ -336,10 +336,11 @@ cleanup: } -int +static int remoteDispatchClientCall (struct qemud_server *server, struct qemud_client *client, - struct qemud_client_message *msg); + struct qemud_client_message *msg, + int qemu_protocol); /* @@ -356,12 +357,13 @@ remoteDispatchClientCall (struct qemud_server *server, * Returns 0 if the message was dispatched, -1 upon fatal error */ int -remoteDispatchClientRequest (struct qemud_server *server, - struct qemud_client *client, - struct qemud_client_message *msg) +remoteDispatchClientRequest(struct qemud_server *server, + struct qemud_client *client, + struct qemud_client_message *msg) { int ret; remote_error rerr; + int qemu_call; DEBUG("prog=%d ver=%d type=%d status=%d serial=%d proc=%d", msg->hdr.prog, msg->hdr.vers, msg->hdr.type, @@ -370,22 +372,33 @@ remoteDispatchClientRequest (struct qemud_server *server, memset(&rerr, 0, sizeof rerr); /* Check version, etc. */ - if (msg->hdr.prog != REMOTE_PROGRAM) { + if (msg->hdr.prog == REMOTE_PROGRAM) + qemu_call = 0; + else if (msg->hdr.prog == QEMU_PROGRAM) + qemu_call = 1; + else { remoteDispatchFormatError (&rerr, - _("program mismatch (actual %x, expected %x)"), - msg->hdr.prog, REMOTE_PROGRAM); + _("program mismatch (actual %x, expected %x or %x)"), + msg->hdr.prog, REMOTE_PROGRAM, QEMU_PROGRAM); goto error; } - if (msg->hdr.vers != REMOTE_PROTOCOL_VERSION) { + + if (!qemu_call && msg->hdr.vers != REMOTE_PROTOCOL_VERSION) { remoteDispatchFormatError (&rerr, _("version mismatch (actual %x, expected %x)"), msg->hdr.vers, REMOTE_PROTOCOL_VERSION); goto error; } + else if (qemu_call && msg->hdr.vers != QEMU_PROTOCOL_VERSION) { + remoteDispatchFormatError (&rerr, + _("version mismatch (actual %x, expected %x)"), + msg->hdr.vers, QEMU_PROTOCOL_VERSION); + goto error; + } switch (msg->hdr.type) { case REMOTE_CALL: - return remoteDispatchClientCall(server, client, msg); + return remoteDispatchClientCall(server, client, msg, qemu_call); case REMOTE_STREAM: /* Since stream data is non-acked, async, we may continue to received @@ -427,10 +440,11 @@ error: * * Returns 0 if the reply was sent, or -1 upon fatal error */ -int +static int remoteDispatchClientCall (struct qemud_server *server, struct qemud_client *client, - struct qemud_client_message *msg) + struct qemud_client_message *msg, + int qemu_protocol) { XDR xdr; remote_error rerr; @@ -469,7 +483,10 @@ remoteDispatchClientCall (struct qemud_server *server, } } - data = remoteGetDispatchData(msg->hdr.proc); + if (qemu_protocol) + data = qemuGetDispatchData(msg->hdr.proc); + else + data = remoteGetDispatchData(msg->hdr.proc); if (!data) { remoteDispatchFormatError (&rerr, _("unknown procedure: %d"), diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index d292681..56b79d6 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -45,6 +45,7 @@ # include <rpc/types.h> # include <rpc/xdr.h> # include "remote_protocol.h" +# include "qemu_protocol.h" # include "logging.h" # include "threads.h" diff --git a/daemon/qemu_dispatch_args.h b/daemon/qemu_dispatch_args.h new file mode 100644 index 0000000..2eee3bd --- /dev/null +++ b/daemon/qemu_dispatch_args.h @@ -0,0 +1,5 @@ +/* Automatically generated by qemu_generate_stubs.pl. + * Do not edit this file. Any changes you make will be lost. + */ + + qemu_monitor_command_args val_qemu_monitor_command_args; diff --git a/daemon/qemu_dispatch_prototypes.h b/daemon/qemu_dispatch_prototypes.h new file mode 100644 index 0000000..920ad73 --- /dev/null +++ b/daemon/qemu_dispatch_prototypes.h @@ -0,0 +1,12 @@ +/* Automatically generated by qemu_generate_stubs.pl. + * Do not edit this file. Any changes you make will be lost. + */ + +static int qemuDispatchMonitorCommand( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + qemu_monitor_command_args *args, + qemu_monitor_command_ret *ret); diff --git a/daemon/qemu_dispatch_ret.h b/daemon/qemu_dispatch_ret.h new file mode 100644 index 0000000..9feb699 --- /dev/null +++ b/daemon/qemu_dispatch_ret.h @@ -0,0 +1,5 @@ +/* Automatically generated by qemu_generate_stubs.pl. + * Do not edit this file. Any changes you make will be lost. + */ + + qemu_monitor_command_ret val_qemu_monitor_command_ret; diff --git a/daemon/qemu_dispatch_table.h b/daemon/qemu_dispatch_table.h new file mode 100644 index 0000000..e6078de --- /dev/null +++ b/daemon/qemu_dispatch_table.h @@ -0,0 +1,14 @@ +/* Automatically generated by qemu_generate_stubs.pl. + * Do not edit this file. Any changes you make will be lost. + */ + +{ /* (unused) => 0 */ + .fn = NULL, + .args_filter = (xdrproc_t) xdr_void, + .ret_filter = (xdrproc_t) xdr_void, +}, +{ /* MonitorCommand => 1 */ + .fn = (dispatch_fn) qemuDispatchMonitorCommand, + .args_filter = (xdrproc_t) xdr_qemu_monitor_command_args, + .ret_filter = (xdrproc_t) xdr_qemu_monitor_command_ret, +}, diff --git a/daemon/remote.c b/daemon/remote.c index 149176f..af76c4f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -56,6 +56,7 @@ #include "memory.h" #include "util.h" #include "stream.h" +#include "libvirt/libvirt-qemu.h" #define VIR_FROM_THIS VIR_FROM_REMOTE #define REMOTE_DEBUG(fmt, ...) DEBUG(fmt, __VA_ARGS__) @@ -80,11 +81,16 @@ static void make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot *snapsh #include "remote_dispatch_prototypes.h" +#include "qemu_dispatch_prototypes.h" static const dispatch_data const dispatch_table[] = { #include "remote_dispatch_table.h" }; +static const dispatch_data const qemu_dispatch_table[] = { +#include "qemu_dispatch_table.h" +}; + const dispatch_data const *remoteGetDispatchData(int proc) { if (proc >= ARRAY_CARDINALITY(dispatch_table) || @@ -95,6 +101,16 @@ const dispatch_data const *remoteGetDispatchData(int proc) return &(dispatch_table[proc]); } +const dispatch_data const *qemuGetDispatchData(int proc) +{ + if (proc >= ARRAY_CARDINALITY(qemu_dispatch_table) || + qemu_dispatch_table[proc].fn == NULL) { + return NULL; + } + + return &(qemu_dispatch_table[proc]); +} + /* Prototypes */ static void remoteDispatchDomainEventSend (struct qemud_client *client, @@ -6390,6 +6406,35 @@ remoteDispatchNumOfNwfilters (struct qemud_server *server ATTRIBUTE_UNUSED, return 0; } +static int +qemuDispatchMonitorCommand (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr, + qemu_monitor_command_args *args, + qemu_monitor_command_ret *ret) +{ + virDomainPtr domain; + + domain = get_nonnull_domain(conn, args->domain); + if (domain == NULL) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + if (virDomainQemuMonitorCommand(domain, args->cmd, &ret->result, + args->flags) == -1) { + virDomainFree(domain); + remoteDispatchConnError(rerr, conn); + return -1; + } + + virDomainFree(domain); + + return 0; +} + /*----- Helpers. -----*/ diff --git a/daemon/remote.h b/daemon/remote.h index 9db7432..fd8d381 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -35,6 +35,13 @@ typedef union { # include "remote_dispatch_ret.h" } dispatch_ret; +typedef union { +# include "qemu_dispatch_args.h" +} qemu_dispatch_args; + +typedef union { +# include "qemu_dispatch_ret.h" +} qemu_dispatch_ret; @@ -67,6 +74,7 @@ typedef struct { const dispatch_data const *remoteGetDispatchData(int proc); +const dispatch_data const *qemuGetDispatchData(int proc); diff --git a/daemon/remote_generate_stubs.pl b/daemon/remote_generate_stubs.pl index 8b26e3d..8c83673 100755 --- a/daemon/remote_generate_stubs.pl +++ b/daemon/remote_generate_stubs.pl @@ -1,7 +1,16 @@ #!/usr/bin/perl -w # -# This script parses remote_protocol.x and produces lots of boilerplate -# code for both ends of the remote connection. +# This script parses remote_protocol.x or qemu_protocol.x and produces lots of +# boilerplate code for both ends of the remote connection. +# +# The first non-option argument specifies the prefix to be searched for, and +# output to, the boilerplate code. The second non-option argument is the +# file you want to operate on. For instance, to generate the dispatch table +# for both remote_protocol.x and qemu_protocol.x, you would run the +# following: +# +# remote_generate_stubs.pl -c -t remote ../src/remote/remote_protocol.x +# remote_generate_stubs.pl -c -t qemu ../src/remote/qemu_protocol.x # # By Richard Jones <rjones@redhat.com> @@ -10,8 +19,12 @@ use strict; use Getopt::Std; # Command line options. -our ($opt_p, $opt_t, $opt_a, $opt_r, $opt_d); -getopts ('ptard'); +our ($opt_p, $opt_t, $opt_a, $opt_r, $opt_d, $opt_c); +getopts ('ptardc'); + +my $structprefix = $ARGV[0]; +my $procprefix = uc $structprefix; +shift; # Convert name_of_call to NameOfCall. sub name_to_ProcName { @@ -25,47 +38,50 @@ sub name_to_ProcName { # opinion about the name, args and return type of each RPC. my ($name, $ProcName, $id, %calls, @calls); -# REMOTE_PROC_CLOSE has no args or ret. -$calls{close} = { - name => "close", - ProcName => "Close", - UC_NAME => "CLOSE", - args => "void", - ret => "void", -}; +# only generate a close method if -c was passed +if ($opt_c) { + # REMOTE_PROC_CLOSE has no args or ret. + $calls{close} = { + name => "close", + ProcName => "Close", + UC_NAME => "CLOSE", + args => "void", + ret => "void", + }; +} while (<>) { - if (/^struct remote_(.*)_args/) { + if (/^struct ${structprefix}_(.*)_args/) { $name = $1; $ProcName = name_to_ProcName ($name); - die "duplicate definition of remote_${name}_args" + die "duplicate definition of ${structprefix}_${name}_args" if exists $calls{$name}; $calls{$name} = { name => $name, ProcName => $ProcName, UC_NAME => uc $name, - args => "remote_${name}_args", + args => "${structprefix}_${name}_args", ret => "void", }; - } elsif (/^struct remote_(.*)_ret/) { + } elsif (/^struct ${structprefix}_(.*)_ret/) { $name = $1; $ProcName = name_to_ProcName ($name); if (exists $calls{$name}) { - $calls{$name}->{ret} = "remote_${name}_ret"; + $calls{$name}->{ret} = "${structprefix}_${name}_ret"; } else { $calls{$name} = { name => $name, ProcName => $ProcName, UC_NAME => uc $name, args => "void", - ret => "remote_${name}_ret" + ret => "${structprefix}_${name}_ret" } } - } elsif (/^struct remote_(.*)_msg/) { + } elsif (/^struct ${structprefix}_(.*)_msg/) { $name = $1; $ProcName = name_to_ProcName ($name); @@ -73,9 +89,9 @@ while (<>) { name => $name, ProcName => $ProcName, UC_NAME => uc $name, - msg => "remote_${name}_msg" + msg => "${structprefix}_${name}_msg" } - } elsif (/^\s*REMOTE_PROC_(.*?)\s+=\s+(\d+),?$/) { + } elsif (/^\s*${procprefix}_PROC_(.*?)\s+=\s+(\d+),?$/) { $name = lc $1; $id = $2; $ProcName = name_to_ProcName ($name); @@ -88,7 +104,7 @@ while (<>) { # Output print <<__EOF__; -/* Automatically generated by remote_generate_stubs.pl. +/* Automatically generated by ${structprefix}_generate_stubs.pl. * Do not edit this file. Any changes you make will be lost. */ @@ -111,7 +127,7 @@ elsif ($opt_p) { # Skip things which are REMOTE_MESSAGE next if $calls{$_}->{msg}; - print "static int remoteDispatch$calls{$_}->{ProcName}(\n"; + print "static int ${structprefix}Dispatch$calls{$_}->{ProcName}(\n"; print " struct qemud_server *server,\n"; print " struct qemud_client *client,\n"; print " virConnectPtr conn,\n"; @@ -152,7 +168,7 @@ elsif ($opt_t) { for ($id = 0 ; $id <= $#calls ; $id++) { if (defined $calls[$id] && !$calls[$id]->{msg}) { print "{ /* $calls[$id]->{ProcName} => $id */\n"; - print " .fn = (dispatch_fn) remoteDispatch$calls[$id]->{ProcName},\n"; + print " .fn = (dispatch_fn) ${structprefix}Dispatch$calls[$id]->{ProcName},\n"; if ($calls[$id]->{args} ne "void") { print " .args_filter = (xdrproc_t) xdr_$calls[$id]->{args},\n"; } else { diff --git a/src/Makefile.am b/src/Makefile.am index c57a7b8..9ed3bd7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -152,7 +152,9 @@ REMOTE_DRIVER_SOURCES = \ gnutls_1_0_compat.h \ remote/remote_driver.c remote/remote_driver.h \ remote/remote_protocol.c \ - remote/remote_protocol.h + remote/remote_protocol.h \ + remote/qemu_protocol.c \ + remote/qemu_protocol.h EXTRA_DIST += remote/remote_protocol.x remote/rpcgen_fix.pl @@ -431,7 +433,7 @@ if HAVE_RPCGEN # Support for non-GLIB rpcgen is here as a convenience for # non-Linux people needing to test changes during dev. # -rpcgen: +rpcgen-normal: rm -f rp.c-t rp.h-t rp.c-t1 rp.c-t2 rp.h-t1 $(RPCGEN) -h -o rp.h-t $(srcdir)/remote/remote_protocol.x $(RPCGEN) -c -o rp.c-t $(srcdir)/remote/remote_protocol.x @@ -448,6 +450,37 @@ else mv -f rp.h-t $(srcdir)/remote/remote_protocol.h mv -f rp.c-t $(srcdir)/remote/remote_protocol.c endif + +rpcgen-qemu: + rm -f rp_qemu.c-t rp_qemu.h-t rp_qemu.c-t1 rp_qemu.c-t2 rp_qemu.h-t1 + $(RPCGEN) -h -o rp_qemu.h-t $(srcdir)/remote/qemu_protocol.x + $(RPCGEN) -c -o rp_qemu.c-t $(srcdir)/remote/qemu_protocol.x +if HAVE_GLIBC_RPCGEN + perl -w $(srcdir)/remote/rpcgen_fix.pl rp_qemu.h-t > rp_qemu.h-t1 + perl -w $(srcdir)/remote/rpcgen_fix.pl rp_qemu.c-t > rp_qemu.c-t1 + (echo '#include <config.h>'; cat rp_qemu.c-t1) > rp_qemu.c-t2 + chmod 0444 rp_qemu.c-t2 rp_qemu.h-t1 + mv -f rp_qemu.h-t1 $(srcdir)/remote/qemu_protocol.h + mv -f rp_qemu.c-t2 $(srcdir)/remote/qemu_protocol.c + rm -f rp_qemu.c-t rp_qemu.h-t rp_qemu.c-t1 +else + chmod 0444 rp_qemu.c-t rp_qemu.h-t + mv -f rp_qemu.h-t $(srcdir)/remote/qemu_protocol.h + mv -f rp_qemu.c-t $(srcdir)/remote/qemu_protocol.c +endif + +# +# Maintainer-only target for re-generating the derived .c/.h source +# files, which are actually derived from the .x file. +# +# For committing protocol changes to GIT, the GLIBC rpcgen *must* +# be used. +# +# Support for non-GLIB rpcgen is here as a convenience for +# non-Linux people needing to test changes during dev. +# +rpcgen: rpcgen-normal rpcgen-qemu + endif remote/remote_protocol.c: remote/remote_protocol.h diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index a8aeebc..4ca6d79 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -5,6 +5,7 @@ #include "datatypes.h" #include "libvirt/libvirt-qemu.h" #include "qemu/qemu_driver.h" +#include "remote/remote_driver.h" /** * virLibConnError: @@ -84,6 +85,8 @@ virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd, if (STREQ(conn->driver->name, "QEMU")) ret = qemuMonitorCommand(domain, cmd, result, flags); + else if (STREQ(conn->driver->name, "remote")) + ret = remoteQemuMonitorCommand(domain, cmd, result, flags); else { virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); goto error; diff --git a/src/remote/qemu_protocol.c b/src/remote/qemu_protocol.c new file mode 100644 index 0000000..81916ed --- /dev/null +++ b/src/remote/qemu_protocol.c @@ -0,0 +1,41 @@ +#include <config.h> +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "./remote/qemu_protocol.h" +#include "internal.h" +#include "remote_protocol.h" +#include <arpa/inet.h> + +bool_t +xdr_qemu_monitor_command_args (XDR *xdrs, qemu_monitor_command_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->domain)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->cmd)) + return FALSE; + if (!xdr_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_qemu_monitor_command_ret (XDR *xdrs, qemu_monitor_command_ret *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->result)) + return FALSE; + return TRUE; +} + +bool_t +xdr_qemu_procedure (XDR *xdrs, qemu_procedure *objp) +{ + + if (!xdr_enum (xdrs, (enum_t *) objp)) + return FALSE; + return TRUE; +} diff --git a/src/remote/qemu_protocol.h b/src/remote/qemu_protocol.h new file mode 100644 index 0000000..b822187 --- /dev/null +++ b/src/remote/qemu_protocol.h @@ -0,0 +1,57 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _RP_QEMU_H_RPCGEN +#define _RP_QEMU_H_RPCGEN + +#include <rpc/rpc.h> + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "internal.h" +#include "remote_protocol.h" +#include <arpa/inet.h> + +struct qemu_monitor_command_args { + remote_nonnull_domain domain; + remote_nonnull_string cmd; + int flags; +}; +typedef struct qemu_monitor_command_args qemu_monitor_command_args; + +struct qemu_monitor_command_ret { + remote_nonnull_string result; +}; +typedef struct qemu_monitor_command_ret qemu_monitor_command_ret; +#define QEMU_PROGRAM 0x20008087 +#define QEMU_PROTOCOL_VERSION 1 + +enum qemu_procedure { + QEMU_PROC_MONITOR_COMMAND = 1, +}; +typedef enum qemu_procedure qemu_procedure; + +/* the xdr functions */ + +#if defined(__STDC__) || defined(__cplusplus) +extern bool_t xdr_qemu_monitor_command_args (XDR *, qemu_monitor_command_args*); +extern bool_t xdr_qemu_monitor_command_ret (XDR *, qemu_monitor_command_ret*); +extern bool_t xdr_qemu_procedure (XDR *, qemu_procedure*); + +#else /* K&R C */ +extern bool_t xdr_qemu_monitor_command_args (); +extern bool_t xdr_qemu_monitor_command_ret (); +extern bool_t xdr_qemu_procedure (); + +#endif /* K&R C */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_RP_QEMU_H_RPCGEN */ diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x new file mode 100644 index 0000000..1d07895 --- /dev/null +++ b/src/remote/qemu_protocol.x @@ -0,0 +1,46 @@ +/* -*- c -*- + * qemu_protocol.x: private protocol for communicating between + * remote_internal driver and libvirtd. This protocol is + * internal and may change at any time. + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Chris Lalancette <clalance@redhat.com> + */ + +%#include "internal.h" +%#include "remote_protocol.h" +%#include <arpa/inet.h> + +/*----- Protocol. -----*/ +struct qemu_monitor_command_args { + remote_nonnull_domain domain; + remote_nonnull_string cmd; + int flags; +}; + +struct qemu_monitor_command_ret { + remote_nonnull_string result; +}; + +/* Define the program number, protocol version and procedure numbers here. */ +const QEMU_PROGRAM = 0x20008087; +const QEMU_PROTOCOL_VERSION = 1; + +enum qemu_procedure { + QEMU_PROC_MONITOR_COMMAND = 1 +}; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 44d8c26..b8c4fbb 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -84,6 +84,7 @@ #include "qparams.h" #include "remote_driver.h" #include "remote_protocol.h" +#include "qemu_protocol.h" #include "memory.h" #include "util.h" #include "event.h" @@ -206,8 +207,9 @@ struct private_data { }; enum { - REMOTE_CALL_IN_OPEN = 1, - REMOTE_CALL_QUIET_MISSING_RPC = 2, + REMOTE_CALL_IN_OPEN = (1 << 0), + REMOTE_CALL_QUIET_MISSING_RPC = (1 << 1), + REMOTE_QEMU_CALL = (1 << 2), }; @@ -8724,9 +8726,49 @@ done: /*----------------------------------------------------------------------*/ +int +remoteQemuMonitorCommand (virDomainPtr domain, const char *cmd, char **result, + unsigned int flags) +{ + int rv = -1; + qemu_monitor_command_args args; + qemu_monitor_command_ret ret; + struct private_data *priv = domain->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.domain, domain); + args.cmd = (char *)cmd; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (domain->conn, priv, REMOTE_QEMU_CALL, QEMU_PROC_MONITOR_COMMAND, + (xdrproc_t) xdr_qemu_monitor_command_args, (char *) &args, + (xdrproc_t) xdr_qemu_monitor_command_ret, (char *) &ret) == -1) + goto done; + + *result = strdup(ret.result); + if (*result == NULL) { + + virReportOOMError(); + goto cleanup; + } + + rv = 0; + +cleanup: + xdr_free ((xdrproc_t) xdr_qemu_monitor_command_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + +/*----------------------------------------------------------------------*/ static struct remote_thread_call * prepareCall(struct private_data *priv, + int flags, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret) @@ -8754,8 +8796,14 @@ prepareCall(struct private_data *priv, rv->ret = ret; rv->want_reply = 1; - hdr.prog = REMOTE_PROGRAM; - hdr.vers = REMOTE_PROTOCOL_VERSION; + if (flags & REMOTE_QEMU_CALL) { + hdr.prog = QEMU_PROGRAM; + hdr.vers = QEMU_PROTOCOL_VERSION; + } + else { + hdr.prog = REMOTE_PROGRAM; + hdr.vers = REMOTE_PROTOCOL_VERSION; + } hdr.proc = proc_nr; hdr.type = REMOTE_CALL; hdr.serial = rv->serial; @@ -9102,7 +9150,6 @@ remoteIODecodeMessageLength(struct private_data *priv) { static int processCallDispatchReply(virConnectPtr conn, struct private_data *priv, - int in_open, remote_message_header *hdr, XDR *xdr); @@ -9114,18 +9161,19 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, static int processCallDispatchStream(virConnectPtr conn, struct private_data *priv, - int in_open, remote_message_header *hdr, XDR *xdr); static int processCallDispatch(virConnectPtr conn, struct private_data *priv, - int in_open) { + int flags) { XDR xdr; struct remote_message_header hdr; int len = priv->bufferLength - 4; int rv = -1; + int expectedprog; + int expectedvers; /* Length word has already been read */ priv->bufferOffset = 4; @@ -9139,35 +9187,40 @@ processCallDispatch(virConnectPtr conn, struct private_data *priv, priv->bufferOffset += xdr_getpos(&xdr); + expectedprog = REMOTE_PROGRAM; + expectedvers = REMOTE_PROTOCOL_VERSION; + if (flags & REMOTE_QEMU_CALL) { + expectedprog = QEMU_PROGRAM; + expectedvers = QEMU_PROTOCOL_VERSION; + } + /* Check program, version, etc. are what we expect. */ - if (hdr.prog != REMOTE_PROGRAM) { + if (hdr.prog != expectedprog) { remoteError(VIR_ERR_RPC, _("unknown program (received %x, expected %x)"), - hdr.prog, REMOTE_PROGRAM); + hdr.prog, expectedprog); return -1; } - if (hdr.vers != REMOTE_PROTOCOL_VERSION) { + if (hdr.vers != expectedvers) { remoteError(VIR_ERR_RPC, _("unknown protocol version (received %x, expected %x)"), - hdr.vers, REMOTE_PROTOCOL_VERSION); + hdr.vers, expectedvers); return -1; } switch (hdr.type) { case REMOTE_REPLY: /* Normal RPC replies */ - rv = processCallDispatchReply(conn, priv, in_open, - &hdr, &xdr); + rv = processCallDispatchReply(conn, priv, &hdr, &xdr); break; case REMOTE_MESSAGE: /* Async notifications */ - rv = processCallDispatchMessage(conn, priv, in_open, + rv = processCallDispatchMessage(conn, priv, flags & REMOTE_CALL_IN_OPEN, &hdr, &xdr); break; case REMOTE_STREAM: /* Stream protocol */ - rv = processCallDispatchStream(conn, priv, in_open, - &hdr, &xdr); + rv = processCallDispatchStream(conn, priv, &hdr, &xdr); break; default: @@ -9186,7 +9239,6 @@ processCallDispatch(virConnectPtr conn, struct private_data *priv, static int processCallDispatchReply(virConnectPtr conn ATTRIBUTE_UNUSED, struct private_data *priv, - int in_open ATTRIBUTE_UNUSED, remote_message_header *hdr, XDR *xdr) { struct remote_thread_call *thecall; @@ -9302,7 +9354,6 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, static int processCallDispatchStream(virConnectPtr conn ATTRIBUTE_UNUSED, struct private_data *priv, - int in_open ATTRIBUTE_UNUSED, remote_message_header *hdr, XDR *xdr) { struct private_stream_data *privst; @@ -9408,7 +9459,7 @@ processCallDispatchStream(virConnectPtr conn ATTRIBUTE_UNUSED, static int remoteIOHandleInput(virConnectPtr conn, struct private_data *priv, - int in_open) + int flags) { /* Read as much data as is available, until we get * EAGAIN @@ -9435,7 +9486,7 @@ remoteIOHandleInput(virConnectPtr conn, struct private_data *priv, * next iteration. */ } else { - ret = processCallDispatch(conn, priv, in_open); + ret = processCallDispatch(conn, priv, flags); priv->bufferOffset = priv->bufferLength = 0; /* * We've completed one call, so return even @@ -9458,7 +9509,7 @@ remoteIOHandleInput(virConnectPtr conn, struct private_data *priv, static int remoteIOEventLoop(virConnectPtr conn, struct private_data *priv, - int in_open, + int flags, struct remote_thread_call *thiscall) { struct pollfd fds[2]; @@ -9548,7 +9599,7 @@ remoteIOEventLoop(virConnectPtr conn, } if (fds[0].revents & POLLIN) { - if (remoteIOHandleInput(conn, priv, in_open) < 0) + if (remoteIOHandleInput(conn, priv, flags) < 0) goto error; } @@ -9753,9 +9804,7 @@ remoteIO(virConnectPtr conn, if (priv->watch >= 0) virEventUpdateHandle(priv->watch, 0); - rv = remoteIOEventLoop(conn, priv, - flags & REMOTE_CALL_IN_OPEN ? 1 : 0, - thiscall); + rv = remoteIOEventLoop(conn, priv, flags, thiscall); if (priv->watch >= 0) virEventUpdateHandle(priv->watch, VIR_EVENT_HANDLE_READABLE); @@ -9827,14 +9876,14 @@ cleanup: */ static int call (virConnectPtr conn, struct private_data *priv, - int flags /* if we are in virConnectOpen */, + int flags, int proc_nr, xdrproc_t args_filter, char *args, xdrproc_t ret_filter, char *ret) { struct remote_thread_call *thiscall; - thiscall = prepareCall(priv, proc_nr, args_filter, args, + thiscall = prepareCall(priv, flags, proc_nr, args_filter, args, ret_filter, ret); if (!thiscall) { diff --git a/src/remote/remote_driver.h b/src/remote/remote_driver.h index 49a63bd..d79f75d 100644 --- a/src/remote/remote_driver.h +++ b/src/remote/remote_driver.h @@ -46,5 +46,8 @@ unsigned long remoteVersion(void); # define LIBVIRT_SERVERKEY LIBVIRT_PKI_DIR "/libvirt/private/serverkey.pem" # define LIBVIRT_SERVERCERT LIBVIRT_PKI_DIR "/libvirt/servercert.pem" +int +remoteQemuMonitorCommand (virDomainPtr domain, const char *cmd, char **result, + unsigned int flags); #endif /* __VIR_REMOTE_INTERNAL_H__ */ diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index c9816dd..10c7d94 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -3541,12 +3541,60 @@ xdr_remote_message_status (XDR *xdrs, remote_message_status *objp) bool_t xdr_remote_message_header (XDR *xdrs, remote_message_header *objp) { + register int32_t *buf; + + + if (xdrs->x_op == XDR_ENCODE) { + buf = (int32_t*)XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->prog)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->vers)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->proc)) + return FALSE; + + } else { + (void)IXDR_PUT_U_INT32(buf, objp->prog); + (void)IXDR_PUT_U_INT32(buf, objp->vers); + (void)IXDR_PUT_U_INT32(buf, objp->proc); + } + if (!xdr_remote_message_type (xdrs, &objp->type)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->serial)) + return FALSE; + if (!xdr_remote_message_status (xdrs, &objp->status)) + return FALSE; + return TRUE; + } else if (xdrs->x_op == XDR_DECODE) { + buf = (int32_t*)XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_u_int (xdrs, &objp->prog)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->vers)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->proc)) + return FALSE; + + } else { + objp->prog = IXDR_GET_U_LONG(buf); + objp->vers = IXDR_GET_U_LONG(buf); + objp->proc = IXDR_GET_U_LONG(buf); + } + if (!xdr_remote_message_type (xdrs, &objp->type)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->serial)) + return FALSE; + if (!xdr_remote_message_status (xdrs, &objp->status)) + return FALSE; + return TRUE; + } if (!xdr_u_int (xdrs, &objp->prog)) return FALSE; if (!xdr_u_int (xdrs, &objp->vers)) return FALSE; - if (!xdr_remote_procedure (xdrs, &objp->proc)) + if (!xdr_u_int (xdrs, &objp->proc)) return FALSE; if (!xdr_remote_message_type (xdrs, &objp->type)) return FALSE; diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index 57ed123..a4a840a 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -2204,7 +2204,7 @@ typedef enum remote_message_status remote_message_status; struct remote_message_header { u_int prog; u_int vers; - remote_procedure proc; + u_int proc; remote_message_type type; u_int serial; remote_message_status status; diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 9aa3a7e..0ee7646 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2078,7 +2078,7 @@ const REMOTE_MESSAGE_HEADER_XDR_LEN = 4; struct remote_message_header { unsigned prog; /* REMOTE_PROGRAM */ unsigned vers; /* REMOTE_PROTOCOL_VERSION */ - remote_procedure proc; /* REMOTE_PROC_x */ + unsigned proc; /* REMOTE_PROC_x */ remote_message_type type; unsigned serial; /* Serial number of message. */ remote_message_status status; -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
Since we are adding a new "per-hypervisor" protocol, we make it so that the qemu remote protocol uses a new PROTOCOL and PROGRAM number. This allows us to easily distinguish it from the normal REMOTE protocol.
This necessitates changing the proc in remote_message_header from a "remote_procedure" to an "unsigned", which should be the same size (and thus preserve the on-wire protocol).
REMOTE_PROTOCOL = $(top_srcdir)/src/remote/remote_protocol.x +QEMU_PROTOCOL = $(top_srcdir)/src/remote/qemu_protocol.x
remote_dispatch_prototypes.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -p $(REMOTE_PROTOCOL) > $@ + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -p remote $(REMOTE_PROTOCOL) > $@
remote_dispatch_table.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -t $(REMOTE_PROTOCOL) > $@ + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -t remote $(REMOTE_PROTOCOL) > $@
remote_dispatch_args.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -a $(REMOTE_PROTOCOL) > $@ + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -a remote $(REMOTE_PROTOCOL) > $@
remote_dispatch_ret.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL) - $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -r $(REMOTE_PROTOCOL) > $@ + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -r remote $(REMOTE_PROTOCOL) > $@ + +qemu_dispatch_prototypes.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL) + $(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -p qemu $(QEMU_PROTOCOL) > $@
Reading through the patch in linear order, it was not clear at this point why remote needs the addition of the new -c option, but not qemu.
+remoteDispatchClientRequest(struct qemud_server *server, + struct qemud_client *client, + struct qemud_client_message *msg) { int ret; remote_error rerr; + int qemu_call;
Based on your use, this is better as bool...
- if (msg->hdr.vers != REMOTE_PROTOCOL_VERSION) { + + if (!qemu_call && msg->hdr.vers != REMOTE_PROTOCOL_VERSION) { remoteDispatchFormatError (&rerr, _("version mismatch (actual %x, expected %x)"), msg->hdr.vers, REMOTE_PROTOCOL_VERSION); goto error; } + else if (qemu_call && msg->hdr.vers != QEMU_PROTOCOL_VERSION) { + remoteDispatchFormatError (&rerr, + _("version mismatch (actual %x, expected %x)"), + msg->hdr.vers, QEMU_PROTOCOL_VERSION); + goto error; + }
switch (msg->hdr.type) { case REMOTE_CALL: - return remoteDispatchClientCall(server, client, msg); + return remoteDispatchClientCall(server, client, msg, qemu_call);
...which affects the prototype of remoteDispatchClientCall.
@@ -6390,6 +6406,35 @@ remoteDispatchNumOfNwfilters (struct qemud_server *server ATTRIBUTE_UNUSED, return 0; }
+static int +qemuDispatchMonitorCommand (struct qemud_server *server ATTRIBUTE_UNUSED,
Given that earlier in the patch, you removed the space for remoteDispatchClientRequest, it seems odd to see the space here.
+++ b/daemon/remote_generate_stubs.pl @@ -1,7 +1,16 @@ #!/usr/bin/perl -w # -# This script parses remote_protocol.x and produces lots of boilerplate -# code for both ends of the remote connection. +# This script parses remote_protocol.x or qemu_protocol.x and produces lots of +# boilerplate code for both ends of the remote connection. +# +# The first non-option argument specifies the prefix to be searched for, and +# output to, the boilerplate code. The second non-option argument is the +# file you want to operate on. For instance, to generate the dispatch table +# for both remote_protocol.x and qemu_protocol.x, you would run the +# following: +# +# remote_generate_stubs.pl -c -t remote ../src/remote/remote_protocol.x +# remote_generate_stubs.pl -c -t qemu ../src/remote/qemu_protocol.x
This comment doesn't match the makefile.
# # By Richard Jones <rjones@redhat.com>
@@ -10,8 +19,12 @@ use strict; use Getopt::Std;
# Command line options. -our ($opt_p, $opt_t, $opt_a, $opt_r, $opt_d); -getopts ('ptard'); +our ($opt_p, $opt_t, $opt_a, $opt_r, $opt_d, $opt_c); +getopts ('ptardc'); + +my $structprefix = $ARGV[0]; +my $procprefix = uc $structprefix; +shift;
# Convert name_of_call to NameOfCall. sub name_to_ProcName { @@ -25,47 +38,50 @@ sub name_to_ProcName { # opinion about the name, args and return type of each RPC. my ($name, $ProcName, $id, %calls, @calls);
-# REMOTE_PROC_CLOSE has no args or ret. -$calls{close} = { - name => "close", - ProcName => "Close", - UC_NAME => "CLOSE", - args => "void", - ret => "void", -}; +# only generate a close method if -c was passed
Ah, now I see why only one of the two generators needed -c.
+if ($opt_c) { + # REMOTE_PROC_CLOSE has no args or ret. + $calls{close} = { + name => "close", + ProcName => "Close", + UC_NAME => "CLOSE", + args => "void", + ret => "void", + }; +}
@@ -88,7 +104,7 @@ while (<>) { # Output
print <<__EOF__; -/* Automatically generated by remote_generate_stubs.pl. +/* Automatically generated by ${structprefix}_generate_stubs.pl.
One search-and-replace too many. The script's name is fixed, so half your output files now have a bogus comment.
+/* Define the program number, protocol version and procedure numbers here. */ +const QEMU_PROGRAM = 0x20008087; +const QEMU_PROTOCOL_VERSION = 1; + +enum qemu_procedure { + QEMU_PROC_MONITOR_COMMAND = 1
Do we want the cute comment here about grouping command ids by 10, or save that until we actually have more commands? Overall, the patch is large, but looks decent. I don't know if it could have been subdivided any further. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Signed-off-by: Chris Lalancette <clalance@redhat.com> --- tools/Makefile.am | 1 + tools/virsh.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 0 deletions(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index 33a3216..b69de75 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -39,6 +39,7 @@ virsh_LDADD = \ $(STATIC_BINARIES) \ $(WARN_CFLAGS) \ ../src/libvirt.la \ + ../src/libvirt-qemu.la \ ../gnulib/lib/libgnu.la \ $(VIRSH_LIBS) virsh_CFLAGS = \ diff --git a/tools/virsh.c b/tools/virsh.c index b2a1538..8b1a355 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -50,6 +50,7 @@ #include "util.h" #include "memory.h" #include "xml.h" +#include "libvirt/libvirt-qemu.h" static char *progname; @@ -8627,6 +8628,58 @@ cleanup: } /* + * "qemu-monitor-command" command + */ +static const vshCmdInfo info_qemu_monitor_command[] = { + {"help", N_("Qemu Monitor Command")}, + {"desc", N_("Qemu Monitor Command")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_qemu_monitor_command[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"cmd", VSH_OT_DATA, VSH_OFLAG_REQ, N_("command")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int ret = FALSE; + char *monitor_cmd; + char *result = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + goto cleanup; + + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (dom == NULL) + goto cleanup; + + monitor_cmd = vshCommandOptString(cmd, "cmd", NULL); + if (monitor_cmd == NULL) { + vshError(ctl, "%s", _("missing monitor command")); + goto cleanup; + } + + if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, 0) < 0) + goto cleanup; + + fprintf(stdout, "%s\n", result); + + ret = TRUE; + +cleanup: + VIR_FREE(result); + if (dom) + virDomainFree(dom); + + return ret; +} + + +/* * Commands */ static const vshCmdDef commands[] = { @@ -8788,6 +8841,8 @@ static const vshCmdDef commands[] = { {"snapshot-list", cmdSnapshotList, opts_snapshot_list, info_snapshot_list}, {"snapshot-revert", cmdDomainSnapshotRevert, opts_snapshot_revert, info_snapshot_revert}, + {"qemu-monitor-command", cmdQemuMonitorCommand, opts_qemu_monitor_command, info_qemu_monitor_command}, + {NULL, NULL, NULL, NULL} }; -- 1.6.6.1

On 04/21/2010 10:01 AM, Chris Lalancette wrote:
Signed-off-by: Chris Lalancette <clalance@redhat.com> --- tools/Makefile.am | 1 + tools/virsh.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
Nothing in tools/virsh.pod? Hmm, we ought to modify docs/api_extension to mention documenting new virsh commands (step 8/8).
+ if (virDomainQemuMonitorCommand(dom, monitor_cmd, &result, 0) < 0) + goto cleanup; + + fprintf(stdout, "%s\n", result);
Isn't puts(result) more efficient, for the same end result? To help keep docs in sync, I'll withhold an ack until seeing the .pod updates in the respin :) -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Thanks to DV for knocking together the Relax-NG changes quickly for me. Signed-off-by: Chris Lalancette <clalance@redhat.com> --- docs/schemas/domain.rng | 26 +++++++++++++++++ src/qemu/qemu_driver.c | 16 +++++----- src/qemu/qemu_driver.h | 12 ++++++++ tests/qemuargv2xmltest.c | 5 +++ .../qemuxml2argv-qemu-ns-no-env.args | 1 + .../qemuxml2argv-qemu-ns-no-env.xml | 28 ++++++++++++++++++ tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.xml | 30 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 2 + tests/testutilsqemu.c | 12 ++++++- 10 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-qemu-ns-no-env.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-qemu-ns-no-env.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.xml diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 56b6705..4117a62 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -41,6 +41,9 @@ <optional> <ref name="seclabel"/> </optional> + <optional> + <ref name='qemucmdline'/> + </optional> </interleave> </element> </define> @@ -1600,6 +1603,29 @@ </define> <!-- + Optional hypervisor extensions in their own namespace: + QEmu + --> + <define name="qemucmdline"> + <element name="commandline" ns="http://libvirt.org/schemas/domain/qemu/1.0"> + <zeroOrMore> + <element name="arg"> + <text/> + </element> + </zeroOrMore> + <zeroOrMore> + <element name="env"> + <attribute name='name'/> + <optional> + <attribute name='value'/> + </optional> + <empty/> + </element> + </zeroOrMore> + </element> + </define> + + <!-- Type library Our unsignedInt doesn't allow a leading '+' in its lexical form diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 973a752..d2e4ec9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -531,7 +531,7 @@ static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virD } } -static void qemuDomainDefNamespaceFree(void *nsdata) +void qemuDomainDefNamespaceFree(void *nsdata) { qemuDomainCmdlineDefPtr cmd = nsdata; int i; @@ -551,10 +551,10 @@ static void qemuDomainDefNamespaceFree(void *nsdata) VIR_FREE(cmd); } -static int qemuDomainDefNamespaceParse(xmlDocPtr xml, - xmlNodePtr root, - xmlXPathContextPtr ctxt, - void **data) +int qemuDomainDefNamespaceParse(xmlDocPtr xml, + xmlNodePtr root, + xmlXPathContextPtr ctxt, + void **data) { qemuDomainCmdlineDefPtr cmd = NULL; xmlNsPtr ns; @@ -643,8 +643,8 @@ error: return -1; } -static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, - void *nsdata) +int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata) { qemuDomainCmdlineDefPtr cmd = nsdata; int i; @@ -665,7 +665,7 @@ static int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, return 0; } -static const char *qemuDomainDefNamespaceHref(void) +const char *qemuDomainDefNamespaceHref(void) { return "xmlns:qemu='" QEMU_NAMESPACE_HREF "'"; } diff --git a/src/qemu/qemu_driver.h b/src/qemu/qemu_driver.h index f061cba..99e011f 100644 --- a/src/qemu/qemu_driver.h +++ b/src/qemu/qemu_driver.h @@ -27,6 +27,8 @@ # include <config.h> +# include <libxml/xpath.h> + # include "internal.h" # if HAVE_LINUX_KVM_H @@ -52,4 +54,14 @@ int qemuRegister(void); int qemuMonitorCommand(virDomainPtr domain, const char *cmd, char **result, unsigned int flags); + +void qemuDomainDefNamespaceFree(void *nsdata); +int qemuDomainDefNamespaceParse(xmlDocPtr xml, + xmlNodePtr root, + xmlXPathContextPtr ctxt, + void **data); +int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata); +const char *qemuDomainDefNamespaceHref(void); + #endif /* QEMUD_DRIVER_H */ diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c index bd81018..501bc75 100644 --- a/tests/qemuargv2xmltest.c +++ b/tests/qemuargv2xmltest.c @@ -228,6 +228,11 @@ mymain(int argc, char **argv) DO_TEST_FULL("restore-v2", QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC, "exec:cat"); DO_TEST_FULL("migrate", QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP, "tcp:10.0.0.1:5000"); + /* it's not really possible to handle the environment variables in a + * generic way, so we run the qemu namespace test without them + */ + DO_TEST("qemu-ns-no-env", 0); + free(driver.stateDir); virCapabilitiesFree(driver.caps); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns-no-env.args b/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns-no-env.args new file mode 100644 index 0000000..b02eb26 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns-no-env.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb -unknown parameter diff --git a/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns-no-env.xml b/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns-no-env.xml new file mode 100644 index 0000000..c2cc901 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns-no-env.xml @@ -0,0 +1,28 @@ +<domain type='qemu' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <controller type='ide' index='0'/> + </devices> + <qemu:commandline> + <qemu:arg>-unknown</qemu:arg> + <qemu:arg>parameter</qemu:arg> + </qemu:commandline> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.args b/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.args new file mode 100644 index 0000000..b7055d5 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test NS=ns BAR= /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb -unknown parameter diff --git a/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.xml b/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.xml new file mode 100644 index 0000000..6101124 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-qemu-ns.xml @@ -0,0 +1,30 @@ +<domain type='qemu' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <controller type='ide' index='0'/> + </devices> + <qemu:commandline> + <qemu:arg>-unknown</qemu:arg> + <qemu:arg>parameter</qemu:arg> + <qemu:env name='NS' value='ns'/> + <qemu:env name='BAR'/> + </qemu:commandline> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 9e4d5bf..19137ba 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -346,6 +346,8 @@ mymain(int argc, char **argv) DO_TEST_FULL("restore-v2", QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC, "exec:cat"); DO_TEST_FULL("migrate", QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP, "tcp:10.0.0.1:5000"); + DO_TEST("qemu-ns", 0); + free(driver.stateDir); virCapabilitiesFree(driver.caps); diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 8dd26d4..84f06dc 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -6,6 +6,7 @@ # include "testutilsqemu.h" # include "testutils.h" # include "memory.h" +# include "qemu/qemu_driver.h" static virCapsGuestMachinePtr *testQemuAllocMachines(int *nmachines) { @@ -57,8 +58,8 @@ virCapsPtr testQemuCapsInit(void) { struct utsname utsname; virCapsPtr caps; virCapsGuestPtr guest; - virCapsGuestMachinePtr *machines; - int nmachines; + virCapsGuestMachinePtr *machines = NULL; + int nmachines = 0; static const char *const xen_machines[] = { "xenner" }; @@ -68,6 +69,13 @@ virCapsPtr testQemuCapsInit(void) { 0, 0)) == NULL) return NULL; + if (VIR_ALLOC(caps->ns) < 0) + goto cleanup; + caps->ns->parse = qemuDomainDefNamespaceParse; + caps->ns->free = qemuDomainDefNamespaceFree; + caps->ns->format = qemuDomainDefNamespaceFormatXML; + caps->ns->href = qemuDomainDefNamespaceHref; + if ((machines = testQemuAllocMachines(&nmachines)) == NULL) goto cleanup; -- 1.6.6.1
participants (4)
-
Chris Lalancette
-
Daniel P. Berrange
-
Eric Blake
-
Matthias Bolte