** PLEASE REVIEW THIS PATCH INSTEAD ** (was: Re: [libvirt] [PATCH] virDomainMemoryPeek - peek into guest memory)

Richard W.M. Jones rjones at redhat.com
Thu Jun 5 23:03:47 UTC 2008


This patch removes a file descriptor leak.  I've now tested this patch
with virt-mem and it appears to work.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
Index: docs/hvsupport.html.in
===================================================================
RCS file: /data/cvs/libvirt/docs/hvsupport.html.in,v
retrieving revision 1.2
diff -u -p -r1.2 hvsupport.html.in
--- docs/hvsupport.html.in	5 Jun 2008 13:17:45 -0000	1.2
+++ docs/hvsupport.html.in	5 Jun 2008 23:05:32 -0000
@@ -145,9 +145,9 @@ updated on <i>2008-06-05</i>.
       <tr>
         <td> virDomainBlockPeek </td>
         <td> 0.4.3 </td>
-        <td> x </td>
-        <td> x </td>
-        <td> x </td>
+        <td> 0.4.3 </td>
+        <td> 0.4.3 </td>
+        <td> 0.4.3 </td>
         <td> x </td>
       </tr>
       <tr>
@@ -487,6 +487,14 @@ updated on <i>2008-06-05</i>.
         <td colspan="4"> not a HV function </td>
       </tr>
       <tr>
+        <td> virDomainMemoryPeek </td>
+        <td> 0.4.3 </td>
+        <td> x </td>
+        <td> 0.4.3 </td>
+        <td> 0.4.3 </td>
+        <td> x </td>
+      </tr>
+      <tr>
         <td> virNodeGetInfo </td>
         <td> 0.1.0 </td>
         <td> &#x2265; 0.1.0 </td>
Index: include/libvirt/libvirt.h
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h,v
retrieving revision 1.75
diff -u -p -r1.75 libvirt.h
--- include/libvirt/libvirt.h	5 Jun 2008 13:17:45 -0000	1.75
+++ include/libvirt/libvirt.h	5 Jun 2008 23:05:33 -0000
@@ -537,6 +537,17 @@ int                     virDomainBlockPe
                                             void *buffer,
                                             unsigned int flags);
 
+/* Memory peeking flags. */
+typedef enum {
+  VIR_MEMORY_VIRTUAL              = 1, /* addresses are virtual addresses */
+} virDomainMemoryFlags;
+
+int                     virDomainMemoryPeek (virDomainPtr dom,
+                                             unsigned long long start,
+                                             size_t size,
+                                             void *buffer,
+                                             unsigned int flags);
+
 /*
  * defined but not running domains
  */
Index: include/libvirt/libvirt.h.in
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h.in,v
retrieving revision 1.50
diff -u -p -r1.50 libvirt.h.in
--- include/libvirt/libvirt.h.in	5 Jun 2008 13:17:45 -0000	1.50
+++ include/libvirt/libvirt.h.in	5 Jun 2008 23:05:34 -0000
@@ -537,6 +537,17 @@ int                     virDomainBlockPe
                                             void *buffer,
                                             unsigned int flags);
 
+/* Memory peeking flags. */
+typedef enum {
+  VIR_MEMORY_VIRTUAL              = 1, /* addresses are virtual addresses */
+} virDomainMemoryFlags;
+
+int                     virDomainMemoryPeek (virDomainPtr dom,
+                                             unsigned long long start,
+                                             size_t size,
+                                             void *buffer,
+                                             unsigned int flags);
+
 /*
  * defined but not running domains
  */
Index: qemud/remote.c
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote.c,v
retrieving revision 1.36
diff -u -p -r1.36 remote.c
--- qemud/remote.c	5 Jun 2008 21:12:26 -0000	1.36
+++ qemud/remote.c	5 Jun 2008 23:05:36 -0000
@@ -938,6 +938,52 @@ remoteDispatchDomainBlockPeek (struct qe
 }
 
 static int
+remoteDispatchDomainMemoryPeek (struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client,
+                                remote_message_header *req,
+                                remote_domain_memory_peek_args *args,
+                                remote_domain_memory_peek_ret *ret)
+{
+    virDomainPtr dom;
+    unsigned long long offset;
+    size_t size;
+    unsigned int flags;
+    CHECK_CONN (client);
+
+    dom = get_nonnull_domain (client->conn, args->dom);
+    if (dom == NULL) {
+        remoteDispatchError (client, req, "%s", _("domain not found"));
+        return -2;
+    }
+    offset = args->offset;
+    size = args->size;
+    flags = args->flags;
+
+    if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) {
+        remoteDispatchError (client, req,
+                             "%s", _("size > maximum buffer size"));
+        return -2;
+    }
+
+    ret->buffer.buffer_len = size;
+    ret->buffer.buffer_val = malloc (size);
+    if (!ret->buffer.buffer_val) {
+        remoteDispatchError (client, req, "%s", strerror (errno));
+        return -2;
+    }
+
+    if (virDomainMemoryPeek (dom, offset, size,
+                             ret->buffer.buffer_val, flags) == -1) {
+        /* free (ret->buffer.buffer_val); - caller frees */
+        virDomainFree (dom);
+        return -1;
+    }
+    virDomainFree (dom);
+
+    return 0;
+}
+
+static int
 remoteDispatchDomainAttachDevice (struct qemud_server *server ATTRIBUTE_UNUSED,
                                   struct qemud_client *client,
                                   remote_message_header *req,
Index: qemud/remote_protocol.x
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_protocol.x,v
retrieving revision 1.14
diff -u -p -r1.14 remote_protocol.x
--- qemud/remote_protocol.x	5 Jun 2008 21:12:27 -0000	1.14
+++ qemud/remote_protocol.x	5 Jun 2008 23:05:37 -0000
@@ -340,6 +340,17 @@ struct remote_domain_block_peek_ret {
     opaque buffer<REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX>;
 };
 
+struct remote_domain_memory_peek_args {
+    remote_nonnull_domain dom;
+    unsigned hyper offset;
+    unsigned size;
+    unsigned flags;
+};
+
+struct remote_domain_memory_peek_ret {
+    opaque buffer<REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX>;
+};
+
 struct remote_list_domains_args {
     int maxids;
 };
@@ -1056,7 +1067,8 @@ enum remote_procedure {
     REMOTE_PROC_NODE_GET_CELLS_FREE_MEMORY = 101,
     REMOTE_PROC_NODE_GET_FREE_MEMORY = 102,
 
-    REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103
+    REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103,
+    REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104
 };
 
 /* Custom RPC structure. */
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.49
diff -u -p -r1.49 driver.h
--- src/driver.h	5 Jun 2008 21:12:27 -0000	1.49
+++ src/driver.h	5 Jun 2008 23:05:37 -0000
@@ -234,6 +234,13 @@ typedef int
                      unsigned int flags);
 
 typedef int
+    (*virDrvDomainMemoryPeek)
+                    (virDomainPtr domain,
+                     unsigned long long start, size_t size,
+                     void *buffer,
+                     unsigned int flags);
+
+typedef int
     (*virDrvDomainMigratePrepare)
                     (virConnectPtr dconn,
                      char **cookie,
@@ -346,6 +353,7 @@ struct _virDriver {
     virDrvDomainBlockStats      domainBlockStats;
     virDrvDomainInterfaceStats  domainInterfaceStats;
     virDrvDomainBlockPeek	domainBlockPeek;
+    virDrvDomainMemoryPeek      domainMemoryPeek;
     virDrvNodeGetCellsFreeMemory	nodeGetCellsFreeMemory;
     virDrvNodeGetFreeMemory		getFreeMemory;
 };
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.145
diff -u -p -r1.145 libvirt.c
--- src/libvirt.c	5 Jun 2008 21:12:27 -0000	1.145
+++ src/libvirt.c	5 Jun 2008 23:05:41 -0000
@@ -2666,6 +2666,92 @@ virDomainBlockPeek (virDomainPtr dom,
     return -1;
 }
 
+/**
+ * virDomainMemoryPeek:
+ * @dom: pointer to the domain object
+ * @start: start of memory to peek
+ * @size: size of memory to peek
+ * @buffer: return buffer (must be at least size bytes)
+ * @flags: flags, see below
+ *
+ * This function allows you to read the contents of a domain's
+ * memory.
+ *
+ * The memory which is read is controlled by the 'start', 'size'
+ * and 'flags' parameters.
+ *
+ * If 'flags' is VIR_MEMORY_VIRTUAL then the 'start' and 'size'
+ * parameters are interpreted as virtual memory addresses for
+ * whichever task happens to be running on the domain at the
+ * moment.  Although this sounds haphazard it is in fact what
+ * you want in order to read Linux kernel state, because it
+ * ensures that pointers in the kernel image can be interpreted
+ * coherently.
+ *
+ * 'buffer' is the return buffer and must be at least 'size' bytes.
+ * 'size' may be 0 to test if the call would succeed.
+ *
+ * Returns: 0 in case of success or -1 in case of failure.
+ */
+int
+virDomainMemoryPeek (virDomainPtr dom,
+                     unsigned long long start /* really 64 bits */,
+                     size_t size,
+                     void *buffer,
+                     unsigned int flags)
+{
+    virConnectPtr conn;
+    DEBUG ("domain=%p, start=%lld, size=%zi, buffer=%p, flags=%d",
+           dom, start, size, buffer, flags);
+
+    if (!VIR_IS_CONNECTED_DOMAIN (dom)) {
+        virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+        return -1;
+    }
+    conn = dom->conn;
+
+    /* Flags must be VIR_MEMORY_VIRTUAL at the moment.
+     *
+     * Note on access to physical memory: A VIR_MEMORY_PHYSICAL flag is
+     * a possibility.  However it isn't really useful unless the caller
+     * can also access registers, particularly CR3 on x86 in order to
+     * get the Page Table Directory.  Since registers are different on
+     * every architecture, that would imply another call to get the
+     * machine registers.
+     *
+     * The QEMU driver handles only VIR_MEMORY_VIRTUAL, mapping it
+     * to the qemu 'memsave' command which does the virtual to physical
+     * mapping inside qemu.
+     *
+     * At time of writing there is no Xen driver.  However the Xen
+     * hypervisor only lets you map physical pages from other domains,
+     * and so the Xen driver would have to do the virtual to physical
+     * mapping by chasing 2, 3 or 4-level page tables from the PTD.
+     * There is example code in libxc (xc_translate_foreign_address)
+     * which does this, although we cannot copy this code directly
+     * because of incompatible licensing.
+     */
+    if (flags != VIR_MEMORY_VIRTUAL) {
+        virLibDomainError (dom, VIR_ERR_INVALID_ARG,
+                           _("flags parameter must be VIR_MEMORY_VIRTUAL"));
+        return -1;
+    }
+
+    /* Allow size == 0 as an access test. */
+    if (size > 0 && !buffer) {
+        virLibDomainError (dom, VIR_ERR_INVALID_ARG,
+                           _("buffer is NULL but size is non-zero"));
+        return -1;
+    }
+
+    if (conn->driver->domainMemoryPeek)
+        return conn->driver->domainMemoryPeek (dom, start, size,
+                                               buffer, flags);
+
+    virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
 
 /************************************************************************
  *									*
Index: src/libvirt_sym.version
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt_sym.version,v
retrieving revision 1.41
diff -u -p -r1.41 libvirt_sym.version
--- src/libvirt_sym.version	5 Jun 2008 13:17:45 -0000	1.41
+++ src/libvirt_sym.version	5 Jun 2008 23:05:41 -0000
@@ -74,6 +74,7 @@
 	virDomainBlockStats;
 	virDomainInterfaceStats;
 	virDomainBlockPeek;
+	virDomainMemoryPeek;
 	virDomainAttachDevice;
 	virDomainDetachDevice;
 
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.84
diff -u -p -r1.84 qemu_driver.c
--- src/qemu_driver.c	5 Jun 2008 21:12:27 -0000	1.84
+++ src/qemu_driver.c	5 Jun 2008 23:05:43 -0000
@@ -3221,6 +3221,68 @@ found:
     return ret;
 }
 
+static int
+qemudDomainMemoryPeek (virDomainPtr dom,
+                       unsigned long long offset, size_t size,
+                       void *buffer,
+                       unsigned int flags)
+{
+    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    struct qemud_vm *vm = qemudFindVMByID (driver, dom->id);
+    char cmd[256], *info;
+    char tmp[] = "/tmp/qemumemXXXXXX";
+    int fd = -1, ret = -1;
+
+    if (flags != VIR_MEMORY_VIRTUAL) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                          _("QEMU driver only supports virtual memory addrs"));
+        return -1;
+    }
+
+    if (!vm) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                          _("no domain with matching id %d"), dom->id);
+        return -1;
+    }
+
+    if (!qemudIsActiveVM(vm)) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "%s", _("domain is not running"));
+        return -1;
+    }
+
+    /* Create a temporary filename. */
+    if ((fd = mkstemp (tmp)) == -1) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
+                          "%s", strerror (errno));
+        return -1;
+    }
+
+    /* Issue the memsave command. */
+    snprintf (cmd, sizeof cmd, "memsave %llu %zi \"%s\"", offset, size, tmp);
+    if (qemudMonitorCommand (driver, vm, cmd, &info) < 0) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          "%s", _("'info blockstats' command failed"));
+        goto done;
+    }
+
+    DEBUG ("memsave reply: %s", info);
+    free (info);
+
+    /* Read the memory file into buffer. */
+    if (saferead (fd, buffer, size) == (ssize_t) -1) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
+                          "%s", strerror (errno));
+        goto done;
+    }
+
+    ret = 0;
+done:
+    if (fd >= 0) close (fd);
+    unlink (tmp);
+    return ret;
+}
+
 static virNetworkPtr qemudNetworkLookupByUUID(virConnectPtr conn ATTRIBUTE_UNUSED,
                                      const unsigned char *uuid) {
     struct qemud_driver *driver = (struct qemud_driver *)conn->networkPrivateData;
@@ -3580,6 +3642,7 @@ static virDriver qemuDriver = {
     qemudDomainBlockStats, /* domainBlockStats */
     qemudDomainInterfaceStats, /* domainInterfaceStats */
     qemudDomainBlockPeek, /* domainBlockPeek */
+    qemudDomainMemoryPeek, /* domainMemoryPeek */
 #if HAVE_NUMACTL
     qemudNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */
     qemudNodeGetFreeMemory,  /* getFreeMemory */
Index: src/remote_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/remote_internal.c,v
retrieving revision 1.77
diff -u -p -r1.77 remote_internal.c
--- src/remote_internal.c	5 Jun 2008 21:12:27 -0000	1.77
+++ src/remote_internal.c	5 Jun 2008 23:05:46 -0000
@@ -2420,6 +2420,50 @@ remoteDomainBlockPeek (virDomainPtr doma
     return 0;
 }
 
+static int
+remoteDomainMemoryPeek (virDomainPtr domain,
+                        unsigned long long offset,
+                        size_t size,
+                        void *buffer,
+                        unsigned int flags)
+{
+    remote_domain_memory_peek_args args;
+    remote_domain_memory_peek_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    if (size > REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX) {
+        errorf (domain->conn, VIR_ERR_RPC,
+                _("memory peek request too large for remote protocol, %zi > %d"),
+                size, REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX);
+        return -1;
+    }
+
+    make_nonnull_domain (&args.dom, domain);
+    args.offset = offset;
+    args.size = size;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_MEMORY_PEEK,
+              (xdrproc_t) xdr_remote_domain_memory_peek_args,
+                (char *) &args,
+              (xdrproc_t) xdr_remote_domain_memory_peek_ret,
+                (char *) &ret) == -1)
+        return -1;
+
+    if (ret.buffer.buffer_len != size) {
+            errorf (domain->conn, VIR_ERR_RPC,
+                    _("returned buffer is not same size as requested"));
+            free (ret.buffer.buffer_val);
+            return -1;
+    }
+
+    memcpy (buffer, ret.buffer.buffer_val, size);
+    free (ret.buffer.buffer_val);
+
+    return 0;
+}
+
 /*----------------------------------------------------------------------*/
 
 static int
@@ -4831,6 +4875,7 @@ static virDriver driver = {
     .domainBlockStats = remoteDomainBlockStats,
     .domainInterfaceStats = remoteDomainInterfaceStats,
     .domainBlockPeek = remoteDomainBlockPeek,
+    .domainMemoryPeek = remoteDomainMemoryPeek,
     .nodeGetCellsFreeMemory = remoteNodeGetCellsFreeMemory,
     .getFreeMemory = remoteNodeGetFreeMemory,
 };
Index: src/test.c
===================================================================
RCS file: /data/cvs/libvirt/src/test.c,v
retrieving revision 1.76
diff -u -p -r1.76 test.c
--- src/test.c	5 Jun 2008 13:17:45 -0000	1.76
+++ src/test.c	5 Jun 2008 23:05:48 -0000
@@ -2061,6 +2061,7 @@ static virDriver testDriver = {
     NULL, /* domainBlockStats */
     NULL, /* domainInterfaceStats */
     NULL, /* domainBlockPeek */
+    NULL, /* domainMemoryPeek */
     testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */
     NULL, /* getFreeMemory */
 };


More information about the libvir-list mailing list