[PATCH] Add virConnectGetCapabilities call to return the capabilities of the driver / hypervisor

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch:

* Adds virConnectGetCapabilities which returns an XML description of the capabilities of the driver or hypervisor under this connection (see below for more about the XML format).

* Adds virsh capabilities command.

* Checks the Python binding works.

Caveats:

* Xen implementation (in xen_internal.c) is not tested because I don't have a working Xen machine right now. QEMU, test and remote work however.

Example:

$ virsh -c test:///default capabilities | tidy -xml -i -q
<capabilities>
  <domain>
    <type>test</type>
  </domain>
  <host_features></host_features>
  <guest_architectures>
    <guest_architecture>
      <model>i686</model>
      <bits>32</bits>
      <guest_features>
        <accelerated />
        <pae />
      </guest_features>
    </guest_architecture>
  </guest_architectures>
</capabilities>

$ virsh -c qemu:///session capabilities | tidy -xml -i -q
<capabilities>
  <domain>
    <type>qemu</type>
  </domain>
  <guest_architectures>
    <guest_architecture>
      <model>i686</model>
      <bits>32</bits>
      <features>
        <emulated />
      </features>
    </guest_architecture>
    <guest_architecture>
      <model>x86_64</model>
      <bits>64</bits>
      <features>
        <emulated />
      </features>
    </guest_architecture>
[... other archs deleted ...]
  </guest_architectures>
</capabilities>

XML format:

The <capabilities> node contains information about the host and a list of supported architectures for guests. About the host we return:

  <domain>
    <type>	The default type acceptable when creating a domain.

  <host_features>
    <hvm>	Does the host CPU support HVM?
      <type>	Type of HVM (eg. "vmx")
      <bios_setting>   "enabled" or "disabled"

About the guest architectures we can return:

  <guest_architecture>
    <model>	The (virtualised) CPU
    <bits>      Bits
    <domain>
      <type>	If different from the default type (eg. for kqemu)
    <features>
      <hvm>		Full virt guest?
      <accelerated>	Accelerated (eg. Xen or kqemu)
      <emulated>	Emulated (eg. qemu or bochs)
      (and a few other esoteric flags here)

Rich.

--
Emerging Technologies, Red Hat  http://et.redhat.com/~rjones/
64 Baker Street, London, W1U 7DF     Mobile: +44 7866 314 421
 "[Negative numbers] darken the very whole doctrines of the equations
 and make dark of the things which are in their nature excessively
 obvious and simple" (Francis Maseres FRS, mathematician, 1759)
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/configure.in libvirt-caps-tmp/configure.in
--- libvirt-cvs/configure.in	2007-03-06 12:00:03.000000000 +0000
+++ libvirt-caps-tmp/configure.in	2007-03-09 13:08:57.000000000 +0000
@@ -43,6 +43,8 @@
 AC_PATH_PROG(XMLLINT, xmllint, /usr/bin/xmllint)
 AC_PATH_PROG(XSLTPROC, xsltproc, /usr/bin/xsltproc)
 
+dnl Availability of various common functions.
+AC_CHECK_FUNCS([regexec])
 
 dnl Make sure we have an ANSI compiler
 AM_C_PROTOTYPES
@@ -50,6 +52,8 @@
 
 AM_PROG_LIBTOOL
 
+AM_PROG_CC_C_O
+
 LIBVIRT_COMPILE_WARNINGS(maximum)
 
 dnl Specific dir for HTML output ?
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/.cvsignore libvirt-caps-tmp/.cvsignore
--- libvirt-cvs/.cvsignore	2007-02-16 17:06:38.000000000 +0000
+++ libvirt-caps-tmp/.cvsignore	2007-03-09 13:08:57.000000000 +0000
@@ -1,3 +1,4 @@
+.git
 Makefile
 aclocal.m4
 autom4te.cache
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/include/libvirt/libvirt.h.in libvirt-caps-tmp/include/libvirt/libvirt.h.in
--- libvirt-cvs/include/libvirt/libvirt.h.in	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/include/libvirt/libvirt.h.in	2007-03-09 13:08:57.000000000 +0000
@@ -231,10 +231,15 @@
 const char *		virConnectGetType	(virConnectPtr conn);
 int			virConnectGetVersion	(virConnectPtr conn,
 						 unsigned long *hvVer);
-int			virConnectGetMaxVcpus	(virConnectPtr conn,
+/*
+ * Capabilities of the connection / driver.
+ */
+
+int                     virConnectGetMaxVcpus   (virConnectPtr conn,
 						 const char *type);
 int			virNodeGetInfo		(virConnectPtr conn,
 						 virNodeInfoPtr info);
+char *                  virConnectGetCapabilities (virConnectPtr conn);
 
 /*
  * Gather list of running domains
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/include/libvirt/virterror.h libvirt-caps-tmp/include/libvirt/virterror.h
--- libvirt-cvs/include/libvirt/virterror.h	2007-02-14 15:40:54.000000000 +0000
+++ libvirt-caps-tmp/include/libvirt/virterror.h	2007-03-09 13:08:57.000000000 +0000
@@ -119,6 +119,7 @@
     VIR_ERR_XML_DETAIL, /* detail of an XML error */
     VIR_ERR_INVALID_NETWORK, /* invalid network object */
     VIR_ERR_NETWORK_EXIST, /* the network already exist */
+    VIR_ERR_SYSTEM_ERROR, /* general system call failure */
 } virErrorNumber;
 
 /**
@@ -133,7 +134,7 @@
 /*
  * Errors can be handled as asynchronous callbacks or after the routine
  * failed. They can also be handled globally at the library level, or
- * at the connection level (which then has priority
+ * at the connection level (which then has priority).
  */
 
 virErrorPtr		virGetLastError		(void);
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/proxy/libvirt_proxy.c libvirt-caps-tmp/proxy/libvirt_proxy.c
--- libvirt-cvs/proxy/libvirt_proxy.c	2007-03-02 11:05:16.000000000 +0000
+++ libvirt-caps-tmp/proxy/libvirt_proxy.c	2007-03-09 13:08:57.000000000 +0000
@@ -786,3 +786,17 @@
     proxyCloseUnixSocket();
     exit(0);
 }
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/proxy/Makefile.am libvirt-caps-tmp/proxy/Makefile.am
--- libvirt-cvs/proxy/Makefile.am	2007-03-02 11:05:16.000000000 +0000
+++ libvirt-caps-tmp/proxy/Makefile.am	2007-03-09 13:08:57.000000000 +0000
@@ -10,7 +10,8 @@
 libvirt_proxy_SOURCES = libvirt_proxy.c @top_srcdir@/src/xend_internal.c \
 	    @top_srcdir@/src/xen_internal.c @top_srcdir@/src/virterror.c \
 	    @top_srcdir@/src/sexpr.c @top_srcdir@/src/xml.c \
-            @top_srcdir@/src/xs_internal.c
+            @top_srcdir@/src/xs_internal.c \
+	    @top_srcdir@/src/lib.c @top_srcdir@/src/lib.h
 libvirt_proxy_LDFLAGS =
 libvirt_proxy_DEPENDENCIES =
 libvirt_proxy_LDADD =
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/qemud/conf.c libvirt-caps-tmp/qemud/conf.c
--- libvirt-cvs/qemud/conf.c	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/qemud/conf.c	2007-03-09 13:08:57.000000000 +0000
@@ -122,8 +122,13 @@
     return 0;
 }
 
+/* NB: I've stored "bits" here but in future it may be that the
+ * qemu-system-* binaries are able to emulate machines with different
+ * register sizes.  That doesn't happen at present though.
+ */
 struct qemu_arch_info {
     const char *arch;
+    int bits;
     const char **machines;
     const char *binary;
 };
@@ -145,12 +150,12 @@
 
 /* The archicture tables for supported QEMU archs */
 static struct qemu_arch_info archs[] = { 
-    {  "i686", arch_info_x86_machines, "qemu" },
-    {  "x86_64", arch_info_x86_machines, "qemu-system-x86_64" },
-    {  "mips", arch_info_mips_machines, "qemu-system-mips" },
-    {  "mipsel", arch_info_mips_machines, "qemu-system-mipsel" },
-    {  "sparc", arch_info_sparc_machines, "qemu-system-sparc" },
-    {  "ppc", arch_info_ppc_machines, "qemu-system-ppc" },
+    {  "i686", 32, arch_info_x86_machines, "qemu" },
+    {  "x86_64", 64, arch_info_x86_machines, "qemu-system-x86_64" },
+    {  "mips", 32, arch_info_mips_machines, "qemu-system-mips" },
+    {  "mipsel", 32, arch_info_mips_machines, "qemu-system-mipsel" },
+    {  "sparc", 32, arch_info_sparc_machines, "qemu-system-sparc" },
+    {  "ppc", 32, arch_info_ppc_machines, "qemu-system-ppc" },
 };
 
 /* Return the default architecture if none is explicitly requested*/
@@ -211,6 +216,24 @@
     return path;
 }
 
+/* Return the list of architectures.  Useful for getCapabilities. */
+int
+qemudGetGuestArchitectures (struct qemud_guest_arch **archs_rtn)
+{
+    const int n = sizeof archs / sizeof archs[0];
+    int i;
+
+    *archs_rtn = malloc (sizeof (struct qemud_guest_arch) * n);
+    if (!*archs_rtn) return -1;
+
+    for (i = 0; i < n; ++i) {
+        (*archs_rtn)[i].model = archs[i].arch;
+        (*archs_rtn)[i].bits = archs[i].bits;
+    }
+
+    return n;
+}
+
 
 static int qemudExtractVersionInfo(const char *qemu, int *version, int *flags) {
     pid_t child;
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/qemud/conf.h libvirt-caps-tmp/qemud/conf.h
--- libvirt-cvs/qemud/conf.h	2007-02-23 17:15:18.000000000 +0000
+++ libvirt-caps-tmp/qemud/conf.h	2007-03-09 13:08:57.000000000 +0000
@@ -78,6 +78,13 @@
                                          struct qemud_network *network,
                                          struct qemud_network_def *def);
 
+struct qemud_guest_arch {
+    const char *model;
+    int bits;
+};
+
+int         qemudGetGuestArchitectures (struct qemud_guest_arch **archs);
+
 #endif
 
 /*
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/qemud/dispatch.c libvirt-caps-tmp/qemud/dispatch.c
--- libvirt-cvs/qemud/dispatch.c	2007-02-23 09:03:25.000000000 +0000
+++ libvirt-caps-tmp/qemud/dispatch.c	2007-03-09 13:08:57.000000000 +0000
@@ -23,6 +23,7 @@
 
 #include <config.h>
 
+#include <unistd.h>
 #include <limits.h>
 #include <string.h>
 #include <stdlib.h>
@@ -33,6 +34,8 @@
 #include "internal.h"
 #include "driver.h"
 #include "dispatch.h"
+#include "conf.h"
+#include "lib.h"
 
 
 static int qemudDispatchFailure(struct qemud_server *server ATTRIBUTE_UNUSED,
@@ -100,6 +103,149 @@
     return 0;
 }
 
+static int
+qemudDispatchGetCapabilities (struct qemud_server *server,
+                              struct qemud_client *client,
+                              struct qemud_packet *in,
+                              struct qemud_packet *out)
+{
+    char *xml, *xml_guest_archs;
+    int len;
+    struct utsname utsname;
+    struct qemud_guest_arch *guest_archs;
+    int nr_guest_archs, i;
+    int have_kqemu = 0;
+    int have_kvm = 0;
+
+    if (in->header.dataSize != 0) return -1;
+
+    have_kqemu = access ("/dev/kqemu", F_OK) == 0;
+    have_kvm = access ("/dev/kvm", F_OK) == 0;
+
+    /* Construct the XML. */
+    /* First the guest architectures for basic <domain type='qemu'>,
+     * all emulated (even x86 on x86).
+     */
+    xml_guest_archs = NULL;
+    nr_guest_archs = qemudGetGuestArchitectures (&guest_archs);
+    for (i = 0; i < nr_guest_archs; ++i) {
+        char bits[16];
+        snprintf (bits, sizeof bits, "%d", guest_archs[i].bits);
+
+        char *old_xml = xml_guest_archs;
+        xml_guest_archs = strjoin
+            ("\n",
+             xml_guest_archs,
+             "<guest_architecture>",
+               "<model>", guest_archs[i].model, "</model>",
+               "<bits>", bits, "</bits>",
+               "<features>",
+                 "<emulated/>",
+               "</features>",
+             "</guest_architecture>",
+             NULL);
+        free (old_xml);
+        if (!xml_guest_archs) {
+            qemudReportError (server, VIR_ERR_NO_MEMORY, NULL);
+            qemudDispatchFailure (server, client, out);
+            free (guest_archs);
+            free (xml_guest_archs);
+            return 0;
+        }
+    }
+    free (guest_archs);
+
+    /* Really, this never fails - look at the man-page. */
+    uname (&utsname);
+
+    /* 32 or 64 bit host? */
+    const char *bits = strstr (utsname.machine, "64") != NULL ? "64" : "32";
+
+    /* If we have kqemu, then there is a <domain type='kqemu'>,
+     * accelerated, but the model is limited to host == guest.
+     */
+    if (have_kqemu) {
+        char *old_xml = xml_guest_archs;
+        xml_guest_archs = strjoin
+            ("\n",
+             xml_guest_archs,
+             "<guest_architecture>",
+               "<domain><type>kqemu</type></domain>",
+               "<model>", utsname.machine, "</model>",
+               "<bits>", bits, "</bits>",
+               "<features>",
+                 "<accelerated/>",
+                 /* XXX There is no mention of PAE in the qemu docs. */
+               "</features>",
+             "</guest_architecture>",
+             NULL);
+        free (old_xml);
+        if (!xml_guest_archs) {
+            qemudReportError (server, VIR_ERR_NO_MEMORY, NULL);
+            qemudDispatchFailure (server, client, out);
+            free (xml_guest_archs);
+            return 0;
+        }
+    }
+
+    /* If we have kvm, then there is a <domain type='kvm'>,
+     * accelerated, HVM, but the model is limited to host == guest.
+     */
+    if (have_kvm) {
+        char *old_xml = xml_guest_archs;
+        xml_guest_archs = strjoin
+            ("\n",
+             xml_guest_archs,
+             "<guest_architecture>",
+               "<domain><type>kvm</type></domain>",
+               "<model>", utsname.machine, "</model>",
+               "<bits>", bits, "</bits>",
+               "<features>",
+                 "<hvm/>",
+                 "<accelerated/>",
+                 /* XXX Does KVM support PAE inside the guest? */
+               "</features>",
+             "</guest_architecture>",
+             NULL);
+        free (old_xml);
+        if (!xml_guest_archs) {
+            qemudReportError (server, VIR_ERR_NO_MEMORY, NULL);
+            qemudDispatchFailure (server, client, out);
+            free (xml_guest_archs);
+            return 0;
+        }
+    }
+
+    /* Finally build the full capabilities. */
+    xml = strjoin
+        ("\n",
+         "<capabilities>",
+           "<domain><type>qemu</type></domain>",
+           "<guest_architectures>",
+             xml_guest_archs,
+           "</guest_architectures>",
+         "</capabilities>",
+         NULL);
+    free (xml_guest_archs);
+    if (!xml) {
+        qemudReportError (server, VIR_ERR_NO_MEMORY, NULL);
+        qemudDispatchFailure (server, client, out);
+        return 0;
+    }
+
+    /* Copy the XML into the outgoing packet, assuming it's not too large. */
+    len = strlen (xml);
+    if (len > QEMUD_MAX_XML_LEN) {
+        qemudReportError (server, VIR_ERR_XML_ERROR, NULL);
+        qemudDispatchFailure (server, client, out);
+        return -1;
+    }
+    out->header.type = QEMUD_PKT_GET_CAPABILITIES;
+    out->header.dataSize = len;
+    strcpy (out->data.getCapabilitiesReply.xml, xml);
+    return 0;
+}
+
 static int qemudDispatchListDomains(struct qemud_server *server, struct qemud_client *client,
                                     struct qemud_packet *in, struct qemud_packet *out) {
     int i, ndomains, domains[QEMUD_MAX_NUM_DOMAINS];
@@ -854,6 +1000,7 @@
     qemudDispatchDomainSetAutostart,
     qemudDispatchNetworkGetAutostart,
     qemudDispatchNetworkSetAutostart,
+    qemudDispatchGetCapabilities,
 };
 
 clientFunc funcsTransmitRO[QEMUD_PKT_MAX] = {
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/qemud/Makefile.am libvirt-caps-tmp/qemud/Makefile.am
--- libvirt-cvs/qemud/Makefile.am	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/qemud/Makefile.am	2007-03-09 13:08:57.000000000 +0000
@@ -10,10 +10,12 @@
                 conf.c conf.h \
                 bridge.c bridge.h \
                 iptables.c iptables.h \
-                uuid.c uuid.h
+                uuid.c uuid.h \
+	        @top_srcdir@/src/lib.c @top_srcdir@/src/lib.h
 #-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L
 libvirt_qemud_CFLAGS = \
-        -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \
+        -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src \
+	$(LIBXML_CFLAGS) \
         $(WARN_CFLAGS) -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \
         -DSYSCONF_DIR="\"$(sysconfdir)\""
 libvirt_qemud_LDFLAGS = $(LIBXML_LIBS) $(SYSFS_LIBS)
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/qemud/protocol.h libvirt-caps-tmp/qemud/protocol.h
--- libvirt-cvs/qemud/protocol.h	2007-02-23 09:03:25.000000000 +0000
+++ libvirt-caps-tmp/qemud/protocol.h	2007-03-09 13:08:57.000000000 +0000
@@ -68,6 +68,7 @@
     QEMUD_PKT_DOMAIN_SET_AUTOSTART,
     QEMUD_PKT_NETWORK_GET_AUTOSTART,
     QEMUD_PKT_NETWORK_SET_AUTOSTART,
+    QEMUD_PKT_GET_CAPABILITIES,
 
     QEMUD_PKT_MAX,
 } qemud_packet_type;
@@ -125,6 +126,9 @@
         uint32_t threads;
     } getNodeInfoReply;
     struct {
+        char xml[QEMUD_MAX_XML_LEN];
+    } getCapabilitiesReply;
+    struct {
         int32_t numDomains;
         int32_t domains[QEMUD_MAX_NUM_DOMAINS];
     } listDomainsReply;
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/driver.h libvirt-caps-tmp/src/driver.h
--- libvirt-cvs/src/driver.h	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/src/driver.h	2007-03-09 13:08:57.000000000 +0000
@@ -48,6 +48,8 @@
 typedef int
 	(*virDrvNodeGetInfo)		(virConnectPtr conn,
 					 virNodeInfoPtr info);
+typedef char *
+	(*virDrvGetCapabilities) (virConnectPtr conn);
 typedef int
 	(*virDrvListDomains)		(virConnectPtr conn,
 					 int *ids,
@@ -164,6 +166,7 @@
 	virDrvGetVersion		version;
 	virDrvGetMaxVcpus		getMaxVcpus;
 	virDrvNodeGetInfo		nodeGetInfo;
+    virDrvGetCapabilities   getCapabilities;
 	virDrvListDomains		listDomains;
 	virDrvNumOfDomains		numOfDomains;
 	virDrvDomainCreateLinux		domainCreateLinux;
@@ -283,3 +286,17 @@
 }
 #endif /* __cplusplus */
 #endif /* __VIR_DRIVER_H__ */
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/internal.h libvirt-caps-tmp/src/internal.h
--- libvirt-cvs/src/internal.h	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/src/internal.h	2007-03-09 13:08:57.000000000 +0000
@@ -239,3 +239,17 @@
 }
 #endif                          /* __cplusplus */
 #endif                          /* __VIR_INTERNAL_H__ */
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/lib.c libvirt-caps-tmp/src/lib.c
--- libvirt-cvs/src/lib.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-caps-tmp/src/lib.c	2007-03-09 13:08:57.000000000 +0000
@@ -0,0 +1,84 @@
+/*
+ * lib.c: some private library functions shared between libvirt source
+ *   files
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the License of this software
+ *
+ * Note: The linker script ensures that these functions are not exported
+ * from libvirt, even though they are not static.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "lib.h"
+
+/**
+ * strjoin:
+ * @sep: Separator string
+ * @str1: First string to join
+ *
+ * Join a list of strings, placing @sep between each
+ * string.  The list of strings begins at @str1 and finishes
+ * with a NULL.
+ *
+ * If @str1 is NULL, it is treated as the empty string.
+ *
+ * Returns the joined string, or NULL if memory could not be
+ * allocated.
+ */
+char *
+strjoin (const char *sep, const char *str1, ...)
+{
+    int len, seplen;
+    char *str, *rtn, *tmp;
+    va_list v1, v2;
+
+    va_start (v1, str1);
+    va_copy (v2, v1);
+
+    /* Find out how much memory to allocate. */
+    len = str1 ? strlen (str1) : 0;
+    seplen = strlen (sep);
+    while ((str = va_arg (v1, char *)) != 0)
+        len += seplen + strlen (str);
+    len++; /* for the \0-terminator */
+
+    va_end (v1);
+
+    /* Copy the strings. */
+    tmp = rtn = malloc (len);
+    if (!rtn) goto done;
+
+    if (str1) {
+        strcpy (tmp, str1);
+        tmp += strlen (str1);
+    }
+    while ((str = va_arg (v2, char *)) != 0) {
+        strcpy (tmp, sep);
+        tmp += seplen;
+        strcpy (tmp, str);
+        tmp += strlen (str);
+    }
+
+ done:
+    va_end (v2);
+    return rtn;
+}
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/lib.h libvirt-caps-tmp/src/lib.h
--- libvirt-cvs/src/lib.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-caps-tmp/src/lib.h	2007-03-09 13:08:57.000000000 +0000
@@ -0,0 +1,29 @@
+/*
+ * lib.c: some private library functions shared between libvirt source
+ *   files
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * See COPYING.LIB for the License of this software
+ */
+
+#ifndef __VIR_LIB_H__
+#define __VIR_LIB_H__
+
+char *strjoin (const char *sep, const char *str1, ...);
+
+#endif /* __VIR_LIB_H__ */
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/libvirt.c libvirt-caps-tmp/src/libvirt.c
--- libvirt-cvs/src/libvirt.c	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/src/libvirt.c	2007-03-09 13:08:57.000000000 +0000
@@ -1701,6 +1701,37 @@
     return(0);
 }
 
+/**
+ * virConnectGetCapabilities:
+ * @conn: pointer to the hypervisor connection
+ *
+ * Return capabilities of the hypervisor / driver.
+ *
+ * Returns NULL in case of error, or a pointer to an opaque
+ * virCapabilities structure (virCapabilitiesPtr).
+ *
+ * The client must free the returned string after use.
+ */
+char *
+virConnectGetCapabilities (virConnectPtr conn)
+{
+    int i;
+
+    if (!VIR_IS_CONNECT (conn)) {
+        virLibConnError (conn, VIR_ERR_INVALID_CONN, __FUNCTION__);
+        return NULL;
+    }
+
+    for (i = 0; i < conn->nb_drivers; i++) {
+        if (conn->drivers[i] && conn->drivers[i]->getCapabilities) {
+            return conn->drivers[i]->getCapabilities (conn);
+        }
+    }
+
+    virLibConnError(conn, VIR_ERR_CALL_FAILED, __FUNCTION__);
+    return NULL;
+}
+
 /************************************************************************
  *									*
  *		Handling of defined but not running domains		*
@@ -2961,3 +2992,17 @@
     virLibConnError(network->conn, VIR_ERR_CALL_FAILED, __FUNCTION__);
     return (-1);
 }
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/libvirt_sym.version libvirt-caps-tmp/src/libvirt_sym.version
--- libvirt-cvs/src/libvirt_sym.version	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/src/libvirt_sym.version	2007-03-09 13:08:57.000000000 +0000
@@ -53,6 +53,7 @@
 	virConnResetLastError;
 	virDefaultErrorFunc;
 	virNodeGetInfo;
+	virConnectGetCapabilities;
 
 	virDomainSetVcpus;
 	virDomainPinVcpu;
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/Makefile.am libvirt-caps-tmp/src/Makefile.am
--- libvirt-cvs/src/Makefile.am	2007-03-02 11:05:16.000000000 +0000
+++ libvirt-caps-tmp/src/Makefile.am	2007-03-09 13:08:57.000000000 +0000
@@ -18,6 +18,7 @@
 libvirt_la_CFLAGS = $(COVERAGE_CFLAGS)
 libvirt_la_SOURCES =						\
 		libvirt.c internal.h				\
+		lib.c lib.h					\
 		hash.c hash.h					\
 		test.c test.h                                   \
 		xml.c xml.h					\
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/proxy_internal.c libvirt-caps-tmp/src/proxy_internal.c
--- libvirt-cvs/src/proxy_internal.c	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/src/proxy_internal.c	2007-03-09 13:08:57.000000000 +0000
@@ -52,6 +52,7 @@
     xenProxyGetVersion, /* version */
     NULL, /* getMaxVcpus */
     xenProxyNodeGetInfo, /* nodeGetInfo */
+    NULL, /* getCapabilities */
     xenProxyListDomains, /* listDomains */
     xenProxyNumOfDomains, /* numOfDomains */
     NULL, /* domainCreateLinux */
@@ -1044,3 +1045,17 @@
 
     return(ostype);
 }
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/proxy_internal.h libvirt-caps-tmp/src/proxy_internal.h
--- libvirt-cvs/src/proxy_internal.h	2006-11-07 16:28:16.000000000 +0000
+++ libvirt-caps-tmp/src/proxy_internal.h	2007-03-09 13:08:57.000000000 +0000
@@ -78,8 +78,8 @@
     union {
         char       str[4080];   /* extra char array */
         int        arg[1020];   /* extra int array */
-	virDomainInfo dinfo;	/* domain information */
-	virNodeInfo   ninfo;	/* node information */
+        virDomainInfo dinfo;	/* domain information */
+        virNodeInfo   ninfo;	/* node information */
     } extra;
 };
 typedef struct _virProxyFullPacket virProxyFullPacket;
@@ -93,3 +93,17 @@
 }
 #endif                          /* __cplusplus */
 #endif /* __LIBVIR_PROXY_H__ */
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/qemu_internal.c libvirt-caps-tmp/src/qemu_internal.c
--- libvirt-cvs/src/qemu_internal.c	2007-03-09 12:23:50.000000000 +0000
+++ libvirt-caps-tmp/src/qemu_internal.c	2007-03-09 13:08:57.000000000 +0000
@@ -451,6 +451,31 @@
 }
 
 
+static char *
+qemuGetCapabilities (virConnectPtr conn)
+{
+    struct qemud_packet req, reply;
+    char *xml;
+
+    /* Punt the request across to the daemon, because the daemon
+     * has tables describing available architectures.
+     */
+    req.header.type = QEMUD_PKT_GET_CAPABILITIES;
+    req.header.dataSize = 0;
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return NULL;
+    }
+
+    xml = strdup (reply.data.getCapabilitiesReply.xml);
+    if (!xml) {
+        qemuError (conn, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    return xml;
+}
+
 static int qemuNumOfDomains(virConnectPtr conn) {
     struct qemud_packet req, reply;
 
@@ -1171,6 +1196,7 @@
     qemuGetVersion, /* version */
     NULL, /* getMaxVcpus */
     qemuNodeGetInfo, /* nodeGetInfo */
+    qemuGetCapabilities, /* getCapabilities */
     qemuListDomains, /* listDomains */
     qemuNumOfDomains, /* numOfDomains */
     qemuDomainCreateLinux, /* domainCreateLinux */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/test.c libvirt-caps-tmp/src/test.c
--- libvirt-cvs/src/test.c	2007-03-09 12:23:51.000000000 +0000
+++ libvirt-caps-tmp/src/test.c	2007-03-09 13:08:57.000000000 +0000
@@ -43,6 +43,7 @@
                    unsigned long *hvVer);
 int testNodeGetInfo(virConnectPtr conn,
                     virNodeInfoPtr info);
+char *testGetCapabilities (virConnectPtr conn);
 int testNumOfDomains(virConnectPtr conn);
 int testListDomains(virConnectPtr conn,
                     int *ids,
@@ -96,6 +97,7 @@
     testGetVersion, /* version */
     NULL, /* getMaxVcpus */
     testNodeGetInfo, /* nodeGetInfo */
+    testGetCapabilities, /* getCapabilities */
     testListDomains, /* listDomains */
     testNumOfDomains, /* numOfDomains */
     testDomainCreateLinux, /* domainCreateLinux */
@@ -178,8 +180,11 @@
 static testNode *node = NULL;
 static int nextDomID = 1;
 
+#define TEST_MODEL "i686"
+#define TEST_MODEL_BITS "32"
+
 static const virNodeInfo defaultNodeInfo = {
-    "i686",
+    TEST_MODEL,
     1024*1024*3, /* 3 GB */
     16,
     1400,
@@ -808,6 +813,35 @@
     return (0);
 }
 
+char *
+testGetCapabilities (virConnectPtr conn)
+{
+    static char caps[] = "\
+<capabilities>\n\
+  <domain><type>test</type></domain>\n\
+  <host_features>\n\
+  </host_features>\n\
+  <guest_architectures>\n\
+    <guest_architecture>\n\
+      <model>" TEST_MODEL "</model>\n\
+      <bits>" TEST_MODEL_BITS "</bits>\n\
+      <guest_features>\n\
+        <accelerated/>\n\
+        <pae/>\n\
+      </guest_features>\n\
+    </guest_architecture>\n\
+  </guest_architectures>\n\
+</capabilities>\n\
+";
+
+    char *caps_copy = strdup (caps);
+    if (!caps_copy) {
+        testError(conn, NULL, VIR_ERR_NO_MEMORY, __FUNCTION__);
+        return NULL;
+    }
+    return caps_copy;
+}
+
 int testNumOfDomains(virConnectPtr conn)
 {
     int numActive = 0, i;
@@ -1377,6 +1411,11 @@
 }
 
 /*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
  * Local variables:
  *  indent-tabs-mode: nil
  *  c-indent-level: 4
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/virsh.c libvirt-caps-tmp/src/virsh.c
--- libvirt-cvs/src/virsh.c	2007-03-09 14:39:48.000000000 +0000
+++ libvirt-caps-tmp/src/virsh.c	2007-03-09 14:39:59.000000000 +0000
@@ -1545,6 +1545,33 @@
 }
 
 /*
+ * "capabilities" command
+ */
+static vshCmdInfo info_capabilities[] = {
+    {"syntax", "capabilities"},
+    {"help", gettext_noop("capabilities")},
+    {"desc", gettext_noop("Returns capabilities of hypervisor/driver.")},
+    {NULL, NULL}
+};
+
+static int
+cmdCapabilities (vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED)
+{
+    char *caps;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    if ((caps = virConnectGetCapabilities (ctl->conn)) == NULL) {
+        vshError(ctl, FALSE, _("failed to get capabilities"));
+        return FALSE;
+    }
+    vshPrint (ctl, "%s\n", caps);
+
+    return TRUE;
+}
+
+/*
  * "dumpxml" command
  */
 static vshCmdInfo info_dumpxml[] = {
@@ -2375,6 +2402,7 @@
  */
 static vshCmdDef commands[] = {
     {"autostart", cmdAutostart, opts_autostart, info_autostart},
+    {"capabilities", cmdCapabilities, NULL, info_capabilities},
     {"connect", cmdConnect, opts_connect, info_connect},
     {"console", cmdConsole, opts_console, info_console},
     {"create", cmdCreate, opts_create, info_create},
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/virterror.c libvirt-caps-tmp/src/virterror.c
--- libvirt-cvs/src/virterror.c	2007-02-14 15:40:54.000000000 +0000
+++ libvirt-caps-tmp/src/virterror.c	2007-03-09 13:10:41.000000000 +0000
@@ -604,6 +604,26 @@
 	    else
 	        errmsg = _("network %s exists already");
             break;
+    case VIR_ERR_SYSTEM_ERROR:
+        if (info == NULL)
+            errmsg = _("system call error");
+        else
+            errmsg = "%s";
+        break;
     }
     return (errmsg);
 }
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/xend_internal.c libvirt-caps-tmp/src/xend_internal.c
--- libvirt-cvs/src/xend_internal.c	2007-03-09 12:23:51.000000000 +0000
+++ libvirt-caps-tmp/src/xend_internal.c	2007-03-09 13:12:25.000000000 +0000
@@ -68,6 +68,7 @@
     xenDaemonGetVersion, /* version */
     NULL, /* getMaxVcpus */
     xenDaemonNodeGetInfo, /* nodeGetInfo */
+    NULL, /* getCapabilities */
     xenDaemonListDomains, /* listDomains */
     xenDaemonNumOfDomains, /* numOfDomains */
     xenDaemonCreateLinux, /* domainCreateLinux */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/xen_internal.c libvirt-caps-tmp/src/xen_internal.c
--- libvirt-cvs/src/xen_internal.c	2007-03-09 12:23:51.000000000 +0000
+++ libvirt-caps-tmp/src/xen_internal.c	2007-03-09 13:12:25.000000000 +0000
@@ -20,6 +20,8 @@
 #include <sys/ioctl.h>
 #include <limits.h>
 #include <stdint.h>
+#include <regex.h>
+#include <errno.h>
 
 /* required for dom0_getdomaininfo_t */
 #include <xen/dom0_ops.h>
@@ -30,6 +32,8 @@
 /* required for shutdown flags */
 #include <xen/sched.h>
 
+#include "lib.h"
+
 /* #define DEBUG */
 /*
  * so far there is 2 versions of the structures usable for doing
@@ -70,6 +74,15 @@
 static int dom_interface_version = -1;
 static int kb_per_pages = 0;
 
+/* Regular expressions used by xenHypervisorGetCapabilities, and
+ * compiled once by xenHypervisorInit.  Note that these are POSIX.2
+ * extended regular expressions (regex(7)).
+ */
+static const char *flags_hvm_re = "^flags[[:blank:]]+:.* (vmx|svm)[[:space:]]";
+static regex_t flags_hvm_rec;
+static const char *xen_cap_re = "(xen|hvm)-[[:digit:]]+\\.[[:digit:]]+-(x86_32|x86_64|ia64)(p|be)?";
+static regex_t xen_cap_rec;
+
 /*
  * The content of the structures for a getdomaininfolist system hypercall
  */
@@ -430,6 +443,7 @@
     xenHypervisorGetVersion, /* version */
     xenHypervisorNumOfMaxVcpus, /* getMaxVcpus */
     NULL, /* nodeGetInfo */
+    xenHypervisorGetCapabilities, /* getCapabilities */
     xenHypervisorListDomains, /* listDomains */
     xenHypervisorNumOfDomains, /* numOfDomains */
     NULL, /* domainCreateLinux */
@@ -468,9 +482,9 @@
 
 /**
  * virXenError:
- * @conn: the connection if available
  * @error: the error number
  * @info: extra information string
+ * @value: extra information number
  *
  * Handle an error at the xend daemon interface
  */
@@ -484,7 +498,31 @@
 
     errmsg = __virErrorMsg(error, info);
     __virRaiseError(NULL, NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR,
-                    errmsg, info, NULL, value, 0, errmsg, info, value);
+                    errmsg, info, NULL, value, 0, errmsg, info);
+}
+
+/**
+ * virXenPerror:
+ * @conn: the connection (if available)
+ * @msg: name of system call or file (as in perror(3))
+ *
+ * Raise error from a failed system call, using errno as the source.
+ */
+static void
+virXenPerror (virConnectPtr conn, const char *msg)
+{
+    char *msg_s;
+
+    msg_s = malloc (strlen (msg) + 10);
+    if (msg_s) {
+        strcpy (msg_s, msg);
+        strcat (msg_s, ": %s");
+    }
+
+    __virRaiseError (conn, NULL, NULL,
+                     VIR_FROM_XEN, VIR_ERR_SYSTEM_ERROR, VIR_ERR_ERROR,
+                     msg, NULL, NULL, errno, 0,
+                     msg_s ? msg_s : msg, strerror (errno));
 }
 
 /**
@@ -1137,7 +1175,7 @@
 static int
 xenHypervisorInit(void)
 {
-    int fd, ret, cmd;
+    int fd, ret, cmd, errcode;
     hypercall_t hc;
     v0_hypercall_t v0_hc;
     xen_getdomaininfo info;
@@ -1150,6 +1188,31 @@
     initialized = 1;
     in_init = 1;
 
+    /* Compile regular expressions used by xenHypervisorGetCapabilities.
+     * Note that errors here are really internal errors since these
+     * regexps should never fail to compile.
+     */
+    errcode = regcomp (&flags_hvm_rec, flags_hvm_re, REG_EXTENDED);
+    if (errcode != 0) {
+        char error[100];
+        regerror (errcode, &flags_hvm_rec, error, sizeof error);
+        regfree (&flags_hvm_rec);
+        virXenError (VIR_ERR_INTERNAL_ERROR, error, 0);
+        in_init = 0;
+        return -1;
+    }
+    errcode = regcomp (&xen_cap_rec, xen_cap_re, REG_EXTENDED);
+    if (errcode != 0) {
+        char error[100];
+        regerror (errcode, &xen_cap_rec, error, sizeof error);
+        regfree (&xen_cap_rec);
+        regfree (&flags_hvm_rec);
+        virXenError (VIR_ERR_INTERNAL_ERROR, error, 0);
+        in_init = 0;
+        return -1;
+    }
+
+    /* Xen hypervisor version detection begins. */
     ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR);
     if (ret < 0) {
         hypervisor_version = -1;
@@ -1359,6 +1422,207 @@
 }
 
 /**
+ * xenHypervisorGetCapabilities:
+ * @conn: pointer to the connection block
+ *
+ * Return the capabilities of this hypervisor.
+ */
+char *
+xenHypervisorGetCapabilities (virConnectPtr conn)
+{
+    char line[1024], *str, *token;
+    regmatch_t subs[3];
+    char *saveptr;
+    FILE *fp;
+    int i;
+
+    char hvm_type[4] = ""; /* "vmx" or "svm" (or "" if not in CPU). */
+    int hvm_enabled_in_bios = 0;
+    const int max_guest_archs = 32;
+    struct {
+        const char *model;
+        const char *bits;
+        int hvm;
+        int pae;
+        int ia64_be;
+    } guest_archs[max_guest_archs];
+    int nr_guest_archs = 0;
+
+    char *xml, *xml_host_features, *xml_guest_archs;
+
+    /* /proc/cpuinfo: flags: Intel calls HVM "vmx", AMD calls it "svm".
+     * It's not clear if this will work on IA64, let alone other
+     * architectures and non-Linux. (XXX)
+     */
+    fp = fopen ("/proc/cpuinfo", "r");
+    if (fp == NULL) {
+        if (errno == ENOENT)
+            goto nocpuinfo;
+        virXenPerror (conn, "/proc/cpuinfo");
+        return NULL;
+    }
+
+    while (fgets (line, sizeof line, fp)) {
+        if (regexec (&flags_hvm_rec, line, 1, subs, 0) == 0
+            && subs[0].rm_so != -1)
+            strncpy (hvm_type,
+                     &line[subs[0].rm_so], subs[0].rm_eo-subs[0].rm_so+1);
+    }
+
+    fclose (fp);
+
+ nocpuinfo:
+    /* Most of the useful info is in /sys/hypervisor/properties/capabilities
+     * which is documented in the code in xen-unstable.hg/xen/arch/.../setup.c.
+     *
+     * It is a space-separated list of supported guest architectures.
+     *
+     * For x86:
+     *    TYP-VER-ARCH[p]
+     *    ^   ^   ^    ^
+     *    |   |   |    +-- PAE supported
+     *    |   |   +------- x86_32 or x86_64
+     *    |   +----------- the version of Xen, eg. "3.0"
+     *    +--------------- "xen" or "hvm" for para or full virt respectively
+     *
+     * For PPC this file appears to be always empty (?)
+     *
+     * For IA64:
+     *    TYP-VER-ARCH[be]
+     *    ^   ^   ^    ^
+     *    |   |   |    +-- Big-endian supported
+     *    |   |   +------- always "ia64"
+     *    |   +----------- the version of Xen, eg. "3.0"
+     *    +--------------- "xen" or "hvm" for para or full virt respectively
+     */
+    fp = fopen ("/sys/hypervisor/properties/capabilities", "r");
+    if (fp == NULL) {
+        if (errno == ENOENT)
+            goto noxencaps;
+        virXenPerror (conn, "/sys/hypervisor/properties/capabilities");
+        return NULL;
+    }
+
+    /* Expecting one line in this file - ignore any more. */
+    if (!fgets (line, sizeof line, fp)) {
+        fclose (fp);
+        goto noxencaps;
+    }
+
+    fclose (fp);
+
+    /* Split the line into tokens.  strtok_r is OK here because we "own"
+     * this buffer.  Parse out the features from each token.
+     */
+    for (str = line, nr_guest_archs = 0;
+         nr_guest_archs < max_guest_archs
+             && (token = strtok_r (str, " ", &saveptr)) != NULL;
+         str = NULL) {
+        if (regexec (&xen_cap_rec, token, 3, subs, 0) == 0) {
+            guest_archs[nr_guest_archs].hvm =
+                strncmp (&token[subs[0].rm_so], "hvm", 3) == 0;
+            if (strncmp (&token[subs[1].rm_so], "x86_32", 6) == 0) {
+                guest_archs[nr_guest_archs].model = "i686";
+                guest_archs[nr_guest_archs].bits = "32";
+            }
+            else if (strncmp (&token[subs[1].rm_so], "x86_64", 6) == 0) {
+                guest_archs[nr_guest_archs].model = "x86_64";
+                guest_archs[nr_guest_archs].bits = "64";
+            }
+            else if (strncmp (&token[subs[1].rm_so], "ia64", 4) == 0) {
+                guest_archs[nr_guest_archs].model = "ia64";
+                guest_archs[nr_guest_archs].bits = "64";
+            }
+            else
+                guest_archs[nr_guest_archs].model = ""; /* can never happen */
+            guest_archs[nr_guest_archs].pae =
+                guest_archs[nr_guest_archs].ia64_be = 0;
+            if (subs[2].rm_so != -1) {
+                if (strncmp (&token[subs[2].rm_so], "p", 1) == 0)
+                    guest_archs[nr_guest_archs].pae = 1;
+                else if (strncmp (&token[subs[2].rm_so], "be", 2) == 0)
+                    guest_archs[nr_guest_archs].ia64_be = 1;
+            }
+            nr_guest_archs++;
+        }
+    }
+
+ noxencaps:
+    /* If HVM in CPU, is it enabled also in the BIOS? */
+    if (strcmp (hvm_type, "") != 0) {
+        for (i = 0; i < nr_guest_archs; ++i)
+            if (guest_archs[i].hvm) {
+                hvm_enabled_in_bios = 1;
+                break;
+            }
+    }
+
+    /* Construct the final XML. */
+    if (strcmp (hvm_type, "") == 0)
+        xml_host_features = strdup ("");
+    else
+        xml_host_features = strjoin
+            ("\n"
+             "<hvm>",
+               "<type>", hvm_type, "</type>",
+               "<bios_setting>",
+                 hvm_enabled_in_bios ? "enabled" : "disabled",
+               "</bios_setting>",
+             "</hvm>",
+             NULL);
+    if (!xml_host_features) {
+        virXenError (VIR_ERR_NO_MEMORY, __FUNCTION__, 0);
+        return NULL;
+    }
+
+    xml_guest_archs = NULL;
+    for (i = 0; i < nr_guest_archs; ++i) {
+        char *old_xml = xml_guest_archs;
+        xml_guest_archs = strjoin
+            ("\n",
+             xml_guest_archs,
+             "<guest_architecture>",
+               "<model>", guest_archs[i].model, "</model>",
+               "<bits>", guest_archs[i].bits, "</bits>",
+               "<guest_features>",
+                 guest_archs[i].hvm ? "<hvm/>" : "",
+                 guest_archs[i].pae ? "<pae/>" : "",
+                 guest_archs[i].ia64_be ? "<ia64_be/>" : "",
+                 "<accelerated/>",
+               "</guest_features>",
+             "</guest_architecture>",
+             NULL);
+        free (old_xml);
+        if (!xml_guest_archs) {
+            virXenError (VIR_ERR_NO_MEMORY, __FUNCTION__, 0);
+            free (xml_host_features);
+            return NULL;
+        }
+    }
+
+    xml = strjoin
+         ("\n",
+          "<capabilities>",
+            "<domain><type>xen</type></domain>",
+            "<host_features>",
+              xml_host_features,
+            "</host_features>",
+            "<guest_architectures>",
+              xml_guest_archs,
+            "</guest_architectures>",
+          "</capabilities>\n",
+          NULL);
+    free (xml_host_features);
+    free (xml_guest_archs);
+    if (!xml) {
+        virXenError (VIR_ERR_NO_MEMORY, __FUNCTION__, 0);
+        return NULL;
+    }
+
+    return xml;
+}
+
+/**
  * xenHypervisorNumOfDomains:
  * @conn: pointer to the connection block
  *
@@ -1881,6 +2145,11 @@
 }
 
 /*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
  * Local variables:
  *  indent-tabs-mode: nil
  *  c-indent-level: 4
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/xen_internal.h libvirt-caps-tmp/src/xen_internal.h
--- libvirt-cvs/src/xen_internal.h	2007-03-09 12:23:51.000000000 +0000
+++ libvirt-caps-tmp/src/xen_internal.h	2007-03-09 13:12:25.000000000 +0000
@@ -25,6 +25,8 @@
 int	xenHypervisorClose		(virConnectPtr conn);
 int	xenHypervisorGetVersion		(virConnectPtr conn,
 				 	 unsigned long *hvVer);
+char *
+        xenHypervisorGetCapabilities    (virConnectPtr conn);
 unsigned long
         xenHypervisorGetDomMaxMemory	(virConnectPtr conn,
 					 int id);
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/xm_internal.c libvirt-caps-tmp/src/xm_internal.c
--- libvirt-cvs/src/xm_internal.c	2007-03-09 12:23:51.000000000 +0000
+++ libvirt-caps-tmp/src/xm_internal.c	2007-03-09 13:12:25.000000000 +0000
@@ -77,6 +77,7 @@
     NULL, /* version */
     NULL, /* getMaxVcpus */
     NULL, /* nodeGetInfo */
+    NULL, /* getCapabilities */
     NULL, /* listDomains */
     NULL, /* numOfDomains */
     NULL, /* domainCreateLinux */
diff -urN --exclude=CVS --exclude=.git --exclude='*.pem' --exclude=demoCA --exclude=.gitignore --exclude='*.orig' --exclude='*.bak' libvirt-cvs/src/xs_internal.c libvirt-caps-tmp/src/xs_internal.c
--- libvirt-cvs/src/xs_internal.c	2007-03-09 12:23:51.000000000 +0000
+++ libvirt-caps-tmp/src/xs_internal.c	2007-03-09 13:12:25.000000000 +0000
@@ -46,6 +46,7 @@
     NULL, /* version */
     NULL, /* getMaxVcpus */
     NULL, /* nodeGetInfo */
+    NULL, /* getCapabilities */
     xenStoreListDomains, /* listDomains */
     NULL, /* numOfDomains */
     NULL, /* domainCreateLinux */

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature


[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]