diff --git a/ChangeLog b/ChangeLog index 1aead245c6..7f4b795978 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +Tue Jun 10 11:34:00 BST 2008 Richard W.M. Jones + + virDomainMemoryPeek API + * include/libvirt/libvirt.h.in, src/libvirt.c, src/driver.h, + src/libvirt_sym.version: New virDomainMemoryPeek API. + * qemud/remote.c, qemud/remote_protocol.x, src/remote_internal.c: + Support for remote. + * src/qemu_driver.c: QEMU driver implementation of API. + * src/test.c: Test driver (null) implementation of API. + * docs/hvsupport.html.in: Document API. + * libvirt.spec.in: New path /var/cache/libvirt for temporary + storage of memory images. + * qemud/libvirtd.init.in: Remove any old temp files in + /var/cache/libvirt on restarts. + * src/Makefile.am: make install creates /var/cache/libvirt. + * configure.in: Detect mkdir -p. + Mon Jun 9 15:42:34 PST 2008 David L. Leskovec * src/lxc_driver.c: Console element is output only. Always open new diff --git a/configure.in b/configure.in index 47633b0d8a..d4758fa666 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/docs/hvsupport.html.in b/docs/hvsupport.html.in index 545d5442c6..42a1d703d0 100644 --- a/docs/hvsupport.html.in +++ b/docs/hvsupport.html.in @@ -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 @@ -486,6 +486,14 @@ updated on 2008-06-05. 0.1.0 not a HV function + + virDomainMemoryPeek + 0.4.3 + x + 0.4.3 + 0.4.3 + x + virNodeGetInfo 0.1.0 diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 1170b76061..22dbc3f71d 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -537,6 +537,17 @@ int virDomainBlockPeek (virDomainPtr dom, 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 */ diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index a711edd304..bd5195c10d 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -537,6 +537,17 @@ int virDomainBlockPeek (virDomainPtr dom, 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 */ diff --git a/libvirt.spec.in b/libvirt.spec.in index aff7ef2567..0adbc207cc 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -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 diff --git a/qemud/libvirtd.init.in b/qemud/libvirtd.init.in index 234c5509aa..0eed4909c2 100644 --- a/qemud/libvirtd.init.in +++ b/qemud/libvirtd.init.in @@ -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 } diff --git a/qemud/remote.c b/qemud/remote.c index de462b930f..b5a6ec9e62 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -938,6 +938,53 @@ remoteDispatchDomainBlockPeek (struct qemud_server *server ATTRIBUTE_UNUSED, return 0; } +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, diff --git a/qemud/remote_dispatch_localvars.h b/qemud/remote_dispatch_localvars.h index 56963ab053..d889c8aba4 100644 --- a/qemud/remote_dispatch_localvars.h +++ b/qemud/remote_dispatch_localvars.h @@ -91,6 +91,8 @@ remote_domain_get_scheduler_parameters_ret lv_remote_domain_get_scheduler_parame remote_node_get_info_ret lv_remote_node_get_info_ret; remote_network_lookup_by_name_args lv_remote_network_lookup_by_name_args; remote_network_lookup_by_name_ret lv_remote_network_lookup_by_name_ret; +remote_domain_memory_peek_args lv_remote_domain_memory_peek_args; +remote_domain_memory_peek_ret lv_remote_domain_memory_peek_ret; remote_num_of_defined_domains_ret lv_remote_num_of_defined_domains_ret; remote_domain_block_stats_args lv_remote_domain_block_stats_args; remote_domain_block_stats_ret lv_remote_domain_block_stats_ret; diff --git a/qemud/remote_dispatch_proc_switch.h b/qemud/remote_dispatch_proc_switch.h index 00c98fe94a..ebb24334ca 100644 --- a/qemud/remote_dispatch_proc_switch.h +++ b/qemud/remote_dispatch_proc_switch.h @@ -224,6 +224,15 @@ case REMOTE_PROC_DOMAIN_LOOKUP_BY_UUID: ret = (char *) &lv_remote_domain_lookup_by_uuid_ret; memset (&lv_remote_domain_lookup_by_uuid_ret, 0, sizeof lv_remote_domain_lookup_by_uuid_ret); break; +case REMOTE_PROC_DOMAIN_MEMORY_PEEK: + fn = (dispatch_fn) remoteDispatchDomainMemoryPeek; + args_filter = (xdrproc_t) xdr_remote_domain_memory_peek_args; + args = (char *) &lv_remote_domain_memory_peek_args; + memset (&lv_remote_domain_memory_peek_args, 0, sizeof lv_remote_domain_memory_peek_args); + ret_filter = (xdrproc_t) xdr_remote_domain_memory_peek_ret; + ret = (char *) &lv_remote_domain_memory_peek_ret; + memset (&lv_remote_domain_memory_peek_ret, 0, sizeof lv_remote_domain_memory_peek_ret); + break; case REMOTE_PROC_DOMAIN_MIGRATE_FINISH: fn = (dispatch_fn) remoteDispatchDomainMigrateFinish; args_filter = (xdrproc_t) xdr_remote_domain_migrate_finish_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index 6d68b8cb4c..1d9d79494e 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -30,6 +30,7 @@ static int remoteDispatchDomainInterfaceStats (struct qemud_server *server, stru static int remoteDispatchDomainLookupById (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_id_args *args, remote_domain_lookup_by_id_ret *ret); static int remoteDispatchDomainLookupByName (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_name_args *args, remote_domain_lookup_by_name_ret *ret); static int remoteDispatchDomainLookupByUuid (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_lookup_by_uuid_args *args, remote_domain_lookup_by_uuid_ret *ret); +static int remoteDispatchDomainMemoryPeek (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_memory_peek_args *args, remote_domain_memory_peek_ret *ret); static int remoteDispatchDomainMigrateFinish (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_finish_args *args, remote_domain_migrate_finish_ret *ret); static int remoteDispatchDomainMigratePerform (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_perform_args *args, void *ret); static int remoteDispatchDomainMigratePrepare (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_migrate_prepare_args *args, remote_domain_migrate_prepare_ret *ret); diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index 69045626e9..1a8e68d60a 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -561,6 +561,31 @@ xdr_remote_domain_block_peek_ret (XDR *xdrs, remote_domain_block_peek_ret *objp) return TRUE; } +bool_t +xdr_remote_domain_memory_peek_args (XDR *xdrs, remote_domain_memory_peek_args *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_u_quad_t (xdrs, &objp->offset)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->size)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_memory_peek_ret (XDR *xdrs, remote_domain_memory_peek_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->buffer.buffer_val; + + if (!xdr_bytes (xdrs, objp_cpp0, (u_int *) &objp->buffer.buffer_len, REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX)) + return FALSE; + return TRUE; +} + bool_t xdr_remote_list_domains_args (XDR *xdrs, remote_list_domains_args *objp) { diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 8eeaba2f40..1ad0c547d8 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -34,6 +34,7 @@ typedef remote_nonnull_string *remote_string; #define REMOTE_AUTH_SASL_DATA_MAX 65536 #define REMOTE_AUTH_TYPE_LIST_MAX 20 #define REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX 65536 +#define REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX 65536 typedef char remote_uuid[VIR_UUID_BUFLEN]; @@ -281,6 +282,22 @@ struct remote_domain_block_peek_ret { }; typedef struct remote_domain_block_peek_ret remote_domain_block_peek_ret; +struct remote_domain_memory_peek_args { + remote_nonnull_domain dom; + u_quad_t offset; + u_int size; + u_int flags; +}; +typedef struct remote_domain_memory_peek_args remote_domain_memory_peek_args; + +struct remote_domain_memory_peek_ret { + struct { + u_int buffer_len; + char *buffer_val; + } buffer; +}; +typedef struct remote_domain_memory_peek_ret remote_domain_memory_peek_ret; + struct remote_list_domains_args { int maxids; }; @@ -1157,6 +1174,7 @@ 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_MEMORY_PEEK = 104, }; typedef enum remote_procedure remote_procedure; @@ -1227,6 +1245,8 @@ extern bool_t xdr_remote_domain_interface_stats_args (XDR *, remote_domain_inte extern bool_t xdr_remote_domain_interface_stats_ret (XDR *, remote_domain_interface_stats_ret*); extern bool_t xdr_remote_domain_block_peek_args (XDR *, remote_domain_block_peek_args*); extern bool_t xdr_remote_domain_block_peek_ret (XDR *, remote_domain_block_peek_ret*); +extern bool_t xdr_remote_domain_memory_peek_args (XDR *, remote_domain_memory_peek_args*); +extern bool_t xdr_remote_domain_memory_peek_ret (XDR *, remote_domain_memory_peek_ret*); extern bool_t xdr_remote_list_domains_args (XDR *, remote_list_domains_args*); extern bool_t xdr_remote_list_domains_ret (XDR *, remote_list_domains_ret*); extern bool_t xdr_remote_num_of_domains_ret (XDR *, remote_num_of_domains_ret*); @@ -1404,6 +1424,8 @@ extern bool_t xdr_remote_domain_interface_stats_args (); extern bool_t xdr_remote_domain_interface_stats_ret (); extern bool_t xdr_remote_domain_block_peek_args (); extern bool_t xdr_remote_domain_block_peek_ret (); +extern bool_t xdr_remote_domain_memory_peek_args (); +extern bool_t xdr_remote_domain_memory_peek_ret (); extern bool_t xdr_remote_list_domains_args (); extern bool_t xdr_remote_list_domains_ret (); extern bool_t xdr_remote_num_of_domains_ret (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index 048fcb6825..340203cbae 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -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. */ diff --git a/src/Makefile.am b/src/Makefile.am index 0549bfda97..51821b041f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/driver.h b/src/driver.h index ed6d2e513d..1404eb9460 100644 --- a/src/driver.h +++ b/src/driver.h @@ -233,6 +233,13 @@ typedef int void *buffer, 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, @@ -346,6 +353,7 @@ struct _virDriver { virDrvDomainBlockStats domainBlockStats; virDrvDomainInterfaceStats domainInterfaceStats; virDrvDomainBlockPeek domainBlockPeek; + virDrvDomainMemoryPeek domainMemoryPeek; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory getFreeMemory; }; diff --git a/src/libvirt.c b/src/libvirt.c index 53a93e3025..48df71dbde 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -2619,6 +2619,10 @@ virDomainInterfaceStats (virDomainPtr dom, const char *path, * * '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; +} + /************************************************************************ * * diff --git a/src/libvirt_sym.version b/src/libvirt_sym.version index 2da720c650..6561bc9671 100644 --- a/src/libvirt_sym.version +++ b/src/libvirt_sym.version @@ -74,6 +74,7 @@ virDomainBlockStats; virDomainInterfaceStats; virDomainBlockPeek; + virDomainMemoryPeek; virDomainAttachDevice; virDomainDetachDevice; diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 969c253eda..97c6483903 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -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 */ diff --git a/src/remote_internal.c b/src/remote_internal.c index cc34ae6e35..713317d8f0 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -2416,6 +2416,50 @@ remoteDomainBlockPeek (virDomainPtr domain, 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, }; diff --git a/src/test.c b/src/test.c index 32e88bde12..1d22720805 100644 --- a/src/test.c +++ b/src/test.c @@ -2061,6 +2061,7 @@ static virDriver testDriver = { NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ NULL, /* domainBlockPeek */ + NULL, /* domainMemoryPeek */ testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ };