Index: configure.in =================================================================== RCS file: /data/cvs/libvirt/configure.in,v retrieving revision 1.152 diff -u -p -r1.152 configure.in --- configure.in 5 Jun 2008 13:17:45 -0000 1.152 +++ configure.in 9 Jun 2008 12:48:07 -0000 @@ -99,6 +99,8 @@ AC_PATH_PROG([TAR], [tar], [/bin/tar]) AC_PATH_PROG([XMLLINT], [xmllint], [/usr/bin/xmllint]) AC_PATH_PROG([XSLTPROC], [xsltproc], [/usr/bin/xsltproc]) +AC_PROG_MKDIR_P + dnl External programs that we can use if they are available. dnl We will hard-code paths to these programs unless we cannot dnl detect them, in which case we'll search for the program Index: libvirt.spec.in =================================================================== RCS file: /data/cvs/libvirt/libvirt.spec.in,v retrieving revision 1.84 diff -u -p -r1.84 libvirt.spec.in --- libvirt.spec.in 22 May 2008 15:29:50 -0000 1.84 +++ libvirt.spec.in 9 Jun 2008 12:48:08 -0000 @@ -139,6 +139,7 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.a rm -f $RPM_BUILD_ROOT%{_libdir}/python*/site-packages/*.la rm -f $RPM_BUILD_ROOT%{_libdir}/python*/site-packages/*.a install -d -m 0755 $RPM_BUILD_ROOT%{_localstatedir}/run/libvirt/ +install -d -m 0755 $RPM_BUILD_ROOT%{_localstatedir}/cache/libvirt/ # We don't want to install /etc/libvirt/qemu/networks in the main %files list # because if the admin wants to delete the default network completely, we don't @@ -202,6 +203,7 @@ fi %dir %{_datadir}/libvirt/networks/ %{_datadir}/libvirt/networks/default.xml %dir %{_localstatedir}/run/libvirt/ +%dir %{_localstatedir}/cache/libvirt/ %dir %{_localstatedir}/lib/libvirt/ %if %{with_polkit} %{_datadir}/PolicyKit/policy/libvirtd.policy 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 9 Jun 2008 12:48:08 -0000 @@ -145,9 +145,9 @@ updated on 2008-06-05. virDomainBlockPeek 0.4.3 - x - x - x + 0.4.3 + 0.4.3 + 0.4.3 x @@ -487,6 +487,14 @@ updated on 2008-06-05. not a HV function + virDomainMemoryPeek + 0.4.3 + x + 0.4.3 + 0.4.3 + x + + virNodeGetInfo 0.1.0 ≥ 0.1.0 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 9 Jun 2008 12:48:09 -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 9 Jun 2008 12:48:10 -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/libvirtd.init.in =================================================================== RCS file: /data/cvs/libvirt/qemud/libvirtd.init.in,v retrieving revision 1.6 diff -u -p -r1.6 libvirtd.init.in --- qemud/libvirtd.init.in 15 May 2008 06:12:32 -0000 1.6 +++ qemud/libvirtd.init.in 9 Jun 2008 12:48:10 -0000 @@ -51,6 +51,8 @@ RETVAL=0 start() { echo -n $"Starting $SERVICE daemon: " + mkdir -p @localstatedir@/cache/libvirt + rm -rf @localstatedir@/cache/libvirt/* KRB5_KTNAME=$KRB5_KTNAME daemon --check $SERVICE $PROCESS --daemon $LIBVIRTD_CONFIG_ARGS $LIBVIRTD_ARGS RETVAL=$? echo @@ -66,6 +68,7 @@ stop() { if [ $RETVAL -eq 0 ]; then rm -f @localstatedir@/lock/subsys/$SERVICE rm -f @localstatedir@/run/$SERVICE.pid + rm -rf @localstatedir@/cache/libvirt/* fi } Index: qemud/remote.c =================================================================== RCS file: /data/cvs/libvirt/qemud/remote.c,v retrieving revision 1.37 diff -u -p -r1.37 remote.c --- qemud/remote.c 6 Jun 2008 10:52:01 -0000 1.37 +++ qemud/remote.c 9 Jun 2008 12:48:13 -0000 @@ -49,6 +49,7 @@ #endif #include "internal.h" +#include "memory.h" #include "qemud.h" #include "memory.h" @@ -939,6 +940,53 @@ 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_MEMORY_PEEK_BUFFER_MAX) { + remoteDispatchError (client, req, + "%s", _("size > maximum buffer size")); + virDomainFree (dom); + return -2; + } + + ret->buffer.buffer_len = size; + if (VIR_ALLOC_N (ret->buffer.buffer_val, size) < 0) { + remoteDispatchError (client, req, "%s", strerror (errno)); + virDomainFree (dom); + 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 9 Jun 2008 12:48:13 -0000 @@ -96,12 +96,18 @@ const REMOTE_AUTH_SASL_DATA_MAX = 65536; /* Maximum number of auth types */ const REMOTE_AUTH_TYPE_LIST_MAX = 20; -/* Maximum length of a block or memory peek buffer message. +/* Maximum length of a block peek buffer message. * Note applications need to be aware of this limit and issue multiple * requests for large amounts of data. */ const REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX = 65536; +/* Maximum length of a memory peek buffer message. + * Note applications need to be aware of this limit and issue multiple + * requests for large amounts of data. + */ +const REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX = 65536; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -340,6 +346,17 @@ struct remote_domain_block_peek_ret { opaque buffer; }; +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; +}; + struct remote_list_domains_args { int maxids; }; @@ -1056,7 +1073,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/Makefile.am =================================================================== RCS file: /data/cvs/libvirt/src/Makefile.am,v retrieving revision 1.81 diff -u -p -r1.81 Makefile.am --- src/Makefile.am 29 May 2008 19:27:04 -0000 1.81 +++ src/Makefile.am 9 Jun 2008 12:48:13 -0000 @@ -149,4 +149,8 @@ else EXTRA_DIST += parthelper.c endif +# Create the /var/cache/libvirt directory when installing. +install-exec-local: + $(MKDIR_P) $(DESTDIR)@localstatedir@/cache/libvirt + CLEANFILES = *.gcov .libs/*.gcda .libs/*.gcno *.gcno *.gcda 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 9 Jun 2008 12:48:14 -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 9 Jun 2008 12:48:17 -0000 @@ -2619,6 +2619,10 @@ virDomainInterfaceStats (virDomainPtr do * * 'buffer' is the return buffer and must be at least 'size' bytes. * + * NB. The remote driver imposes a 64K byte limit on 'size'. + * For your program to be able to work reliably over a remote + * connection you should split large requests to <= 65536 bytes. + * * Returns: 0 in case of success or -1 in case of failure. */ int @@ -2666,6 +2670,96 @@ 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. + * + * NB. The remote driver imposes a 64K byte limit on 'size'. + * For your program to be able to work reliably over a remote + * connection you should split large requests to <= 65536 bytes. + * + * 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.42 diff -u -p -r1.42 libvirt_sym.version --- src/libvirt_sym.version 6 Jun 2008 10:52:01 -0000 1.42 +++ src/libvirt_sym.version 9 Jun 2008 12:48:17 -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 9 Jun 2008 12:48:19 -0000 @@ -66,6 +66,9 @@ #include "capabilities.h" #include "memory.h" +/* For storing short-lived temporary files. */ +#define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" + static int qemudShutdown(void); /* qemudDebug statements should be changed to use this macro instead. */ @@ -3221,6 +3224,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[] = TEMPDIR "/qemu.mem.XXXXXX"; + 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 +3645,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.78 diff -u -p -r1.78 remote_internal.c --- src/remote_internal.c 6 Jun 2008 11:09:57 -0000 1.78 +++ src/remote_internal.c 9 Jun 2008 12:48:23 -0000 @@ -2416,6 +2416,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_MEMORY_PEEK_BUFFER_MAX) { + errorf (domain->conn, VIR_ERR_RPC, + _("memory peek request too large for remote protocol, %zi > %d"), + size, REMOTE_DOMAIN_MEMORY_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 @@ -4824,6 +4868,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 9 Jun 2008 12:48:24 -0000 @@ -2061,6 +2061,7 @@ static virDriver testDriver = { NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ NULL, /* domainBlockPeek */ + NULL, /* domainMemoryPeek */ testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ };