From: Daniel P. Berrange Date: Tue, 15 Sep 2009 15:38:33 +0000 (+0100) Subject: Move xen driver code into src/xen/ directory X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=f7a107f73e2b01a7e1fa64740b5b23e2df363eaa;p=libvirt.git Move xen driver code into src/xen/ directory * src/Makefile.am, src/proxy_internal.c, src/proxy_internal.h src/sexpr.c, src/sexpr.h, src/xen_unified.c, src/xen_unified.h, src/xen_internal.c, src/xen_internal.h, src/xen_inotify.c, src/xen_inotify.h, src/xend_internal.c, src/xend_internal.h, src/xm_internal.c, src/xm_internal.h, src/xs_internal.c, src/xs_internal.h: Move to src/xen/ directory * proxy/Makefile.am, proxy/libvirt_proxy.c, src/Makefile.am, src/libvirt.c, tests/sexpr2xmltest.c, tests/statstest.c, tests/xencapstest.c, tests/xmconfigtest.c, tests/xml2sexprtest.c: Adapt to changed xen location * src/stats_linux.h, src/stats_linux.c: Remove xen specific block stats APIs * src/qemu_driver.c, src/uml_driver.c: Add missing sys/un.h include uncovered after change to stats_linux.h * src/xen/block_stats.h, src/xen/block_stats.c: Add xen specific block stats APIs --- diff --git a/proxy/Makefile.am b/proxy/Makefile.am index 48678c3bab..e92333ae10 100644 --- a/proxy/Makefile.am +++ b/proxy/Makefile.am @@ -3,17 +3,17 @@ if WITH_PROXY INCLUDES = -I$(top_srcdir)/gnulib/lib -I../gnulib/lib \ -I$(top_builddir)/include -I@top_srcdir@/include \ - -I@top_srcdir@/proxy -I@top_srcdir@/src @LIBXML_CFLAGS@ \ + -I@top_srcdir@/proxy -I@top_srcdir@/src -I@top_srcdir@/src/xen @LIBXML_CFLAGS@ \ -DPROXY -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -DGETTEXT_PACKAGE=\"$(PACKAGE)\" $(WARN_CFLAGS) $(XEN_CFLAGS) libexec_PROGRAMS = libvirt_proxy -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 \ +libvirt_proxy_SOURCES = libvirt_proxy.c @top_srcdir@/src/xen/xend_internal.c \ + @top_srcdir@/src/xen/xen_hypervisor.c @top_srcdir@/src/virterror.c \ + @top_srcdir@/src/xen/sexpr.c \ @top_srcdir@/src/threads.c \ - @top_srcdir@/src/xs_internal.c @top_srcdir@/src/buf.c \ + @top_srcdir@/src/xen/xs_internal.c @top_srcdir@/src/buf.c \ @top_srcdir@/src/capabilities.c \ @top_srcdir@/src/memory.c \ @top_srcdir@/src/storage_encryption_conf.c \ diff --git a/proxy/libvirt_proxy.c b/proxy/libvirt_proxy.c index e008a7f4be..aa20d2850f 100644 --- a/proxy/libvirt_proxy.c +++ b/proxy/libvirt_proxy.c @@ -27,10 +27,10 @@ #include "datatypes.h" #include "proxy_internal.h" #include "util.h" -#include "xen_internal.h" +#include "xen_hypervisor.h" #include "xend_internal.h" #include "xs_internal.h" -#include "xen_unified.h" +#include "xen_driver.h" static int fdServer = -1; static int debug = 0; diff --git a/src/Makefile.am b/src/Makefile.am index b1d01ba42f..52f0c24bf8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,6 @@ INCLUDES = \ -I@top_srcdir@/daemon \ $(LIBXML_CFLAGS) \ $(LIBSSH2_CFLAGS) \ - $(XEN_CFLAGS) \ $(SELINUX_CFLAGS) \ $(DRIVER_MODULE_CFLAGS) \ -DLIBDIR=\""$(libdir)"\" \ @@ -111,15 +110,16 @@ TEST_DRIVER_SOURCES = \ # Now the Hypervisor specific drivers XEN_DRIVER_SOURCES = \ - proxy_internal.c proxy_internal.h \ - sexpr.c sexpr.h \ - xen_internal.c xen_internal.h \ - xen_unified.c xen_unified.h \ - xend_internal.c xend_internal.h \ - xm_internal.c xm_internal.h \ - xs_internal.c xs_internal.h + xen/proxy_internal.c xen/proxy_internal.h \ + xen/sexpr.c xen/sexpr.h \ + xen/block_stats.c xen/block_stats.h \ + xen/xen_hypervisor.c xen/xen_hypervisor.h \ + xen/xen_driver.c xen/xen_driver.h \ + xen/xend_internal.c xen/xend_internal.h \ + xen/xm_internal.c xen/xm_internal.h \ + xen/xs_internal.c xen/xs_internal.h if WITH_XEN_INOTIFY -XEN_DRIVER_SOURCES += xen_inotify.c xen_inotify.h +XEN_DRIVER_SOURCES += xen/xen_inotify.c xen/xen_inotify.h endif LXC_DRIVER_SOURCES = \ @@ -261,8 +261,8 @@ libvirt_driver_la_SOURCES = \ $(NODE_DEVICE_CONF_SOURCES) \ $(SECRET_CONF_SOURCES) -libvirt_driver_la_CFLAGS = $(XEN_CFLAGS) $(NUMACTL_CFLAGS) -libvirt_driver_la_LDFLAGS = $(XEN_LIBS) $(NUMACTL_LIBS) +libvirt_driver_la_CFLAGS = $(NUMACTL_CFLAGS) +libvirt_driver_la_LDFLAGS = $(NUMACTL_LIBS) USED_SYM_FILES = libvirt_private.syms diff --git a/src/libvirt.c b/src/libvirt.c index 4a11688312..1139d8bc8f 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -47,7 +47,7 @@ #include "test.h" #endif #ifdef WITH_XEN -#include "xen_unified.h" +#include "xen/xen_driver.h" #endif #ifdef WITH_REMOTE #include "remote_internal.h" diff --git a/src/proxy_internal.c b/src/proxy_internal.c deleted file mode 100644 index 5b92ad8252..0000000000 --- a/src/proxy_internal.c +++ /dev/null @@ -1,1049 +0,0 @@ -/* - * proxy_client.c: client side of the communication with the libvirt proxy. - * - * Copyright (C) 2006, 2008, 2009 Red Hat, Inc. - * - * See COPYING.LIB for the License of this software - * - * Daniel Veillard - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "virterror_internal.h" -#include "logging.h" -#include "datatypes.h" -#include "driver.h" -#include "proxy_internal.h" -#include "util.h" -#include "xen_unified.h" -#include "memory.h" - -#define STANDALONE - -#define VIR_FROM_THIS VIR_FROM_PROXY - -static int xenProxyClose(virConnectPtr conn); -static virDrvOpenStatus xenProxyOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags); -static int xenProxyGetVersion(virConnectPtr conn, unsigned long *hvVer); -static int xenProxyNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info); -static char *xenProxyGetCapabilities(virConnectPtr conn); -static int xenProxyNumOfDomains(virConnectPtr conn); -static unsigned long xenProxyDomainGetMaxMemory(virDomainPtr domain); -static int xenProxyDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info); -static char *xenProxyDomainGetOSType(virDomainPtr domain); - -struct xenUnifiedDriver xenProxyDriver = { - xenProxyOpen, /* open */ - xenProxyClose, /* close */ - xenProxyGetVersion, /* version */ - NULL, /* hostname */ - xenProxyNodeGetInfo, /* nodeGetInfo */ - xenProxyGetCapabilities, /* getCapabilities */ - xenProxyListDomains, /* listDomains */ - xenProxyNumOfDomains, /* numOfDomains */ - NULL, /* domainCreateXML */ - NULL, /* domainSuspend */ - NULL, /* domainResume */ - NULL, /* domainShutdown */ - NULL, /* domainReboot */ - NULL, /* domainDestroy */ - xenProxyDomainGetOSType, /* domainGetOSType */ - xenProxyDomainGetMaxMemory, /* domainGetMaxMemory */ - NULL, /* domainSetMaxMemory */ - NULL, /* domainSetMemory */ - xenProxyDomainGetInfo, /* domainGetInfo */ - NULL, /* domainSave */ - NULL, /* domainRestore */ - NULL, /* domainCoreDump */ - NULL, /* domainSetVcpus */ - NULL, /* domainPinVcpu */ - NULL, /* domainGetVcpus */ - NULL, /* domainGetMaxVcpus */ - NULL, /* listDefinedDomains */ - NULL, /* numOfDefinedDomains */ - NULL, /* domainCreate */ - NULL, /* domainDefineXML */ - NULL, /* domainUndefine */ - NULL, /* domainAttachDevice */ - NULL, /* domainDetachDevice */ - NULL, /* domainGetAutostart */ - NULL, /* domainSetAutostart */ - NULL, /* domainGetSchedulerType */ - NULL, /* domainGetSchedulerParameters */ - NULL, /* domainSetSchedulerParameters */ -}; - - -/************************************************************************ - * * - * Error handling * - * * - ************************************************************************/ - -#define virProxyError(conn, code, fmt...) \ - virReportErrorHelper(conn, VIR_FROM_PROXY, code, __FILE__, \ - __FUNCTION__, __LINE__, fmt) - -/************************************************************************ - * * - * Automatic startup of the proxy server if it is not running * - * * - ************************************************************************/ -/** - * virProxyFindServerPath: - * - * Tries to find the path to the gam_server binary. - * - * Returns path on success or NULL in case of error. - */ -static const char * -virProxyFindServerPath(void) -{ - static const char *serverPaths[] = { - BINDIR "/libvirt_proxy", - "/usr/bin/libvirt_proxy_dbg", - NULL - }; - int i; - const char *debugProxy = getenv("LIBVIRT_DEBUG_PROXY"); - - if (debugProxy) - return(debugProxy); - - for (i = 0; serverPaths[i]; i++) { - if (access(serverPaths[i], X_OK | R_OK) == 0) { - return serverPaths[i]; - } - } - return NULL; -} - -/** - * virProxyForkServer: - * - * Forks and try to launch the proxy server processing the requests for - * libvirt when communicating with Xen. - * - * Returns 0 in case of success or -1 in case of detected error. - */ -static int -virProxyForkServer(void) -{ - const char *proxyPath = virProxyFindServerPath(); - pid_t pid; - const char *proxyarg[2]; - - if (!proxyPath) { - VIR_WARN0("failed to find libvirt_proxy\n"); - return(-1); - } - - VIR_DEBUG("Asking to launch %s\n", proxyPath); - - proxyarg[0] = proxyPath; - proxyarg[1] = NULL; - - if (virExecDaemonize(NULL, proxyarg, NULL, NULL, - &pid, -1, NULL, NULL, 0, - NULL, NULL, NULL) < 0) - VIR_ERROR0("Failed to fork libvirt_proxy\n"); - - return (0); -} - -/************************************************************************ - * * - * Processing of client sockets * - * * - ************************************************************************/ - -/** - * virProxyOpenClientSocket: - * @path: the filename for the socket - * - * try to connect to the socket open by libvirt_proxy - * - * Returns the associated file descriptor or -1 in case of failure - */ -static int -virProxyOpenClientSocket(const char *path) { - int fd; - struct sockaddr_un addr; - int trials = 0; - -retry: - fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - return(-1); - } - - /* - * Abstract socket do not hit the filesystem, way more secure and - * guaranteed to be atomic - */ - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - addr.sun_path[0] = '\0'; - strncpy(&addr.sun_path[1], path, (sizeof(addr) - 4) - 2); - - /* - * now bind the socket to that address and listen on it - */ - if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(fd); - if (trials < 3) { - if (virProxyForkServer() < 0) - return(-1); - trials++; - usleep(5000 * trials * trials); - goto retry; - } - return (-1); - } - - DEBUG("connected to unix socket %s via %d\n", path, fd); - - return (fd); -} - -/** - * virProxyCloseSocket: - * @priv: the Xen proxy data - * - * Close the socket from that client. The caller must - * hold the lock on 'priv' before calling - * - * Returns 0 in case of success and -1 in case of error - */ -static int -virProxyCloseSocket(xenUnifiedPrivatePtr priv) { - int ret; - - if (priv->proxy < 0) - return(-1); - - ret = close(priv->proxy); - if (ret != 0) - VIR_WARN(_("Failed to close socket %d\n"), priv->proxy); - else - VIR_DEBUG("Closed socket %d\n", priv->proxy); - priv->proxy = -1; - return(ret); -} - -/** - * virProxyReadClientSocket: - * @fd: the socket - * @buffer: the target memory area - * @len: the length in bytes - * - * Process a read from a client socket - * - * Returns the number of byte read or -1 in case of error. - */ -static int -virProxyReadClientSocket(int fd, char *buffer, int len) { - int ret; - - if ((fd < 0) || (buffer == NULL) || (len < 0)) - return(-1); - -retry: - ret = read(fd, buffer, len); - if (ret < 0) { - if (errno == EINTR) { - VIR_DEBUG("read socket %d interrupted\n", fd); - goto retry; - } - VIR_WARN("Failed to read socket %d\n", fd); - return(-1); - } - - VIR_DEBUG("read %d bytes from socket %d\n", - ret, fd); - return(ret); -} - -/** - * virProxyWriteClientSocket: - * @fd: the socket - * @data: the data - * @len: the length of data in bytes - * - * Process a read from a client socket - */ -static int -virProxyWriteClientSocket(int fd, const char *data, int len) { - int ret; - - if ((fd < 0) || (data == NULL) || (len < 0)) - return(-1); - - ret = safewrite(fd, data, len); - if (ret < 0) { - VIR_WARN(_("Failed to write to socket %d\n"), fd); - return(-1); - } - VIR_DEBUG("wrote %d bytes to socket %d\n", - len, fd); - - return(0); -} - -/************************************************************************ - * * - * Proxy commands processing * - * * - ************************************************************************/ - -/** - * xenProxyClose: - * @conn: pointer to the hypervisor connection - * - * Shutdown the Xen proxy communication layer - */ -static int -xenProxyClose(virConnectPtr conn) -{ - xenUnifiedPrivatePtr priv; - - if (conn == NULL) { - virProxyError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return -1; - } - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (!priv) { - virProxyError (NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__); - return -1; - } - - xenUnifiedLock(priv); - virProxyCloseSocket (priv); - xenUnifiedUnlock(priv); - - return 0; -} - -static int -xenProxyCommand(virConnectPtr conn, virProxyPacketPtr request, - virProxyFullPacketPtr answer, int quiet) { - static int serial = 0; - int ret; - virProxyPacketPtr res = NULL; - xenUnifiedPrivatePtr priv; - - if (conn == NULL) { - virProxyError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); - return -1; - } - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (!priv) { - virProxyError (NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__); - return -1; - } - - xenUnifiedLock(priv); - - /* Fail silently. */ - if (priv->proxy == -1) - goto error; - - /* - * normal communication serial numbers are in 0..4095 - */ - ++serial; - if (serial >= 4096) - serial = 0; - request->version = PROXY_PROTO_VERSION; - request->serial = serial; - ret = virProxyWriteClientSocket(priv->proxy, (const char *) request, - request->len); - if (ret < 0) { - if (!quiet) - virReportSystemError(conn, errno, "%s", - _("failed to write proxy request")); - goto error; - } -retry: - if (answer == NULL) { - /* read in situ */ - ret = virProxyReadClientSocket(priv->proxy, (char *) request, - sizeof(virProxyPacket)); - if (ret < 0) { - if (!quiet) - virReportSystemError(conn, errno, "%s", - _("failed to read proxy reply")); - goto error; - } - if (ret != sizeof(virProxyPacket)) { - virProxyError(conn, VIR_ERR_INTERNAL_ERROR, - _("Communication error with proxy: got %d bytes of %d\n"), - ret, (int) sizeof(virProxyPacket)); - goto error; - } - res = request; - if (res->len != sizeof(virProxyPacket)) { - virProxyError(conn, VIR_ERR_INTERNAL_ERROR, - _("Communication error with proxy: expected %d bytes got %d\n"), - (int) sizeof(virProxyPacket), res->len); - goto error; - } - } else { - /* read in packet provided */ - ret = virProxyReadClientSocket(priv->proxy, (char *) answer, - sizeof(virProxyPacket)); - if (ret < 0) { - if (!quiet) - virReportSystemError(conn, errno, "%s", - _("failed to read proxy reply")); - goto error; - } - if (ret != sizeof(virProxyPacket)) { - virProxyError(conn, VIR_ERR_INTERNAL_ERROR, - _("Communication error with proxy: got %d bytes of %d\n"), - ret, (int) sizeof(virProxyPacket)); - goto error; - } - res = (virProxyPacketPtr) answer; - if ((res->len < sizeof(virProxyPacket)) || - (res->len > sizeof(virProxyFullPacket))) { - virProxyError(conn, VIR_ERR_INTERNAL_ERROR, - _("Communication error with proxy: got %d bytes packet\n"), - res->len); - goto error; - } - if (res->len > sizeof(virProxyPacket)) { - ret = virProxyReadClientSocket(priv->proxy, - (char *) &(answer->extra.arg[0]), - res->len - ret); - if (ret != (int) (res->len - sizeof(virProxyPacket))) { - virProxyError(conn, VIR_ERR_INTERNAL_ERROR, - _("Communication error with proxy: got %d bytes of %d\n"), - ret, (int) sizeof(virProxyPacket)); - goto error; - } - } - } - /* - * do more checks on the incoming packet. - */ - if ((res == NULL) || (res->version != PROXY_PROTO_VERSION) || - (res->len < sizeof(virProxyPacket))) { - virProxyError(conn, VIR_ERR_INTERNAL_ERROR, "%s", - _("Communication error with proxy: malformed packet\n")); - goto error; - } - if (res->serial != serial) { - VIR_WARN(_("got asynchronous packet number %d\n"), res->serial); - goto retry; - } - - xenUnifiedUnlock(priv); - return 0; - -error: - virProxyCloseSocket(priv); - xenUnifiedUnlock(priv); - return -1; -} - -/** - * xenProxyOpen: - * @conn: pointer to the hypervisor connection - * @name: URL for the target, NULL for local - * @flags: combination of virDrvOpenFlag(s) - * - * Try to initialize the Xen proxy communication layer - * This can be opened only for a read-only kind of access - * - * Returns 0 in case of success, and -1 in case of failure - */ -virDrvOpenStatus -xenProxyOpen(virConnectPtr conn, - virConnectAuthPtr auth ATTRIBUTE_UNUSED, - int flags) -{ - virProxyPacket req; - int ret; - int fd; - xenUnifiedPrivatePtr priv; - - if (!(flags & VIR_CONNECT_RO)) - return(-1); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - priv->proxy = -1; - - fd = virProxyOpenClientSocket(PROXY_SOCKET_PATH); - if (fd < 0) { - virProxyError(NULL, VIR_ERR_NO_XEN, PROXY_SOCKET_PATH); - return(-1); - } - priv->proxy = fd; - - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_NONE; - req.len = sizeof(req); - ret = xenProxyCommand(conn, &req, NULL, 1); - if ((ret < 0) || (req.command != VIR_PROXY_NONE)) { - virProxyError(NULL, VIR_ERR_OPERATION_FAILED, __FUNCTION__); - return(-1); - } - return(0); -} - -/************************************************************************ - * * - * Driver entry points * - * * - ************************************************************************/ - -/** - * xenProxyGetVersion: - * @conn: pointer to the Xen Daemon block - * @hvVer: return value for the version of the running hypervisor (OUT) - * - * Get the version level of the Hypervisor running. - * - * Returns -1 in case of error, 0 otherwise. if the version can't be - * extracted by lack of capacities returns 0 and @hvVer is 0, otherwise - * @hvVer value is major * 1,000,000 + minor * 1,000 + release - */ -static int -xenProxyGetVersion(virConnectPtr conn, unsigned long *hvVer) -{ - virProxyPacket req; - int ret; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - if (hvVer == NULL) { - virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_VERSION; - req.len = sizeof(req); - ret = xenProxyCommand(conn, &req, NULL, 0); - if (ret < 0) { - return(-1); - } - *hvVer = req.data.larg; - return(0); -} - -/** - * xenProxyListDomains: - * @conn: pointer to the hypervisor connection - * @ids: array to collect the list of IDs of active domains - * @maxids: size of @ids - * - * Collect the list of active domains, and store their ID in @maxids - * - * Returns the number of domain found or -1 in case of error - */ -int -xenProxyListDomains(virConnectPtr conn, int *ids, int maxids) -{ - virProxyPacket req; - virProxyFullPacket ans; - int ret; - int nb; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - if ((ids == NULL) || (maxids <= 0)) { - virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_LIST; - req.len = sizeof(req); - ret = xenProxyCommand(conn, &req, &ans, 0); - if (ret < 0) { - return(-1); - } - nb = ans.data.arg; - if ((nb > 1020) || (nb <= 0) || - (ans.len <= sizeof(virProxyPacket)) || - (ans.len > sizeof(virProxyFullPacket))) { - virProxyError(conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); - return(-1); - } - if (nb > maxids) - nb = maxids; - memmove(ids, &ans.extra.arg[0], nb * sizeof(int)); - - return(nb); -} - -/** - * xenProxyNumOfDomains: - * @conn: pointer to the hypervisor connection - * - * Provides the number of active domains. - * - * Returns the number of domain found or -1 in case of error - */ -static int -xenProxyNumOfDomains(virConnectPtr conn) -{ - virProxyPacket req; - int ret; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_NUM_DOMAIN; - req.len = sizeof(req); - ret = xenProxyCommand(conn, &req, NULL, 0); - if (ret < 0) { - return(-1); - } - return(req.data.arg); -} - - -/** - * xenProxyDomainGetDomMaxMemory: - * @conn: pointer to the hypervisor connection - * @id: the domain ID number - * - * Ask the Xen Daemon for the maximum memory allowed for a domain - * - * Returns the memory size in kilobytes or 0 in case of error. - */ -static unsigned long -xenProxyDomainGetDomMaxMemory(virConnectPtr conn, int id) -{ - virProxyPacket req; - int ret; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (0); - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_MAX_MEMORY; - req.data.arg = id; - req.len = sizeof(req); - ret = xenProxyCommand(conn, &req, NULL, 0); - if (ret < 0) { - return(0); - } - return(req.data.larg); -} - -/** - * xenProxyDomainGetMaxMemory: - * @domain: pointer to the domain block - * - * Ask the Xen Daemon for the maximum memory allowed for a domain - * - * Returns the memory size in kilobytes or 0 in case of error. - */ -static unsigned long -xenProxyDomainGetMaxMemory(virDomainPtr domain) -{ - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - if (domain == NULL) - virProxyError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - else - virProxyError(domain->conn, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (0); - } - if (domain->id < 0) - return (0); - return(xenProxyDomainGetDomMaxMemory(domain->conn, domain->id)); -} - -/** - * xenProxyDomainGetInfo: - * @domain: a domain object - * @info: pointer to a virDomainInfo structure allocated by the user - * - * This method looks up information about a domain and update the - * information block provided. - * - * Returns 0 in case of success, -1 in case of error - */ -static int -xenProxyDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) -{ - virProxyPacket req; - virProxyFullPacket ans; - int ret; - - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - if (domain == NULL) - virProxyError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - else - virProxyError(domain->conn, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (-1); - } - if (domain->id < 0) - return (-1); - if (info == NULL) { - virProxyError(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_DOMAIN_INFO; - req.data.arg = domain->id; - req.len = sizeof(req); - ret = xenProxyCommand(domain->conn, &req, &ans, 0); - if (ret < 0) { - return(-1); - } - if (ans.len != sizeof(virProxyPacket) + sizeof(virDomainInfo)) { - virProxyError(domain->conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); - return (-1); - } - memmove(info, &ans.extra.dinfo, sizeof(virDomainInfo)); - - return(0); -} - -/** - * xenProxyLookupByID: - * @conn: pointer to the hypervisor connection - * @id: the domain ID number - * - * Try to find a domain based on the hypervisor ID number - * - * Returns a new domain object or NULL in case of failure - */ -virDomainPtr -xenProxyLookupByID(virConnectPtr conn, int id) -{ - virProxyPacket req; - virProxyFullPacket ans; - unsigned char uuid[VIR_UUID_BUFLEN]; - const char *name; - int ret; - virDomainPtr res; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (id < 0) { - virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_LOOKUP_ID; - req.data.arg = id; - req.len = sizeof(req); - ret = xenProxyCommand(conn, &req, &ans, 0); - if (ret < 0) { - return(NULL); - } - if (ans.data.arg == -1) { - return(NULL); - } - memcpy(uuid, &ans.extra.str[0], VIR_UUID_BUFLEN); - name = &ans.extra.str[VIR_UUID_BUFLEN]; - res = virGetDomain(conn, name, uuid); - if (res) res->id = id; - return(res); -} - -/** - * xenProxyLookupByUUID: - * @conn: pointer to the hypervisor connection - * @uuid: the raw UUID for the domain - * - * Try to lookup a domain on xend based on its UUID. - * - * Returns a new domain object or NULL in case of failure - */ -virDomainPtr -xenProxyLookupByUUID(virConnectPtr conn, const unsigned char *uuid) -{ - virProxyFullPacket req; - const char *name; - int ret; - virDomainPtr res; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (uuid == NULL) { - virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - memset(&req, 0, sizeof(virProxyPacket)); - req.command = VIR_PROXY_LOOKUP_UUID; - req.len = sizeof(virProxyPacket) + VIR_UUID_BUFLEN; - memcpy(&req.extra.str[0], uuid, VIR_UUID_BUFLEN); - - ret = xenProxyCommand(conn, (virProxyPacketPtr) &req, &req, 0); - if (ret < 0) { - return(NULL); - } - if (req.data.arg == -1) { - return(NULL); - } - name = &req.extra.str[0]; - res = virGetDomain(conn, name, uuid); - if (res) res->id = req.data.arg; - return(res); -} - -/** - * xenProxyLookupByName: - * @conn: A xend instance - * @name: The name of the domain - * - * This method looks up information about a domain based on its name - * - * Returns a new domain object or NULL in case of failure - */ -virDomainPtr -xenProxyLookupByName(virConnectPtr conn, const char *name) -{ - virProxyFullPacket req; - int ret, len; - virDomainPtr res; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (name == NULL) { - virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - len = strlen(name); - if (len > 1000) { - virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - memset(&req, 0, sizeof(virProxyPacket)); - req.command = VIR_PROXY_LOOKUP_NAME; - req.len = sizeof(virProxyPacket) + len + 1; - strcpy(&req.extra.str[0], name); - ret = xenProxyCommand(conn, (virProxyPacketPtr) &req, &req, 0); - if (ret < 0) { - return(NULL); - } - if (req.data.arg == -1) { - return(NULL); - } - res = virGetDomain(conn, name, (const unsigned char *)&req.extra.str[0]); - if (res) res->id = req.data.arg; - return(res); -} - -/** - * xenProxyNodeGetInfo: - * @conn: pointer to the Xen Daemon block - * @info: pointer to a virNodeInfo structure allocated by the user - * - * Extract hardware information about the node. - * - * Returns 0 in case of success and -1 in case of failure. - */ -static int -xenProxyNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info) { - virProxyPacket req; - virProxyFullPacket ans; - int ret; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - if (info == NULL) { - virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_NODE_INFO; - req.data.arg = 0; - req.len = sizeof(req); - ret = xenProxyCommand(conn, &req, &ans, 0); - if (ret < 0) { - return(-1); - } - if (ans.data.arg == -1) { - return(-1); - } - if (ans.len != sizeof(virProxyPacket) + sizeof(virNodeInfo)) { - return(-1); - } - memcpy(info, &ans.extra.ninfo, sizeof(virNodeInfo)); - return(0); -} - -/** - * xenProxyGetCapabilities: - * @conn: pointer to the Xen Daemon block - * - * Extract capabilities of the hypervisor. - * - * Returns capabilities in case of success (freed by caller) - * and NULL in case of failure. - */ -static char * -xenProxyGetCapabilities (virConnectPtr conn) -{ - virProxyPacket req; - virProxyFullPacket ans; - int ret, xmllen; - char *xml; - - if (!VIR_IS_CONNECT(conn)) { - virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return NULL; - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_GET_CAPABILITIES; - req.data.arg = 0; - req.len = sizeof(req); - ret = xenProxyCommand(conn, &req, &ans, 0); - if (ret < 0) { - return NULL; - } - if (ans.data.arg == -1) - return NULL; - if (ans.len <= sizeof(virProxyPacket)) { - virProxyError(conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); - return NULL; - } - - xmllen = ans.len - sizeof (virProxyPacket); - if (VIR_ALLOC_N(xml, xmllen+1) < 0) { - virReportOOMError (conn); - return NULL; - } - memmove (xml, ans.extra.str, xmllen); - xml[xmllen] = '\0'; - - return xml; -} - -/** - * xenProxyDomainDumpXML: - * @domain: a domain object - * @flags: xml generation flags - * - * This method generates an XML description of a domain. - * - * Returns the XML document on success, NULL otherwise. - */ -char * -xenProxyDomainDumpXML(virDomainPtr domain, int flags ATTRIBUTE_UNUSED) -{ - virProxyPacket req; - virProxyFullPacket ans; - int ret; - int xmllen; - char *xml; - - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - if (domain == NULL) - virProxyError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - else - virProxyError(domain->conn, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (NULL); - } - if (domain->id < 0) - return (NULL); - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_DOMAIN_XML; - req.data.arg = domain->id; - req.len = sizeof(req); - ret = xenProxyCommand(domain->conn, &req, &ans, 0); - if (ret < 0) { - return(NULL); - } - if (ans.len <= sizeof(virProxyPacket)) { - virProxyError(domain->conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); - return (NULL); - } - xmllen = ans.len - sizeof(virProxyPacket); - if (VIR_ALLOC_N(xml, xmllen+1) < 0) { - virReportOOMError(domain->conn); - return NULL; - } - memmove(xml, &ans.extra.dinfo, xmllen); - xml[xmllen] = '\0'; - - return(xml); -} - -/** - * xenProxyDomainGetOSType: - * @domain: a domain object - * - * Get the type of domain operation system. - * - * Returns the new string or NULL in case of error, the string must be - * freed by the caller. - */ -static char * -xenProxyDomainGetOSType(virDomainPtr domain) -{ - virProxyPacket req; - virProxyFullPacket ans; - int ret; - int oslen; - char *ostype; - - if (!VIR_IS_CONNECTED_DOMAIN(domain)) { - if (domain == NULL) - virProxyError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - else - virProxyError(domain->conn, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); - return (NULL); - } - memset(&req, 0, sizeof(req)); - req.command = VIR_PROXY_DOMAIN_OSTYPE; - req.data.arg = domain->id; - req.len = sizeof(req); - ret = xenProxyCommand(domain->conn, &req, &ans, 0); - if (ret < 0) { - return(NULL); - } - if ((ans.len == sizeof(virProxyPacket)) && (ans.data.arg < 0)) { - return(NULL); - } - - if (ans.len <= sizeof(virProxyPacket)) { - virProxyError(domain->conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); - return (NULL); - } - oslen = ans.len - sizeof(virProxyPacket); - if (VIR_ALLOC_N(ostype, oslen+1) < 0) { - virReportOOMError(domain->conn); - return NULL; - } - memmove(ostype, &ans.extra.dinfo, oslen); - ostype[oslen] = '\0'; - - return(ostype); -} diff --git a/src/proxy_internal.h b/src/proxy_internal.h deleted file mode 100644 index 185fa674c9..0000000000 --- a/src/proxy_internal.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * proxy.h: common definitions for proxy usage - * - * Copyright (C) 2006 Red Hat, Inc. - * - * See COPYING.LIB for the License of this software - * - * Daniel Veillard - */ - - -#ifndef __LIBVIR_PROXY_H__ -#define __LIBVIR_PROXY_H__ - -#include "internal.h" - -#define PROXY_SOCKET_PATH "/tmp/livirt_proxy_conn" -#define PROXY_PROTO_VERSION 1 - -/* - * the command allowed though the proxy - */ -typedef enum { - VIR_PROXY_NONE = 0, - VIR_PROXY_VERSION = 1, - VIR_PROXY_NODE_INFO = 2, - VIR_PROXY_LIST = 3, - VIR_PROXY_NUM_DOMAIN = 4, - VIR_PROXY_LOOKUP_ID = 5, - VIR_PROXY_LOOKUP_UUID = 6, - VIR_PROXY_LOOKUP_NAME = 7, - VIR_PROXY_MAX_MEMORY = 8, - VIR_PROXY_DOMAIN_INFO = 9, - VIR_PROXY_DOMAIN_XML = 10, - VIR_PROXY_DOMAIN_OSTYPE = 11, - VIR_PROXY_GET_CAPABILITIES = 12 -} virProxyCommand; - -/* - * structure used by the client to make a request to the proxy - * and by the proxy when answering the client. - * the size may not be fixed, it's passed as len. - */ -struct _virProxyPacket { - unsigned short version; /* version of the proxy protocol */ - unsigned short command; /* command number a virProxyCommand */ - unsigned short serial; /* command serial number */ - unsigned short len; /* the length of the request */ - union { - char string[8]; /* string data */ - int arg; /* or int argument */ - long larg; /* or long argument */ - } data; -}; -typedef struct _virProxyPacket virProxyPacket; -typedef virProxyPacket *virProxyPacketPtr; - -/* - * If there is extra data sent from the proxy to the client, - * they are appended after the packet. - * the size may not be fixed, it's passed as len and includes the - * extra data. - */ -struct _virProxyFullPacket { - unsigned short version; /* version of the proxy protocol */ - unsigned short command; /* command number a virProxyCommand */ - unsigned short serial; /* command serial number */ - unsigned short len; /* the length of the request */ - union { - char string[8]; /* string data */ - int arg; /* or int argument */ - long larg; /* or long argument */ - } data; - /* that should be aligned on a 16bytes boundary */ - union { - char str[4080]; /* extra char array */ - int arg[1020]; /* extra int array */ - virDomainInfo dinfo; /* domain information */ - virNodeInfo ninfo; /* node information */ - } extra; -}; -typedef struct _virProxyFullPacket virProxyFullPacket; -typedef virProxyFullPacket *virProxyFullPacketPtr; - -/* xen_unified makes direct calls or indirect calls through here. */ -extern struct xenUnifiedDriver xenProxyDriver; -extern int xenProxyInit (void); - -extern virDomainPtr xenProxyLookupByID(virConnectPtr conn, int id); -extern virDomainPtr xenProxyLookupByUUID(virConnectPtr conn, - const unsigned char *uuid); -extern virDomainPtr xenProxyLookupByName(virConnectPtr conn, - const char *domname); - -extern char * xenProxyDomainDumpXML(virDomainPtr domain, - int flags); -extern int xenProxyListDomains(virConnectPtr conn, int *ids, - int maxids); -#endif /* __LIBVIR_PROXY_H__ */ diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 2ddcdc0f6e..068b0642f3 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -44,6 +44,7 @@ #include #include #include +#include #if HAVE_SCHED_H #include diff --git a/src/sexpr.c b/src/sexpr.c deleted file mode 100644 index 81cb49f269..0000000000 --- a/src/sexpr.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - * sexpr.c : S-Expression routines to communicate with the Xen Daemon - * - * Copyright (C) 2005 - * - * Anthony Liguori - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License. See the file COPYING.LIB in the main directory of this - * archive for more details. - */ - -#include - -#include -#include -#include -#include "c-ctype.h" -#include - -#include "virterror_internal.h" -#include "sexpr.h" -#include "util.h" -#include "memory.h" - -#define VIR_FROM_THIS VIR_FROM_SEXPR - -#define virSexprError(code, fmt...) \ - virReportErrorHelper(NULL, VIR_FROM_SEXPR, code, __FILE__, \ - __FUNCTION__, __LINE__, fmt) - -/** - * sexpr_new: - * - * Create a new S-Expression - * - * Returns the new node or NULL in case of memory allocation error - */ -static struct sexpr * -sexpr_new(void) -{ - struct sexpr *ret; - - if (VIR_ALLOC(ret) < 0) { - virReportOOMError(NULL); - return (NULL); - } - ret->kind = SEXPR_NIL; - return ret; -} - -/** - * sexpr_free: - * @sexpr: the S-Expression pointer - * - * Free an S-Expression - */ -void -sexpr_free(struct sexpr *sexpr) -{ - int serrno = errno; - - if (sexpr == NULL) { - return; - } - - switch (sexpr->kind) { - case SEXPR_CONS: - sexpr_free(sexpr->u.s.car); - sexpr_free(sexpr->u.s.cdr); - break; - case SEXPR_VALUE: - VIR_FREE(sexpr->u.value); - break; - case SEXPR_NIL: - break; - } - - VIR_FREE(sexpr); - - errno = serrno; -} - -/** - * sexpr_nil: - * - * Provide a NIL S-Expression (the pointer is not shared so NIL equality - * testing won't work at the pointer level). - * - * Returns a new NIL S-Expression of NULL in case of error. - */ -struct sexpr * -sexpr_nil(void) -{ - return sexpr_new(); -} - -/** - * sexpr_string: - * @str: the input string, assumed to be UTF-8 - * @len: the length in bytes of the input - * - * Parse the input S-Expression and return a pointer to the result - * - * Returns the S-Expression pointer or NULL in case of error - */ -struct sexpr * -sexpr_string(const char *str, ssize_t len) -{ - struct sexpr *ret = sexpr_new(); - - if (ret == NULL) - return ret; - ret->kind = SEXPR_VALUE; - if (len > 0) { - ret->u.value = strndup(str, len); - } else { - ret->u.value = strdup(str); - } - - if (ret->u.value == NULL) { - return NULL; - } - - return ret; -} - -/** - * sexpr_cons: - * @car: the left operand - * @cdr: the right operand - * - * Implement the CONS operation assembling 2 existing S-Expressions. - * Note that in case of error the input data are not freed. - * - * Returns the resulting S-Expression pointer or NULL in case of error. - */ -struct sexpr * -sexpr_cons(const struct sexpr *car, const struct sexpr *cdr) -{ - struct sexpr *ret = sexpr_new(); - - if (ret == NULL) - return ret; - ret->kind = SEXPR_CONS; - ret->u.s.car = (struct sexpr *) car; - ret->u.s.cdr = (struct sexpr *) cdr; - - return ret; -} - -/** - * append: - * @lst: an existing list - * @value: the value - * - * Internal operation appending a value at the end of an existing list - */ -static int -append(struct sexpr *lst, const struct sexpr *value) -{ - struct sexpr *nil = sexpr_nil(); - - if (nil == NULL) - return -1; - - while (lst->kind != SEXPR_NIL) { - lst = lst->u.s.cdr; - } - - lst->kind = SEXPR_CONS; - lst->u.s.car = (struct sexpr *) value; - lst->u.s.cdr = nil; - - return 0; -} - -/** - * @lst: an existing list - * @value: the value - * - * Append a value at the end of an existing list - * - * Returns lst or NULL in case of error - */ -struct sexpr * -sexpr_append(struct sexpr *lst, const struct sexpr *value) -{ - if (lst == NULL) - return (NULL); - if (value == NULL) - return (lst); - if (append(lst, value) < 0) - return (NULL); - return (lst); -} - -/** - * sexpr2string: - * @sexpr: an S-Expression pointer - * @buffer: the output buffer - * @n_buffer: the size of the buffer in bytes - * - * Serialize the S-Expression in the buffer. - * Note that the output may be truncated if @n_buffer is too small - * resulting in an unparseable value. - * - * Returns the number of bytes used by the serialization in the buffer or - * 0 in case of error. - */ -size_t -sexpr2string(const struct sexpr * sexpr, char *buffer, size_t n_buffer) -{ - size_t ret = 0, tmp; - - if ((sexpr == NULL) || (buffer == NULL) || (n_buffer <= 0)) - return (0); - - switch (sexpr->kind) { - case SEXPR_CONS: - tmp = snprintf(buffer + ret, n_buffer - ret, "("); - if (tmp == 0) - goto error; - ret += tmp; - tmp = sexpr2string(sexpr->u.s.car, buffer + ret, n_buffer - ret); - if (tmp == 0) - goto error; - ret += tmp; - while (sexpr->u.s.cdr->kind != SEXPR_NIL) { - sexpr = sexpr->u.s.cdr; - tmp = snprintf(buffer + ret, n_buffer - ret, " "); - if (tmp == 0) - goto error; - ret += tmp; - tmp = - sexpr2string(sexpr->u.s.car, buffer + ret, n_buffer - ret); - if (tmp == 0) - goto error; - ret += tmp; - } - tmp = snprintf(buffer + ret, n_buffer - ret, ")"); - if (tmp == 0) - goto error; - ret += tmp; - break; - case SEXPR_VALUE: - if (strchr(sexpr->u.value, ' ')) - tmp = snprintf(buffer + ret, n_buffer - ret, "'%s'", - sexpr->u.value); - else - tmp = snprintf(buffer + ret, n_buffer - ret, "%s", - sexpr->u.value); - if (tmp == 0) - goto error; - ret += tmp; - break; - case SEXPR_NIL: - tmp = snprintf(buffer + ret, n_buffer - ret, "()"); - if (tmp == 0) - goto error; - ret += tmp; - break; - default: - goto error; - } - - return (ret); - error: - buffer[n_buffer - 1] = 0; - virSexprError(VIR_ERR_SEXPR_SERIAL, "%s", buffer); - return (0); -} - -#define IS_SPACE(c) ((c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA)) - -static const char * -trim(const char *string) -{ - while (IS_SPACE(*string)) - string++; - return (string); -} - -/** - * _string2sexpr: - * @buffer: a zero terminated buffer containing an S-Expression in UTF-8 - * @end: pointer to an index in the buffer for the already parsed bytes - * - * Internal routine implementing the parse of S-Expression - * Note that failure in this function is catastrophic. If it returns - * NULL, you've leaked memory and you're currently OOM. It will always - * parse an SEXPR given a buffer - * - * Returns a pointer to the resulting parsed S-Expression, or NULL in case of - * hard error. - */ -static struct sexpr * -_string2sexpr(const char *buffer, size_t * end) -{ - const char *ptr = buffer + *end; - struct sexpr *ret = sexpr_new(); - - if (ret == NULL) - return NULL; - - ptr = trim(ptr); - - if (ptr[0] == '(') { - ret->kind = SEXPR_NIL; - - ptr = trim(ptr + 1); - while (*ptr && *ptr != ')') { - struct sexpr *tmp; - size_t tmp_len = 0; - - tmp = _string2sexpr(ptr, &tmp_len); - if (tmp == NULL) - goto error; - if (append(ret, tmp) < 0) { - sexpr_free(tmp); - goto error; - } -#if 0 - if (0) { - char buf[4096]; - - sexpr2string(ret, buf, sizeof(buf)); - printf("%s\n", buffer); - } -#endif - ptr = trim(ptr + tmp_len); - } - - if (*ptr == ')') { - ptr++; - } - } else { - const char *start; - - if (*ptr == '\'') { - ptr++; - start = ptr; - - while (*ptr && *ptr != '\'') { - if (*ptr == '\\' && ptr[1]) - ptr++; - ptr++; - } - - ret->u.value = strndup(start, ptr - start); - if (ret->u.value == NULL) { - virReportOOMError(NULL); - goto error; - } - - if (*ptr == '\'') - ptr++; - } else { - start = ptr; - - while (*ptr && !c_isspace(*ptr) - && *ptr != ')' && *ptr != '(') { - ptr++; - } - - ret->u.value = strndup(start, ptr - start); - if (ret->u.value == NULL) { - virReportOOMError(NULL); - goto error; - } - } - - ret->kind = SEXPR_VALUE; - if (ret->u.value == NULL) - goto error; - } - - *end = ptr - buffer; - - return ret; - - error: - sexpr_free(ret); - return (NULL); -} - -/** - * string2sexpr: - * @buffer: a zero terminated buffer containing an S-Expression in UTF-8 - * - * Parse the S-Expression in the buffer. - * Note that failure in this function is catastrophic. If it returns - * NULL, you've leaked memory and you're currently OOM. It will always - * parse an SEXPR given a buffer - * - * Returns a pointer to the resulting parsed S-Expression, or NULL in case of - * hard error. - */ -struct sexpr * -string2sexpr(const char *buffer) -{ - size_t dummy = 0; - - return _string2sexpr(buffer, &dummy); -} - - -/** - * sexpr_lookup_key: - * @sexpr: a pointer to a parsed S-Expression - * @node: a path for the sub expression to lookup in the S-Expression - * - * Search a sub expression in the S-Expression based on its path - * Returns the key node, rather than the data node. - * NOTE: path are limited to 4096 bytes. - * - * Returns the pointer to the sub expression or NULL if not found. - */ -static struct sexpr * -sexpr_lookup_key(const struct sexpr *sexpr, const char *node) -{ - char buffer[4096], *ptr, *token; - - if ((node == NULL) || (sexpr == NULL)) - return (NULL); - - snprintf(buffer, sizeof(buffer), "%s", node); - - ptr = buffer; - token = strsep(&ptr, "/"); - - if (sexpr->kind != SEXPR_CONS || sexpr->u.s.car->kind != SEXPR_VALUE) { - return NULL; - } - - if (STRNEQ(sexpr->u.s.car->u.value, token)) { - return NULL; - } - - for (token = strsep(&ptr, "/"); token; token = strsep(&ptr, "/")) { - const struct sexpr *i; - - if (token == NULL) - continue; - - sexpr = sexpr->u.s.cdr; - for (i = sexpr; i->kind != SEXPR_NIL; i = i->u.s.cdr) { - if (i->kind != SEXPR_CONS || - i->u.s.car->kind != SEXPR_CONS || - i->u.s.car->u.s.car->kind != SEXPR_VALUE) { - continue; - } - - if (STREQ(i->u.s.car->u.s.car->u.value, token)) { - sexpr = i->u.s.car; - break; - } - } - - if (i->kind == SEXPR_NIL) { - break; - } - } - - if (token != NULL) { - return NULL; - } - - return (struct sexpr *) sexpr; -} - -/** - * sexpr_lookup: - * @sexpr: a pointer to a parsed S-Expression - * @node: a path for the sub expression to lookup in the S-Expression - * - * Search a sub expression in the S-Expression based on its path. - * NOTE: path are limited to 4096 bytes. - * - * Returns the pointer to the sub expression or NULL if not found. - */ -struct sexpr * -sexpr_lookup(const struct sexpr *sexpr, const char *node) -{ - struct sexpr *s = sexpr_lookup_key(sexpr, node); - - if (s == NULL) - return NULL; - - if (s->kind != SEXPR_CONS || s->u.s.cdr->kind != SEXPR_CONS) - return NULL; - - return s->u.s.cdr; -} - -/** - * sexpr_has: - * @sexpr: a pointer to a parsed S-Expression - * @node: a path for the sub expression to lookup in the S-Expression - * - * Search a sub expression in the S-Expression based on its path. - * NOTE: path are limited to 4096 bytes. - * NB, even if the key was found sexpr_lookup may return NULL if - * the corresponding value was empty - * - * Returns true if the key was found, false otherwise - */ -int -sexpr_has(const struct sexpr *sexpr, const char *node) -{ - struct sexpr *s = sexpr_lookup_key(sexpr, node); - - if (s == NULL) - return 0; - - if (s->kind != SEXPR_CONS) - return 0; - - return 1; -} - -/** - * sexpr_node: - * @sexpr: a pointer to a parsed S-Expression - * @node: a path for the node to lookup in the S-Expression - * - * Search a node value in the S-Expression based on its path - * NOTE: path are limited to 4096 bytes. - * - * Returns the value of the node or NULL if not found. - */ -const char * -sexpr_node(const struct sexpr *sexpr, const char *node) -{ - struct sexpr *n = sexpr_lookup(sexpr, node); - - return (n && n->u.s.car->kind == SEXPR_VALUE) ? n->u.s.car->u.value : NULL; -} - -int sexpr_node_copy(const struct sexpr *sexpr, const char *node, char **dst) -{ - const char *val = sexpr_node(sexpr, node); - - if (val) { - *dst = strdup(val); - if (!(*dst)) - return -1; - } else { - *dst = NULL; - } - return 0; -} - - -/** - * sexpr_fmt_node: - * @sexpr: a pointer to a parsed S-Expression - * @fmt: a path for the node to lookup in the S-Expression - * @... extra data to build the path - * - * Search a node value in the S-Expression based on its path - * NOTE: path are limited to 4096 bytes. - * - * Returns the value of the node or NULL if not found. - */ -const char * -sexpr_fmt_node(const struct sexpr *sexpr, const char *fmt, ...) -{ - va_list ap; - char node[4096]; - - va_start(ap, fmt); - vsnprintf(node, sizeof(node), fmt, ap); - va_end(ap); - - return sexpr_node(sexpr, node); -} diff --git a/src/sexpr.h b/src/sexpr.h deleted file mode 100644 index 1d9adaa592..0000000000 --- a/src/sexpr.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * sexpr.h : S-Expression interfaces needed to communicate with the Xen Daemon - * - * Copyright (C) 2005 - * - * Anthony Liguori - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License. See the file COPYING.LIB in the main directory of this - * archive for more details. - */ - -#ifndef _LIBVIR_SEXPR_H_ -#define _LIBVIR_SEXPR_H_ - -#include "internal.h" - -#include - -enum sexpr_type { - SEXPR_NIL, - SEXPR_CONS, - SEXPR_VALUE, -}; - -struct sexpr { - enum sexpr_type kind; - union { - struct { - struct sexpr *car; - struct sexpr *cdr; - } s; - char *value; - } u; -}; - -/* conversion to/from strings */ -size_t sexpr2string(const struct sexpr *sexpr, char *buffer, size_t n_buffer); -struct sexpr *string2sexpr(const char *buffer); - -/* constructors and destructors */ -struct sexpr *sexpr_nil(void); -struct sexpr *sexpr_string(const char *str, ssize_t len); -struct sexpr *sexpr_cons(const struct sexpr *car, const struct sexpr *cdr); -struct sexpr *sexpr_append(struct sexpr *lst, const struct sexpr *item); -void sexpr_free(struct sexpr *sexpr); - -/* lookup in S-Expressions */ -const char *sexpr_node(const struct sexpr *sexpr, const char *node); -int sexpr_node_copy(const struct sexpr *sexpr, const char *node, char **dst); -const char *sexpr_fmt_node(const struct sexpr *sexpr, const char *fmt, ...) - ATTRIBUTE_FMT_PRINTF(2,3); -struct sexpr *sexpr_lookup(const struct sexpr *sexpr, const char *node); -int sexpr_has(const struct sexpr *sexpr, const char *node); -#endif diff --git a/src/stats_linux.c b/src/stats_linux.c index eccda9466a..7d8a5f9e0a 100644 --- a/src/stats_linux.c +++ b/src/stats_linux.c @@ -20,14 +20,9 @@ #include #include -#ifdef WITH_XEN -#include -#endif - #include "virterror_internal.h" #include "datatypes.h" #include "util.h" -#include "xen_unified.h" #include "stats_linux.h" #include "memory.h" @@ -63,315 +58,6 @@ statsErrorFunc (virConnectPtr conn, value); } -#ifdef WITH_XEN -/*-------------------- Xen: block stats --------------------*/ - -#include - -/* This is normally defined in but previously we - * hard-coded it. So if it's not defined, hard-code again. - */ -#ifndef XENVBD_MAJOR -#define XENVBD_MAJOR 202 -#endif - -static int -xstrtoint64 (char const *s, int base, int64_t *result) -{ - long long int lli; - char *p; - - errno = 0; - lli = strtoll (s, &p, base); - if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli) - return -1; - *result = lli; - return 0; -} - -static int64_t -read_stat (const char *path) -{ - char str[64]; - int64_t r; - int i; - FILE *fp; - - fp = fopen (path, "r"); - if (!fp) - return -1; - - /* read, but don't bail out before closing */ - i = fread (str, 1, sizeof str - 1, fp); - - if (fclose (fp) != 0 /* disk error */ - || i < 1) /* ensure we read at least one byte */ - return -1; - - str[i] = '\0'; /* make sure the string is nul-terminated */ - if (xstrtoint64 (str, 10, &r) == -1) - return -1; - - return r; -} - -static int64_t -read_bd_stat (int device, int domid, const char *str) -{ - char path[PATH_MAX]; - int64_t r; - - snprintf (path, sizeof path, - "/sys/devices/xen-backend/vbd-%d-%d/statistics/%s", - domid, device, str); - r = read_stat (path); - if (r >= 0) return r; - - snprintf (path, sizeof path, - "/sys/devices/xen-backend/tap-%d-%d/statistics/%s", - domid, device, str); - r = read_stat (path); - return r; -} - -/* In Xenstore, /local/domain/0/backend/vbd///state, - * if available, must be XenbusStateConnected (= 4), otherwise there - * is no connected device. - */ -static int -check_bd_connected (xenUnifiedPrivatePtr priv, int device, int domid) -{ - char s[256], *rs; - int r; - unsigned len = 0; - - /* This code assumes we're connected if we can't get to - * xenstore, etc. - */ - if (!priv->xshandle) return 1; - snprintf (s, sizeof s, "/local/domain/0/backend/vbd/%d/%d/state", - domid, device); - s[sizeof s - 1] = '\0'; - - rs = xs_read (priv->xshandle, 0, s, &len); - if (!rs) return 1; - if (len == 0) { - /* Hmmm ... we can get to xenstore but it returns an empty - * string instead of an error. Assume it's not connected - * in this case. - */ - free (rs); - return 0; - } - - r = STREQ (rs, "4"); - free (rs); - return r; -} - -static int -read_bd_stats (virConnectPtr conn, xenUnifiedPrivatePtr priv, - int device, int domid, struct _virDomainBlockStats *stats) -{ - stats->rd_req = read_bd_stat (device, domid, "rd_req"); - stats->rd_bytes = read_bd_stat (device, domid, "rd_sect"); - stats->wr_req = read_bd_stat (device, domid, "wr_req"); - stats->wr_bytes = read_bd_stat (device, domid, "wr_sect"); - stats->errs = read_bd_stat (device, domid, "oo_req"); - - /* None of the files were found - it's likely that this version - * of Xen is an old one which just doesn't support stats collection. - */ - if (stats->rd_req == -1 && stats->rd_bytes == -1 && - stats->wr_req == -1 && stats->wr_bytes == -1 && - stats->errs == -1) { - statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "Failed to read any block statistics", domid); - return -1; - } - - /* If stats are all zero then either there really isn't any block - * device activity, or there is no connected front end device - * in which case there are no stats. - */ - if (stats->rd_req == 0 && stats->rd_bytes == 0 && - stats->wr_req == 0 && stats->wr_bytes == 0 && - stats->errs == 0 && - !check_bd_connected (priv, device, domid)) { - statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "Frontend block device not connected", domid); - return -1; - } - - /* 'Bytes' was really sectors when we read it. Scale up by - * an assumed sector size. - */ - if (stats->rd_bytes > 0) { - if (stats->rd_bytes >= ((unsigned long long)1)<<(63-9)) { - statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "stats->rd_bytes would overflow 64 bit counter", - domid); - return -1; - } - stats->rd_bytes *= 512; - } - if (stats->wr_bytes > 0) { - if (stats->wr_bytes >= ((unsigned long long)1)<<(63-9)) { - statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "stats->wr_bytes would overflow 64 bit counter", - domid); - return -1; - } - stats->wr_bytes *= 512; - } - - return 0; -} - -static int -disk_re_match(const char *regex, const char *path, int *part) -{ - regex_t myreg; - int err; - int retval; - regmatch_t pmatch[3]; - - retval = 0; - - err = regcomp(&myreg, regex, REG_EXTENDED); - if (err != 0) - return 0; - - err = regexec(&myreg, path, 3, pmatch, 0); - - if (err == 0) { - /* OK, we have a match; see if we have a partition */ - *part = 0; - retval = 1; - if (pmatch[1].rm_so != -1) { - if (virStrToLong_i(path + pmatch[1].rm_so, NULL, 10, part) < 0) - retval = 0; - } - } - - regfree(&myreg); - - return retval; -} - -int -xenLinuxDomainDeviceID(virConnectPtr conn, int domid, const char *path) -{ - int major, minor; - int part; - int retval; - char *mod_path; - - int const scsi_majors[] = { SCSI_DISK0_MAJOR, SCSI_DISK1_MAJOR, - SCSI_DISK2_MAJOR, SCSI_DISK3_MAJOR, - SCSI_DISK4_MAJOR, SCSI_DISK5_MAJOR, - SCSI_DISK6_MAJOR, SCSI_DISK7_MAJOR, - SCSI_DISK8_MAJOR, SCSI_DISK9_MAJOR, - SCSI_DISK10_MAJOR, SCSI_DISK11_MAJOR, - SCSI_DISK12_MAJOR, SCSI_DISK13_MAJOR, - SCSI_DISK14_MAJOR, SCSI_DISK15_MAJOR }; - int const ide_majors[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, - IDE4_MAJOR, IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, - IDE8_MAJOR, IDE9_MAJOR }; - - /* - * Possible block device majors & partition ranges. This - * matches the ranges supported in Xend xen/util/blkif.py - * - * hdNM: N=a-t, M=1-63, major={IDE0_MAJOR -> IDE9_MAJOR} - * sdNM: N=a-z,aa-iv, M=1-15, major={SCSI_DISK0_MAJOR -> SCSI_DISK15_MAJOR} - * xvdNM: N=a-p M=1-15, major=XENVBD_MAJOR - * xvdNM: N=q-z,aa-iz M=1-15, major=(1<<28) - * - * The path for statistics will be - * - * /sys/devices/xen-backend/(vbd|tap)-{domid}-{devid}/statistics/{...} - */ - - if (strlen(path) >= 5 && STRPREFIX(path, "/dev/")) - retval = virAsprintf(&mod_path, "%s", path); - else - retval = virAsprintf(&mod_path, "/dev/%s", path); - - if (retval < 0) { - virReportOOMError (conn); - return -1; - } - - retval = -1; - - if (disk_re_match("/dev/sd[a-z]([1-9]|1[0-5])?$", mod_path, &part)) { - major = scsi_majors[(mod_path[7] - 'a') / 16]; - minor = ((mod_path[7] - 'a') % 16) * 16 + part; - retval = major * 256 + minor; - } - else if (disk_re_match("/dev/sd[a-h][a-z]([1-9]|1[0-5])?$", - mod_path, &part) || - disk_re_match("/dev/sdi[a-v]([1-9]|1[0-5])?$", - mod_path, &part)) { - major = scsi_majors[((mod_path[7] - 'a' + 1) * 26 + (mod_path[8] - 'a')) / 16]; - minor = (((mod_path[7] - 'a' + 1) * 26 + (mod_path[8] - 'a')) % 16) - * 16 + part; - retval = major * 256 + minor; - } - else if (disk_re_match("/dev/hd[a-t]([1-9]|[1-5][0-9]|6[0-3])?$", - mod_path, &part)) { - major = ide_majors[(mod_path[7] - 'a') / 2]; - minor = ((mod_path[7] - 'a') % 2) * 64 + part; - retval = major * 256 + minor; - } - else if (disk_re_match("/dev/xvd[a-p]([1-9]|1[0-5])?$", mod_path, &part)) - retval = (202 << 8) + ((mod_path[8] - 'a') << 4) + part; - else if (disk_re_match("/dev/xvd[q-z]([1-9]|1[0-5])?$", mod_path, &part)) - retval = (1 << 28) + ((mod_path[8] - 'a') << 8) + part; - else if (disk_re_match("/dev/xvd[a-i][a-z]([1-9]|1[0-5])?$", - mod_path, &part)) - retval = (1 << 28) + (((mod_path[8] - 'a' + 1) * 26 + (mod_path[9] - 'a')) << 8) + part; - /* - * OK, we've now checked the common case (things that work); check the - * beginning of the strings for better error messages - */ - else if (strlen(mod_path) >= 7 && STRPREFIX(mod_path, "/dev/sd")) - statsErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "invalid path, device names must be in the range sda[1-15] - sdiv[1-15]", - domid); - else if (strlen(mod_path) >= 7 && STRPREFIX(mod_path, "/dev/hd")) - statsErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "invalid path, device names must be in the range hda[1-63] - hdt[1-63]", - domid); - else if (strlen(mod_path) >= 8 && STRPREFIX(mod_path, "/dev/xvd")) - statsErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "invalid path, device names must be in the range xvda[1-15] - xvdiz[1-15]", - domid); - else - statsErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "unsupported path, use xvdN, hdN, or sdN", domid); - - VIR_FREE(mod_path); - - return retval; -} - -int -xenLinuxDomainBlockStats (xenUnifiedPrivatePtr priv, - virDomainPtr dom, - const char *path, - struct _virDomainBlockStats *stats) -{ - int device = xenLinuxDomainDeviceID(dom->conn, dom->id, path); - - if (device < 0) - return -1; - - return read_bd_stats (dom->conn, priv, device, dom->id, stats); -} - -#endif /* WITH_XEN */ /*-------------------- interface stats --------------------*/ /* Just reads the named interface, so not Xen or QEMU-specific. diff --git a/src/stats_linux.h b/src/stats_linux.h index 076ccd7c8a..033a50b643 100644 --- a/src/stats_linux.h +++ b/src/stats_linux.h @@ -13,16 +13,11 @@ #ifdef __linux__ -#include "xen_unified.h" +#include "internal.h" -extern int xenLinuxDomainBlockStats (xenUnifiedPrivatePtr priv, - virDomainPtr dom, const char *path, - struct _virDomainBlockStats *stats); extern int linuxDomainInterfaceStats (virConnectPtr conn, const char *path, struct _virDomainInterfaceStats *stats); -extern int xenLinuxDomainDeviceID(virConnectPtr conn, int domid, const char *dev); - #endif /* __linux__ */ #endif /* __STATS_LINUX_H__ */ diff --git a/src/uml_driver.c b/src/uml_driver.c index a2b949551d..c2898f72cb 100644 --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "uml_driver.h" #include "uml_conf.h" diff --git a/src/xen/block_stats.c b/src/xen/block_stats.c new file mode 100644 index 0000000000..73b94c161f --- /dev/null +++ b/src/xen/block_stats.c @@ -0,0 +1,370 @@ +/* + * Linux block and network stats. + * + * Copyright (C) 2007-2009 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Richard W.M. Jones + */ + +#include + +/* This file only applies on Linux. */ +#ifdef __linux__ + +#include +#include +#include +#include +#include +#include + +#include + +#include "virterror_internal.h" +#include "datatypes.h" +#include "util.h" +#include "block_stats.h" +#include "memory.h" + +#define VIR_FROM_THIS VIR_FROM_STATS_LINUX + +/** + * statsErrorFunc: + * @conn: the connection + * @error: the error number + * @func: the function failing + * @info: extra information string + * @value: extra information number + * + * Handle a stats error. + */ +static void +statsErrorFunc (virConnectPtr conn, + virErrorNumber error, const char *func, const char *info, + int value) +{ + char fullinfo[1000]; + const char *errmsg; + + errmsg = virErrorMsg(error, info); + if (func != NULL) { + snprintf(fullinfo, sizeof (fullinfo) - 1, "%s: %s", func, info); + fullinfo[sizeof (fullinfo) - 1] = 0; + info = fullinfo; + } + virRaiseError(conn, NULL, NULL, VIR_FROM_STATS_LINUX, error, + VIR_ERR_ERROR, + errmsg, info, NULL, value, 0, errmsg, info, + value); +} + +/*-------------------- Xen: block stats --------------------*/ + +#include + +/* This is normally defined in but previously we + * hard-coded it. So if it's not defined, hard-code again. + */ +#ifndef XENVBD_MAJOR +#define XENVBD_MAJOR 202 +#endif + +static int +xstrtoint64 (char const *s, int base, int64_t *result) +{ + long long int lli; + char *p; + + errno = 0; + lli = strtoll (s, &p, base); + if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli) + return -1; + *result = lli; + return 0; +} + +static int64_t +read_stat (const char *path) +{ + char str[64]; + int64_t r; + int i; + FILE *fp; + + fp = fopen (path, "r"); + if (!fp) + return -1; + + /* read, but don't bail out before closing */ + i = fread (str, 1, sizeof str - 1, fp); + + if (fclose (fp) != 0 /* disk error */ + || i < 1) /* ensure we read at least one byte */ + return -1; + + str[i] = '\0'; /* make sure the string is nul-terminated */ + if (xstrtoint64 (str, 10, &r) == -1) + return -1; + + return r; +} + +static int64_t +read_bd_stat (int device, int domid, const char *str) +{ + char path[PATH_MAX]; + int64_t r; + + snprintf (path, sizeof path, + "/sys/devices/xen-backend/vbd-%d-%d/statistics/%s", + domid, device, str); + r = read_stat (path); + if (r >= 0) return r; + + snprintf (path, sizeof path, + "/sys/devices/xen-backend/tap-%d-%d/statistics/%s", + domid, device, str); + r = read_stat (path); + return r; +} + +/* In Xenstore, /local/domain/0/backend/vbd///state, + * if available, must be XenbusStateConnected (= 4), otherwise there + * is no connected device. + */ +static int +check_bd_connected (xenUnifiedPrivatePtr priv, int device, int domid) +{ + char s[256], *rs; + int r; + unsigned len = 0; + + /* This code assumes we're connected if we can't get to + * xenstore, etc. + */ + if (!priv->xshandle) return 1; + snprintf (s, sizeof s, "/local/domain/0/backend/vbd/%d/%d/state", + domid, device); + s[sizeof s - 1] = '\0'; + + rs = xs_read (priv->xshandle, 0, s, &len); + if (!rs) return 1; + if (len == 0) { + /* Hmmm ... we can get to xenstore but it returns an empty + * string instead of an error. Assume it's not connected + * in this case. + */ + free (rs); + return 0; + } + + r = STREQ (rs, "4"); + free (rs); + return r; +} + +static int +read_bd_stats (virConnectPtr conn, xenUnifiedPrivatePtr priv, + int device, int domid, struct _virDomainBlockStats *stats) +{ + stats->rd_req = read_bd_stat (device, domid, "rd_req"); + stats->rd_bytes = read_bd_stat (device, domid, "rd_sect"); + stats->wr_req = read_bd_stat (device, domid, "wr_req"); + stats->wr_bytes = read_bd_stat (device, domid, "wr_sect"); + stats->errs = read_bd_stat (device, domid, "oo_req"); + + /* None of the files were found - it's likely that this version + * of Xen is an old one which just doesn't support stats collection. + */ + if (stats->rd_req == -1 && stats->rd_bytes == -1 && + stats->wr_req == -1 && stats->wr_bytes == -1 && + stats->errs == -1) { + statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "Failed to read any block statistics", domid); + return -1; + } + + /* If stats are all zero then either there really isn't any block + * device activity, or there is no connected front end device + * in which case there are no stats. + */ + if (stats->rd_req == 0 && stats->rd_bytes == 0 && + stats->wr_req == 0 && stats->wr_bytes == 0 && + stats->errs == 0 && + !check_bd_connected (priv, device, domid)) { + statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "Frontend block device not connected", domid); + return -1; + } + + /* 'Bytes' was really sectors when we read it. Scale up by + * an assumed sector size. + */ + if (stats->rd_bytes > 0) { + if (stats->rd_bytes >= ((unsigned long long)1)<<(63-9)) { + statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "stats->rd_bytes would overflow 64 bit counter", + domid); + return -1; + } + stats->rd_bytes *= 512; + } + if (stats->wr_bytes > 0) { + if (stats->wr_bytes >= ((unsigned long long)1)<<(63-9)) { + statsErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "stats->wr_bytes would overflow 64 bit counter", + domid); + return -1; + } + stats->wr_bytes *= 512; + } + + return 0; +} + +static int +disk_re_match(const char *regex, const char *path, int *part) +{ + regex_t myreg; + int err; + int retval; + regmatch_t pmatch[3]; + + retval = 0; + + err = regcomp(&myreg, regex, REG_EXTENDED); + if (err != 0) + return 0; + + err = regexec(&myreg, path, 3, pmatch, 0); + + if (err == 0) { + /* OK, we have a match; see if we have a partition */ + *part = 0; + retval = 1; + if (pmatch[1].rm_so != -1) { + if (virStrToLong_i(path + pmatch[1].rm_so, NULL, 10, part) < 0) + retval = 0; + } + } + + regfree(&myreg); + + return retval; +} + +int +xenLinuxDomainDeviceID(virConnectPtr conn, int domid, const char *path) +{ + int major, minor; + int part; + int retval; + char *mod_path; + + int const scsi_majors[] = { SCSI_DISK0_MAJOR, SCSI_DISK1_MAJOR, + SCSI_DISK2_MAJOR, SCSI_DISK3_MAJOR, + SCSI_DISK4_MAJOR, SCSI_DISK5_MAJOR, + SCSI_DISK6_MAJOR, SCSI_DISK7_MAJOR, + SCSI_DISK8_MAJOR, SCSI_DISK9_MAJOR, + SCSI_DISK10_MAJOR, SCSI_DISK11_MAJOR, + SCSI_DISK12_MAJOR, SCSI_DISK13_MAJOR, + SCSI_DISK14_MAJOR, SCSI_DISK15_MAJOR }; + int const ide_majors[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, + IDE4_MAJOR, IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, + IDE8_MAJOR, IDE9_MAJOR }; + + /* + * Possible block device majors & partition ranges. This + * matches the ranges supported in Xend xen/util/blkif.py + * + * hdNM: N=a-t, M=1-63, major={IDE0_MAJOR -> IDE9_MAJOR} + * sdNM: N=a-z,aa-iv, M=1-15, major={SCSI_DISK0_MAJOR -> SCSI_DISK15_MAJOR} + * xvdNM: N=a-p M=1-15, major=XENVBD_MAJOR + * xvdNM: N=q-z,aa-iz M=1-15, major=(1<<28) + * + * The path for statistics will be + * + * /sys/devices/xen-backend/(vbd|tap)-{domid}-{devid}/statistics/{...} + */ + + if (strlen(path) >= 5 && STRPREFIX(path, "/dev/")) + retval = virAsprintf(&mod_path, "%s", path); + else + retval = virAsprintf(&mod_path, "/dev/%s", path); + + if (retval < 0) { + virReportOOMError (conn); + return -1; + } + + retval = -1; + + if (disk_re_match("/dev/sd[a-z]([1-9]|1[0-5])?$", mod_path, &part)) { + major = scsi_majors[(mod_path[7] - 'a') / 16]; + minor = ((mod_path[7] - 'a') % 16) * 16 + part; + retval = major * 256 + minor; + } + else if (disk_re_match("/dev/sd[a-h][a-z]([1-9]|1[0-5])?$", + mod_path, &part) || + disk_re_match("/dev/sdi[a-v]([1-9]|1[0-5])?$", + mod_path, &part)) { + major = scsi_majors[((mod_path[7] - 'a' + 1) * 26 + (mod_path[8] - 'a')) / 16]; + minor = (((mod_path[7] - 'a' + 1) * 26 + (mod_path[8] - 'a')) % 16) + * 16 + part; + retval = major * 256 + minor; + } + else if (disk_re_match("/dev/hd[a-t]([1-9]|[1-5][0-9]|6[0-3])?$", + mod_path, &part)) { + major = ide_majors[(mod_path[7] - 'a') / 2]; + minor = ((mod_path[7] - 'a') % 2) * 64 + part; + retval = major * 256 + minor; + } + else if (disk_re_match("/dev/xvd[a-p]([1-9]|1[0-5])?$", mod_path, &part)) + retval = (202 << 8) + ((mod_path[8] - 'a') << 4) + part; + else if (disk_re_match("/dev/xvd[q-z]([1-9]|1[0-5])?$", mod_path, &part)) + retval = (1 << 28) + ((mod_path[8] - 'a') << 8) + part; + else if (disk_re_match("/dev/xvd[a-i][a-z]([1-9]|1[0-5])?$", + mod_path, &part)) + retval = (1 << 28) + (((mod_path[8] - 'a' + 1) * 26 + (mod_path[9] - 'a')) << 8) + part; + /* + * OK, we've now checked the common case (things that work); check the + * beginning of the strings for better error messages + */ + else if (strlen(mod_path) >= 7 && STRPREFIX(mod_path, "/dev/sd")) + statsErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid path, device names must be in the range sda[1-15] - sdiv[1-15]", + domid); + else if (strlen(mod_path) >= 7 && STRPREFIX(mod_path, "/dev/hd")) + statsErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid path, device names must be in the range hda[1-63] - hdt[1-63]", + domid); + else if (strlen(mod_path) >= 8 && STRPREFIX(mod_path, "/dev/xvd")) + statsErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid path, device names must be in the range xvda[1-15] - xvdiz[1-15]", + domid); + else + statsErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "unsupported path, use xvdN, hdN, or sdN", domid); + + VIR_FREE(mod_path); + + return retval; +} + +int +xenLinuxDomainBlockStats (xenUnifiedPrivatePtr priv, + virDomainPtr dom, + const char *path, + struct _virDomainBlockStats *stats) +{ + int device = xenLinuxDomainDeviceID(dom->conn, dom->id, path); + + if (device < 0) + return -1; + + return read_bd_stats (dom->conn, priv, device, dom->id, stats); +} + +#endif /* __linux__ */ diff --git a/src/xen/block_stats.h b/src/xen/block_stats.h new file mode 100644 index 0000000000..e29c0b55c5 --- /dev/null +++ b/src/xen/block_stats.h @@ -0,0 +1,26 @@ +/* + * Linux block and network stats. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Richard W.M. Jones + */ + +#ifndef __BLOCK_STATS_H__ +#define __BLOCK_STATS_H__ + +#ifdef __linux__ + +#include "xen_driver.h" + +extern int xenLinuxDomainBlockStats (xenUnifiedPrivatePtr priv, + virDomainPtr dom, const char *path, + struct _virDomainBlockStats *stats); + +extern int xenLinuxDomainDeviceID(virConnectPtr conn, int domid, const char *dev); + +#endif /* __linux__ */ + +#endif /* __STATS_LINUX_H__ */ diff --git a/src/xen/proxy_internal.c b/src/xen/proxy_internal.c new file mode 100644 index 0000000000..49127d7cf2 --- /dev/null +++ b/src/xen/proxy_internal.c @@ -0,0 +1,1049 @@ +/* + * proxy_client.c: client side of the communication with the libvirt proxy. + * + * Copyright (C) 2006, 2008, 2009 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Veillard + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "virterror_internal.h" +#include "logging.h" +#include "datatypes.h" +#include "driver.h" +#include "proxy_internal.h" +#include "util.h" +#include "xen_driver.h" +#include "memory.h" + +#define STANDALONE + +#define VIR_FROM_THIS VIR_FROM_PROXY + +static int xenProxyClose(virConnectPtr conn); +static virDrvOpenStatus xenProxyOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags); +static int xenProxyGetVersion(virConnectPtr conn, unsigned long *hvVer); +static int xenProxyNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info); +static char *xenProxyGetCapabilities(virConnectPtr conn); +static int xenProxyNumOfDomains(virConnectPtr conn); +static unsigned long xenProxyDomainGetMaxMemory(virDomainPtr domain); +static int xenProxyDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info); +static char *xenProxyDomainGetOSType(virDomainPtr domain); + +struct xenUnifiedDriver xenProxyDriver = { + xenProxyOpen, /* open */ + xenProxyClose, /* close */ + xenProxyGetVersion, /* version */ + NULL, /* hostname */ + xenProxyNodeGetInfo, /* nodeGetInfo */ + xenProxyGetCapabilities, /* getCapabilities */ + xenProxyListDomains, /* listDomains */ + xenProxyNumOfDomains, /* numOfDomains */ + NULL, /* domainCreateXML */ + NULL, /* domainSuspend */ + NULL, /* domainResume */ + NULL, /* domainShutdown */ + NULL, /* domainReboot */ + NULL, /* domainDestroy */ + xenProxyDomainGetOSType, /* domainGetOSType */ + xenProxyDomainGetMaxMemory, /* domainGetMaxMemory */ + NULL, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + xenProxyDomainGetInfo, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + NULL, /* domainCoreDump */ + NULL, /* domainSetVcpus */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + NULL, /* domainGetMaxVcpus */ + NULL, /* listDefinedDomains */ + NULL, /* numOfDefinedDomains */ + NULL, /* domainCreate */ + NULL, /* domainDefineXML */ + NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ + NULL, /* domainGetAutostart */ + NULL, /* domainSetAutostart */ + NULL, /* domainGetSchedulerType */ + NULL, /* domainGetSchedulerParameters */ + NULL, /* domainSetSchedulerParameters */ +}; + + +/************************************************************************ + * * + * Error handling * + * * + ************************************************************************/ + +#define virProxyError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_PROXY, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +/************************************************************************ + * * + * Automatic startup of the proxy server if it is not running * + * * + ************************************************************************/ +/** + * virProxyFindServerPath: + * + * Tries to find the path to the gam_server binary. + * + * Returns path on success or NULL in case of error. + */ +static const char * +virProxyFindServerPath(void) +{ + static const char *serverPaths[] = { + BINDIR "/libvirt_proxy", + "/usr/bin/libvirt_proxy_dbg", + NULL + }; + int i; + const char *debugProxy = getenv("LIBVIRT_DEBUG_PROXY"); + + if (debugProxy) + return(debugProxy); + + for (i = 0; serverPaths[i]; i++) { + if (access(serverPaths[i], X_OK | R_OK) == 0) { + return serverPaths[i]; + } + } + return NULL; +} + +/** + * virProxyForkServer: + * + * Forks and try to launch the proxy server processing the requests for + * libvirt when communicating with Xen. + * + * Returns 0 in case of success or -1 in case of detected error. + */ +static int +virProxyForkServer(void) +{ + const char *proxyPath = virProxyFindServerPath(); + pid_t pid; + const char *proxyarg[2]; + + if (!proxyPath) { + VIR_WARN0("failed to find libvirt_proxy\n"); + return(-1); + } + + VIR_DEBUG("Asking to launch %s\n", proxyPath); + + proxyarg[0] = proxyPath; + proxyarg[1] = NULL; + + if (virExecDaemonize(NULL, proxyarg, NULL, NULL, + &pid, -1, NULL, NULL, 0, + NULL, NULL, NULL) < 0) + VIR_ERROR0("Failed to fork libvirt_proxy\n"); + + return (0); +} + +/************************************************************************ + * * + * Processing of client sockets * + * * + ************************************************************************/ + +/** + * virProxyOpenClientSocket: + * @path: the filename for the socket + * + * try to connect to the socket open by libvirt_proxy + * + * Returns the associated file descriptor or -1 in case of failure + */ +static int +virProxyOpenClientSocket(const char *path) { + int fd; + struct sockaddr_un addr; + int trials = 0; + +retry: + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + return(-1); + } + + /* + * Abstract socket do not hit the filesystem, way more secure and + * guaranteed to be atomic + */ + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + addr.sun_path[0] = '\0'; + strncpy(&addr.sun_path[1], path, (sizeof(addr) - 4) - 2); + + /* + * now bind the socket to that address and listen on it + */ + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(fd); + if (trials < 3) { + if (virProxyForkServer() < 0) + return(-1); + trials++; + usleep(5000 * trials * trials); + goto retry; + } + return (-1); + } + + DEBUG("connected to unix socket %s via %d\n", path, fd); + + return (fd); +} + +/** + * virProxyCloseSocket: + * @priv: the Xen proxy data + * + * Close the socket from that client. The caller must + * hold the lock on 'priv' before calling + * + * Returns 0 in case of success and -1 in case of error + */ +static int +virProxyCloseSocket(xenUnifiedPrivatePtr priv) { + int ret; + + if (priv->proxy < 0) + return(-1); + + ret = close(priv->proxy); + if (ret != 0) + VIR_WARN(_("Failed to close socket %d\n"), priv->proxy); + else + VIR_DEBUG("Closed socket %d\n", priv->proxy); + priv->proxy = -1; + return(ret); +} + +/** + * virProxyReadClientSocket: + * @fd: the socket + * @buffer: the target memory area + * @len: the length in bytes + * + * Process a read from a client socket + * + * Returns the number of byte read or -1 in case of error. + */ +static int +virProxyReadClientSocket(int fd, char *buffer, int len) { + int ret; + + if ((fd < 0) || (buffer == NULL) || (len < 0)) + return(-1); + +retry: + ret = read(fd, buffer, len); + if (ret < 0) { + if (errno == EINTR) { + VIR_DEBUG("read socket %d interrupted\n", fd); + goto retry; + } + VIR_WARN("Failed to read socket %d\n", fd); + return(-1); + } + + VIR_DEBUG("read %d bytes from socket %d\n", + ret, fd); + return(ret); +} + +/** + * virProxyWriteClientSocket: + * @fd: the socket + * @data: the data + * @len: the length of data in bytes + * + * Process a read from a client socket + */ +static int +virProxyWriteClientSocket(int fd, const char *data, int len) { + int ret; + + if ((fd < 0) || (data == NULL) || (len < 0)) + return(-1); + + ret = safewrite(fd, data, len); + if (ret < 0) { + VIR_WARN(_("Failed to write to socket %d\n"), fd); + return(-1); + } + VIR_DEBUG("wrote %d bytes to socket %d\n", + len, fd); + + return(0); +} + +/************************************************************************ + * * + * Proxy commands processing * + * * + ************************************************************************/ + +/** + * xenProxyClose: + * @conn: pointer to the hypervisor connection + * + * Shutdown the Xen proxy communication layer + */ +static int +xenProxyClose(virConnectPtr conn) +{ + xenUnifiedPrivatePtr priv; + + if (conn == NULL) { + virProxyError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; + } + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (!priv) { + virProxyError (NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__); + return -1; + } + + xenUnifiedLock(priv); + virProxyCloseSocket (priv); + xenUnifiedUnlock(priv); + + return 0; +} + +static int +xenProxyCommand(virConnectPtr conn, virProxyPacketPtr request, + virProxyFullPacketPtr answer, int quiet) { + static int serial = 0; + int ret; + virProxyPacketPtr res = NULL; + xenUnifiedPrivatePtr priv; + + if (conn == NULL) { + virProxyError (NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return -1; + } + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (!priv) { + virProxyError (NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__); + return -1; + } + + xenUnifiedLock(priv); + + /* Fail silently. */ + if (priv->proxy == -1) + goto error; + + /* + * normal communication serial numbers are in 0..4095 + */ + ++serial; + if (serial >= 4096) + serial = 0; + request->version = PROXY_PROTO_VERSION; + request->serial = serial; + ret = virProxyWriteClientSocket(priv->proxy, (const char *) request, + request->len); + if (ret < 0) { + if (!quiet) + virReportSystemError(conn, errno, "%s", + _("failed to write proxy request")); + goto error; + } +retry: + if (answer == NULL) { + /* read in situ */ + ret = virProxyReadClientSocket(priv->proxy, (char *) request, + sizeof(virProxyPacket)); + if (ret < 0) { + if (!quiet) + virReportSystemError(conn, errno, "%s", + _("failed to read proxy reply")); + goto error; + } + if (ret != sizeof(virProxyPacket)) { + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: got %d bytes of %d\n"), + ret, (int) sizeof(virProxyPacket)); + goto error; + } + res = request; + if (res->len != sizeof(virProxyPacket)) { + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: expected %d bytes got %d\n"), + (int) sizeof(virProxyPacket), res->len); + goto error; + } + } else { + /* read in packet provided */ + ret = virProxyReadClientSocket(priv->proxy, (char *) answer, + sizeof(virProxyPacket)); + if (ret < 0) { + if (!quiet) + virReportSystemError(conn, errno, "%s", + _("failed to read proxy reply")); + goto error; + } + if (ret != sizeof(virProxyPacket)) { + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: got %d bytes of %d\n"), + ret, (int) sizeof(virProxyPacket)); + goto error; + } + res = (virProxyPacketPtr) answer; + if ((res->len < sizeof(virProxyPacket)) || + (res->len > sizeof(virProxyFullPacket))) { + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: got %d bytes packet\n"), + res->len); + goto error; + } + if (res->len > sizeof(virProxyPacket)) { + ret = virProxyReadClientSocket(priv->proxy, + (char *) &(answer->extra.arg[0]), + res->len - ret); + if (ret != (int) (res->len - sizeof(virProxyPacket))) { + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, + _("Communication error with proxy: got %d bytes of %d\n"), + ret, (int) sizeof(virProxyPacket)); + goto error; + } + } + } + /* + * do more checks on the incoming packet. + */ + if ((res == NULL) || (res->version != PROXY_PROTO_VERSION) || + (res->len < sizeof(virProxyPacket))) { + virProxyError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("Communication error with proxy: malformed packet\n")); + goto error; + } + if (res->serial != serial) { + VIR_WARN(_("got asynchronous packet number %d\n"), res->serial); + goto retry; + } + + xenUnifiedUnlock(priv); + return 0; + +error: + virProxyCloseSocket(priv); + xenUnifiedUnlock(priv); + return -1; +} + +/** + * xenProxyOpen: + * @conn: pointer to the hypervisor connection + * @name: URL for the target, NULL for local + * @flags: combination of virDrvOpenFlag(s) + * + * Try to initialize the Xen proxy communication layer + * This can be opened only for a read-only kind of access + * + * Returns 0 in case of success, and -1 in case of failure + */ +virDrvOpenStatus +xenProxyOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags) +{ + virProxyPacket req; + int ret; + int fd; + xenUnifiedPrivatePtr priv; + + if (!(flags & VIR_CONNECT_RO)) + return(-1); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + priv->proxy = -1; + + fd = virProxyOpenClientSocket(PROXY_SOCKET_PATH); + if (fd < 0) { + virProxyError(NULL, VIR_ERR_NO_XEN, PROXY_SOCKET_PATH); + return(-1); + } + priv->proxy = fd; + + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_NONE; + req.len = sizeof(req); + ret = xenProxyCommand(conn, &req, NULL, 1); + if ((ret < 0) || (req.command != VIR_PROXY_NONE)) { + virProxyError(NULL, VIR_ERR_OPERATION_FAILED, __FUNCTION__); + return(-1); + } + return(0); +} + +/************************************************************************ + * * + * Driver entry points * + * * + ************************************************************************/ + +/** + * xenProxyGetVersion: + * @conn: pointer to the Xen Daemon block + * @hvVer: return value for the version of the running hypervisor (OUT) + * + * Get the version level of the Hypervisor running. + * + * Returns -1 in case of error, 0 otherwise. if the version can't be + * extracted by lack of capacities returns 0 and @hvVer is 0, otherwise + * @hvVer value is major * 1,000,000 + minor * 1,000 + release + */ +static int +xenProxyGetVersion(virConnectPtr conn, unsigned long *hvVer) +{ + virProxyPacket req; + int ret; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if (hvVer == NULL) { + virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_VERSION; + req.len = sizeof(req); + ret = xenProxyCommand(conn, &req, NULL, 0); + if (ret < 0) { + return(-1); + } + *hvVer = req.data.larg; + return(0); +} + +/** + * xenProxyListDomains: + * @conn: pointer to the hypervisor connection + * @ids: array to collect the list of IDs of active domains + * @maxids: size of @ids + * + * Collect the list of active domains, and store their ID in @maxids + * + * Returns the number of domain found or -1 in case of error + */ +int +xenProxyListDomains(virConnectPtr conn, int *ids, int maxids) +{ + virProxyPacket req; + virProxyFullPacket ans; + int ret; + int nb; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if ((ids == NULL) || (maxids <= 0)) { + virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_LIST; + req.len = sizeof(req); + ret = xenProxyCommand(conn, &req, &ans, 0); + if (ret < 0) { + return(-1); + } + nb = ans.data.arg; + if ((nb > 1020) || (nb <= 0) || + (ans.len <= sizeof(virProxyPacket)) || + (ans.len > sizeof(virProxyFullPacket))) { + virProxyError(conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); + return(-1); + } + if (nb > maxids) + nb = maxids; + memmove(ids, &ans.extra.arg[0], nb * sizeof(int)); + + return(nb); +} + +/** + * xenProxyNumOfDomains: + * @conn: pointer to the hypervisor connection + * + * Provides the number of active domains. + * + * Returns the number of domain found or -1 in case of error + */ +static int +xenProxyNumOfDomains(virConnectPtr conn) +{ + virProxyPacket req; + int ret; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_NUM_DOMAIN; + req.len = sizeof(req); + ret = xenProxyCommand(conn, &req, NULL, 0); + if (ret < 0) { + return(-1); + } + return(req.data.arg); +} + + +/** + * xenProxyDomainGetDomMaxMemory: + * @conn: pointer to the hypervisor connection + * @id: the domain ID number + * + * Ask the Xen Daemon for the maximum memory allowed for a domain + * + * Returns the memory size in kilobytes or 0 in case of error. + */ +static unsigned long +xenProxyDomainGetDomMaxMemory(virConnectPtr conn, int id) +{ + virProxyPacket req; + int ret; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (0); + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_MAX_MEMORY; + req.data.arg = id; + req.len = sizeof(req); + ret = xenProxyCommand(conn, &req, NULL, 0); + if (ret < 0) { + return(0); + } + return(req.data.larg); +} + +/** + * xenProxyDomainGetMaxMemory: + * @domain: pointer to the domain block + * + * Ask the Xen Daemon for the maximum memory allowed for a domain + * + * Returns the memory size in kilobytes or 0 in case of error. + */ +static unsigned long +xenProxyDomainGetMaxMemory(virDomainPtr domain) +{ + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + if (domain == NULL) + virProxyError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + else + virProxyError(domain->conn, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (0); + } + if (domain->id < 0) + return (0); + return(xenProxyDomainGetDomMaxMemory(domain->conn, domain->id)); +} + +/** + * xenProxyDomainGetInfo: + * @domain: a domain object + * @info: pointer to a virDomainInfo structure allocated by the user + * + * This method looks up information about a domain and update the + * information block provided. + * + * Returns 0 in case of success, -1 in case of error + */ +static int +xenProxyDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) +{ + virProxyPacket req; + virProxyFullPacket ans; + int ret; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + if (domain == NULL) + virProxyError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + else + virProxyError(domain->conn, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (-1); + } + if (domain->id < 0) + return (-1); + if (info == NULL) { + virProxyError(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_DOMAIN_INFO; + req.data.arg = domain->id; + req.len = sizeof(req); + ret = xenProxyCommand(domain->conn, &req, &ans, 0); + if (ret < 0) { + return(-1); + } + if (ans.len != sizeof(virProxyPacket) + sizeof(virDomainInfo)) { + virProxyError(domain->conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); + return (-1); + } + memmove(info, &ans.extra.dinfo, sizeof(virDomainInfo)); + + return(0); +} + +/** + * xenProxyLookupByID: + * @conn: pointer to the hypervisor connection + * @id: the domain ID number + * + * Try to find a domain based on the hypervisor ID number + * + * Returns a new domain object or NULL in case of failure + */ +virDomainPtr +xenProxyLookupByID(virConnectPtr conn, int id) +{ + virProxyPacket req; + virProxyFullPacket ans; + unsigned char uuid[VIR_UUID_BUFLEN]; + const char *name; + int ret; + virDomainPtr res; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (id < 0) { + virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_LOOKUP_ID; + req.data.arg = id; + req.len = sizeof(req); + ret = xenProxyCommand(conn, &req, &ans, 0); + if (ret < 0) { + return(NULL); + } + if (ans.data.arg == -1) { + return(NULL); + } + memcpy(uuid, &ans.extra.str[0], VIR_UUID_BUFLEN); + name = &ans.extra.str[VIR_UUID_BUFLEN]; + res = virGetDomain(conn, name, uuid); + if (res) res->id = id; + return(res); +} + +/** + * xenProxyLookupByUUID: + * @conn: pointer to the hypervisor connection + * @uuid: the raw UUID for the domain + * + * Try to lookup a domain on xend based on its UUID. + * + * Returns a new domain object or NULL in case of failure + */ +virDomainPtr +xenProxyLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + virProxyFullPacket req; + const char *name; + int ret; + virDomainPtr res; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuid == NULL) { + virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + memset(&req, 0, sizeof(virProxyPacket)); + req.command = VIR_PROXY_LOOKUP_UUID; + req.len = sizeof(virProxyPacket) + VIR_UUID_BUFLEN; + memcpy(&req.extra.str[0], uuid, VIR_UUID_BUFLEN); + + ret = xenProxyCommand(conn, (virProxyPacketPtr) &req, &req, 0); + if (ret < 0) { + return(NULL); + } + if (req.data.arg == -1) { + return(NULL); + } + name = &req.extra.str[0]; + res = virGetDomain(conn, name, uuid); + if (res) res->id = req.data.arg; + return(res); +} + +/** + * xenProxyLookupByName: + * @conn: A xend instance + * @name: The name of the domain + * + * This method looks up information about a domain based on its name + * + * Returns a new domain object or NULL in case of failure + */ +virDomainPtr +xenProxyLookupByName(virConnectPtr conn, const char *name) +{ + virProxyFullPacket req; + int ret, len; + virDomainPtr res; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (name == NULL) { + virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + len = strlen(name); + if (len > 1000) { + virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + memset(&req, 0, sizeof(virProxyPacket)); + req.command = VIR_PROXY_LOOKUP_NAME; + req.len = sizeof(virProxyPacket) + len + 1; + strcpy(&req.extra.str[0], name); + ret = xenProxyCommand(conn, (virProxyPacketPtr) &req, &req, 0); + if (ret < 0) { + return(NULL); + } + if (req.data.arg == -1) { + return(NULL); + } + res = virGetDomain(conn, name, (const unsigned char *)&req.extra.str[0]); + if (res) res->id = req.data.arg; + return(res); +} + +/** + * xenProxyNodeGetInfo: + * @conn: pointer to the Xen Daemon block + * @info: pointer to a virNodeInfo structure allocated by the user + * + * Extract hardware information about the node. + * + * Returns 0 in case of success and -1 in case of failure. + */ +static int +xenProxyNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info) { + virProxyPacket req; + virProxyFullPacket ans; + int ret; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if (info == NULL) { + virProxyError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_NODE_INFO; + req.data.arg = 0; + req.len = sizeof(req); + ret = xenProxyCommand(conn, &req, &ans, 0); + if (ret < 0) { + return(-1); + } + if (ans.data.arg == -1) { + return(-1); + } + if (ans.len != sizeof(virProxyPacket) + sizeof(virNodeInfo)) { + return(-1); + } + memcpy(info, &ans.extra.ninfo, sizeof(virNodeInfo)); + return(0); +} + +/** + * xenProxyGetCapabilities: + * @conn: pointer to the Xen Daemon block + * + * Extract capabilities of the hypervisor. + * + * Returns capabilities in case of success (freed by caller) + * and NULL in case of failure. + */ +static char * +xenProxyGetCapabilities (virConnectPtr conn) +{ + virProxyPacket req; + virProxyFullPacket ans; + int ret, xmllen; + char *xml; + + if (!VIR_IS_CONNECT(conn)) { + virProxyError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return NULL; + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_GET_CAPABILITIES; + req.data.arg = 0; + req.len = sizeof(req); + ret = xenProxyCommand(conn, &req, &ans, 0); + if (ret < 0) { + return NULL; + } + if (ans.data.arg == -1) + return NULL; + if (ans.len <= sizeof(virProxyPacket)) { + virProxyError(conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); + return NULL; + } + + xmllen = ans.len - sizeof (virProxyPacket); + if (VIR_ALLOC_N(xml, xmllen+1) < 0) { + virReportOOMError (conn); + return NULL; + } + memmove (xml, ans.extra.str, xmllen); + xml[xmllen] = '\0'; + + return xml; +} + +/** + * xenProxyDomainDumpXML: + * @domain: a domain object + * @flags: xml generation flags + * + * This method generates an XML description of a domain. + * + * Returns the XML document on success, NULL otherwise. + */ +char * +xenProxyDomainDumpXML(virDomainPtr domain, int flags ATTRIBUTE_UNUSED) +{ + virProxyPacket req; + virProxyFullPacket ans; + int ret; + int xmllen; + char *xml; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + if (domain == NULL) + virProxyError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + else + virProxyError(domain->conn, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (NULL); + } + if (domain->id < 0) + return (NULL); + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_DOMAIN_XML; + req.data.arg = domain->id; + req.len = sizeof(req); + ret = xenProxyCommand(domain->conn, &req, &ans, 0); + if (ret < 0) { + return(NULL); + } + if (ans.len <= sizeof(virProxyPacket)) { + virProxyError(domain->conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); + return (NULL); + } + xmllen = ans.len - sizeof(virProxyPacket); + if (VIR_ALLOC_N(xml, xmllen+1) < 0) { + virReportOOMError(domain->conn); + return NULL; + } + memmove(xml, &ans.extra.dinfo, xmllen); + xml[xmllen] = '\0'; + + return(xml); +} + +/** + * xenProxyDomainGetOSType: + * @domain: a domain object + * + * Get the type of domain operation system. + * + * Returns the new string or NULL in case of error, the string must be + * freed by the caller. + */ +static char * +xenProxyDomainGetOSType(virDomainPtr domain) +{ + virProxyPacket req; + virProxyFullPacket ans; + int ret; + int oslen; + char *ostype; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + if (domain == NULL) + virProxyError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + else + virProxyError(domain->conn, VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + return (NULL); + } + memset(&req, 0, sizeof(req)); + req.command = VIR_PROXY_DOMAIN_OSTYPE; + req.data.arg = domain->id; + req.len = sizeof(req); + ret = xenProxyCommand(domain->conn, &req, &ans, 0); + if (ret < 0) { + return(NULL); + } + if ((ans.len == sizeof(virProxyPacket)) && (ans.data.arg < 0)) { + return(NULL); + } + + if (ans.len <= sizeof(virProxyPacket)) { + virProxyError(domain->conn, VIR_ERR_OPERATION_FAILED, __FUNCTION__); + return (NULL); + } + oslen = ans.len - sizeof(virProxyPacket); + if (VIR_ALLOC_N(ostype, oslen+1) < 0) { + virReportOOMError(domain->conn); + return NULL; + } + memmove(ostype, &ans.extra.dinfo, oslen); + ostype[oslen] = '\0'; + + return(ostype); +} diff --git a/src/xen/proxy_internal.h b/src/xen/proxy_internal.h new file mode 100644 index 0000000000..185fa674c9 --- /dev/null +++ b/src/xen/proxy_internal.h @@ -0,0 +1,99 @@ +/* + * proxy.h: common definitions for proxy usage + * + * Copyright (C) 2006 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Veillard + */ + + +#ifndef __LIBVIR_PROXY_H__ +#define __LIBVIR_PROXY_H__ + +#include "internal.h" + +#define PROXY_SOCKET_PATH "/tmp/livirt_proxy_conn" +#define PROXY_PROTO_VERSION 1 + +/* + * the command allowed though the proxy + */ +typedef enum { + VIR_PROXY_NONE = 0, + VIR_PROXY_VERSION = 1, + VIR_PROXY_NODE_INFO = 2, + VIR_PROXY_LIST = 3, + VIR_PROXY_NUM_DOMAIN = 4, + VIR_PROXY_LOOKUP_ID = 5, + VIR_PROXY_LOOKUP_UUID = 6, + VIR_PROXY_LOOKUP_NAME = 7, + VIR_PROXY_MAX_MEMORY = 8, + VIR_PROXY_DOMAIN_INFO = 9, + VIR_PROXY_DOMAIN_XML = 10, + VIR_PROXY_DOMAIN_OSTYPE = 11, + VIR_PROXY_GET_CAPABILITIES = 12 +} virProxyCommand; + +/* + * structure used by the client to make a request to the proxy + * and by the proxy when answering the client. + * the size may not be fixed, it's passed as len. + */ +struct _virProxyPacket { + unsigned short version; /* version of the proxy protocol */ + unsigned short command; /* command number a virProxyCommand */ + unsigned short serial; /* command serial number */ + unsigned short len; /* the length of the request */ + union { + char string[8]; /* string data */ + int arg; /* or int argument */ + long larg; /* or long argument */ + } data; +}; +typedef struct _virProxyPacket virProxyPacket; +typedef virProxyPacket *virProxyPacketPtr; + +/* + * If there is extra data sent from the proxy to the client, + * they are appended after the packet. + * the size may not be fixed, it's passed as len and includes the + * extra data. + */ +struct _virProxyFullPacket { + unsigned short version; /* version of the proxy protocol */ + unsigned short command; /* command number a virProxyCommand */ + unsigned short serial; /* command serial number */ + unsigned short len; /* the length of the request */ + union { + char string[8]; /* string data */ + int arg; /* or int argument */ + long larg; /* or long argument */ + } data; + /* that should be aligned on a 16bytes boundary */ + union { + char str[4080]; /* extra char array */ + int arg[1020]; /* extra int array */ + virDomainInfo dinfo; /* domain information */ + virNodeInfo ninfo; /* node information */ + } extra; +}; +typedef struct _virProxyFullPacket virProxyFullPacket; +typedef virProxyFullPacket *virProxyFullPacketPtr; + +/* xen_unified makes direct calls or indirect calls through here. */ +extern struct xenUnifiedDriver xenProxyDriver; +extern int xenProxyInit (void); + +extern virDomainPtr xenProxyLookupByID(virConnectPtr conn, int id); +extern virDomainPtr xenProxyLookupByUUID(virConnectPtr conn, + const unsigned char *uuid); +extern virDomainPtr xenProxyLookupByName(virConnectPtr conn, + const char *domname); + +extern char * xenProxyDomainDumpXML(virDomainPtr domain, + int flags); +extern int xenProxyListDomains(virConnectPtr conn, int *ids, + int maxids); +#endif /* __LIBVIR_PROXY_H__ */ diff --git a/src/xen/sexpr.c b/src/xen/sexpr.c new file mode 100644 index 0000000000..81cb49f269 --- /dev/null +++ b/src/xen/sexpr.c @@ -0,0 +1,577 @@ +/* + * sexpr.c : S-Expression routines to communicate with the Xen Daemon + * + * Copyright (C) 2005 + * + * Anthony Liguori + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.LIB in the main directory of this + * archive for more details. + */ + +#include + +#include +#include +#include +#include "c-ctype.h" +#include + +#include "virterror_internal.h" +#include "sexpr.h" +#include "util.h" +#include "memory.h" + +#define VIR_FROM_THIS VIR_FROM_SEXPR + +#define virSexprError(code, fmt...) \ + virReportErrorHelper(NULL, VIR_FROM_SEXPR, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +/** + * sexpr_new: + * + * Create a new S-Expression + * + * Returns the new node or NULL in case of memory allocation error + */ +static struct sexpr * +sexpr_new(void) +{ + struct sexpr *ret; + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(NULL); + return (NULL); + } + ret->kind = SEXPR_NIL; + return ret; +} + +/** + * sexpr_free: + * @sexpr: the S-Expression pointer + * + * Free an S-Expression + */ +void +sexpr_free(struct sexpr *sexpr) +{ + int serrno = errno; + + if (sexpr == NULL) { + return; + } + + switch (sexpr->kind) { + case SEXPR_CONS: + sexpr_free(sexpr->u.s.car); + sexpr_free(sexpr->u.s.cdr); + break; + case SEXPR_VALUE: + VIR_FREE(sexpr->u.value); + break; + case SEXPR_NIL: + break; + } + + VIR_FREE(sexpr); + + errno = serrno; +} + +/** + * sexpr_nil: + * + * Provide a NIL S-Expression (the pointer is not shared so NIL equality + * testing won't work at the pointer level). + * + * Returns a new NIL S-Expression of NULL in case of error. + */ +struct sexpr * +sexpr_nil(void) +{ + return sexpr_new(); +} + +/** + * sexpr_string: + * @str: the input string, assumed to be UTF-8 + * @len: the length in bytes of the input + * + * Parse the input S-Expression and return a pointer to the result + * + * Returns the S-Expression pointer or NULL in case of error + */ +struct sexpr * +sexpr_string(const char *str, ssize_t len) +{ + struct sexpr *ret = sexpr_new(); + + if (ret == NULL) + return ret; + ret->kind = SEXPR_VALUE; + if (len > 0) { + ret->u.value = strndup(str, len); + } else { + ret->u.value = strdup(str); + } + + if (ret->u.value == NULL) { + return NULL; + } + + return ret; +} + +/** + * sexpr_cons: + * @car: the left operand + * @cdr: the right operand + * + * Implement the CONS operation assembling 2 existing S-Expressions. + * Note that in case of error the input data are not freed. + * + * Returns the resulting S-Expression pointer or NULL in case of error. + */ +struct sexpr * +sexpr_cons(const struct sexpr *car, const struct sexpr *cdr) +{ + struct sexpr *ret = sexpr_new(); + + if (ret == NULL) + return ret; + ret->kind = SEXPR_CONS; + ret->u.s.car = (struct sexpr *) car; + ret->u.s.cdr = (struct sexpr *) cdr; + + return ret; +} + +/** + * append: + * @lst: an existing list + * @value: the value + * + * Internal operation appending a value at the end of an existing list + */ +static int +append(struct sexpr *lst, const struct sexpr *value) +{ + struct sexpr *nil = sexpr_nil(); + + if (nil == NULL) + return -1; + + while (lst->kind != SEXPR_NIL) { + lst = lst->u.s.cdr; + } + + lst->kind = SEXPR_CONS; + lst->u.s.car = (struct sexpr *) value; + lst->u.s.cdr = nil; + + return 0; +} + +/** + * @lst: an existing list + * @value: the value + * + * Append a value at the end of an existing list + * + * Returns lst or NULL in case of error + */ +struct sexpr * +sexpr_append(struct sexpr *lst, const struct sexpr *value) +{ + if (lst == NULL) + return (NULL); + if (value == NULL) + return (lst); + if (append(lst, value) < 0) + return (NULL); + return (lst); +} + +/** + * sexpr2string: + * @sexpr: an S-Expression pointer + * @buffer: the output buffer + * @n_buffer: the size of the buffer in bytes + * + * Serialize the S-Expression in the buffer. + * Note that the output may be truncated if @n_buffer is too small + * resulting in an unparseable value. + * + * Returns the number of bytes used by the serialization in the buffer or + * 0 in case of error. + */ +size_t +sexpr2string(const struct sexpr * sexpr, char *buffer, size_t n_buffer) +{ + size_t ret = 0, tmp; + + if ((sexpr == NULL) || (buffer == NULL) || (n_buffer <= 0)) + return (0); + + switch (sexpr->kind) { + case SEXPR_CONS: + tmp = snprintf(buffer + ret, n_buffer - ret, "("); + if (tmp == 0) + goto error; + ret += tmp; + tmp = sexpr2string(sexpr->u.s.car, buffer + ret, n_buffer - ret); + if (tmp == 0) + goto error; + ret += tmp; + while (sexpr->u.s.cdr->kind != SEXPR_NIL) { + sexpr = sexpr->u.s.cdr; + tmp = snprintf(buffer + ret, n_buffer - ret, " "); + if (tmp == 0) + goto error; + ret += tmp; + tmp = + sexpr2string(sexpr->u.s.car, buffer + ret, n_buffer - ret); + if (tmp == 0) + goto error; + ret += tmp; + } + tmp = snprintf(buffer + ret, n_buffer - ret, ")"); + if (tmp == 0) + goto error; + ret += tmp; + break; + case SEXPR_VALUE: + if (strchr(sexpr->u.value, ' ')) + tmp = snprintf(buffer + ret, n_buffer - ret, "'%s'", + sexpr->u.value); + else + tmp = snprintf(buffer + ret, n_buffer - ret, "%s", + sexpr->u.value); + if (tmp == 0) + goto error; + ret += tmp; + break; + case SEXPR_NIL: + tmp = snprintf(buffer + ret, n_buffer - ret, "()"); + if (tmp == 0) + goto error; + ret += tmp; + break; + default: + goto error; + } + + return (ret); + error: + buffer[n_buffer - 1] = 0; + virSexprError(VIR_ERR_SEXPR_SERIAL, "%s", buffer); + return (0); +} + +#define IS_SPACE(c) ((c == 0x20) || (c == 0x9) || (c == 0xD) || (c == 0xA)) + +static const char * +trim(const char *string) +{ + while (IS_SPACE(*string)) + string++; + return (string); +} + +/** + * _string2sexpr: + * @buffer: a zero terminated buffer containing an S-Expression in UTF-8 + * @end: pointer to an index in the buffer for the already parsed bytes + * + * Internal routine implementing the parse of S-Expression + * Note that failure in this function is catastrophic. If it returns + * NULL, you've leaked memory and you're currently OOM. It will always + * parse an SEXPR given a buffer + * + * Returns a pointer to the resulting parsed S-Expression, or NULL in case of + * hard error. + */ +static struct sexpr * +_string2sexpr(const char *buffer, size_t * end) +{ + const char *ptr = buffer + *end; + struct sexpr *ret = sexpr_new(); + + if (ret == NULL) + return NULL; + + ptr = trim(ptr); + + if (ptr[0] == '(') { + ret->kind = SEXPR_NIL; + + ptr = trim(ptr + 1); + while (*ptr && *ptr != ')') { + struct sexpr *tmp; + size_t tmp_len = 0; + + tmp = _string2sexpr(ptr, &tmp_len); + if (tmp == NULL) + goto error; + if (append(ret, tmp) < 0) { + sexpr_free(tmp); + goto error; + } +#if 0 + if (0) { + char buf[4096]; + + sexpr2string(ret, buf, sizeof(buf)); + printf("%s\n", buffer); + } +#endif + ptr = trim(ptr + tmp_len); + } + + if (*ptr == ')') { + ptr++; + } + } else { + const char *start; + + if (*ptr == '\'') { + ptr++; + start = ptr; + + while (*ptr && *ptr != '\'') { + if (*ptr == '\\' && ptr[1]) + ptr++; + ptr++; + } + + ret->u.value = strndup(start, ptr - start); + if (ret->u.value == NULL) { + virReportOOMError(NULL); + goto error; + } + + if (*ptr == '\'') + ptr++; + } else { + start = ptr; + + while (*ptr && !c_isspace(*ptr) + && *ptr != ')' && *ptr != '(') { + ptr++; + } + + ret->u.value = strndup(start, ptr - start); + if (ret->u.value == NULL) { + virReportOOMError(NULL); + goto error; + } + } + + ret->kind = SEXPR_VALUE; + if (ret->u.value == NULL) + goto error; + } + + *end = ptr - buffer; + + return ret; + + error: + sexpr_free(ret); + return (NULL); +} + +/** + * string2sexpr: + * @buffer: a zero terminated buffer containing an S-Expression in UTF-8 + * + * Parse the S-Expression in the buffer. + * Note that failure in this function is catastrophic. If it returns + * NULL, you've leaked memory and you're currently OOM. It will always + * parse an SEXPR given a buffer + * + * Returns a pointer to the resulting parsed S-Expression, or NULL in case of + * hard error. + */ +struct sexpr * +string2sexpr(const char *buffer) +{ + size_t dummy = 0; + + return _string2sexpr(buffer, &dummy); +} + + +/** + * sexpr_lookup_key: + * @sexpr: a pointer to a parsed S-Expression + * @node: a path for the sub expression to lookup in the S-Expression + * + * Search a sub expression in the S-Expression based on its path + * Returns the key node, rather than the data node. + * NOTE: path are limited to 4096 bytes. + * + * Returns the pointer to the sub expression or NULL if not found. + */ +static struct sexpr * +sexpr_lookup_key(const struct sexpr *sexpr, const char *node) +{ + char buffer[4096], *ptr, *token; + + if ((node == NULL) || (sexpr == NULL)) + return (NULL); + + snprintf(buffer, sizeof(buffer), "%s", node); + + ptr = buffer; + token = strsep(&ptr, "/"); + + if (sexpr->kind != SEXPR_CONS || sexpr->u.s.car->kind != SEXPR_VALUE) { + return NULL; + } + + if (STRNEQ(sexpr->u.s.car->u.value, token)) { + return NULL; + } + + for (token = strsep(&ptr, "/"); token; token = strsep(&ptr, "/")) { + const struct sexpr *i; + + if (token == NULL) + continue; + + sexpr = sexpr->u.s.cdr; + for (i = sexpr; i->kind != SEXPR_NIL; i = i->u.s.cdr) { + if (i->kind != SEXPR_CONS || + i->u.s.car->kind != SEXPR_CONS || + i->u.s.car->u.s.car->kind != SEXPR_VALUE) { + continue; + } + + if (STREQ(i->u.s.car->u.s.car->u.value, token)) { + sexpr = i->u.s.car; + break; + } + } + + if (i->kind == SEXPR_NIL) { + break; + } + } + + if (token != NULL) { + return NULL; + } + + return (struct sexpr *) sexpr; +} + +/** + * sexpr_lookup: + * @sexpr: a pointer to a parsed S-Expression + * @node: a path for the sub expression to lookup in the S-Expression + * + * Search a sub expression in the S-Expression based on its path. + * NOTE: path are limited to 4096 bytes. + * + * Returns the pointer to the sub expression or NULL if not found. + */ +struct sexpr * +sexpr_lookup(const struct sexpr *sexpr, const char *node) +{ + struct sexpr *s = sexpr_lookup_key(sexpr, node); + + if (s == NULL) + return NULL; + + if (s->kind != SEXPR_CONS || s->u.s.cdr->kind != SEXPR_CONS) + return NULL; + + return s->u.s.cdr; +} + +/** + * sexpr_has: + * @sexpr: a pointer to a parsed S-Expression + * @node: a path for the sub expression to lookup in the S-Expression + * + * Search a sub expression in the S-Expression based on its path. + * NOTE: path are limited to 4096 bytes. + * NB, even if the key was found sexpr_lookup may return NULL if + * the corresponding value was empty + * + * Returns true if the key was found, false otherwise + */ +int +sexpr_has(const struct sexpr *sexpr, const char *node) +{ + struct sexpr *s = sexpr_lookup_key(sexpr, node); + + if (s == NULL) + return 0; + + if (s->kind != SEXPR_CONS) + return 0; + + return 1; +} + +/** + * sexpr_node: + * @sexpr: a pointer to a parsed S-Expression + * @node: a path for the node to lookup in the S-Expression + * + * Search a node value in the S-Expression based on its path + * NOTE: path are limited to 4096 bytes. + * + * Returns the value of the node or NULL if not found. + */ +const char * +sexpr_node(const struct sexpr *sexpr, const char *node) +{ + struct sexpr *n = sexpr_lookup(sexpr, node); + + return (n && n->u.s.car->kind == SEXPR_VALUE) ? n->u.s.car->u.value : NULL; +} + +int sexpr_node_copy(const struct sexpr *sexpr, const char *node, char **dst) +{ + const char *val = sexpr_node(sexpr, node); + + if (val) { + *dst = strdup(val); + if (!(*dst)) + return -1; + } else { + *dst = NULL; + } + return 0; +} + + +/** + * sexpr_fmt_node: + * @sexpr: a pointer to a parsed S-Expression + * @fmt: a path for the node to lookup in the S-Expression + * @... extra data to build the path + * + * Search a node value in the S-Expression based on its path + * NOTE: path are limited to 4096 bytes. + * + * Returns the value of the node or NULL if not found. + */ +const char * +sexpr_fmt_node(const struct sexpr *sexpr, const char *fmt, ...) +{ + va_list ap; + char node[4096]; + + va_start(ap, fmt); + vsnprintf(node, sizeof(node), fmt, ap); + va_end(ap); + + return sexpr_node(sexpr, node); +} diff --git a/src/xen/sexpr.h b/src/xen/sexpr.h new file mode 100644 index 0000000000..1d9adaa592 --- /dev/null +++ b/src/xen/sexpr.h @@ -0,0 +1,55 @@ +/* + * sexpr.h : S-Expression interfaces needed to communicate with the Xen Daemon + * + * Copyright (C) 2005 + * + * Anthony Liguori + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.LIB in the main directory of this + * archive for more details. + */ + +#ifndef _LIBVIR_SEXPR_H_ +#define _LIBVIR_SEXPR_H_ + +#include "internal.h" + +#include + +enum sexpr_type { + SEXPR_NIL, + SEXPR_CONS, + SEXPR_VALUE, +}; + +struct sexpr { + enum sexpr_type kind; + union { + struct { + struct sexpr *car; + struct sexpr *cdr; + } s; + char *value; + } u; +}; + +/* conversion to/from strings */ +size_t sexpr2string(const struct sexpr *sexpr, char *buffer, size_t n_buffer); +struct sexpr *string2sexpr(const char *buffer); + +/* constructors and destructors */ +struct sexpr *sexpr_nil(void); +struct sexpr *sexpr_string(const char *str, ssize_t len); +struct sexpr *sexpr_cons(const struct sexpr *car, const struct sexpr *cdr); +struct sexpr *sexpr_append(struct sexpr *lst, const struct sexpr *item); +void sexpr_free(struct sexpr *sexpr); + +/* lookup in S-Expressions */ +const char *sexpr_node(const struct sexpr *sexpr, const char *node); +int sexpr_node_copy(const struct sexpr *sexpr, const char *node, char **dst); +const char *sexpr_fmt_node(const struct sexpr *sexpr, const char *fmt, ...) + ATTRIBUTE_FMT_PRINTF(2,3); +struct sexpr *sexpr_lookup(const struct sexpr *sexpr, const char *node); +int sexpr_has(const struct sexpr *sexpr, const char *node); +#endif diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c new file mode 100644 index 0000000000..3dd023756d --- /dev/null +++ b/src/xen/xen_driver.c @@ -0,0 +1,1917 @@ +/* + * xen_driver.c: Unified Xen driver. + * + * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Richard W.M. Jones + */ + +#include + +/* Note: + * + * This driver provides a unified interface to the five + * separate underlying Xen drivers (xen_internal, proxy_internal, + * xend_internal, xs_internal and xm_internal). Historically + * the body of libvirt.c handled the five Xen drivers, + * and contained Xen-specific code. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "virterror_internal.h" +#include "logging.h" +#include "datatypes.h" +#include "xen_driver.h" + +#include "xen_hypervisor.h" +#include "proxy_internal.h" +#include "xend_internal.h" +#include "xs_internal.h" +#include "xm_internal.h" +#if WITH_XEN_INOTIFY +#include "xen_inotify.h" +#endif +#include "xml.h" +#include "util.h" +#include "memory.h" +#include "node_device_conf.h" +#include "pci.h" + +#define VIR_FROM_THIS VIR_FROM_XEN + +static int +xenUnifiedNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info); +static int +xenUnifiedDomainGetMaxVcpus (virDomainPtr dom); +static int +xenUnifiedDomainGetVcpus (virDomainPtr dom, + virVcpuInfoPtr info, int maxinfo, + unsigned char *cpumaps, int maplen); + +/* The five Xen drivers below us. */ +static struct xenUnifiedDriver const * const drivers[XEN_UNIFIED_NR_DRIVERS] = { + [XEN_UNIFIED_HYPERVISOR_OFFSET] = &xenHypervisorDriver, + [XEN_UNIFIED_PROXY_OFFSET] = &xenProxyDriver, + [XEN_UNIFIED_XEND_OFFSET] = &xenDaemonDriver, + [XEN_UNIFIED_XS_OFFSET] = &xenStoreDriver, + [XEN_UNIFIED_XM_OFFSET] = &xenXMDriver, +#if WITH_XEN_INOTIFY + [XEN_UNIFIED_INOTIFY_OFFSET] = &xenInotifyDriver, +#endif +}; + +static int inside_daemon; + +#define xenUnifiedError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_XEN, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +/** + * xenNumaInit: + * @conn: pointer to the hypervisor connection + * + * Initializer for previous variables. We currently assume that + * the number of physical CPU and the number of NUMA cell is fixed + * until reboot which might be false in future Xen implementations. + */ +static void +xenNumaInit(virConnectPtr conn) { + virNodeInfo nodeInfo; + xenUnifiedPrivatePtr priv; + int ret; + + ret = xenUnifiedNodeGetInfo(conn, &nodeInfo); + if (ret < 0) + return; + + priv = conn->privateData; + + priv->nbNodeCells = nodeInfo.nodes; + priv->nbNodeCpus = nodeInfo.cpus; +} + + +/** + * xenDomainUsedCpus: + * @dom: the domain + * + * Analyze which set of CPUs are used by the domain and + * return a string providing the ranges. + * + * Returns the string which needs to be freed by the caller or + * NULL if the domain uses all CPU or in case of error. + */ +char * +xenDomainUsedCpus(virDomainPtr dom) +{ + char *res = NULL; + int ncpus; + int nb_vcpu; + char *cpulist = NULL; + unsigned char *cpumap = NULL; + size_t cpumaplen; + int nb = 0; + int n, m; + virVcpuInfoPtr cpuinfo = NULL; + virNodeInfo nodeinfo; + xenUnifiedPrivatePtr priv; + + if (!VIR_IS_CONNECTED_DOMAIN(dom)) + return (NULL); + + priv = dom->conn->privateData; + + if (priv->nbNodeCpus <= 0) + return(NULL); + nb_vcpu = xenUnifiedDomainGetMaxVcpus(dom); + if (nb_vcpu <= 0) + return(NULL); + if (xenUnifiedNodeGetInfo(dom->conn, &nodeinfo) < 0) + return(NULL); + + if (VIR_ALLOC_N(cpulist, priv->nbNodeCpus) < 0) + goto done; + if (VIR_ALLOC_N(cpuinfo, nb_vcpu) < 0) + goto done; + cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo)); + if (xalloc_oversized(nb_vcpu, cpumaplen) || + VIR_ALLOC_N(cpumap, nb_vcpu * cpumaplen) < 0) + goto done; + + if ((ncpus = xenUnifiedDomainGetVcpus(dom, cpuinfo, nb_vcpu, + cpumap, cpumaplen)) >= 0) { + for (n = 0 ; n < ncpus ; n++) { + for (m = 0 ; m < priv->nbNodeCpus; m++) { + if ((cpulist[m] == 0) && + (VIR_CPU_USABLE(cpumap, cpumaplen, n, m))) { + cpulist[m] = 1; + nb++; + /* if all CPU are used just return NULL */ + if (nb == priv->nbNodeCpus) + goto done; + + } + } + } + res = virDomainCpuSetFormat(dom->conn, cpulist, priv->nbNodeCpus); + } + +done: + VIR_FREE(cpulist); + VIR_FREE(cpumap); + VIR_FREE(cpuinfo); + return(res); +} + +#ifdef WITH_LIBVIRTD + +static int +xenInitialize (int privileged ATTRIBUTE_UNUSED) +{ + inside_daemon = 1; + return 0; +} + +static virStateDriver state_driver = { + .initialize = xenInitialize, +}; + +#endif + +/*----- Dispatch functions. -----*/ + +/* These dispatch functions follow the model used historically + * by libvirt.c -- trying each low-level Xen driver in turn + * until one succeeds. However since we know what low-level + * drivers can perform which functions, it is probably better + * in future to optimise these dispatch functions to just call + * the single function (or small number of appropriate functions) + * in the low level drivers directly. + */ + +static int +xenUnifiedProbe (void) +{ +#ifdef __linux__ + if (virFileExists("/proc/xen")) + return 1; +#endif +#ifdef __sun + FILE *fh; + + if (fh = fopen("/dev/xen/domcaps", "r")) { + fclose(fh); + return 1; + } +#endif + return 0; +} + +static virDrvOpenStatus +xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags) +{ + int i, ret = VIR_DRV_OPEN_DECLINED; + xenUnifiedPrivatePtr priv; + virDomainEventCallbackListPtr cbList; + +#ifdef __sun + /* + * Only the libvirtd instance can open this driver. + * Everything else falls back to the remote driver. + */ + if (!inside_daemon) + return VIR_DRV_OPEN_DECLINED; +#endif + + if (conn->uri == NULL) { + if (!xenUnifiedProbe()) + return VIR_DRV_OPEN_DECLINED; + + conn->uri = xmlParseURI("xen:///"); + if (!conn->uri) { + virReportOOMError (NULL); + return VIR_DRV_OPEN_ERROR; + } + } else { + if (conn->uri->scheme) { + /* Decline any scheme which isn't "xen://" or "http://". */ + if (STRCASENEQ(conn->uri->scheme, "xen") && + STRCASENEQ(conn->uri->scheme, "http")) + return VIR_DRV_OPEN_DECLINED; + + + /* Return an error if the path isn't '' or '/' */ + if (conn->uri->path && + STRNEQ(conn->uri->path, "") && + STRNEQ(conn->uri->path, "/")) { + xenUnifiedError(NULL, VIR_ERR_INTERNAL_ERROR, + _("unexpected Xen URI path '%s', try xen:///"), + conn->uri->path); + return VIR_DRV_OPEN_ERROR; + } + + /* Decline any xen:// URI with a server specified, allowing remote + * driver to handle, but keep any http:/// URIs */ + if (STRCASEEQ(conn->uri->scheme, "xen") && + conn->uri->server) + return VIR_DRV_OPEN_DECLINED; + } else { + /* Special case URI for Xen driver only: + * + * Treat a plain path as a Xen UNIX socket path, and give + * error unless path is absolute + */ + if (!conn->uri->path || conn->uri->path[0] != '/') { + xenUnifiedError(NULL, VIR_ERR_INTERNAL_ERROR, + _("unexpected Xen URI path '%s', try ///var/lib/xen/xend-socket"), + NULLSTR(conn->uri->path)); + return VIR_DRV_OPEN_ERROR; + } + } + } + + /* We now know the URI is definitely for this driver, so beyond + * here, don't return DECLINED, always use ERROR */ + + /* Allocate per-connection private data. */ + if (VIR_ALLOC(priv) < 0) { + virReportOOMError (NULL); + return VIR_DRV_OPEN_ERROR; + } + if (virMutexInit(&priv->lock) < 0) { + xenUnifiedError (NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialise mutex")); + VIR_FREE(priv); + return VIR_DRV_OPEN_ERROR; + } + + /* Allocate callback list */ + if (VIR_ALLOC(cbList) < 0) { + virReportOOMError (NULL); + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + return VIR_DRV_OPEN_ERROR; + } + conn->privateData = priv; + + priv->domainEventCallbacks = cbList; + + priv->handle = -1; + priv->xendConfigVersion = -1; + priv->xshandle = NULL; + priv->proxy = -1; + + + /* Hypervisor is only run with privilege & required to succeed */ + if (xenHavePrivilege()) { + DEBUG0("Trying hypervisor sub-driver"); + if (drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->open(conn, auth, flags) == + VIR_DRV_OPEN_SUCCESS) { + DEBUG0("Activated hypervisor sub-driver"); + priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] = 1; + } + } + + /* XenD is required to succeed if privileged. + * If it fails as non-root, then the proxy driver may take over + */ + DEBUG0("Trying XenD sub-driver"); + if (drivers[XEN_UNIFIED_XEND_OFFSET]->open(conn, auth, flags) == + VIR_DRV_OPEN_SUCCESS) { + DEBUG0("Activated XenD sub-driver"); + priv->opened[XEN_UNIFIED_XEND_OFFSET] = 1; + + /* XenD is active, so try the xm & xs drivers too, both requird to + * succeed if root, optional otherwise */ + if (priv->xendConfigVersion <= 2) { + DEBUG0("Trying XM sub-driver"); + if (drivers[XEN_UNIFIED_XM_OFFSET]->open(conn, auth, flags) == + VIR_DRV_OPEN_SUCCESS) { + DEBUG0("Activated XM sub-driver"); + priv->opened[XEN_UNIFIED_XM_OFFSET] = 1; + } + } + DEBUG0("Trying XS sub-driver"); + if (drivers[XEN_UNIFIED_XS_OFFSET]->open(conn, auth, flags) == + VIR_DRV_OPEN_SUCCESS) { + DEBUG0("Activated XS sub-driver"); + priv->opened[XEN_UNIFIED_XS_OFFSET] = 1; + } else { + if (xenHavePrivilege()) + goto fail; /* XS is mandatory when privileged */ + } + } else { + if (xenHavePrivilege()) { + goto fail; /* XenD is mandatory when privileged */ + } else { +#if WITH_PROXY + DEBUG0("Trying proxy sub-driver"); + if (drivers[XEN_UNIFIED_PROXY_OFFSET]->open(conn, auth, flags) == + VIR_DRV_OPEN_SUCCESS) { + DEBUG0("Activated proxy sub-driver"); + priv->opened[XEN_UNIFIED_PROXY_OFFSET] = 1; + } else { + goto fail; /* Proxy is mandatory if XenD failed */ + } +#else + DEBUG0("Handing off for remote driver"); + ret = VIR_DRV_OPEN_DECLINED; /* Let remote_driver try instead */ + goto clean; +#endif + } + } + + xenNumaInit(conn); + + if (!(priv->caps = xenHypervisorMakeCapabilities(conn))) { + DEBUG0("Failed to make capabilities"); + goto fail; + } + +#if WITH_XEN_INOTIFY + if (xenHavePrivilege()) { + DEBUG0("Trying Xen inotify sub-driver"); + if (drivers[XEN_UNIFIED_INOTIFY_OFFSET]->open(conn, auth, flags) == + VIR_DRV_OPEN_SUCCESS) { + DEBUG0("Activated Xen inotify sub-driver"); + priv->opened[XEN_UNIFIED_INOTIFY_OFFSET] = 1; + } + } +#endif + + return VIR_DRV_OPEN_SUCCESS; + +fail: + ret = VIR_DRV_OPEN_ERROR; +#ifndef WITH_PROXY +clean: +#endif + DEBUG0("Failed to activate a mandatory sub-driver"); + for (i = 0 ; i < XEN_UNIFIED_NR_DRIVERS ; i++) + if (priv->opened[i]) drivers[i]->close(conn); + virMutexDestroy(&priv->lock); + VIR_FREE(priv); + conn->privateData = NULL; + return ret; +} + +#define GET_PRIVATE(conn) \ + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) (conn)->privateData + +static int +xenUnifiedClose (virConnectPtr conn) +{ + GET_PRIVATE(conn); + int i; + + virCapabilitiesFree(priv->caps); + virDomainEventCallbackListFree(priv->domainEventCallbacks); + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->close) + (void) drivers[i]->close (conn); + + virMutexDestroy(&priv->lock); + VIR_FREE(conn->privateData); + + return 0; +} + + +#define HV_VERSION ((DOM0_INTERFACE_VERSION >> 24) * 1000000 + \ + ((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 + \ + (DOM0_INTERFACE_VERSION & 0xFFFF)) + +unsigned long xenUnifiedVersion(void) +{ + return HV_VERSION; +} + + +static const char * +xenUnifiedType (virConnectPtr conn) +{ + GET_PRIVATE(conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i]) + return "Xen"; + + return NULL; +} + +/* Which features are supported by this driver? */ +static int +xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) +{ + switch (feature) { + case VIR_DRV_FEATURE_MIGRATION_V1: return 1; + default: return 0; + } +} + +static int +xenUnifiedGetVersion (virConnectPtr conn, unsigned long *hvVer) +{ + GET_PRIVATE(conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->version && + drivers[i]->version (conn, hvVer) == 0) + return 0; + + return -1; +} + +/* NB: Even if connected to the proxy, we're still on the + * same machine. + */ +static char * +xenUnifiedGetHostname (virConnectPtr conn) +{ + char *result; + + result = virGetHostname(); + if (result == NULL) { + virReportSystemError(conn, errno, + "%s", _("cannot lookup hostname")); + return NULL; + } + /* Caller frees this string. */ + return result; +} + +static int +xenUnifiedGetMaxVcpus (virConnectPtr conn, const char *type) +{ + GET_PRIVATE(conn); + + if (type && STRCASENEQ (type, "Xen")) { + xenUnifiedError (conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) + return xenHypervisorGetMaxVcpus (conn, type); + else { + xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; + } +} + +static int +xenUnifiedNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info) +{ + GET_PRIVATE(conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->nodeGetInfo && + drivers[i]->nodeGetInfo (conn, info) == 0) + return 0; + + return -1; +} + +static char * +xenUnifiedGetCapabilities (virConnectPtr conn) +{ + xenUnifiedPrivatePtr priv = conn->privateData; + char *xml; + + if (!(xml = virCapabilitiesFormatXML(priv->caps))) { + virReportOOMError(conn); + return NULL; + } + + return xml; +} + +static int +xenUnifiedListDomains (virConnectPtr conn, int *ids, int maxids) +{ + GET_PRIVATE(conn); + int ret; + + /* Try xenstore. */ + if (priv->opened[XEN_UNIFIED_XS_OFFSET]) { + ret = xenStoreListDomains (conn, ids, maxids); + if (ret >= 0) return ret; + } + + /* Try HV. */ + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) { + ret = xenHypervisorListDomains (conn, ids, maxids); + if (ret >= 0) return ret; + } + + /* Try xend. */ + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { + ret = xenDaemonListDomains (conn, ids, maxids); + if (ret >= 0) return ret; + } + + /* Try proxy. */ + if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) { + ret = xenProxyListDomains (conn, ids, maxids); + if (ret >= 0) return ret; + } + return -1; +} + +static int +xenUnifiedNumOfDomains (virConnectPtr conn) +{ + GET_PRIVATE(conn); + int i, ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->numOfDomains) { + ret = drivers[i]->numOfDomains (conn); + if (ret >= 0) return ret; + } + + return -1; +} + +static virDomainPtr +xenUnifiedDomainCreateXML (virConnectPtr conn, + const char *xmlDesc, unsigned int flags) +{ + GET_PRIVATE(conn); + int i; + virDomainPtr ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainCreateXML) { + ret = drivers[i]->domainCreateXML (conn, xmlDesc, flags); + if (ret) return ret; + } + + return NULL; +} + +/* Assumption made in underlying drivers: + * If the domain is "not found" and there is no other error, then + * the Lookup* functions return a NULL but do not set virterror. + */ +static virDomainPtr +xenUnifiedDomainLookupByID (virConnectPtr conn, int id) +{ + GET_PRIVATE(conn); + virDomainPtr ret; + + /* Reset any connection-level errors in virterror first, in case + * there is one hanging around from a previous call. + */ + virConnResetLastError (conn); + + /* Try hypervisor/xenstore combo. */ + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) { + ret = xenHypervisorLookupDomainByID (conn, id); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Try proxy. */ + if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) { + ret = xenProxyLookupByID (conn, id); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Try xend. */ + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { + ret = xenDaemonLookupByID (conn, id); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Not found. */ + xenUnifiedError (conn, VIR_ERR_NO_DOMAIN, __FUNCTION__); + return NULL; +} + +static virDomainPtr +xenUnifiedDomainLookupByUUID (virConnectPtr conn, + const unsigned char *uuid) +{ + GET_PRIVATE(conn); + virDomainPtr ret; + + /* Reset any connection-level errors in virterror first, in case + * there is one hanging around from a previous call. + */ + virConnResetLastError (conn); + + /* Try hypervisor/xenstore combo. */ + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) { + ret = xenHypervisorLookupDomainByUUID (conn, uuid); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Try proxy. */ + if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) { + ret = xenProxyLookupByUUID (conn, uuid); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Try xend. */ + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { + ret = xenDaemonLookupByUUID (conn, uuid); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Try XM for inactive domains. */ + if (priv->opened[XEN_UNIFIED_XM_OFFSET]) { + ret = xenXMDomainLookupByUUID (conn, uuid); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Not found. */ + xenUnifiedError (conn, VIR_ERR_NO_DOMAIN, __FUNCTION__); + return NULL; +} + +static virDomainPtr +xenUnifiedDomainLookupByName (virConnectPtr conn, + const char *name) +{ + GET_PRIVATE(conn); + virDomainPtr ret; + + /* Reset any connection-level errors in virterror first, in case + * there is one hanging around from a previous call. + */ + virConnResetLastError (conn); + + /* Try proxy. */ + if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) { + ret = xenProxyLookupByName (conn, name); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Try xend. */ + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { + ret = xenDaemonLookupByName (conn, name); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Try xenstore for inactive domains. */ + if (priv->opened[XEN_UNIFIED_XS_OFFSET]) { + ret = xenStoreLookupByName (conn, name); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Try XM for inactive domains. */ + if (priv->opened[XEN_UNIFIED_XM_OFFSET]) { + ret = xenXMDomainLookupByName (conn, name); + if (ret || conn->err.code != VIR_ERR_OK) + return ret; + } + + /* Not found. */ + xenUnifiedError (conn, VIR_ERR_NO_DOMAIN, __FUNCTION__); + return NULL; +} + +static int +xenUnifiedDomainSuspend (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i; + + /* Try non-hypervisor methods first, then hypervisor direct method + * as a last resort. + */ + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (i != XEN_UNIFIED_HYPERVISOR_OFFSET && + priv->opened[i] && + drivers[i]->domainSuspend && + drivers[i]->domainSuspend (dom) == 0) + return 0; + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] && + drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainSuspend && + drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainSuspend (dom) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainResume (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i; + + /* Try non-hypervisor methods first, then hypervisor direct method + * as a last resort. + */ + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (i != XEN_UNIFIED_HYPERVISOR_OFFSET && + priv->opened[i] && + drivers[i]->domainResume && + drivers[i]->domainResume (dom) == 0) + return 0; + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] && + drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainResume && + drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainResume (dom) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainShutdown (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->domainShutdown && + drivers[i]->domainShutdown (dom) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainReboot (virDomainPtr dom, unsigned int flags) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->domainReboot && + drivers[i]->domainReboot (dom, flags) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainDestroy (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i; + + /* Try non-hypervisor methods first, then hypervisor direct method + * as a last resort. + */ + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (i != XEN_UNIFIED_HYPERVISOR_OFFSET && + priv->opened[i] && + drivers[i]->domainDestroy && + drivers[i]->domainDestroy (dom) == 0) + return 0; + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] && + drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainDestroy && + drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainDestroy (dom) == 0) + return 0; + + return -1; +} + +static char * +xenUnifiedDomainGetOSType (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i; + char *ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainGetOSType) { + ret = drivers[i]->domainGetOSType (dom); + if (ret) return ret; + } + + return NULL; +} + +static unsigned long +xenUnifiedDomainGetMaxMemory (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i; + unsigned long ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainGetMaxMemory) { + ret = drivers[i]->domainGetMaxMemory (dom); + if (ret != 0) return ret; + } + + return 0; +} + +static int +xenUnifiedDomainSetMaxMemory (virDomainPtr dom, unsigned long memory) +{ + GET_PRIVATE(dom->conn); + int i; + + /* Prefer xend for setting max memory */ + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { + if (xenDaemonDomainSetMaxMemory (dom, memory) == 0) + return 0; + } + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (i != XEN_UNIFIED_XEND_OFFSET && + priv->opened[i] && + drivers[i]->domainSetMaxMemory && + drivers[i]->domainSetMaxMemory (dom, memory) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainSetMemory (virDomainPtr dom, unsigned long memory) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->domainSetMemory && + drivers[i]->domainSetMemory (dom, memory) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainGetInfo (virDomainPtr dom, virDomainInfoPtr info) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->domainGetInfo && + drivers[i]->domainGetInfo (dom, info) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainSave (virDomainPtr dom, const char *to) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->domainSave && + drivers[i]->domainSave (dom, to) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainRestore (virConnectPtr conn, const char *from) +{ + GET_PRIVATE(conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->domainRestore && + drivers[i]->domainRestore (conn, from) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainCoreDump (virDomainPtr dom, const char *to, int flags) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->domainCoreDump && + drivers[i]->domainCoreDump (dom, to, flags) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainSetVcpus (virDomainPtr dom, unsigned int nvcpus) +{ + GET_PRIVATE(dom->conn); + int i; + + /* Try non-hypervisor methods first, then hypervisor direct method + * as a last resort. + */ + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (i != XEN_UNIFIED_HYPERVISOR_OFFSET && + priv->opened[i] && + drivers[i]->domainSetVcpus && + drivers[i]->domainSetVcpus (dom, nvcpus) == 0) + return 0; + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] && + drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainSetVcpus && + drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainSetVcpus (dom, nvcpus) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainPinVcpu (virDomainPtr dom, unsigned int vcpu, + unsigned char *cpumap, int maplen) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && + drivers[i]->domainPinVcpu && + drivers[i]->domainPinVcpu (dom, vcpu, cpumap, maplen) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainGetVcpus (virDomainPtr dom, + virVcpuInfoPtr info, int maxinfo, + unsigned char *cpumaps, int maplen) +{ + GET_PRIVATE(dom->conn); + int i, ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainGetVcpus) { + ret = drivers[i]->domainGetVcpus (dom, info, maxinfo, cpumaps, maplen); + if (ret > 0) + return ret; + } + return -1; +} + +static int +xenUnifiedDomainGetMaxVcpus (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i, ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainGetMaxVcpus) { + ret = drivers[i]->domainGetMaxVcpus (dom); + if (ret != 0) return ret; + } + + return -1; +} + +static char * +xenUnifiedDomainDumpXML (virDomainPtr dom, int flags) +{ + GET_PRIVATE(dom->conn); + + if (dom->id == -1 && priv->xendConfigVersion < 3 ) { + if (priv->opened[XEN_UNIFIED_XM_OFFSET]) + return xenXMDomainDumpXML(dom, flags); + } else { + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { + char *cpus, *res; + xenUnifiedLock(priv); + cpus = xenDomainUsedCpus(dom); + xenUnifiedUnlock(priv); + res = xenDaemonDomainDumpXML(dom, flags, cpus); + VIR_FREE(cpus); + return(res); + } + if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) + return xenProxyDomainDumpXML(dom, flags); + } + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + + +static char * +xenUnifiedDomainXMLFromNative(virConnectPtr conn, + const char *format, + const char *config, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virDomainDefPtr def = NULL; + char *ret = NULL; + virConfPtr conf = NULL; + GET_PRIVATE(conn); + + if (STRNEQ(format, XEN_CONFIG_FORMAT_XM) && + STRNEQ(format, XEN_CONFIG_FORMAT_SEXPR)) { + xenUnifiedError(conn, VIR_ERR_INVALID_ARG, + _("unsupported config type %s"), format); + return NULL; + } + + if (STREQ(format, XEN_CONFIG_FORMAT_XM)) { + conf = virConfReadMem(config, strlen(config), 0); + if (!conf) + goto cleanup; + + def = xenXMDomainConfigParse(conn, conf); + } else if (STREQ(format, XEN_CONFIG_FORMAT_SEXPR)) { + def = xenDaemonParseSxprString(conn, config, priv->xendConfigVersion); + } + if (!def) + goto cleanup; + + ret = virDomainDefFormat(conn, def, 0); + +cleanup: + virDomainDefFree(def); + return ret; +} + + +#define MAX_CONFIG_SIZE (1024 * 65) +static char * +xenUnifiedDomainXMLToNative(virConnectPtr conn, + const char *format, + const char *xmlData, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virDomainDefPtr def = NULL; + char *ret = NULL; + virConfPtr conf = NULL; + GET_PRIVATE(conn); + + if (STRNEQ(format, XEN_CONFIG_FORMAT_XM) && + STRNEQ(format, XEN_CONFIG_FORMAT_SEXPR)) { + xenUnifiedError(conn, VIR_ERR_INVALID_ARG, + _("unsupported config type %s"), format); + goto cleanup; + } + + if (!(def = virDomainDefParseString(conn, + priv->caps, + xmlData, + 0))) + goto cleanup; + + if (STREQ(format, XEN_CONFIG_FORMAT_XM)) { + int len = MAX_CONFIG_SIZE; + conf = xenXMDomainConfigFormat(conn, def); + if (!conf) + goto cleanup; + + if (VIR_ALLOC_N(ret, len) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + if (virConfWriteMem(ret, &len, conf) < 0) { + VIR_FREE(ret); + goto cleanup; + } + } else if (STREQ(format, XEN_CONFIG_FORMAT_SEXPR)) { + ret = xenDaemonFormatSxpr(conn, def, priv->xendConfigVersion); + } + +cleanup: + virDomainDefFree(def); + if (conf) + virConfFree(conf); + return ret; +} + + +static int +xenUnifiedDomainMigratePrepare (virConnectPtr dconn, + char **cookie, + int *cookielen, + const char *uri_in, + char **uri_out, + unsigned long flags, + const char *dname, + unsigned long resource) +{ + GET_PRIVATE(dconn); + + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) + return xenDaemonDomainMigratePrepare (dconn, cookie, cookielen, + uri_in, uri_out, + flags, dname, resource); + + xenUnifiedError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static int +xenUnifiedDomainMigratePerform (virDomainPtr dom, + const char *cookie, + int cookielen, + const char *uri, + unsigned long flags, + const char *dname, + unsigned long resource) +{ + GET_PRIVATE(dom->conn); + + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) + return xenDaemonDomainMigratePerform (dom, cookie, cookielen, uri, + flags, dname, resource); + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static virDomainPtr +xenUnifiedDomainMigrateFinish (virConnectPtr dconn, + const char *dname, + const char *cookie ATTRIBUTE_UNUSED, + int cookielen ATTRIBUTE_UNUSED, + const char *uri ATTRIBUTE_UNUSED, + unsigned long flags ATTRIBUTE_UNUSED) +{ + return xenUnifiedDomainLookupByName (dconn, dname); +} + +static int +xenUnifiedListDefinedDomains (virConnectPtr conn, char **const names, + int maxnames) +{ + GET_PRIVATE(conn); + int i; + int ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->listDefinedDomains) { + ret = drivers[i]->listDefinedDomains (conn, names, maxnames); + if (ret >= 0) return ret; + } + + return -1; +} + +static int +xenUnifiedNumOfDefinedDomains (virConnectPtr conn) +{ + GET_PRIVATE(conn); + int i; + int ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->numOfDefinedDomains) { + ret = drivers[i]->numOfDefinedDomains (conn); + if (ret >= 0) return ret; + } + + return -1; +} + +static int +xenUnifiedDomainCreate (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainCreate && + drivers[i]->domainCreate (dom) == 0) + return 0; + + return -1; +} + +static virDomainPtr +xenUnifiedDomainDefineXML (virConnectPtr conn, const char *xml) +{ + GET_PRIVATE(conn); + int i; + virDomainPtr ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainDefineXML) { + ret = drivers[i]->domainDefineXML (conn, xml); + if (ret) return ret; + } + + return NULL; +} + +static int +xenUnifiedDomainUndefine (virDomainPtr dom) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainUndefine && + drivers[i]->domainUndefine (dom) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainAttachDevice (virDomainPtr dom, const char *xml) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainAttachDevice && + drivers[i]->domainAttachDevice (dom, xml) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainDetachDevice (virDomainPtr dom, const char *xml) +{ + GET_PRIVATE(dom->conn); + int i; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) + if (priv->opened[i] && drivers[i]->domainDetachDevice && + drivers[i]->domainDetachDevice (dom, xml) == 0) + return 0; + + return -1; +} + +static int +xenUnifiedDomainGetAutostart (virDomainPtr dom, int *autostart) +{ + GET_PRIVATE(dom->conn); + + if (priv->xendConfigVersion < 3) { + if (priv->opened[XEN_UNIFIED_XM_OFFSET]) + return xenXMDomainGetAutostart(dom, autostart); + } else { + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) + return xenDaemonDomainGetAutostart(dom, autostart); + } + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static int +xenUnifiedDomainSetAutostart (virDomainPtr dom, int autostart) +{ + GET_PRIVATE(dom->conn); + + if (priv->xendConfigVersion < 3) { + if (priv->opened[XEN_UNIFIED_XM_OFFSET]) + return xenXMDomainSetAutostart(dom, autostart); + } else { + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) + return xenDaemonDomainSetAutostart(dom, autostart); + } + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static char * +xenUnifiedDomainGetSchedulerType (virDomainPtr dom, int *nparams) +{ + GET_PRIVATE(dom->conn); + int i; + char *schedulertype; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; i++) { + if (priv->opened[i] && drivers[i]->domainGetSchedulerType) { + schedulertype = drivers[i]->domainGetSchedulerType (dom, nparams); + if (schedulertype != NULL) + return(schedulertype); + } + } + return(NULL); +} + +static int +xenUnifiedDomainGetSchedulerParameters (virDomainPtr dom, + virSchedParameterPtr params, int *nparams) +{ + GET_PRIVATE(dom->conn); + int i, ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) { + if (priv->opened[i] && drivers[i]->domainGetSchedulerParameters) { + ret = drivers[i]->domainGetSchedulerParameters(dom, params, nparams); + if (ret == 0) + return(0); + } + } + return(-1); +} + +static int +xenUnifiedDomainSetSchedulerParameters (virDomainPtr dom, + virSchedParameterPtr params, int nparams) +{ + GET_PRIVATE(dom->conn); + int i, ret; + + for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) { + if (priv->opened[i] && drivers[i]->domainSetSchedulerParameters) { + ret = drivers[i]->domainSetSchedulerParameters(dom, params, nparams); + if (ret == 0) + return 0; + } + } + + return(-1); +} + +static int +xenUnifiedDomainBlockStats (virDomainPtr dom, const char *path, + struct _virDomainBlockStats *stats) +{ + GET_PRIVATE (dom->conn); + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) + return xenHypervisorDomainBlockStats (dom, path, stats); + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static int +xenUnifiedDomainInterfaceStats (virDomainPtr dom, const char *path, + struct _virDomainInterfaceStats *stats) +{ + GET_PRIVATE (dom->conn); + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) + return xenHypervisorDomainInterfaceStats (dom, path, stats); + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static int +xenUnifiedDomainBlockPeek (virDomainPtr dom, const char *path, + unsigned long long offset, size_t size, + void *buffer, unsigned int flags ATTRIBUTE_UNUSED) +{ + int r; + GET_PRIVATE (dom->conn); + + if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { + r = xenDaemonDomainBlockPeek (dom, path, offset, size, buffer); + if (r != -2) return r; + /* r == -2 means declined, so fall through to XM driver ... */ + } + + if (priv->opened[XEN_UNIFIED_XM_OFFSET]) { + if (xenXMDomainBlockPeek (dom, path, offset, size, buffer) == 0) + return 0; + } + + xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static int +xenUnifiedNodeGetCellsFreeMemory (virConnectPtr conn, unsigned long long *freeMems, + int startCell, int maxCells) +{ + GET_PRIVATE (conn); + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) + return xenHypervisorNodeGetCellsFreeMemory (conn, freeMems, + startCell, maxCells); + + xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +static unsigned long long +xenUnifiedNodeGetFreeMemory (virConnectPtr conn) +{ + unsigned long long freeMem = 0; + int ret; + GET_PRIVATE (conn); + + if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) { + ret = xenHypervisorNodeGetCellsFreeMemory (conn, &freeMem, + -1, 1); + if (ret != 1) + return (0); + return(freeMem); + } + + xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return(0); +} + +static int +xenUnifiedDomainEventRegister (virConnectPtr conn, + virConnectDomainEventCallback callback, + void *opaque, + void (*freefunc)(void *)) +{ + GET_PRIVATE (conn); + + int ret; + xenUnifiedLock(priv); + + if (priv->xsWatch == -1) { + xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + xenUnifiedUnlock(priv); + return -1; + } + + ret = virDomainEventCallbackListAdd(conn, priv->domainEventCallbacks, + callback, opaque, freefunc); + + if (ret == 0) + conn->refs++; + + xenUnifiedUnlock(priv); + return (ret); +} + +static int +xenUnifiedDomainEventDeregister (virConnectPtr conn, + virConnectDomainEventCallback callback) +{ + int ret; + GET_PRIVATE (conn); + xenUnifiedLock(priv); + + if (priv->xsWatch == -1) { + xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + xenUnifiedUnlock(priv); + return -1; + } + + if (priv->domainEventDispatching) + ret = virDomainEventCallbackListMarkDelete(conn, priv->domainEventCallbacks, + callback); + else + ret = virDomainEventCallbackListRemove(conn, priv->domainEventCallbacks, + callback); + + if (ret == 0) + virUnrefConnect(conn); + + xenUnifiedUnlock(priv); + return ret; +} + + +static int +xenUnifiedNodeDeviceGetPciInfo (virNodeDevicePtr dev, + unsigned *domain, + unsigned *bus, + unsigned *slot, + unsigned *function) +{ + virNodeDeviceDefPtr def = NULL; + virNodeDevCapsDefPtr cap; + char *xml = NULL; + int ret = -1; + + xml = virNodeDeviceGetXMLDesc(dev, 0); + if (!xml) + goto out; + + def = virNodeDeviceDefParseString(dev->conn, xml, EXISTING_DEVICE); + if (!def) + goto out; + + cap = def->caps; + while (cap) { + if (cap->type == VIR_NODE_DEV_CAP_PCI_DEV) { + *domain = cap->data.pci_dev.domain; + *bus = cap->data.pci_dev.bus; + *slot = cap->data.pci_dev.slot; + *function = cap->data.pci_dev.function; + break; + } + + cap = cap->next; + } + + if (!cap) { + xenUnifiedError(dev->conn, VIR_ERR_INVALID_ARG, + _("device %s is not a PCI device"), dev->name); + goto out; + } + + ret = 0; +out: + virNodeDeviceDefFree(def); + VIR_FREE(xml); + return ret; +} + +static int +xenUnifiedNodeDeviceDettach (virNodeDevicePtr dev) +{ + pciDevice *pci; + unsigned domain, bus, slot, function; + int ret = -1; + + if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) + return -1; + + pci = pciGetDevice(dev->conn, domain, bus, slot, function); + if (!pci) + return -1; + + if (pciDettachDevice(dev->conn, pci) < 0) + goto out; + + ret = 0; +out: + pciFreeDevice(dev->conn, pci); + return ret; +} + +static int +xenUnifiedNodeDeviceReAttach (virNodeDevicePtr dev) +{ + pciDevice *pci; + unsigned domain, bus, slot, function; + int ret = -1; + + if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) + return -1; + + pci = pciGetDevice(dev->conn, domain, bus, slot, function); + if (!pci) + return -1; + + if (pciReAttachDevice(dev->conn, pci) < 0) + goto out; + + ret = 0; +out: + pciFreeDevice(dev->conn, pci); + return ret; +} + +static int +xenUnifiedNodeDeviceReset (virNodeDevicePtr dev) +{ + pciDevice *pci; + unsigned domain, bus, slot, function; + int ret = -1; + + if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) + return -1; + + pci = pciGetDevice(dev->conn, domain, bus, slot, function); + if (!pci) + return -1; + + if (pciResetDevice(dev->conn, pci, NULL) < 0) + goto out; + + ret = 0; +out: + pciFreeDevice(dev->conn, pci); + return ret; +} + + +/*----- Register with libvirt.c, and initialise Xen drivers. -----*/ + +/* The interface which we export upwards to libvirt.c. */ +static virDriver xenUnifiedDriver = { + VIR_DRV_XEN_UNIFIED, + "Xen", + xenUnifiedOpen, /* open */ + xenUnifiedClose, /* close */ + xenUnifiedSupportsFeature, /* supports_feature */ + xenUnifiedType, /* type */ + xenUnifiedGetVersion, /* version */ + xenUnifiedGetHostname, /* getHostname */ + xenUnifiedGetMaxVcpus, /* getMaxVcpus */ + xenUnifiedNodeGetInfo, /* nodeGetInfo */ + xenUnifiedGetCapabilities, /* getCapabilities */ + xenUnifiedListDomains, /* listDomains */ + xenUnifiedNumOfDomains, /* numOfDomains */ + xenUnifiedDomainCreateXML, /* domainCreateXML */ + xenUnifiedDomainLookupByID, /* domainLookupByID */ + xenUnifiedDomainLookupByUUID, /* domainLookupByUUID */ + xenUnifiedDomainLookupByName, /* domainLookupByName */ + xenUnifiedDomainSuspend, /* domainSuspend */ + xenUnifiedDomainResume, /* domainResume */ + xenUnifiedDomainShutdown, /* domainShutdown */ + xenUnifiedDomainReboot, /* domainReboot */ + xenUnifiedDomainDestroy, /* domainDestroy */ + xenUnifiedDomainGetOSType, /* domainGetOSType */ + xenUnifiedDomainGetMaxMemory, /* domainGetMaxMemory */ + xenUnifiedDomainSetMaxMemory, /* domainSetMaxMemory */ + xenUnifiedDomainSetMemory, /* domainSetMemory */ + xenUnifiedDomainGetInfo, /* domainGetInfo */ + xenUnifiedDomainSave, /* domainSave */ + xenUnifiedDomainRestore, /* domainRestore */ + xenUnifiedDomainCoreDump, /* domainCoreDump */ + xenUnifiedDomainSetVcpus, /* domainSetVcpus */ + xenUnifiedDomainPinVcpu, /* domainPinVcpu */ + xenUnifiedDomainGetVcpus, /* domainGetVcpus */ + xenUnifiedDomainGetMaxVcpus, /* domainGetMaxVcpus */ + NULL, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ + xenUnifiedDomainDumpXML, /* domainDumpXML */ + xenUnifiedDomainXMLFromNative, /* domainXmlFromNative */ + xenUnifiedDomainXMLToNative, /* domainXmlToNative */ + xenUnifiedListDefinedDomains, /* listDefinedDomains */ + xenUnifiedNumOfDefinedDomains, /* numOfDefinedDomains */ + xenUnifiedDomainCreate, /* domainCreate */ + xenUnifiedDomainDefineXML, /* domainDefineXML */ + xenUnifiedDomainUndefine, /* domainUndefine */ + xenUnifiedDomainAttachDevice, /* domainAttachDevice */ + xenUnifiedDomainDetachDevice, /* domainDetachDevice */ + xenUnifiedDomainGetAutostart, /* domainGetAutostart */ + xenUnifiedDomainSetAutostart, /* domainSetAutostart */ + xenUnifiedDomainGetSchedulerType, /* domainGetSchedulerType */ + xenUnifiedDomainGetSchedulerParameters, /* domainGetSchedulerParameters */ + xenUnifiedDomainSetSchedulerParameters, /* domainSetSchedulerParameters */ + xenUnifiedDomainMigratePrepare, /* domainMigratePrepare */ + xenUnifiedDomainMigratePerform, /* domainMigratePerform */ + xenUnifiedDomainMigrateFinish, /* domainMigrateFinish */ + xenUnifiedDomainBlockStats, /* domainBlockStats */ + xenUnifiedDomainInterfaceStats, /* domainInterfaceStats */ + xenUnifiedDomainBlockPeek, /* domainBlockPeek */ + NULL, /* domainMemoryPeek */ + xenUnifiedNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ + xenUnifiedNodeGetFreeMemory, /* getFreeMemory */ + xenUnifiedDomainEventRegister, /* domainEventRegister */ + xenUnifiedDomainEventDeregister, /* domainEventDeregister */ + NULL, /* domainMigratePrepare2 */ + NULL, /* domainMigrateFinish2 */ + xenUnifiedNodeDeviceDettach, /* nodeDeviceDettach */ + xenUnifiedNodeDeviceReAttach, /* nodeDeviceReAttach */ + xenUnifiedNodeDeviceReset, /* nodeDeviceReset */ +}; + +/** + * xenRegister: + * + * Register xen related drivers + * + * Returns the driver priority or -1 in case of error. + */ +int +xenRegister (void) +{ + /* Ignore failures here. */ + (void) xenHypervisorInit (); + +#ifdef WITH_LIBVIRTD + if (virRegisterStateDriver (&state_driver) == -1) return -1; +#endif + + return virRegisterDriver (&xenUnifiedDriver); +} + +/** + * xenUnifiedDomainInfoListFree: + * + * Free the Domain Info List + */ +void +xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr list) +{ + int i; + + if (list == NULL) + return; + + for (i=0; icount; i++) { + VIR_FREE(list->doms[i]->name); + VIR_FREE(list->doms[i]); + } + VIR_FREE(list); +} + +/** + * xenUnifiedAddDomainInfo: + * + * Add name and uuid to the domain info list + * + * Returns: 0 on success, -1 on failure + */ +int +xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr list, + int id, char *name, + unsigned char *uuid) +{ + xenUnifiedDomainInfoPtr info; + int n; + + /* check if we already have this callback on our list */ + for (n=0; n < list->count; n++) { + if (STREQ(list->doms[n]->name, name) && + !memcmp(list->doms[n]->uuid, uuid, VIR_UUID_BUFLEN)) { + DEBUG0("WARNING: dom already tracked"); + return -1; + } + } + + if (VIR_ALLOC(info) < 0) + goto memory_error; + if (!(info->name = strdup(name))) + goto memory_error; + + memcpy(info->uuid, uuid, VIR_UUID_BUFLEN); + info->id = id; + + /* Make space on list */ + n = list->count; + if (VIR_REALLOC_N(list->doms, n + 1) < 0) { + goto memory_error; + } + + list->doms[n] = info; + list->count++; + return 0; +memory_error: + virReportOOMError (NULL); + if (info) + VIR_FREE(info->name); + VIR_FREE(info); + return -1; +} + +/** + * xenUnifiedRemoveDomainInfo: + * + * Removes name and uuid to the domain info list + * + * Returns: 0 on success, -1 on failure + */ +int +xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr list, + int id, char *name, + unsigned char *uuid) +{ + int i; + for (i = 0 ; i < list->count ; i++) { + if( list->doms[i]->id == id && + STREQ(list->doms[i]->name, name) && + !memcmp(list->doms[i]->uuid, uuid, VIR_UUID_BUFLEN)) { + + VIR_FREE(list->doms[i]->name); + VIR_FREE(list->doms[i]); + + if (i < (list->count - 1)) + memmove(list->doms + i, + list->doms + i + 1, + sizeof(*(list->doms)) * + (list->count - (i + 1))); + + if (VIR_REALLOC_N(list->doms, + list->count - 1) < 0) { + ; /* Failure to reduce memory allocation isn't fatal */ + } + list->count--; + + return 0; + } + } + return -1; +} + +static void +xenUnifiedDomainEventDispatchFunc(virConnectPtr conn, + virDomainEventPtr event, + virConnectDomainEventCallback cb, + void *cbopaque, + void *opaque) +{ + xenUnifiedPrivatePtr priv = opaque; + + /* + * Release the lock while the callback is running so that + * we're re-entrant safe for callback work - the callback + * may want to invoke other virt functions & we have already + * protected the one piece of state we have - the callback + * list + */ + xenUnifiedUnlock(priv); + virDomainEventDispatchDefaultFunc(conn, event, cb, cbopaque, NULL); + xenUnifiedLock(priv); +} + +/** + * xenUnifiedDomainEventDispatch: + * @priv: the connection to dispatch events on + * @event: the event to dispatch + * + * Dispatch domain events to registered callbacks + * + * The caller must hold the lock in 'priv' before invoking + * + */ +void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv, + virDomainEventPtr event) +{ + if (!priv) + return; + + priv->domainEventDispatching = 1; + + if (priv->domainEventCallbacks) { + virDomainEventDispatch(event, + priv->domainEventCallbacks, + xenUnifiedDomainEventDispatchFunc, + priv); + + /* Purge any deleted callbacks */ + virDomainEventCallbackListPurgeMarked(priv->domainEventCallbacks); + } + + virDomainEventFree(event); + + priv->domainEventDispatching = 0; +} + +void xenUnifiedLock(xenUnifiedPrivatePtr priv) +{ + virMutexLock(&priv->lock); +} + +void xenUnifiedUnlock(xenUnifiedPrivatePtr priv) +{ + virMutexUnlock(&priv->lock); +} diff --git a/src/xen/xen_driver.h b/src/xen/xen_driver.h new file mode 100644 index 0000000000..9cc877b03a --- /dev/null +++ b/src/xen/xen_driver.h @@ -0,0 +1,229 @@ +/* + * xen_unified.c: Unified Xen driver. + * + * Copyright (C) 2007 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Richard W.M. Jones + */ + +#ifndef __VIR_XEN_UNIFIED_H__ +#define __VIR_XEN_UNIFIED_H__ + +#include "internal.h" +#include "capabilities.h" +#include "driver.h" +#include "domain_conf.h" +#include "xs_internal.h" +#if WITH_XEN_INOTIFY +#include "xen_inotify.h" +#endif +#include "domain_event.h" +#include "hash.h" + +#ifndef HAVE_WINSOCK2_H +#include +#include +#else +#include +#endif + +extern int xenRegister (void); + +#define XEN_UNIFIED_HYPERVISOR_OFFSET 0 +#define XEN_UNIFIED_PROXY_OFFSET 1 +#define XEN_UNIFIED_XEND_OFFSET 2 +#define XEN_UNIFIED_XS_OFFSET 3 +#define XEN_UNIFIED_XM_OFFSET 4 + +#if WITH_XEN_INOTIFY +#define XEN_UNIFIED_INOTIFY_OFFSET 5 +#define XEN_UNIFIED_NR_DRIVERS 6 +#else +#define XEN_UNIFIED_NR_DRIVERS 5 +#endif + +#define MIN_XEN_GUEST_SIZE 64 /* 64 megabytes */ + +#define XEN_CONFIG_FORMAT_XM "xen-xm" +#define XEN_CONFIG_FORMAT_SEXPR "xen-sxpr" + +/* _xenUnifiedDriver: + * + * Entry points into the underlying Xen drivers. This structure + * will eventually go away and instead xen unified will make direct + * calls to the underlying Xen drivers. + * + * To reiterate - the goal is to remove elements from this structure + * until it is empty, replacing indirect calls through this + * structure with direct calls in xen_unified.c. + */ +struct xenUnifiedDriver { + virDrvOpen open; + virDrvClose close; + virDrvGetVersion version; + virDrvGetHostname getHostname; + virDrvNodeGetInfo nodeGetInfo; + virDrvGetCapabilities getCapabilities; + virDrvListDomains listDomains; + virDrvNumOfDomains numOfDomains; + virDrvDomainCreateXML domainCreateXML; + virDrvDomainSuspend domainSuspend; + virDrvDomainResume domainResume; + virDrvDomainShutdown domainShutdown; + virDrvDomainReboot domainReboot; + virDrvDomainDestroy domainDestroy; + virDrvDomainGetOSType domainGetOSType; + virDrvDomainGetMaxMemory domainGetMaxMemory; + virDrvDomainSetMaxMemory domainSetMaxMemory; + virDrvDomainSetMemory domainSetMemory; + virDrvDomainGetInfo domainGetInfo; + virDrvDomainSave domainSave; + virDrvDomainRestore domainRestore; + virDrvDomainCoreDump domainCoreDump; + virDrvDomainSetVcpus domainSetVcpus; + virDrvDomainPinVcpu domainPinVcpu; + virDrvDomainGetVcpus domainGetVcpus; + virDrvDomainGetMaxVcpus domainGetMaxVcpus; + virDrvListDefinedDomains listDefinedDomains; + virDrvNumOfDefinedDomains numOfDefinedDomains; + virDrvDomainCreate domainCreate; + virDrvDomainDefineXML domainDefineXML; + virDrvDomainUndefine domainUndefine; + virDrvDomainAttachDevice domainAttachDevice; + virDrvDomainDetachDevice domainDetachDevice; + virDrvDomainGetAutostart domainGetAutostart; + virDrvDomainSetAutostart domainSetAutostart; + virDrvDomainGetSchedulerType domainGetSchedulerType; + virDrvDomainGetSchedulerParameters domainGetSchedulerParameters; + virDrvDomainSetSchedulerParameters domainSetSchedulerParameters; +}; + +typedef struct xenXMConfCache *xenXMConfCachePtr; +typedef struct xenXMConfCache { + time_t refreshedAt; + char filename[PATH_MAX]; + virDomainDefPtr def; +} xenXMConfCache; + +/* xenUnifiedDomainInfoPtr: + * The minimal state we have about active domains + * This is the minmal info necessary to still get a + * virDomainPtr when the domain goes away + */ +struct _xenUnifiedDomainInfo { + int id; + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; +}; +typedef struct _xenUnifiedDomainInfo xenUnifiedDomainInfo; +typedef xenUnifiedDomainInfo *xenUnifiedDomainInfoPtr; + +struct _xenUnifiedDomainInfoList { + unsigned int count; + xenUnifiedDomainInfoPtr *doms; +}; +typedef struct _xenUnifiedDomainInfoList xenUnifiedDomainInfoList; +typedef xenUnifiedDomainInfoList *xenUnifiedDomainInfoListPtr; + +/* xenUnifiedPrivatePtr: + * + * Per-connection private data, stored in conn->privateData. All Xen + * low-level drivers access parts of this structure. + */ +struct _xenUnifiedPrivate { + virMutex lock; + + /* These initial vars are initialized in Open method + * and readonly thereafter, so can be used without + * holding the lock + */ + virCapsPtr caps; + int handle; /* Xen hypervisor handle */ + + int xendConfigVersion; /* XenD config version */ + + /* connection to xend */ + struct sockaddr_storage addr; + socklen_t addrlen; + int addrfamily; + int addrprotocol; + + /* Keep track of the drivers which opened. We keep a yes/no flag + * here for each driver, corresponding to the array drivers in + * xen_unified.c. + */ + int opened[XEN_UNIFIED_NR_DRIVERS]; + + + /* + * Everything from this point onwards must be protected + * by the lock when used + */ + + struct xs_handle *xshandle; /* handle to talk to the xenstore */ + + int proxy; /* fd of proxy. */ + + + /* A list of xenstore watches */ + xenStoreWatchListPtr xsWatchList; + int xsWatch; + /* A list of active domain name/uuids */ + xenUnifiedDomainInfoListPtr activeDomainList; + + /* NUMA topology info cache */ + int nbNodeCells; + int nbNodeCpus; + + /* An list of callbacks */ + virDomainEventCallbackListPtr domainEventCallbacks; + int domainEventDispatching; + + /* Location of config files, either /etc + * or /var/lib/xen */ + const char *configDir; + +#if WITH_XEN_INOTIFY + /* The inotify fd */ + int inotifyFD; + int inotifyWatch; + + int useXenConfigCache ; + xenUnifiedDomainInfoListPtr configInfoList; +#endif + + /* For the 'xm' driver */ + /* Primary config file name -> virDomainDef map */ + virHashTablePtr configCache; + /* Domain name to config file name */ + virHashTablePtr nameConfigMap; + /* So we don't refresh too often */ + time_t lastRefresh; +}; + +typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; + +char *xenDomainUsedCpus(virDomainPtr dom); + +void xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr info); +int xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr info, + int id, char *name, + unsigned char *uuid); +int xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr info, + int id, char *name, + unsigned char *uuid); +void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv, + virDomainEventPtr event); +unsigned long xenUnifiedVersion(void); + +#ifndef PROXY +void xenUnifiedLock(xenUnifiedPrivatePtr priv); +void xenUnifiedUnlock(xenUnifiedPrivatePtr priv); +#else +#define xenUnifiedLock(p) do {} while(0) +#define xenUnifiedUnlock(p) do {} while(0) +#endif + +#endif /* __VIR_XEN_UNIFIED_H__ */ diff --git a/src/xen/xen_hypervisor.c b/src/xen/xen_hypervisor.c new file mode 100644 index 0000000000..9f3f5fe797 --- /dev/null +++ b/src/xen/xen_hypervisor.c @@ -0,0 +1,3458 @@ +/* + * xen_internal.c: direct access to Xen hypervisor level + * + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Veillard + */ + +#include + +#include +#include +/* required for uint8_t, uint32_t, etc ... */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __sun +#include + +#include + +#ifndef PRIV_XVM_CONTROL +#define PRIV_XVM_CONTROL ((const char *)"xvm_control") +#endif + +#endif /* __sun */ + +/* required for dom0_getdomaininfo_t */ +#include +#include +#ifdef HAVE_XEN_LINUX_PRIVCMD_H +#include +#else +#ifdef HAVE_XEN_SYS_PRIVCMD_H +#include +#endif +#endif + +/* required for shutdown flags */ +#include + +#include "virterror_internal.h" +#include "logging.h" +#include "datatypes.h" +#include "driver.h" +#include "util.h" +#include "xen_driver.h" +#include "xen_hypervisor.h" +#include "xs_internal.h" +#include "stats_linux.h" +#include "block_stats.h" +#include "xend_internal.h" +#include "buf.h" +#include "capabilities.h" +#include "memory.h" + +#define VIR_FROM_THIS VIR_FROM_XEN + +/* + * so far there is 2 versions of the structures usable for doing + * hypervisor calls. + */ +/* the old one */ +typedef struct v0_hypercall_struct { + unsigned long op; + unsigned long arg[5]; +} v0_hypercall_t; + +#ifdef __linux__ +#define XEN_V0_IOCTL_HYPERCALL_CMD \ + _IOC(_IOC_NONE, 'P', 0, sizeof(v0_hypercall_t)) +/* the new one */ +typedef struct v1_hypercall_struct +{ + uint64_t op; + uint64_t arg[5]; +} v1_hypercall_t; +#define XEN_V1_IOCTL_HYPERCALL_CMD \ + _IOC(_IOC_NONE, 'P', 0, sizeof(v1_hypercall_t)) +typedef v1_hypercall_t hypercall_t; +#elif defined(__sun) +typedef privcmd_hypercall_t hypercall_t; +#else +#error "unsupported platform" +#endif + +#ifndef __HYPERVISOR_sysctl +#define __HYPERVISOR_sysctl 35 +#endif +#ifndef __HYPERVISOR_domctl +#define __HYPERVISOR_domctl 36 +#endif + +#ifdef WITH_RHEL5_API +#define SYS_IFACE_MIN_VERS_NUMA 3 +#else +#define SYS_IFACE_MIN_VERS_NUMA 4 +#endif + +static int xen_ioctl_hypercall_cmd = 0; +static int initialized = 0; +static int in_init = 0; +static int hv_version = 0; +static int hypervisor_version = 2; +static int sys_interface_version = -1; +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 *flags_pae_re = "^flags[[:blank:]]+:.* pae[[:space:]]"; +static regex_t flags_pae_rec; +static const char *xen_cap_re = "(xen|hvm)-[[:digit:]]+\\.[[:digit:]]+-(x86_32|x86_64|ia64|powerpc64)(p|be)?"; +static regex_t xen_cap_rec; + +/* + * The content of the structures for a getdomaininfolist system hypercall + */ +#ifndef DOMFLAGS_DYING +#define DOMFLAGS_DYING (1<<0) /* Domain is scheduled to die. */ +#define DOMFLAGS_HVM (1<<1) /* Domain is HVM */ +#define DOMFLAGS_SHUTDOWN (1<<2) /* The guest OS has shut down. */ +#define DOMFLAGS_PAUSED (1<<3) /* Currently paused by control software. */ +#define DOMFLAGS_BLOCKED (1<<4) /* Currently blocked pending an event. */ +#define DOMFLAGS_RUNNING (1<<5) /* Domain is currently running. */ +#define DOMFLAGS_CPUMASK 255 /* CPU to which this domain is bound. */ +#define DOMFLAGS_CPUSHIFT 8 +#define DOMFLAGS_SHUTDOWNMASK 255 /* DOMFLAGS_SHUTDOWN guest-supplied code. */ +#define DOMFLAGS_SHUTDOWNSHIFT 16 +#endif + +/* + * These flags explain why a system is in the state of "shutdown". Normally, + * They are defined in xen/sched.h + */ +#ifndef SHUTDOWN_poweroff +#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ +#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ +#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ +#define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ +#endif + +#define XEN_V0_OP_GETDOMAININFOLIST 38 +#define XEN_V1_OP_GETDOMAININFOLIST 38 +#define XEN_V2_OP_GETDOMAININFOLIST 6 + +struct xen_v0_getdomaininfo { + domid_t domain; /* the domain number */ + uint32_t flags; /* flags, see before */ + uint64_t tot_pages; /* total number of pages used */ + uint64_t max_pages; /* maximum number of pages allowed */ + unsigned long shared_info_frame; /* MFN of shared_info struct */ + uint64_t cpu_time; /* CPU time used */ + uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ + uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ + uint32_t ssidref; + xen_domain_handle_t handle; +}; +typedef struct xen_v0_getdomaininfo xen_v0_getdomaininfo; + +struct xen_v2_getdomaininfo { + domid_t domain; /* the domain number */ + uint32_t flags; /* flags, see before */ + uint64_t tot_pages; /* total number of pages used */ + uint64_t max_pages; /* maximum number of pages allowed */ + uint64_t shared_info_frame; /* MFN of shared_info struct */ + uint64_t cpu_time; /* CPU time used */ + uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ + uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ + uint32_t ssidref; + xen_domain_handle_t handle; +}; +typedef struct xen_v2_getdomaininfo xen_v2_getdomaininfo; + + +/* As of Hypervisor Call v2, DomCtl v5 we are now 8-byte aligned + even on 32-bit archs when dealing with uint64_t */ +#define ALIGN_64 __attribute__((aligned(8))) + +struct xen_v2d5_getdomaininfo { + domid_t domain; /* the domain number */ + uint32_t flags; /* flags, see before */ + uint64_t tot_pages ALIGN_64; /* total number of pages used */ + uint64_t max_pages ALIGN_64; /* maximum number of pages allowed */ + uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */ + uint64_t cpu_time ALIGN_64; /* CPU time used */ + uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ + uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ + uint32_t ssidref; + xen_domain_handle_t handle; +}; +typedef struct xen_v2d5_getdomaininfo xen_v2d5_getdomaininfo; + +union xen_getdomaininfo { + struct xen_v0_getdomaininfo v0; + struct xen_v2_getdomaininfo v2; + struct xen_v2d5_getdomaininfo v2d5; +}; +typedef union xen_getdomaininfo xen_getdomaininfo; + +union xen_getdomaininfolist { + struct xen_v0_getdomaininfo *v0; + struct xen_v2_getdomaininfo *v2; + struct xen_v2d5_getdomaininfo *v2d5; +}; +typedef union xen_getdomaininfolist xen_getdomaininfolist; + + +struct xen_v2_getschedulerid { + uint32_t sched_id; /* Get Scheduler ID from Xen */ +}; +typedef struct xen_v2_getschedulerid xen_v2_getschedulerid; + + +union xen_getschedulerid { + struct xen_v2_getschedulerid *v2; +}; +typedef union xen_getschedulerid xen_getschedulerid; + +struct xen_v2s4_availheap { + uint32_t min_bitwidth; /* Smallest address width (zero if don't care). */ + uint32_t max_bitwidth; /* Largest address width (zero if don't care). */ + int32_t node; /* NUMA node (-1 for sum across all nodes). */ + uint64_t avail_bytes; /* Bytes available in the specified region. */ +}; + +typedef struct xen_v2s4_availheap xen_v2s4_availheap; + +struct xen_v2s5_availheap { + uint32_t min_bitwidth; /* Smallest address width (zero if don't care). */ + uint32_t max_bitwidth; /* Largest address width (zero if don't care). */ + int32_t node; /* NUMA node (-1 for sum across all nodes). */ + uint64_t avail_bytes ALIGN_64; /* Bytes available in the specified region. */ +}; + +typedef struct xen_v2s5_availheap xen_v2s5_availheap; + + +#define XEN_GETDOMAININFOLIST_ALLOC(domlist, size) \ + (hypervisor_version < 2 ? \ + (VIR_ALLOC_N(domlist.v0, (size)) == 0) : \ + (dom_interface_version < 5 ? \ + (VIR_ALLOC_N(domlist.v2, (size)) == 0) : \ + (VIR_ALLOC_N(domlist.v2d5, (size)) == 0))) + +#define XEN_GETDOMAININFOLIST_FREE(domlist) \ + (hypervisor_version < 2 ? \ + VIR_FREE(domlist.v0) : \ + (dom_interface_version < 5 ? \ + VIR_FREE(domlist.v2) : \ + VIR_FREE(domlist.v2d5))) + +#define XEN_GETDOMAININFOLIST_CLEAR(domlist, size) \ + (hypervisor_version < 2 ? \ + memset(domlist.v0, 0, sizeof(*domlist.v0) * size) : \ + (dom_interface_version < 5 ? \ + memset(domlist.v2, 0, sizeof(*domlist.v2) * size) : \ + memset(domlist.v2d5, 0, sizeof(*domlist.v2d5) * size))) + +#define XEN_GETDOMAININFOLIST_DOMAIN(domlist, n) \ + (hypervisor_version < 2 ? \ + domlist.v0[n].domain : \ + (dom_interface_version < 5 ? \ + domlist.v2[n].domain : \ + domlist.v2d5[n].domain)) + +#define XEN_GETDOMAININFOLIST_UUID(domlist, n) \ + (hypervisor_version < 2 ? \ + domlist.v0[n].handle : \ + (dom_interface_version < 5 ? \ + domlist.v2[n].handle : \ + domlist.v2d5[n].handle)) + +#define XEN_GETDOMAININFOLIST_DATA(domlist) \ + (hypervisor_version < 2 ? \ + (void*)(domlist->v0) : \ + (dom_interface_version < 5 ? \ + (void*)(domlist->v2) : \ + (void*)(domlist->v2d5))) + +#define XEN_GETDOMAININFO_SIZE \ + (hypervisor_version < 2 ? \ + sizeof(xen_v0_getdomaininfo) : \ + (dom_interface_version < 5 ? \ + sizeof(xen_v2_getdomaininfo) : \ + sizeof(xen_v2d5_getdomaininfo))) + +#define XEN_GETDOMAININFO_CLEAR(dominfo) \ + (hypervisor_version < 2 ? \ + memset(&(dominfo.v0), 0, sizeof(xen_v0_getdomaininfo)) : \ + (dom_interface_version < 5 ? \ + memset(&(dominfo.v2), 0, sizeof(xen_v2_getdomaininfo)) : \ + memset(&(dominfo.v2d5), 0, sizeof(xen_v2d5_getdomaininfo)))) + +#define XEN_GETDOMAININFO_DOMAIN(dominfo) \ + (hypervisor_version < 2 ? \ + dominfo.v0.domain : \ + (dom_interface_version < 5 ? \ + dominfo.v2.domain : \ + dominfo.v2d5.domain)) + +#define XEN_GETDOMAININFO_CPUTIME(dominfo) \ + (hypervisor_version < 2 ? \ + dominfo.v0.cpu_time : \ + (dom_interface_version < 5 ? \ + dominfo.v2.cpu_time : \ + dominfo.v2d5.cpu_time)) + +#define XEN_GETDOMAININFO_CPUCOUNT(dominfo) \ + (hypervisor_version < 2 ? \ + dominfo.v0.nr_online_vcpus : \ + (dom_interface_version < 5 ? \ + dominfo.v2.nr_online_vcpus : \ + dominfo.v2d5.nr_online_vcpus)) + +#define XEN_GETDOMAININFO_MAXCPUID(dominfo) \ + (hypervisor_version < 2 ? \ + dominfo.v0.max_vcpu_id : \ + (dom_interface_version < 5 ? \ + dominfo.v2.max_vcpu_id : \ + dominfo.v2d5.max_vcpu_id)) + +#define XEN_GETDOMAININFO_FLAGS(dominfo) \ + (hypervisor_version < 2 ? \ + dominfo.v0.flags : \ + (dom_interface_version < 5 ? \ + dominfo.v2.flags : \ + dominfo.v2d5.flags)) + +#define XEN_GETDOMAININFO_TOT_PAGES(dominfo) \ + (hypervisor_version < 2 ? \ + dominfo.v0.tot_pages : \ + (dom_interface_version < 5 ? \ + dominfo.v2.tot_pages : \ + dominfo.v2d5.tot_pages)) + +#define XEN_GETDOMAININFO_MAX_PAGES(dominfo) \ + (hypervisor_version < 2 ? \ + dominfo.v0.max_pages : \ + (dom_interface_version < 5 ? \ + dominfo.v2.max_pages : \ + dominfo.v2d5.max_pages)) + +#define XEN_GETDOMAININFO_UUID(dominfo) \ + (hypervisor_version < 2 ? \ + dominfo.v0.handle : \ + (dom_interface_version < 5 ? \ + dominfo.v2.handle : \ + dominfo.v2d5.handle)) + + +static int +lock_pages(void *addr, size_t len) +{ +#ifdef __linux__ + return (mlock(addr, len)); +#elif defined(__sun) + return (0); +#endif +} + +static int +unlock_pages(void *addr, size_t len) +{ +#ifdef __linux__ + return (munlock(addr, len)); +#elif defined(__sun) + return (0); +#endif +} + + +struct xen_v0_getdomaininfolistop { + domid_t first_domain; + uint32_t max_domains; + struct xen_v0_getdomaininfo *buffer; + uint32_t num_domains; +}; +typedef struct xen_v0_getdomaininfolistop xen_v0_getdomaininfolistop; + + +struct xen_v2_getdomaininfolistop { + domid_t first_domain; + uint32_t max_domains; + struct xen_v2_getdomaininfo *buffer; + uint32_t num_domains; +}; +typedef struct xen_v2_getdomaininfolistop xen_v2_getdomaininfolistop; + +/* As of HV version 2, sysctl version 3 the *buffer pointer is 64-bit aligned */ +struct xen_v2s3_getdomaininfolistop { + domid_t first_domain; + uint32_t max_domains; +#ifdef __BIG_ENDIAN__ + struct { + int __pad[(sizeof (long long) - sizeof (struct xen_v2d5_getdomaininfo *)) / sizeof (int)]; + struct xen_v2d5_getdomaininfo *v; + } buffer; +#else + union { + struct xen_v2d5_getdomaininfo *v; + uint64_t pad ALIGN_64; + } buffer; +#endif + uint32_t num_domains; +}; +typedef struct xen_v2s3_getdomaininfolistop xen_v2s3_getdomaininfolistop; + + + +struct xen_v0_domainop { + domid_t domain; +}; +typedef struct xen_v0_domainop xen_v0_domainop; + +/* + * The information for a destroydomain system hypercall + */ +#define XEN_V0_OP_DESTROYDOMAIN 9 +#define XEN_V1_OP_DESTROYDOMAIN 9 +#define XEN_V2_OP_DESTROYDOMAIN 2 + +/* + * The information for a pausedomain system hypercall + */ +#define XEN_V0_OP_PAUSEDOMAIN 10 +#define XEN_V1_OP_PAUSEDOMAIN 10 +#define XEN_V2_OP_PAUSEDOMAIN 3 + +/* + * The information for an unpausedomain system hypercall + */ +#define XEN_V0_OP_UNPAUSEDOMAIN 11 +#define XEN_V1_OP_UNPAUSEDOMAIN 11 +#define XEN_V2_OP_UNPAUSEDOMAIN 4 + +/* + * The information for an setmaxmem system hypercall + */ +#define XEN_V0_OP_SETMAXMEM 28 +#define XEN_V1_OP_SETMAXMEM 28 +#define XEN_V2_OP_SETMAXMEM 11 + +struct xen_v0_setmaxmem { + domid_t domain; + uint64_t maxmem; +}; +typedef struct xen_v0_setmaxmem xen_v0_setmaxmem; +typedef struct xen_v0_setmaxmem xen_v1_setmaxmem; + +struct xen_v2_setmaxmem { + uint64_t maxmem; +}; +typedef struct xen_v2_setmaxmem xen_v2_setmaxmem; + +struct xen_v2d5_setmaxmem { + uint64_t maxmem ALIGN_64; +}; +typedef struct xen_v2d5_setmaxmem xen_v2d5_setmaxmem; + +/* + * The information for an setmaxvcpu system hypercall + */ +#define XEN_V0_OP_SETMAXVCPU 41 +#define XEN_V1_OP_SETMAXVCPU 41 +#define XEN_V2_OP_SETMAXVCPU 15 + +struct xen_v0_setmaxvcpu { + domid_t domain; + uint32_t maxvcpu; +}; +typedef struct xen_v0_setmaxvcpu xen_v0_setmaxvcpu; +typedef struct xen_v0_setmaxvcpu xen_v1_setmaxvcpu; + +struct xen_v2_setmaxvcpu { + uint32_t maxvcpu; +}; +typedef struct xen_v2_setmaxvcpu xen_v2_setmaxvcpu; + +/* + * The information for an setvcpumap system hypercall + * Note that between 1 and 2 the limitation to 64 physical CPU was lifted + * hence the difference in structures + */ +#define XEN_V0_OP_SETVCPUMAP 20 +#define XEN_V1_OP_SETVCPUMAP 20 +#define XEN_V2_OP_SETVCPUMAP 9 + +struct xen_v0_setvcpumap { + domid_t domain; + uint32_t vcpu; + cpumap_t cpumap; +}; +typedef struct xen_v0_setvcpumap xen_v0_setvcpumap; +typedef struct xen_v0_setvcpumap xen_v1_setvcpumap; + +struct xen_v2_cpumap { + uint8_t *bitmap; + uint32_t nr_cpus; +}; +struct xen_v2_setvcpumap { + uint32_t vcpu; + struct xen_v2_cpumap cpumap; +}; +typedef struct xen_v2_setvcpumap xen_v2_setvcpumap; + +/* HV version 2, Dom version 5 requires 64-bit alignment */ +struct xen_v2d5_cpumap { +#ifdef __BIG_ENDIAN__ + struct { + int __pad[(sizeof (long long) - sizeof (uint8_t *)) / sizeof (int)]; + uint8_t *v; + } bitmap; +#else + union { + uint8_t *v; + uint64_t pad ALIGN_64; + } bitmap; +#endif + uint32_t nr_cpus; +}; +struct xen_v2d5_setvcpumap { + uint32_t vcpu; + struct xen_v2d5_cpumap cpumap; +}; +typedef struct xen_v2d5_setvcpumap xen_v2d5_setvcpumap; + +/* + * The information for an vcpuinfo system hypercall + */ +#define XEN_V0_OP_GETVCPUINFO 43 +#define XEN_V1_OP_GETVCPUINFO 43 +#define XEN_V2_OP_GETVCPUINFO 14 + +struct xen_v0_vcpuinfo { + domid_t domain; /* owner's domain */ + uint32_t vcpu; /* the vcpu number */ + uint8_t online; /* seen as on line */ + uint8_t blocked; /* blocked on event */ + uint8_t running; /* scheduled on CPU */ + uint64_t cpu_time; /* nanosecond of CPU used */ + uint32_t cpu; /* current mapping */ + cpumap_t cpumap; /* deprecated in V2 */ +}; +typedef struct xen_v0_vcpuinfo xen_v0_vcpuinfo; +typedef struct xen_v0_vcpuinfo xen_v1_vcpuinfo; + +struct xen_v2_vcpuinfo { + uint32_t vcpu; /* the vcpu number */ + uint8_t online; /* seen as on line */ + uint8_t blocked; /* blocked on event */ + uint8_t running; /* scheduled on CPU */ + uint64_t cpu_time; /* nanosecond of CPU used */ + uint32_t cpu; /* current mapping */ +}; +typedef struct xen_v2_vcpuinfo xen_v2_vcpuinfo; + +struct xen_v2d5_vcpuinfo { + uint32_t vcpu; /* the vcpu number */ + uint8_t online; /* seen as on line */ + uint8_t blocked; /* blocked on event */ + uint8_t running; /* scheduled on CPU */ + uint64_t cpu_time ALIGN_64; /* nanosecond of CPU used */ + uint32_t cpu; /* current mapping */ +}; +typedef struct xen_v2d5_vcpuinfo xen_v2d5_vcpuinfo; + +/* + * from V2 the pinning of a vcpu is read with a separate call + */ +#define XEN_V2_OP_GETVCPUMAP 25 +typedef struct xen_v2_setvcpumap xen_v2_getvcpumap; +typedef struct xen_v2d5_setvcpumap xen_v2d5_getvcpumap; + +/* + * from V2 we get the scheduler information + */ +#define XEN_V2_OP_GETSCHEDULERID 4 + +/* + * from V2 we get the available heap information + */ +#define XEN_V2_OP_GETAVAILHEAP 9 + +/* + * from V2 we get the scheduler parameter + */ +#define XEN_V2_OP_SCHEDULER 16 +/* Scheduler types. */ +#define XEN_SCHEDULER_SEDF 4 +#define XEN_SCHEDULER_CREDIT 5 +/* get/set scheduler parameters */ +#define XEN_DOMCTL_SCHEDOP_putinfo 0 +#define XEN_DOMCTL_SCHEDOP_getinfo 1 + +struct xen_v2_setschedinfo { + uint32_t sched_id; + uint32_t cmd; + union { + struct xen_domctl_sched_sedf { + uint64_t period ALIGN_64; + uint64_t slice ALIGN_64; + uint64_t latency ALIGN_64; + uint32_t extratime; + uint32_t weight; + } sedf; + struct xen_domctl_sched_credit { + uint16_t weight; + uint16_t cap; + } credit; + } u; +}; +typedef struct xen_v2_setschedinfo xen_v2_setschedinfo; +typedef struct xen_v2_setschedinfo xen_v2_getschedinfo; + + +/* + * The hypercall operation structures also have changed on + * changeset 86d26e6ec89b + */ +/* the old structure */ +struct xen_op_v0 { + uint32_t cmd; + uint32_t interface_version; + union { + xen_v0_getdomaininfolistop getdomaininfolist; + xen_v0_domainop domain; + xen_v0_setmaxmem setmaxmem; + xen_v0_setmaxvcpu setmaxvcpu; + xen_v0_setvcpumap setvcpumap; + xen_v0_vcpuinfo getvcpuinfo; + uint8_t padding[128]; + } u; +}; +typedef struct xen_op_v0 xen_op_v0; +typedef struct xen_op_v0 xen_op_v1; + +/* the new structure for systems operations */ +struct xen_op_v2_sys { + uint32_t cmd; + uint32_t interface_version; + union { + xen_v2_getdomaininfolistop getdomaininfolist; + xen_v2s3_getdomaininfolistop getdomaininfolists3; + xen_v2_getschedulerid getschedulerid; + xen_v2s4_availheap availheap; + xen_v2s5_availheap availheap5; + uint8_t padding[128]; + } u; +}; +typedef struct xen_op_v2_sys xen_op_v2_sys; + +/* the new structure for domains operation */ +struct xen_op_v2_dom { + uint32_t cmd; + uint32_t interface_version; + domid_t domain; + union { + xen_v2_setmaxmem setmaxmem; + xen_v2d5_setmaxmem setmaxmemd5; + xen_v2_setmaxvcpu setmaxvcpu; + xen_v2_setvcpumap setvcpumap; + xen_v2d5_setvcpumap setvcpumapd5; + xen_v2_vcpuinfo getvcpuinfo; + xen_v2d5_vcpuinfo getvcpuinfod5; + xen_v2_getvcpumap getvcpumap; + xen_v2d5_getvcpumap getvcpumapd5; + xen_v2_setschedinfo setschedinfo; + xen_v2_getschedinfo getschedinfo; + uint8_t padding[128]; + } u; +}; +typedef struct xen_op_v2_dom xen_op_v2_dom; + + +#ifdef __linux__ +#define XEN_HYPERVISOR_SOCKET "/proc/xen/privcmd" +#define HYPERVISOR_CAPABILITIES "/sys/hypervisor/properties/capabilities" +#elif defined(__sun) +#define XEN_HYPERVISOR_SOCKET "/dev/xen/privcmd" +#else +#error "unsupported platform" +#endif + +#ifndef PROXY +static unsigned long xenHypervisorGetMaxMemory(virDomainPtr domain); +#endif + +#ifndef PROXY +struct xenUnifiedDriver xenHypervisorDriver = { + xenHypervisorOpen, /* open */ + xenHypervisorClose, /* close */ + xenHypervisorGetVersion, /* version */ + NULL, /* hostname */ + NULL, /* nodeGetInfo */ + xenHypervisorGetCapabilities, /* getCapabilities */ + xenHypervisorListDomains, /* listDomains */ + xenHypervisorNumOfDomains, /* numOfDomains */ + NULL, /* domainCreateXML */ + xenHypervisorPauseDomain, /* domainSuspend */ + xenHypervisorResumeDomain, /* domainResume */ + NULL, /* domainShutdown */ + NULL, /* domainReboot */ + xenHypervisorDestroyDomain, /* domainDestroy */ + xenHypervisorDomainGetOSType, /* domainGetOSType */ + xenHypervisorGetMaxMemory, /* domainGetMaxMemory */ + xenHypervisorSetMaxMemory, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + xenHypervisorGetDomainInfo, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + NULL, /* domainCoreDump */ + xenHypervisorSetVcpus, /* domainSetVcpus */ + xenHypervisorPinVcpu, /* domainPinVcpu */ + xenHypervisorGetVcpus, /* domainGetVcpus */ + xenHypervisorGetVcpuMax, /* domainGetMaxVcpus */ + NULL, /* listDefinedDomains */ + NULL, /* numOfDefinedDomains */ + NULL, /* domainCreate */ + NULL, /* domainDefineXML */ + NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ + NULL, /* domainGetAutostart */ + NULL, /* domainSetAutostart */ + xenHypervisorGetSchedulerType, /* domainGetSchedulerType */ + xenHypervisorGetSchedulerParameters, /* domainGetSchedulerParameters */ + xenHypervisorSetSchedulerParameters, /* domainSetSchedulerParameters */ +}; +#endif /* !PROXY */ + +#define virXenError(conn, code, fmt...) \ + if (in_init == 0) \ + virReportErrorHelper(conn, VIR_FROM_XEN, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +#ifndef PROXY + +/** + * virXenErrorFunc: + * @conn: connection, if known + * @error: the error number + * @func: the function failing + * @info: extra information string + * @value: extra information number + * + * Handle an error at the xend daemon interface + */ +static void +virXenErrorFunc(virConnectPtr conn, + virErrorNumber error, const char *func, const char *info, + int value) +{ + char fullinfo[1000]; + const char *errmsg; + + if ((error == VIR_ERR_OK) || (in_init != 0)) + return; + + + errmsg =virErrorMsg(error, info); + if (func != NULL) { + snprintf(fullinfo, 999, "%s: %s", func, info); + fullinfo[999] = 0; + virRaiseError(conn, NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR, + errmsg, fullinfo, NULL, value, 0, errmsg, fullinfo, + value); + } else { + virRaiseError(conn, NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR, + errmsg, info, NULL, value, 0, errmsg, info, + value); + } +} + +#endif /* PROXY */ + +/** + * xenHypervisorDoV0Op: + * @handle: the handle to the Xen hypervisor + * @op: pointer to the hypervisor operation structure + * + * Do an hypervisor operation though the old interface, + * this leads to an hypervisor call through ioctl. + * + * Returns 0 in case of success and -1 in case of error. + */ +static int +xenHypervisorDoV0Op(int handle, xen_op_v0 * op) +{ + int ret; + v0_hypercall_t hc; + + memset(&hc, 0, sizeof(hc)); + op->interface_version = hv_version << 8; + hc.op = __HYPERVISOR_dom0_op; + hc.arg[0] = (unsigned long) op; + + if (lock_pages(op, sizeof(dom0_op_t)) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); + return (-1); + } + + ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); + if (ret < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " ioctl %d", + xen_ioctl_hypercall_cmd); + } + + if (unlock_pages(op, sizeof(dom0_op_t)) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " releasing"); + ret = -1; + } + + if (ret < 0) + return (-1); + + return (0); +} +/** + * xenHypervisorDoV1Op: + * @handle: the handle to the Xen hypervisor + * @op: pointer to the hypervisor operation structure + * + * Do an hypervisor v1 operation, this leads to an hypervisor call through + * ioctl. + * + * Returns 0 in case of success and -1 in case of error. + */ +static int +xenHypervisorDoV1Op(int handle, xen_op_v1* op) +{ + int ret; + hypercall_t hc; + + memset(&hc, 0, sizeof(hc)); + op->interface_version = DOM0_INTERFACE_VERSION; + hc.op = __HYPERVISOR_dom0_op; + hc.arg[0] = (unsigned long) op; + + if (lock_pages(op, sizeof(dom0_op_t)) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); + return (-1); + } + + ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); + if (ret < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " ioctl %d", + xen_ioctl_hypercall_cmd); + } + + if (unlock_pages(op, sizeof(dom0_op_t)) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " releasing"); + ret = -1; + } + + if (ret < 0) + return (-1); + + return (0); +} + +/** + * xenHypervisorDoV2Sys: + * @handle: the handle to the Xen hypervisor + * @op: pointer to the hypervisor operation structure + * + * Do an hypervisor v2 system operation, this leads to an hypervisor + * call through ioctl. + * + * Returns 0 in case of success and -1 in case of error. + */ +static int +xenHypervisorDoV2Sys(int handle, xen_op_v2_sys* op) +{ + int ret; + hypercall_t hc; + + memset(&hc, 0, sizeof(hc)); + op->interface_version = sys_interface_version; + hc.op = __HYPERVISOR_sysctl; + hc.arg[0] = (unsigned long) op; + + if (lock_pages(op, sizeof(dom0_op_t)) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); + return (-1); + } + + ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); + if (ret < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " sys ioctl %d", + xen_ioctl_hypercall_cmd); + } + + if (unlock_pages(op, sizeof(dom0_op_t)) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " releasing"); + ret = -1; + } + + if (ret < 0) + return (-1); + + return (0); +} + +/** + * xenHypervisorDoV2Dom: + * @handle: the handle to the Xen hypervisor + * @op: pointer to the hypervisor domain operation structure + * + * Do an hypervisor v2 domain operation, this leads to an hypervisor + * call through ioctl. + * + * Returns 0 in case of success and -1 in case of error. + */ +static int +xenHypervisorDoV2Dom(int handle, xen_op_v2_dom* op) +{ + int ret; + hypercall_t hc; + + memset(&hc, 0, sizeof(hc)); + op->interface_version = dom_interface_version; + hc.op = __HYPERVISOR_domctl; + hc.arg[0] = (unsigned long) op; + + if (lock_pages(op, sizeof(dom0_op_t)) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); + return (-1); + } + + ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); + if (ret < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " ioctl %d", + xen_ioctl_hypercall_cmd); + } + + if (unlock_pages(op, sizeof(dom0_op_t)) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " releasing"); + ret = -1; + } + + if (ret < 0) + return (-1); + + return (0); +} + +/** + * virXen_getdomaininfolist: + * @handle: the hypervisor handle + * @first_domain: first domain in the range + * @maxids: maximum number of domains to list + * @dominfos: output structures + * + * Do a low level hypercall to list existing domains information + * + * Returns the number of domains or -1 in case of failure + */ +static int +virXen_getdomaininfolist(int handle, int first_domain, int maxids, + xen_getdomaininfolist *dominfos) +{ + int ret = -1; + + if (lock_pages(XEN_GETDOMAININFOLIST_DATA(dominfos), + XEN_GETDOMAININFO_SIZE * maxids) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); + return (-1); + } + if (hypervisor_version > 1) { + xen_op_v2_sys op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_GETDOMAININFOLIST; + + if (sys_interface_version < 3) { + op.u.getdomaininfolist.first_domain = (domid_t) first_domain; + op.u.getdomaininfolist.max_domains = maxids; + op.u.getdomaininfolist.buffer = dominfos->v2; + op.u.getdomaininfolist.num_domains = maxids; + } else { + op.u.getdomaininfolists3.first_domain = (domid_t) first_domain; + op.u.getdomaininfolists3.max_domains = maxids; + op.u.getdomaininfolists3.buffer.v = dominfos->v2d5; + op.u.getdomaininfolists3.num_domains = maxids; + } + ret = xenHypervisorDoV2Sys(handle, &op); + + if (ret == 0) { + if (sys_interface_version < 3) + ret = op.u.getdomaininfolist.num_domains; + else + ret = op.u.getdomaininfolists3.num_domains; + } + } else if (hypervisor_version == 1) { + xen_op_v1 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V1_OP_GETDOMAININFOLIST; + op.u.getdomaininfolist.first_domain = (domid_t) first_domain; + op.u.getdomaininfolist.max_domains = maxids; + op.u.getdomaininfolist.buffer = dominfos->v0; + op.u.getdomaininfolist.num_domains = maxids; + ret = xenHypervisorDoV1Op(handle, &op); + if (ret == 0) + ret = op.u.getdomaininfolist.num_domains; + } else if (hypervisor_version == 0) { + xen_op_v0 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V0_OP_GETDOMAININFOLIST; + op.u.getdomaininfolist.first_domain = (domid_t) first_domain; + op.u.getdomaininfolist.max_domains = maxids; + op.u.getdomaininfolist.buffer = dominfos->v0; + op.u.getdomaininfolist.num_domains = maxids; + ret = xenHypervisorDoV0Op(handle, &op); + if (ret == 0) + ret = op.u.getdomaininfolist.num_domains; + } + if (unlock_pages(XEN_GETDOMAININFOLIST_DATA(dominfos), + XEN_GETDOMAININFO_SIZE * maxids) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " release"); + ret = -1; + } + return(ret); +} + +static int +virXen_getdomaininfo(int handle, int first_domain, + xen_getdomaininfo *dominfo) { + xen_getdomaininfolist dominfos; + + if (hypervisor_version < 2) { + dominfos.v0 = &(dominfo->v0); + } else { + dominfos.v2 = &(dominfo->v2); + } + + return virXen_getdomaininfolist(handle, first_domain, 1, &dominfos); +} + + +#ifndef PROXY +/** + * xenHypervisorGetSchedulerType: + * @domain: pointer to the Xen Hypervisor block + * @nparams:give a number of scheduler parameters. + * + * Do a low level hypercall to get scheduler type + * + * Returns scheduler name or NULL in case of failure + */ +char * +xenHypervisorGetSchedulerType(virDomainPtr domain, int *nparams) +{ + char *schedulertype = NULL; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL)) { + virXenErrorFunc(NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "domain or conn is NULL", 0); + return NULL; + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0) { + virXenErrorFunc(domain->conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "priv->handle invalid", 0); + return NULL; + } + if (domain->id < 0) { + virXenError(domain->conn, VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + return NULL; + } + + /* + * Support only dom_interface_version >=5 + * (Xen3.1.0 or later) + * TODO: check on Xen 3.0.3 + */ + if (dom_interface_version < 5) { + virXenErrorFunc(domain->conn, VIR_ERR_NO_XEN, __FUNCTION__, + "unsupported in dom interface < 5", 0); + return NULL; + } + + if (hypervisor_version > 1) { + xen_op_v2_sys op; + int ret; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_GETSCHEDULERID; + ret = xenHypervisorDoV2Sys(priv->handle, &op); + if (ret < 0) + return(NULL); + + switch (op.u.getschedulerid.sched_id){ + case XEN_SCHEDULER_SEDF: + schedulertype = strdup("sedf"); + if (nparams) + *nparams = 6; + break; + case XEN_SCHEDULER_CREDIT: + schedulertype = strdup("credit"); + if (nparams) + *nparams = 2; + break; + default: + break; + } + } + + return schedulertype; +} + +static const char *str_weight = "weight"; +static const char *str_cap = "cap"; + +/** + * xenHypervisorGetSchedulerParameters: + * @domain: pointer to the Xen Hypervisor block + * @params: pointer to scheduler parameters. + * This memory area should be allocated before calling. + * @nparams:this parameter should be same as + * a given number of scheduler parameters. + * from xenHypervisorGetSchedulerType(). + * + * Do a low level hypercall to get scheduler parameters + * + * Returns 0 or -1 in case of failure + */ +int +xenHypervisorGetSchedulerParameters(virDomainPtr domain, + virSchedParameterPtr params, int *nparams) +{ + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL)) { + virXenErrorFunc(NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "domain or conn is NULL", 0); + return -1; + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0) { + virXenErrorFunc(domain->conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "priv->handle invalid", 0); + return -1; + } + if (domain->id < 0) { + virXenError(domain->conn, VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + return -1; + } + + /* + * Support only dom_interface_version >=5 + * (Xen3.1.0 or later) + * TODO: check on Xen 3.0.3 + */ + if (dom_interface_version < 5) { + virXenErrorFunc(domain->conn, VIR_ERR_NO_XEN, __FUNCTION__, + "unsupported in dom interface < 5", 0); + return -1; + } + + if (hypervisor_version > 1) { + xen_op_v2_sys op_sys; + xen_op_v2_dom op_dom; + int ret; + + memset(&op_sys, 0, sizeof(op_sys)); + op_sys.cmd = XEN_V2_OP_GETSCHEDULERID; + ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); + if (ret < 0) + return -1; + + switch (op_sys.u.getschedulerid.sched_id){ + case XEN_SCHEDULER_SEDF: + /* TODO: Implement for Xen/SEDF */ + TODO + return(-1); + case XEN_SCHEDULER_CREDIT: + if (*nparams < 2) + return(-1); + memset(&op_dom, 0, sizeof(op_dom)); + op_dom.cmd = XEN_V2_OP_SCHEDULER; + op_dom.domain = (domid_t) domain->id; + op_dom.u.getschedinfo.sched_id = XEN_SCHEDULER_CREDIT; + op_dom.u.getschedinfo.cmd = XEN_DOMCTL_SCHEDOP_getinfo; + ret = xenHypervisorDoV2Dom(priv->handle, &op_dom); + if (ret < 0) + return(-1); + + strncpy (params[0].field, str_weight, VIR_DOMAIN_SCHED_FIELD_LENGTH); + params[0].field[VIR_DOMAIN_SCHED_FIELD_LENGTH-1] = '\0'; + params[0].type = VIR_DOMAIN_SCHED_FIELD_UINT; + params[0].value.ui = op_dom.u.getschedinfo.u.credit.weight; + + strncpy (params[1].field, str_cap, VIR_DOMAIN_SCHED_FIELD_LENGTH); + params[1].field[VIR_DOMAIN_SCHED_FIELD_LENGTH-1] = '\0'; + params[1].type = VIR_DOMAIN_SCHED_FIELD_UINT; + params[1].value.ui = op_dom.u.getschedinfo.u.credit.cap; + + *nparams = 2; + break; + default: + virXenErrorFunc(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "Unknown scheduler", op_sys.u.getschedulerid.sched_id); + return -1; + } + } + + return 0; +} + +/** + * xenHypervisorSetSchedulerParameters: + * @domain: pointer to the Xen Hypervisor block + * @nparams:give a number of scheduler setting parameters . + * + * Do a low level hypercall to set scheduler parameters + * + * Returns 0 or -1 in case of failure + */ +int +xenHypervisorSetSchedulerParameters(virDomainPtr domain, + virSchedParameterPtr params, int nparams) +{ + int i; + unsigned int val; + xenUnifiedPrivatePtr priv; + char buf[256]; + + if ((domain == NULL) || (domain->conn == NULL)) { + virXenErrorFunc (NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "domain or conn is NULL", 0); + return -1; + } + + if ((nparams == 0) || (params == NULL)) { + virXenErrorFunc (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "Noparameters given", 0); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0) { + virXenErrorFunc(domain->conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "priv->handle invalid", 0); + return -1; + } + if (domain->id < 0) { + virXenError(domain->conn, VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + return -1; + } + + /* + * Support only dom_interface_version >=5 + * (Xen3.1.0 or later) + * TODO: check on Xen 3.0.3 + */ + if (dom_interface_version < 5) { + virXenErrorFunc(domain->conn, VIR_ERR_NO_XEN, __FUNCTION__, + "unsupported in dom interface < 5", 0); + return -1; + } + + if (hypervisor_version > 1) { + xen_op_v2_sys op_sys; + xen_op_v2_dom op_dom; + int ret; + + memset(&op_sys, 0, sizeof(op_sys)); + op_sys.cmd = XEN_V2_OP_GETSCHEDULERID; + ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); + if (ret == -1) return -1; + + switch (op_sys.u.getschedulerid.sched_id){ + case XEN_SCHEDULER_SEDF: + /* TODO: Implement for Xen/SEDF */ + TODO + return(-1); + case XEN_SCHEDULER_CREDIT: { + memset(&op_dom, 0, sizeof(op_dom)); + op_dom.cmd = XEN_V2_OP_SCHEDULER; + op_dom.domain = (domid_t) domain->id; + op_dom.u.getschedinfo.sched_id = XEN_SCHEDULER_CREDIT; + op_dom.u.getschedinfo.cmd = XEN_DOMCTL_SCHEDOP_putinfo; + + /* + * credit scheduler parameters + * following values do not change the parameters + */ + op_dom.u.getschedinfo.u.credit.weight = 0; + op_dom.u.getschedinfo.u.credit.cap = (uint16_t)~0U; + + for (i = 0; i < nparams; i++) { + memset(&buf, 0, sizeof(buf)); + if (STREQ (params[i].field, str_weight) && + params[i].type == VIR_DOMAIN_SCHED_FIELD_UINT) { + val = params[i].value.ui; + if ((val < 1) || (val > USHRT_MAX)) { + snprintf(buf, sizeof(buf), _("Credit scheduler weight parameter (%d) is out of range (1-65535)"), val); + virXenErrorFunc (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, buf, val); + return(-1); + } + op_dom.u.getschedinfo.u.credit.weight = val; + } else if (STREQ (params[i].field, str_cap) && + params[i].type == VIR_DOMAIN_SCHED_FIELD_UINT) { + val = params[i].value.ui; + if (val > USHRT_MAX) { + snprintf(buf, sizeof(buf), _("Credit scheduler cap parameter (%d) is out of range (0-65535)"), val); + virXenErrorFunc (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, buf, val); + return(-1); + } + op_dom.u.getschedinfo.u.credit.cap = val; + } else { + virXenErrorFunc (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "Credit scheduler accepts 'cap' and 'weight' integer parameters", + 0); + return(-1); + } + } + + ret = xenHypervisorDoV2Dom(priv->handle, &op_dom); + if (ret < 0) + return -1; + break; + } + default: + virXenErrorFunc(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "Unknown scheduler", op_sys.u.getschedulerid.sched_id); + return -1; + } + } + + return 0; +} + + +int +xenHypervisorDomainBlockStats (virDomainPtr dom, + const char *path, + struct _virDomainBlockStats *stats) +{ +#ifdef __linux__ + xenUnifiedPrivatePtr priv; + int ret; + + priv = (xenUnifiedPrivatePtr) dom->conn->privateData; + xenUnifiedLock(priv); + /* Need to lock because it hits the xenstore handle :-( */ + ret = xenLinuxDomainBlockStats (priv, dom, path, stats); + xenUnifiedUnlock(priv); + return ret; +#else + virXenErrorFunc (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__, + "block statistics not supported on this platform", + dom->id); + return -1; +#endif +} + +/* Paths have the form vif. (this interface checks that + * is the real domain ID and returns an error if not). + * + * In future we may allow you to query bridge stats (virbrX or + * xenbrX), but that will probably be through a separate + * virNetwork interface, as yet not decided. + */ +int +xenHypervisorDomainInterfaceStats (virDomainPtr dom, + const char *path, + struct _virDomainInterfaceStats *stats) +{ +#ifdef __linux__ + int rqdomid, device; + + /* Verify that the vif requested is one belonging to the current + * domain. + */ + if (sscanf (path, "vif%d.%d", &rqdomid, &device) != 2) { + virXenErrorFunc (dom->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid path, should be vif..", 0); + return -1; + } + if (rqdomid != dom->id) { + virXenErrorFunc (dom->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid path, vif should match this domain ID", 0); + return -1; + } + + return linuxDomainInterfaceStats (dom->conn, path, stats); +#else + virXenErrorFunc (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__, + "/proc/net/dev: Interface not found", 0); + return -1; +#endif +} + +/** + * virXen_pausedomain: + * @handle: the hypervisor handle + * @id: the domain id + * + * Do a low level hypercall to pause the domain + * + * Returns 0 or -1 in case of failure + */ +static int +virXen_pausedomain(int handle, int id) +{ + int ret = -1; + + if (hypervisor_version > 1) { + xen_op_v2_dom op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_PAUSEDOMAIN; + op.domain = (domid_t) id; + ret = xenHypervisorDoV2Dom(handle, &op); + } else if (hypervisor_version == 1) { + xen_op_v1 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V1_OP_PAUSEDOMAIN; + op.u.domain.domain = (domid_t) id; + ret = xenHypervisorDoV1Op(handle, &op); + } else if (hypervisor_version == 0) { + xen_op_v0 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V0_OP_PAUSEDOMAIN; + op.u.domain.domain = (domid_t) id; + ret = xenHypervisorDoV0Op(handle, &op); + } + return(ret); +} + +/** + * virXen_unpausedomain: + * @handle: the hypervisor handle + * @id: the domain id + * + * Do a low level hypercall to unpause the domain + * + * Returns 0 or -1 in case of failure + */ +static int +virXen_unpausedomain(int handle, int id) +{ + int ret = -1; + + if (hypervisor_version > 1) { + xen_op_v2_dom op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_UNPAUSEDOMAIN; + op.domain = (domid_t) id; + ret = xenHypervisorDoV2Dom(handle, &op); + } else if (hypervisor_version == 1) { + xen_op_v1 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V1_OP_UNPAUSEDOMAIN; + op.u.domain.domain = (domid_t) id; + ret = xenHypervisorDoV1Op(handle, &op); + } else if (hypervisor_version == 0) { + xen_op_v0 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V0_OP_UNPAUSEDOMAIN; + op.u.domain.domain = (domid_t) id; + ret = xenHypervisorDoV0Op(handle, &op); + } + return(ret); +} + +/** + * virXen_destroydomain: + * @handle: the hypervisor handle + * @id: the domain id + * + * Do a low level hypercall to destroy the domain + * + * Returns 0 or -1 in case of failure + */ +static int +virXen_destroydomain(int handle, int id) +{ + int ret = -1; + + if (hypervisor_version > 1) { + xen_op_v2_dom op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_DESTROYDOMAIN; + op.domain = (domid_t) id; + ret = xenHypervisorDoV2Dom(handle, &op); + } else if (hypervisor_version == 1) { + xen_op_v1 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V1_OP_DESTROYDOMAIN; + op.u.domain.domain = (domid_t) id; + ret = xenHypervisorDoV1Op(handle, &op); + } else if (hypervisor_version == 0) { + xen_op_v0 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V0_OP_DESTROYDOMAIN; + op.u.domain.domain = (domid_t) id; + ret = xenHypervisorDoV0Op(handle, &op); + } + return(ret); +} + +/** + * virXen_setmaxmem: + * @handle: the hypervisor handle + * @id: the domain id + * @memory: the amount of memory in kilobytes + * + * Do a low level hypercall to change the max memory amount + * + * Returns 0 or -1 in case of failure + */ +static int +virXen_setmaxmem(int handle, int id, unsigned long memory) +{ + int ret = -1; + + if (hypervisor_version > 1) { + xen_op_v2_dom op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_SETMAXMEM; + op.domain = (domid_t) id; + if (dom_interface_version < 5) + op.u.setmaxmem.maxmem = memory; + else + op.u.setmaxmemd5.maxmem = memory; + ret = xenHypervisorDoV2Dom(handle, &op); + } else if (hypervisor_version == 1) { + xen_op_v1 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V1_OP_SETMAXMEM; + op.u.setmaxmem.domain = (domid_t) id; + op.u.setmaxmem.maxmem = memory; + ret = xenHypervisorDoV1Op(handle, &op); + } else if (hypervisor_version == 0) { + xen_op_v0 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V0_OP_SETMAXMEM; + op.u.setmaxmem.domain = (domid_t) id; + op.u.setmaxmem.maxmem = memory; + ret = xenHypervisorDoV0Op(handle, &op); + } + return(ret); +} + +/** + * virXen_setmaxvcpus: + * @handle: the hypervisor handle + * @id: the domain id + * @vcpus: the numbers of vcpus + * + * Do a low level hypercall to change the max vcpus amount + * + * Returns 0 or -1 in case of failure + */ +static int +virXen_setmaxvcpus(int handle, int id, unsigned int vcpus) +{ + int ret = -1; + + if (hypervisor_version > 1) { + xen_op_v2_dom op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_SETMAXVCPU; + op.domain = (domid_t) id; + op.u.setmaxvcpu.maxvcpu = vcpus; + ret = xenHypervisorDoV2Dom(handle, &op); + } else if (hypervisor_version == 1) { + xen_op_v1 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V1_OP_SETMAXVCPU; + op.u.setmaxvcpu.domain = (domid_t) id; + op.u.setmaxvcpu.maxvcpu = vcpus; + ret = xenHypervisorDoV1Op(handle, &op); + } else if (hypervisor_version == 0) { + xen_op_v0 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V0_OP_SETMAXVCPU; + op.u.setmaxvcpu.domain = (domid_t) id; + op.u.setmaxvcpu.maxvcpu = vcpus; + ret = xenHypervisorDoV0Op(handle, &op); + } + return(ret); +} + +/** + * virXen_setvcpumap: + * @handle: the hypervisor handle + * @id: the domain id + * @vcpu: the vcpu to map + * @cpumap: the bitmap for this vcpu + * @maplen: the size of the bitmap in bytes + * + * Do a low level hypercall to change the pinning for vcpu + * + * Returns 0 or -1 in case of failure + */ +static int +virXen_setvcpumap(int handle, int id, unsigned int vcpu, + unsigned char * cpumap, int maplen) +{ + int ret = -1; + unsigned char *new = NULL; + unsigned char *bitmap = NULL; + uint32_t nr_cpus; + + if (hypervisor_version > 1) { + xen_op_v2_dom op; + + if (lock_pages(cpumap, maplen) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); + return (-1); + } + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_SETVCPUMAP; + op.domain = (domid_t) id; + + /* The allocated memory to cpumap must be 'sizeof(uint64_t)' byte * + * for Xen, and also nr_cpus must be 'sizeof(uint64_t) * 8' */ + if (maplen < 8) { + if (VIR_ALLOC_N(new, sizeof(uint64_t)) < 0) { + virReportOOMError(NULL); + return (-1); + } + memcpy(new, cpumap, maplen); + bitmap = new; + nr_cpus = sizeof(uint64_t) * 8; + } else { + bitmap = cpumap; + nr_cpus = maplen * 8; + } + + if (dom_interface_version < 5) { + op.u.setvcpumap.vcpu = vcpu; + op.u.setvcpumap.cpumap.bitmap = bitmap; + op.u.setvcpumap.cpumap.nr_cpus = nr_cpus; + } else { + op.u.setvcpumapd5.vcpu = vcpu; + op.u.setvcpumapd5.cpumap.bitmap.v = bitmap; + op.u.setvcpumapd5.cpumap.nr_cpus = nr_cpus; + } + ret = xenHypervisorDoV2Dom(handle, &op); + VIR_FREE(new); + + if (unlock_pages(cpumap, maplen) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " release"); + ret = -1; + } + } else { + cpumap_t xen_cpumap; /* limited to 64 CPUs in old hypervisors */ + uint64_t *pm = &xen_cpumap; + int j; + + if ((maplen > (int)sizeof(cpumap_t)) || (sizeof(cpumap_t) & 7)) + return (-1); + + memset(pm, 0, sizeof(cpumap_t)); + for (j = 0; j < maplen; j++) + *(pm + (j / 8)) |= cpumap[j] << (8 * (j & 7)); + + if (hypervisor_version == 1) { + xen_op_v1 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V1_OP_SETVCPUMAP; + op.u.setvcpumap.domain = (domid_t) id; + op.u.setvcpumap.vcpu = vcpu; + op.u.setvcpumap.cpumap = xen_cpumap; + ret = xenHypervisorDoV1Op(handle, &op); + } else if (hypervisor_version == 0) { + xen_op_v0 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V0_OP_SETVCPUMAP; + op.u.setvcpumap.domain = (domid_t) id; + op.u.setvcpumap.vcpu = vcpu; + op.u.setvcpumap.cpumap = xen_cpumap; + ret = xenHypervisorDoV0Op(handle, &op); + } + } + return(ret); +} +#endif /* !PROXY*/ + +/** + * virXen_getvcpusinfo: + * @handle: the hypervisor handle + * @id: the domain id + * @vcpu: the vcpu to map + * @cpumap: the bitmap for this vcpu + * @maplen: the size of the bitmap in bytes + * + * Do a low level hypercall to change the pinning for vcpu + * + * Returns 0 or -1 in case of failure + */ +static int +virXen_getvcpusinfo(int handle, int id, unsigned int vcpu, virVcpuInfoPtr ipt, + unsigned char *cpumap, int maplen) +{ + int ret = -1; + + if (hypervisor_version > 1) { + xen_op_v2_dom op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_GETVCPUINFO; + op.domain = (domid_t) id; + if (dom_interface_version < 5) + op.u.getvcpuinfo.vcpu = (uint16_t) vcpu; + else + op.u.getvcpuinfod5.vcpu = (uint16_t) vcpu; + ret = xenHypervisorDoV2Dom(handle, &op); + + if (ret < 0) + return(-1); + ipt->number = vcpu; + if (dom_interface_version < 5) { + if (op.u.getvcpuinfo.online) { + if (op.u.getvcpuinfo.running) + ipt->state = VIR_VCPU_RUNNING; + if (op.u.getvcpuinfo.blocked) + ipt->state = VIR_VCPU_BLOCKED; + } else + ipt->state = VIR_VCPU_OFFLINE; + + ipt->cpuTime = op.u.getvcpuinfo.cpu_time; + ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; + } else { + if (op.u.getvcpuinfod5.online) { + if (op.u.getvcpuinfod5.running) + ipt->state = VIR_VCPU_RUNNING; + if (op.u.getvcpuinfod5.blocked) + ipt->state = VIR_VCPU_BLOCKED; + } else + ipt->state = VIR_VCPU_OFFLINE; + + ipt->cpuTime = op.u.getvcpuinfod5.cpu_time; + ipt->cpu = op.u.getvcpuinfod5.online ? (int)op.u.getvcpuinfod5.cpu : -1; + } + if ((cpumap != NULL) && (maplen > 0)) { + if (lock_pages(cpumap, maplen) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); + return (-1); + } + memset(cpumap, 0, maplen); + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V2_OP_GETVCPUMAP; + op.domain = (domid_t) id; + if (dom_interface_version < 5) { + op.u.getvcpumap.vcpu = vcpu; + op.u.getvcpumap.cpumap.bitmap = cpumap; + op.u.getvcpumap.cpumap.nr_cpus = maplen * 8; + } else { + op.u.getvcpumapd5.vcpu = vcpu; + op.u.getvcpumapd5.cpumap.bitmap.v = cpumap; + op.u.getvcpumapd5.cpumap.nr_cpus = maplen * 8; + } + ret = xenHypervisorDoV2Dom(handle, &op); + if (unlock_pages(cpumap, maplen) < 0) { + virXenError(NULL, VIR_ERR_XEN_CALL, " release"); + ret = -1; + } + } + } else { + int mapl = maplen; + int cpu; + + if (maplen > (int)sizeof(cpumap_t)) + mapl = (int)sizeof(cpumap_t); + + if (hypervisor_version == 1) { + xen_op_v1 op; + + memset(&op, 0, sizeof(op)); + op.cmd = XEN_V1_OP_GETVCPUINFO; + op.u.getvcpuinfo.domain = (domid_t) id; + op.u.getvcpuinfo.vcpu = vcpu; + ret = xenHypervisorDoV1Op(handle, &op); + if (ret < 0) + return(-1); + ipt->number = vcpu; + if (op.u.getvcpuinfo.online) { + if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING; + if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED; + } + else ipt->state = VIR_VCPU_OFFLINE; + ipt->cpuTime = op.u.getvcpuinfo.cpu_time; + ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; + if ((cpumap != NULL) && (maplen > 0)) { + for (cpu = 0; cpu < (mapl * 8); cpu++) { + if (op.u.getvcpuinfo.cpumap & ((uint64_t)1<number = vcpu; + if (op.u.getvcpuinfo.online) { + if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING; + if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED; + } + else ipt->state = VIR_VCPU_OFFLINE; + ipt->cpuTime = op.u.getvcpuinfo.cpu_time; + ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; + if ((cpumap != NULL) && (maplen > 0)) { + for (cpu = 0; cpu < (mapl * 8); cpu++) { + if (op.u.getvcpuinfo.cpumap & ((uint64_t)1<privateData; + + if (initialized == 0) + if (xenHypervisorInit() == -1) + return -1; + + priv->handle = -1; + + ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR); + if (ret < 0) { + virXenError(conn, VIR_ERR_NO_XEN, "%s", XEN_HYPERVISOR_SOCKET); + return (-1); + } + + priv->handle = ret; + + return(0); +} + +/** + * xenHypervisorClose: + * @conn: pointer to the connection block + * + * Close the connection to the Xen hypervisor. + * + * Returns 0 in case of success or -1 in case of error. + */ +int +xenHypervisorClose(virConnectPtr conn) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if (conn == NULL) + return (-1); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (priv->handle < 0) + return -1; + + ret = close(priv->handle); + if (ret < 0) + return (-1); + + return (0); +} + + +/** + * xenHypervisorGetVersion: + * @conn: pointer to the connection block + * @hvVer: where to store the version + * + * Call the hypervisor to extracts his own internal API version + * + * Returns 0 in case of success, -1 in case of error + */ +int +xenHypervisorGetVersion(virConnectPtr conn, unsigned long *hvVer) +{ + xenUnifiedPrivatePtr priv; + + if (conn == NULL) + return -1; + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->handle < 0 || hvVer == NULL) + return (-1); + *hvVer = (hv_version >> 16) * 1000000 + (hv_version & 0xFFFF) * 1000; + return(0); +} + +struct guest_arch { + const char *model; + int bits; + int hvm; + int pae; + int nonpae; + int ia64_be; +}; + + +static virCapsPtr +xenHypervisorBuildCapabilities(virConnectPtr conn, + const char *hostmachine, + int host_pae, + const char *hvm_type, + struct guest_arch *guest_archs, + int nr_guest_archs) { + virCapsPtr caps; + int i; + int hv_major = hv_version >> 16; + int hv_minor = hv_version & 0xFFFF; + + if ((caps = virCapabilitiesNew(hostmachine, 1, 1)) == NULL) + goto no_memory; + + virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x00, 0x16, 0x3e }); + + if (hvm_type && STRNEQ(hvm_type, "") && + virCapabilitiesAddHostFeature(caps, hvm_type) < 0) + goto no_memory; + if (host_pae && + virCapabilitiesAddHostFeature(caps, "pae") < 0) + goto no_memory; + + + if (virCapabilitiesAddHostMigrateTransport(caps, + "xenmigr") < 0) + goto no_memory; + + + if (sys_interface_version >= SYS_IFACE_MIN_VERS_NUMA) { + if (xenDaemonNodeGetTopology(conn, caps) != 0) { + virCapabilitiesFree(caps); + return NULL; + } + } + + for (i = 0; i < nr_guest_archs; ++i) { + virCapsGuestPtr guest; + char const *const xen_machines[] = {guest_archs[i].hvm ? "xenfv" : "xenpv"}; + virCapsGuestMachinePtr *machines; + + if ((machines = virCapabilitiesAllocMachines(xen_machines, 1)) == NULL) + goto no_memory; + + if ((guest = virCapabilitiesAddGuest(caps, + guest_archs[i].hvm ? "hvm" : "xen", + guest_archs[i].model, + guest_archs[i].bits, + (STREQ(hostmachine, "x86_64") ? + "/usr/lib64/xen/bin/qemu-dm" : + "/usr/lib/xen/bin/qemu-dm"), + (guest_archs[i].hvm ? + "/usr/lib/xen/boot/hvmloader" : + NULL), + 1, + machines)) == NULL) { + virCapabilitiesFreeMachines(machines, 1); + goto no_memory; + } + machines = NULL; + + if (virCapabilitiesAddGuestDomain(guest, + "xen", + NULL, + NULL, + 0, + NULL) == NULL) + goto no_memory; + + if (guest_archs[i].pae && + virCapabilitiesAddGuestFeature(guest, + "pae", + 1, + 0) == NULL) + goto no_memory; + + if (guest_archs[i].nonpae && + virCapabilitiesAddGuestFeature(guest, + "nonpae", + 1, + 0) == NULL) + goto no_memory; + + if (guest_archs[i].ia64_be && + virCapabilitiesAddGuestFeature(guest, + "ia64_be", + 1, + 0) == NULL) + goto no_memory; + + if (guest_archs[i].hvm) { + if (virCapabilitiesAddGuestFeature(guest, + "acpi", + 1, 1) == NULL) + goto no_memory; + + // In Xen 3.1.0, APIC is always on and can't be toggled + if (virCapabilitiesAddGuestFeature(guest, + "apic", + 1, + (hv_major > 3 && + hv_minor > 0 ? + 0 : 1)) == NULL) + goto no_memory; + } + } + + return caps; + + no_memory: + virCapabilitiesFree(caps); + return NULL; +} + +#ifdef __sun + +static int +get_cpu_flags(virConnectPtr conn, const char **hvm, int *pae, int *longmode) +{ + struct { + uint32_t r_eax, r_ebx, r_ecx, r_edx; + } regs; + + char tmpbuf[20]; + int ret = 0; + int fd; + + /* returns -1, errno 22 if in 32-bit mode */ + *longmode = (sysinfo(SI_ARCHITECTURE_64, tmpbuf, sizeof(tmpbuf)) != -1); + + if ((fd = open("/dev/cpu/self/cpuid", O_RDONLY)) == -1 || + pread(fd, ®s, sizeof(regs), 0) != sizeof(regs)) { + char ebuf[1024]; + virXenError(conn, VIR_ERR_SYSTEM_ERROR, + "couldn't read CPU flags: %s", virStrerror(errno, ebuf, sizeof ebuf)); + goto out; + } + + *pae = 0; + *hvm = ""; + + if (STREQLEN((const char *)®s.r_ebx, "AuthcAMDenti", 12)) { + if (pread(fd, ®s, sizeof (regs), 0x80000001) == sizeof (regs)) { + /* Read secure virtual machine bit (bit 2 of ECX feature ID) */ + if ((regs.r_ecx >> 2) & 1) { + *hvm = "svm"; + } + if ((regs.r_edx >> 6) & 1) + *pae = 1; + } + } else if (STREQLEN((const char *)®s.r_ebx, "GenuntelineI", 12)) { + if (pread(fd, ®s, sizeof (regs), 0x00000001) == sizeof (regs)) { + /* Read VMXE feature bit (bit 5 of ECX feature ID) */ + if ((regs.r_ecx >> 5) & 1) + *hvm = "vmx"; + if ((regs.r_edx >> 6) & 1) + *pae = 1; + } + } + + ret = 1; + +out: + if (fd != -1) + close(fd); + return ret; +} + +static virCapsPtr +xenHypervisorMakeCapabilitiesSunOS(virConnectPtr conn) +{ + struct guest_arch guest_arches[32]; + int i = 0; + virCapsPtr caps = NULL; + struct utsname utsname; + int pae, longmode; + const char *hvm; + + if (!get_cpu_flags(conn, &hvm, &pae, &longmode)) + return NULL; + + /* Really, this never fails - look at the man-page. */ + uname (&utsname); + + guest_arches[i].model = "i686"; + guest_arches[i].bits = 32; + guest_arches[i].hvm = 0; + guest_arches[i].pae = pae; + guest_arches[i].nonpae = !pae; + guest_arches[i].ia64_be = 0; + i++; + + if (longmode) { + guest_arches[i].model = "x86_64"; + guest_arches[i].bits = 64; + guest_arches[i].hvm = 0; + guest_arches[i].pae = 0; + guest_arches[i].nonpae = 0; + guest_arches[i].ia64_be = 0; + i++; + } + + if (hvm[0] != '\0') { + guest_arches[i].model = "i686"; + guest_arches[i].bits = 32; + guest_arches[i].hvm = 1; + guest_arches[i].pae = pae; + guest_arches[i].nonpae = 1; + guest_arches[i].ia64_be = 0; + i++; + + if (longmode) { + guest_arches[i].model = "x86_64"; + guest_arches[i].bits = 64; + guest_arches[i].hvm = 1; + guest_arches[i].pae = 0; + guest_arches[i].nonpae = 0; + guest_arches[i].ia64_be = 0; + i++; + } + } + + if ((caps = xenHypervisorBuildCapabilities(conn, + utsname.machine, + pae, hvm, + guest_arches, i)) == NULL) + virReportOOMError(NULL); + + return caps; +} + +#endif /* __sun */ + +/** + * xenHypervisorMakeCapabilitiesInternal: + * @conn: pointer to the connection block + * @cpuinfo: file handle containing /proc/cpuinfo data, or NULL + * @capabilities: file handle containing /sys/hypervisor/properties/capabilities data, or NULL + * + * Return the capabilities of this hypervisor. + */ +virCapsPtr +xenHypervisorMakeCapabilitiesInternal(virConnectPtr conn, + const char *hostmachine, + FILE *cpuinfo, FILE *capabilities) +{ + char line[1024], *str, *token; + regmatch_t subs[4]; + char *saveptr = NULL; + int i; + + char hvm_type[4] = ""; /* "vmx" or "svm" (or "" if not in CPU). */ + int host_pae = 0; + struct guest_arch guest_archs[32]; + int nr_guest_archs = 0; + virCapsPtr caps = NULL; + + memset(guest_archs, 0, sizeof(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) + */ + if (cpuinfo) { + while (fgets (line, sizeof line, cpuinfo)) { + if (regexec (&flags_hvm_rec, line, sizeof(subs)/sizeof(regmatch_t), subs, 0) == 0 + && subs[0].rm_so != -1) { + strncpy (hvm_type, + &line[subs[1].rm_so], subs[1].rm_eo-subs[1].rm_so+1); + hvm_type[subs[1].rm_eo-subs[1].rm_so] = '\0'; + } else if (regexec (&flags_pae_rec, line, 0, NULL, 0) == 0) + host_pae = 1; + } + } + + /* 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 + */ + + /* Expecting one line in this file - ignore any more. */ + if ((capabilities) && (fgets (line, sizeof line, capabilities))) { + /* 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 < sizeof guest_archs / sizeof guest_archs[0] + && (token = strtok_r (str, " ", &saveptr)) != NULL; + str = NULL) { + + if (regexec (&xen_cap_rec, token, sizeof subs / sizeof subs[0], + subs, 0) == 0) { + int hvm = STRPREFIX(&token[subs[1].rm_so], "hvm"); + const char *model; + int bits, pae = 0, nonpae = 0, ia64_be = 0; + + if (STRPREFIX(&token[subs[2].rm_so], "x86_32")) { + model = "i686"; + bits = 32; + if (subs[3].rm_so != -1 && + STRPREFIX(&token[subs[3].rm_so], "p")) + pae = 1; + else + nonpae = 1; + } + else if (STRPREFIX(&token[subs[2].rm_so], "x86_64")) { + model = "x86_64"; + bits = 64; + } + else if (STRPREFIX(&token[subs[2].rm_so], "ia64")) { + model = "ia64"; + bits = 64; + if (subs[3].rm_so != -1 && + STRPREFIX(&token[subs[3].rm_so], "be")) + ia64_be = 1; + } + else if (STRPREFIX(&token[subs[2].rm_so], "powerpc64")) { + model = "ppc64"; + bits = 64; + } else { + /* XXX surely no other Xen archs exist */ + continue; + } + + /* Search for existing matching (model,hvm) tuple */ + for (i = 0 ; i < nr_guest_archs ; i++) { + if (STREQ(guest_archs[i].model, model) && + guest_archs[i].hvm == hvm) { + break; + } + } + + /* Too many arch flavours - highly unlikely ! */ + if (i >= ARRAY_CARDINALITY(guest_archs)) + continue; + /* Didn't find a match, so create a new one */ + if (i == nr_guest_archs) + nr_guest_archs++; + + guest_archs[i].model = model; + guest_archs[i].bits = bits; + guest_archs[i].hvm = hvm; + + /* Careful not to overwrite a previous positive + setting with a negative one here - some archs + can do both pae & non-pae, but Xen reports + separately capabilities so we're merging archs */ + if (pae) + guest_archs[i].pae = pae; + if (nonpae) + guest_archs[i].nonpae = nonpae; + if (ia64_be) + guest_archs[i].ia64_be = ia64_be; + } + } + } + + if ((caps = xenHypervisorBuildCapabilities(conn, + hostmachine, + host_pae, + hvm_type, + guest_archs, + nr_guest_archs)) == NULL) + goto no_memory; + + return caps; + + no_memory: + virReportOOMError(NULL); + virCapabilitiesFree(caps); + return NULL; +} + +/** + * xenHypervisorMakeCapabilities: + * + * Return the capabilities of this hypervisor. + */ +virCapsPtr +xenHypervisorMakeCapabilities(virConnectPtr conn) +{ +#ifdef __sun + return xenHypervisorMakeCapabilitiesSunOS(conn); +#else + virCapsPtr caps; + FILE *cpuinfo, *capabilities; + struct utsname utsname; + + /* Really, this never fails - look at the man-page. */ + uname (&utsname); + + cpuinfo = fopen ("/proc/cpuinfo", "r"); + if (cpuinfo == NULL) { + if (errno != ENOENT) { + virReportSystemError(conn, errno, + _("cannot read file %s"), + "/proc/cpuinfo"); + return NULL; + } + } + + capabilities = fopen ("/sys/hypervisor/properties/capabilities", "r"); + if (capabilities == NULL) { + if (errno != ENOENT) { + fclose(cpuinfo); + virReportSystemError(conn, errno, + _("cannot read file %s"), + "/sys/hypervisor/properties/capabilities"); + return NULL; + } + } + + caps = xenHypervisorMakeCapabilitiesInternal(conn, + utsname.machine, + cpuinfo, + capabilities); + + if (cpuinfo) + fclose(cpuinfo); + if (capabilities) + fclose(capabilities); + + return caps; +#endif /* __sun */ +} + + + +/** + * xenHypervisorGetCapabilities: + * @conn: pointer to the connection block + * + * Return the capabilities of this hypervisor. + */ +char * +xenHypervisorGetCapabilities (virConnectPtr conn) +{ + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + char *xml; + + if (!(xml = virCapabilitiesFormatXML(priv->caps))) { + virReportOOMError(conn); + return NULL; + } + + return xml; +} + + +/** + * xenHypervisorNumOfDomains: + * @conn: pointer to the connection block + * + * Provides the number of active domains. + * + * Returns the number of domain found or -1 in case of error + */ +int +xenHypervisorNumOfDomains(virConnectPtr conn) +{ + xen_getdomaininfolist dominfos; + int ret, nbids; + static int last_maxids = 2; + int maxids = last_maxids; + xenUnifiedPrivatePtr priv; + + if (conn == NULL) + return -1; + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->handle < 0) + return (-1); + + retry: + if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) { + virReportOOMError(conn); + return(-1); + } + + XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids); + + ret = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos); + + XEN_GETDOMAININFOLIST_FREE(dominfos); + + if (ret < 0) + return (-1); + + nbids = ret; + /* Can't possibly have more than 65,000 concurrent guests + * so limit how many times we try, to avoid increasing + * without bound & thus allocating all of system memory ! + * XXX I'll regret this comment in a few years time ;-) + */ + if (nbids == maxids) { + if (maxids < 65000) { + last_maxids *= 2; + maxids *= 2; + goto retry; + } + nbids = -1; + } + if ((nbids < 0) || (nbids > maxids)) + return(-1); + return(nbids); +} + +/** + * xenHypervisorListDomains: + * @conn: pointer to the connection block + * @ids: array to collect the list of IDs of active domains + * @maxids: size of @ids + * + * Collect the list of active domains, and store their ID in @maxids + * + * Returns the number of domain found or -1 in case of error + */ +int +xenHypervisorListDomains(virConnectPtr conn, int *ids, int maxids) +{ + xen_getdomaininfolist dominfos; + int ret, nbids, i; + xenUnifiedPrivatePtr priv; + + if (conn == NULL) + return -1; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->handle < 0 || + (ids == NULL) || (maxids < 0)) + return (-1); + + if (maxids == 0) + return(0); + + if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) { + virReportOOMError(conn); + return(-1); + } + + XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids); + memset(ids, 0, maxids * sizeof(int)); + + ret = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos); + + if (ret < 0) { + XEN_GETDOMAININFOLIST_FREE(dominfos); + return (-1); + } + + nbids = ret; + if ((nbids < 0) || (nbids > maxids)) { + XEN_GETDOMAININFOLIST_FREE(dominfos); + return(-1); + } + + for (i = 0;i < nbids;i++) { + ids[i] = XEN_GETDOMAININFOLIST_DOMAIN(dominfos, i); + } + + XEN_GETDOMAININFOLIST_FREE(dominfos); + return (nbids); +} + + +#ifndef PROXY +char * +xenHypervisorDomainGetOSType (virDomainPtr dom) +{ + xenUnifiedPrivatePtr priv; + xen_getdomaininfo dominfo; + + priv = (xenUnifiedPrivatePtr) dom->conn->privateData; + if (priv->handle < 0) + return (NULL); + + /* HV's earlier than 3.1.0 don't include the HVM flags in guests status*/ + if (hypervisor_version < 2 || + dom_interface_version < 4) + return (NULL); + + XEN_GETDOMAININFO_CLEAR(dominfo); + + if (virXen_getdomaininfo(priv->handle, dom->id, &dominfo) < 0) + return (NULL); + + if (XEN_GETDOMAININFO_DOMAIN(dominfo) != dom->id) + return (NULL); + + if (XEN_GETDOMAININFO_FLAGS(dominfo) & DOMFLAGS_HVM) + return strdup("hvm"); + return strdup("linux"); +} + +virDomainPtr +xenHypervisorLookupDomainByID(virConnectPtr conn, + int id) +{ + xenUnifiedPrivatePtr priv; + xen_getdomaininfo dominfo; + virDomainPtr ret; + char *name; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->handle < 0) + return (NULL); + + XEN_GETDOMAININFO_CLEAR(dominfo); + + if (virXen_getdomaininfo(priv->handle, id, &dominfo) < 0) + return (NULL); + + if (XEN_GETDOMAININFO_DOMAIN(dominfo) != id) + return (NULL); + + xenUnifiedLock(priv); + name = xenStoreDomainGetName(conn, id); + xenUnifiedUnlock(priv); + if (!name) + return (NULL); + + ret = virGetDomain(conn, name, XEN_GETDOMAININFO_UUID(dominfo)); + if (ret) + ret->id = id; + VIR_FREE(name); + return ret; +} + + +virDomainPtr +xenHypervisorLookupDomainByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + xen_getdomaininfolist dominfos; + xenUnifiedPrivatePtr priv; + virDomainPtr ret; + char *name; + int maxids = 100, nids, i, id; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->handle < 0) + return (NULL); + + retry: + if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) { + virReportOOMError(conn); + return(NULL); + } + + XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids); + + nids = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos); + + if (nids < 0) { + XEN_GETDOMAININFOLIST_FREE(dominfos); + return (NULL); + } + + /* Can't possibly have more than 65,000 concurrent guests + * so limit how many times we try, to avoid increasing + * without bound & thus allocating all of system memory ! + * XXX I'll regret this comment in a few years time ;-) + */ + if (nids == maxids) { + XEN_GETDOMAININFOLIST_FREE(dominfos); + if (maxids < 65000) { + maxids *= 2; + goto retry; + } + return (NULL); + } + + id = -1; + for (i = 0 ; i < nids ; i++) { + if (memcmp(XEN_GETDOMAININFOLIST_UUID(dominfos, i), uuid, VIR_UUID_BUFLEN) == 0) { + id = XEN_GETDOMAININFOLIST_DOMAIN(dominfos, i); + break; + } + } + XEN_GETDOMAININFOLIST_FREE(dominfos); + + if (id == -1) + return (NULL); + + xenUnifiedLock(priv); + name = xenStoreDomainGetName(conn, id); + xenUnifiedUnlock(priv); + if (!name) + return (NULL); + + ret = virGetDomain(conn, name, uuid); + if (ret) + ret->id = id; + VIR_FREE(name); + return ret; +} +#endif + +/** + * xenHypervisorGetMaxVcpus: + * + * Returns the maximum of CPU defined by Xen. + */ +int +xenHypervisorGetMaxVcpus(virConnectPtr conn, + const char *type ATTRIBUTE_UNUSED) +{ + xenUnifiedPrivatePtr priv; + + if (conn == NULL) + return -1; + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->handle < 0) + return (-1); + + return MAX_VIRT_CPUS; +} + +/** + * xenHypervisorGetDomMaxMemory: + * @conn: connection data + * @id: domain id + * + * Retrieve the maximum amount of physical memory allocated to a + * domain. + * + * Returns the memory size in kilobytes or 0 in case of error. + */ +unsigned long +xenHypervisorGetDomMaxMemory(virConnectPtr conn, int id) +{ + xenUnifiedPrivatePtr priv; + xen_getdomaininfo dominfo; + int ret; + + if (conn == NULL) + return 0; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->handle < 0) + return 0; + + if (kb_per_pages == 0) { + kb_per_pages = sysconf(_SC_PAGESIZE) / 1024; + if (kb_per_pages <= 0) + kb_per_pages = 4; + } + + XEN_GETDOMAININFO_CLEAR(dominfo); + + ret = virXen_getdomaininfo(priv->handle, id, &dominfo); + + if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != id)) + return (0); + + return((unsigned long) XEN_GETDOMAININFO_MAX_PAGES(dominfo) * kb_per_pages); +} + +#ifndef PROXY +/** + * xenHypervisorGetMaxMemory: + * @domain: a domain object or NULL + * + * Retrieve the maximum amount of physical memory allocated to a + * domain. If domain is NULL, then this get the amount of memory reserved + * to Domain0 i.e. the domain where the application runs. + * + * Returns the memory size in kilobytes or 0 in case of error. + */ +static unsigned long +xenHypervisorGetMaxMemory(virDomainPtr domain) +{ + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL)) + return 0; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || domain->id < 0) + return (0); + + return(xenHypervisorGetDomMaxMemory(domain->conn, domain->id)); +} +#endif + +/** + * xenHypervisorGetDomInfo: + * @conn: connection data + * @id: the domain ID + * @info: the place where information should be stored + * + * Do an hypervisor call to get the related set of domain information. + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenHypervisorGetDomInfo(virConnectPtr conn, int id, virDomainInfoPtr info) +{ + xenUnifiedPrivatePtr priv; + xen_getdomaininfo dominfo; + int ret; + uint32_t domain_flags, domain_state, domain_shutdown_cause; + + if (kb_per_pages == 0) { + kb_per_pages = sysconf(_SC_PAGESIZE) / 1024; + if (kb_per_pages <= 0) + kb_per_pages = 4; + } + + if (conn == NULL) + return -1; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->handle < 0 || info == NULL) + return (-1); + + memset(info, 0, sizeof(virDomainInfo)); + XEN_GETDOMAININFO_CLEAR(dominfo); + + ret = virXen_getdomaininfo(priv->handle, id, &dominfo); + + if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != id)) + return (-1); + + domain_flags = XEN_GETDOMAININFO_FLAGS(dominfo); + domain_flags &= ~DOMFLAGS_HVM; /* Mask out HVM flags */ + domain_state = domain_flags & 0xFF; /* Mask out high bits */ + switch (domain_state) { + case DOMFLAGS_DYING: + info->state = VIR_DOMAIN_SHUTDOWN; + break; + case DOMFLAGS_SHUTDOWN: + /* The domain is shutdown. Determine the cause. */ + domain_shutdown_cause = domain_flags >> DOMFLAGS_SHUTDOWNSHIFT; + switch (domain_shutdown_cause) { + case SHUTDOWN_crash: + info->state = VIR_DOMAIN_CRASHED; + break; + default: + info->state = VIR_DOMAIN_SHUTOFF; + } + break; + case DOMFLAGS_PAUSED: + info->state = VIR_DOMAIN_PAUSED; + break; + case DOMFLAGS_BLOCKED: + info->state = VIR_DOMAIN_BLOCKED; + break; + case DOMFLAGS_RUNNING: + info->state = VIR_DOMAIN_RUNNING; + break; + default: + info->state = VIR_DOMAIN_NOSTATE; + } + + /* + * the API brings back the cpu time in nanoseconds, + * convert to microseconds, same thing convert to + * kilobytes from page counts + */ + info->cpuTime = XEN_GETDOMAININFO_CPUTIME(dominfo); + info->memory = XEN_GETDOMAININFO_TOT_PAGES(dominfo) * kb_per_pages; + info->maxMem = XEN_GETDOMAININFO_MAX_PAGES(dominfo); + if(info->maxMem != UINT_MAX) + info->maxMem *= kb_per_pages; + info->nrVirtCpu = XEN_GETDOMAININFO_CPUCOUNT(dominfo); + return (0); +} + +/** + * xenHypervisorGetDomainInfo: + * @domain: pointer to the domain block + * @info: the place where information should be stored + * + * Do an hypervisor call to get the related set of domain information. + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenHypervisorGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info) +{ + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL)) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || info == NULL || + (domain->id < 0)) + return (-1); + + return(xenHypervisorGetDomInfo(domain->conn, domain->id, info)); + +} + +#ifndef PROXY +/** + * xenHypervisorNodeGetCellsFreeMemory: + * @conn: pointer to the hypervisor connection + * @freeMems: pointer to the array of unsigned long long + * @startCell: index of first cell to return freeMems info on. + * @maxCells: Maximum number of cells for which freeMems information can + * be returned. + * + * This call returns the amount of free memory in one or more NUMA cells. + * The @freeMems array must be allocated by the caller and will be filled + * with the amount of free memory in kilobytes for each cell requested, + * starting with startCell (in freeMems[0]), up to either + * (startCell + maxCells), or the number of additional cells in the node, + * whichever is smaller. + * + * Returns the number of entries filled in freeMems, or -1 in case of error. + */ +int +xenHypervisorNodeGetCellsFreeMemory(virConnectPtr conn, unsigned long long *freeMems, + int startCell, int maxCells) +{ + xen_op_v2_sys op_sys; + int i, j, ret; + xenUnifiedPrivatePtr priv; + + if (conn == NULL) { + virXenErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid argument", 0); + return -1; + } + + priv = conn->privateData; + + if (priv->nbNodeCells < 0) { + virXenErrorFunc (conn, VIR_ERR_XEN_CALL, __FUNCTION__, + "cannot determine actual number of cells",0); + return(-1); + } + + if ((maxCells < 1) || (startCell >= priv->nbNodeCells)) { + virXenErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, + "invalid argument", 0); + return -1; + } + + /* + * Support only sys_interface_version >=4 + */ + if (sys_interface_version < SYS_IFACE_MIN_VERS_NUMA) { + virXenErrorFunc (conn, VIR_ERR_XEN_CALL, __FUNCTION__, + "unsupported in sys interface < 4", 0); + return -1; + } + + if (priv->handle < 0) { + virXenErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, + "priv->handle invalid", 0); + return -1; + } + + memset(&op_sys, 0, sizeof(op_sys)); + op_sys.cmd = XEN_V2_OP_GETAVAILHEAP; + + for (i = startCell, j = 0;(i < priv->nbNodeCells) && (j < maxCells);i++,j++) { + if (sys_interface_version >= 5) + op_sys.u.availheap5.node = i; + else + op_sys.u.availheap.node = i; + ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); + if (ret < 0) { + return(-1); + } + if (sys_interface_version >= 5) + freeMems[j] = op_sys.u.availheap5.avail_bytes; + else + freeMems[j] = op_sys.u.availheap.avail_bytes; + } + return (j); +} + + +/** + * xenHypervisorPauseDomain: + * @domain: pointer to the domain block + * + * Do an hypervisor call to pause the given domain + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenHypervisorPauseDomain(virDomainPtr domain) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL)) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || domain->id < 0) + return (-1); + + ret = virXen_pausedomain(priv->handle, domain->id); + if (ret < 0) + return (-1); + return (0); +} + +/** + * xenHypervisorResumeDomain: + * @domain: pointer to the domain block + * + * Do an hypervisor call to resume the given domain + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenHypervisorResumeDomain(virDomainPtr domain) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL)) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || domain->id < 0) + return (-1); + + ret = virXen_unpausedomain(priv->handle, domain->id); + if (ret < 0) + return (-1); + return (0); +} + +/** + * xenHypervisorDestroyDomain: + * @domain: pointer to the domain block + * + * Do an hypervisor call to destroy the given domain + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenHypervisorDestroyDomain(virDomainPtr domain) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if (domain == NULL || domain->conn == NULL) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || domain->id < 0) + return (-1); + + ret = virXen_destroydomain(priv->handle, domain->id); + if (ret < 0) + return (-1); + return (0); +} + +/** + * xenHypervisorSetMaxMemory: + * @domain: pointer to the domain block + * @memory: the max memory size in kilobytes. + * + * Do an hypervisor call to change the maximum amount of memory used + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenHypervisorSetMaxMemory(virDomainPtr domain, unsigned long memory) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if (domain == NULL || domain->conn == NULL) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || domain->id < 0) + return (-1); + + ret = virXen_setmaxmem(priv->handle, domain->id, memory); + if (ret < 0) + return (-1); + return (0); +} +#endif /* PROXY */ + +#ifndef PROXY +/** + * xenHypervisorSetVcpus: + * @domain: pointer to domain object + * @nvcpus: the new number of virtual CPUs for this domain + * + * Dynamically change the number of virtual CPUs used by the domain. + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int +xenHypervisorSetVcpus(virDomainPtr domain, unsigned int nvcpus) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if (domain == NULL || domain->conn == NULL) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || domain->id < 0 || nvcpus < 1) + return (-1); + + ret = virXen_setmaxvcpus(priv->handle, domain->id, nvcpus); + if (ret < 0) + return (-1); + return (0); +} + +/** + * xenHypervisorPinVcpu: + * @domain: pointer to domain object + * @vcpu: virtual CPU number + * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) + * @maplen: length of cpumap in bytes + * + * Dynamically change the real CPUs which can be allocated to a virtual CPU. + * + * Returns 0 in case of success, -1 in case of failure. + */ + +int +xenHypervisorPinVcpu(virDomainPtr domain, unsigned int vcpu, + unsigned char *cpumap, int maplen) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if (domain == NULL || domain->conn == NULL) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || (domain->id < 0) || + (cpumap == NULL) || (maplen < 1)) + return (-1); + + ret = virXen_setvcpumap(priv->handle, domain->id, vcpu, + cpumap, maplen); + if (ret < 0) + return (-1); + return (0); +} +#endif + +/** + * virDomainGetVcpus: + * @domain: pointer to domain object, or NULL for Domain0 + * @info: pointer to an array of virVcpuInfo structures (OUT) + * @maxinfo: number of structures in info array + * @cpumaps: pointer to an bit map of real CPUs for all vcpus of this domain (in 8-bit bytes) (OUT) + * If cpumaps is NULL, then no cpumap information is returned by the API. + * It's assumed there is cpumap in cpumaps array. + * The memory allocated to cpumaps must be (maxinfo * maplen) bytes + * (ie: calloc(maxinfo, maplen)). + * One cpumap inside cpumaps has the format described in virDomainPinVcpu() API. + * @maplen: number of bytes in one cpumap, from 1 up to size of CPU map in + * underlying virtualization system (Xen...). + * + * Extract information about virtual CPUs of domain, store it in info array + * and also in cpumaps if this pointer isn't NULL. + * + * Returns the number of info filled in case of success, -1 in case of failure. + */ +#ifndef PROXY +int +xenHypervisorGetVcpus(virDomainPtr domain, virVcpuInfoPtr info, int maxinfo, + unsigned char *cpumaps, int maplen) +{ + xen_getdomaininfo dominfo; + int ret; + xenUnifiedPrivatePtr priv; + virVcpuInfoPtr ipt; + int nbinfo, i; + + if (domain == NULL || domain->conn == NULL) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0 || (domain->id < 0) || + (info == NULL) || (maxinfo < 1) || + (sizeof(cpumap_t) & 7)) + return (-1); + if ((cpumaps != NULL) && (maplen < 1)) + return -1; + + /* first get the number of virtual CPUs in this domain */ + XEN_GETDOMAININFO_CLEAR(dominfo); + ret = virXen_getdomaininfo(priv->handle, domain->id, + &dominfo); + + if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != domain->id)) + return (-1); + nbinfo = XEN_GETDOMAININFO_CPUCOUNT(dominfo) + 1; + if (nbinfo > maxinfo) nbinfo = maxinfo; + + if (cpumaps != NULL) + memset(cpumaps, 0, maxinfo * maplen); + + for (i = 0, ipt = info; i < nbinfo; i++, ipt++) { + if ((cpumaps != NULL) && (i < maxinfo)) { + ret = virXen_getvcpusinfo(priv->handle, domain->id, i, + ipt, + (unsigned char *)VIR_GET_CPUMAP(cpumaps, maplen, i), + maplen); + if (ret < 0) + return(-1); + } else { + ret = virXen_getvcpusinfo(priv->handle, domain->id, i, + ipt, NULL, 0); + if (ret < 0) + return(-1); + } + } + return nbinfo; +} +#endif /* PROXY */ + +/** + * xenHypervisorGetVcpuMax: + * + * Returns the maximum number of virtual CPUs supported for + * the guest VM. If the guest is inactive, this is the maximum + * of CPU defined by Xen. If the guest is running this reflect + * the maximum number of virtual CPUs the guest was booted with. + */ +int +xenHypervisorGetVcpuMax(virDomainPtr domain) +{ + xen_getdomaininfo dominfo; + int ret; + int maxcpu; + xenUnifiedPrivatePtr priv; + + if (domain == NULL || domain->conn == NULL) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->handle < 0) + return (-1); + + /* inactive domain */ + if (domain->id < 0) { + maxcpu = MAX_VIRT_CPUS; + } else { + XEN_GETDOMAININFO_CLEAR(dominfo); + ret = virXen_getdomaininfo(priv->handle, domain->id, + &dominfo); + + if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != domain->id)) + return (-1); + maxcpu = XEN_GETDOMAININFO_MAXCPUID(dominfo) + 1; + } + + return maxcpu; +} + +/** + * xenHavePrivilege() + * + * Return true if the current process should be able to connect to Xen. + */ +int +xenHavePrivilege() +{ +#ifdef __sun + return priv_ineffect (PRIV_XVM_CONTROL); +#else + return access(XEN_HYPERVISOR_SOCKET, R_OK) == 0; +#endif +} diff --git a/src/xen/xen_hypervisor.h b/src/xen/xen_hypervisor.h new file mode 100644 index 0000000000..766f676836 --- /dev/null +++ b/src/xen/xen_hypervisor.h @@ -0,0 +1,109 @@ +/* + * xen_internal.h: internal API for direct access to Xen hypervisor level + * + * Copyright (C) 2005 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Veillard + */ + +#ifndef __VIR_XEN_INTERNAL_H__ +#define __VIR_XEN_INTERNAL_H__ + +#include + +#include "internal.h" +#include "capabilities.h" +#include "driver.h" + +extern struct xenUnifiedDriver xenHypervisorDriver; +int xenHypervisorInit (void); + +virCapsPtr xenHypervisorMakeCapabilities (virConnectPtr conn); + +/* The following calls are made directly by the Xen proxy: */ + +virDomainPtr + xenHypervisorLookupDomainByID (virConnectPtr conn, + int id); +virDomainPtr + xenHypervisorLookupDomainByUUID (virConnectPtr conn, + const unsigned char *uuid); +char * + xenHypervisorDomainGetOSType (virDomainPtr dom); + +virDrvOpenStatus + xenHypervisorOpen (virConnectPtr conn, + virConnectAuthPtr auth, + int flags); +int xenHypervisorClose (virConnectPtr conn); +int xenHypervisorGetVersion (virConnectPtr conn, + unsigned long *hvVer); +virCapsPtr + xenHypervisorMakeCapabilitiesInternal(virConnectPtr conn, + const char *hostmachine, + FILE *cpuinfo, + FILE *capabilities); +char * + xenHypervisorGetCapabilities (virConnectPtr conn); +unsigned long + xenHypervisorGetDomMaxMemory (virConnectPtr conn, + int id); +int xenHypervisorNumOfDomains (virConnectPtr conn); +int xenHypervisorListDomains (virConnectPtr conn, + int *ids, + int maxids); +int xenHypervisorGetMaxVcpus (virConnectPtr conn, + const char *type); +int xenHypervisorDestroyDomain (virDomainPtr domain); +int xenHypervisorResumeDomain (virDomainPtr domain); +int xenHypervisorPauseDomain (virDomainPtr domain); +int xenHypervisorGetDomainInfo (virDomainPtr domain, + virDomainInfoPtr info); +int xenHypervisorGetDomInfo (virConnectPtr conn, + int id, + virDomainInfoPtr info); +int xenHypervisorSetMaxMemory (virDomainPtr domain, + unsigned long memory); +int xenHypervisorCheckID (virConnectPtr conn, + int id); +int xenHypervisorSetVcpus (virDomainPtr domain, + unsigned int nvcpus); +int xenHypervisorPinVcpu (virDomainPtr domain, + unsigned int vcpu, + unsigned char *cpumap, + int maplen); +int xenHypervisorGetVcpus (virDomainPtr domain, + virVcpuInfoPtr info, + int maxinfo, + unsigned char *cpumaps, + int maplen); +int xenHypervisorGetVcpuMax (virDomainPtr domain); + +char * xenHypervisorGetSchedulerType (virDomainPtr domain, + int *nparams); + +int xenHypervisorGetSchedulerParameters(virDomainPtr domain, + virSchedParameterPtr params, + int *nparams); + +int xenHypervisorSetSchedulerParameters(virDomainPtr domain, + virSchedParameterPtr params, + int nparams); + +int xenHypervisorDomainBlockStats (virDomainPtr domain, + const char *path, + struct _virDomainBlockStats *stats); +int xenHypervisorDomainInterfaceStats (virDomainPtr domain, + const char *path, + struct _virDomainInterfaceStats *stats); + +int xenHypervisorNodeGetCellsFreeMemory(virConnectPtr conn, + unsigned long long *freeMems, + int startCell, + int maxCells); + +int xenHavePrivilege(void); + +#endif /* __VIR_XEN_INTERNAL_H__ */ diff --git a/src/xen/xen_inotify.c b/src/xen/xen_inotify.c new file mode 100644 index 0000000000..7deb1dbe99 --- /dev/null +++ b/src/xen/xen_inotify.c @@ -0,0 +1,492 @@ +/* + * xen_inofify.c: Xen notification of xml file activity in the + * following dirs: + * /etc/xen + * /var/lib/xend/domains + * + * Copyright (C) 2008 VirtualIron + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Ben Guthro + */ +#include +#include +#include + +#include "virterror_internal.h" +#include "datatypes.h" +#include "driver.h" +#include "memory.h" +#include "event.h" +#include "xen_driver.h" +#include "conf.h" +#include "domain_conf.h" +#include "xen_inotify.h" +#include "xend_internal.h" +#include "logging.h" +#include "uuid.h" + +#include "xm_internal.h" /* for xenXMDomainConfigParse */ + +#define VIR_FROM_THIS VIR_FROM_XEN_INOTIFY + +#define virXenInotifyError(conn, code, fmt...) \ + virReportErrorHelper(NULL, VIR_FROM_XEN_INOTIFY, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +#define LIBVIRTD_DOMAINS_DIR "/var/lib/xend/domains" + +struct xenUnifiedDriver xenInotifyDriver = { + xenInotifyOpen, /* open */ + xenInotifyClose, /* close */ + NULL, /* version */ + NULL, /* hostname */ + NULL, /* nodeGetInfo */ + NULL, /* getCapabilities */ + NULL, /* listDomains */ + NULL, /* numOfDomains */ + NULL, /* domainCreateLinux */ + NULL, /* domainSuspend */ + NULL, /* domainResume */ + NULL, /* domainShutdown */ + NULL, /* domainReboot */ + NULL, /* domainDestroy */ + NULL, /* domainGetOSType */ + NULL, /* domainGetMaxMemory */ + NULL, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + NULL, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + NULL, /* domainCoreDump */ + NULL, /* domainSetVcpus */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + NULL, /* domainGetMaxVcpus */ + NULL, /* listDefinedDomains */ + NULL, /* numOfDefinedDomains */ + NULL, /* domainCreate */ + NULL, /* domainDefineXML */ + NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ + NULL, /* domainGetAutostart */ + NULL, /* domainSetAutostart */ + NULL, /* domainGetSchedulerType */ + NULL, /* domainGetSchedulerParameters */ + NULL, /* domainSetSchedulerParameters */ +}; + +static int +xenInotifyXenCacheLookup(virConnectPtr conn, + const char *filename, + char **name, unsigned char *uuid) { + xenUnifiedPrivatePtr priv = conn->privateData; + xenXMConfCachePtr entry; + + if (!(entry = virHashLookup(priv->configCache, filename))) { + DEBUG("No config found for %s", filename); + return -1; + } + + *name = strdup(entry->def->name); + memcpy(uuid, entry->def->uuid, VIR_UUID_BUFLEN); + + if (!*name) { + DEBUG0("Error getting dom from def"); + return -1; + } + return 0; +} + +static int +xenInotifyXendDomainsDirLookup(virConnectPtr conn, const char *filename, + char **name, unsigned char *uuid) { + int i; + virDomainPtr dom; + const char *uuid_str; + unsigned char rawuuid[VIR_UUID_BUFLEN]; + xenUnifiedPrivatePtr priv = conn->privateData; + + /* xend is managing domains. we will get + * a filename in the manner: + * /var/lib/xend/domains// + */ + uuid_str = filename + strlen(LIBVIRTD_DOMAINS_DIR) + 1; + + if (virUUIDParse(uuid_str, rawuuid) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + _("parsing uuid %s"), uuid_str); + return -1; + } + /* call directly into xend here, as driver may not yet + be set during open while we are building our + initial list of domains */ + DEBUG("Looking for dom with uuid: %s", uuid_str); + /* XXX Should not have to go via a virDomainPtr obj instance */ + if(!(dom = xenDaemonLookupByUUID(conn, rawuuid))) { + /* If we are here, the domain has gone away. + search for, and create a domain from the stored + list info */ + for (i = 0 ; i < priv->configInfoList->count ; i++) { + if (!memcmp(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) { + *name = strdup(priv->configInfoList->doms[i]->name); + if (!*name) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + _("finding dom for %s"), uuid_str); + return -1; + } + memcpy(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN); + DEBUG0("Found dom on list"); + return 0; + } + } + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("finding dom on config list")); + return -1; + } + + if (!(*name = strdup(dom->name))) + return -1; + memcpy(uuid, dom->uuid, VIR_UUID_BUFLEN); + virDomainFree(dom); + /* succeeded too find domain by uuid */ + return 0; +} + +static int +xenInotifyDomainLookup(virConnectPtr conn, + const char *filename, + char **name, unsigned char *uuid) { + xenUnifiedPrivatePtr priv = conn->privateData; + if (priv->useXenConfigCache) + return xenInotifyXenCacheLookup(conn, filename, name, uuid); + else + return xenInotifyXendDomainsDirLookup(conn, filename, name, uuid); +} + +static virDomainEventPtr +xenInotifyDomainEventFromFile(virConnectPtr conn, + const char *filename, + int type, int detail) { + virDomainEventPtr event; + char *name = NULL; + unsigned char uuid[VIR_UUID_BUFLEN]; + + if (xenInotifyDomainLookup(conn, filename, &name, uuid) < 0) + return NULL; + + event = virDomainEventNew(-1, name, uuid, type, detail); + VIR_FREE(name); + return event; +} + +static int +xenInotifyXendDomainsDirRemoveEntry(virConnectPtr conn, + const char *fname) { + xenUnifiedPrivatePtr priv = conn->privateData; + const char *uuidstr = fname + strlen(LIBVIRTD_DOMAINS_DIR) + 1; + unsigned char uuid[VIR_UUID_BUFLEN]; + int i; + + if (virUUIDParse(uuidstr, uuid) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + _("parsing uuid %s"), uuidstr); + return -1; + } + + /* match and remove on uuid */ + for (i = 0 ; i < priv->configInfoList->count ; i++) { + if (!memcmp(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) { + VIR_FREE(priv->configInfoList->doms[i]->name); + VIR_FREE(priv->configInfoList->doms[i]); + + if (i < (priv->configInfoList->count - 1)) + memmove(priv->configInfoList->doms + i, + priv->configInfoList->doms + i + 1, + sizeof(*(priv->configInfoList->doms)) * + (priv->configInfoList->count - (i + 1))); + + if (VIR_REALLOC_N(priv->configInfoList->doms, + priv->configInfoList->count - 1) < 0) { + ; /* Failure to reduce memory allocation isn't fatal */ + } + priv->configInfoList->count--; + return 0; + } + } + return -1; +} + +static int +xenInotifyXendDomainsDirAddEntry(virConnectPtr conn, + const char *fname) { + char *name = NULL; + unsigned char uuid[VIR_UUID_BUFLEN]; + xenUnifiedPrivatePtr priv = conn->privateData; + + if (xenInotifyDomainLookup(conn, fname, &name, uuid) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Error looking up domain")); + return -1; + } + + if (xenUnifiedAddDomainInfo(priv->configInfoList, + -1, name, uuid) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Error adding file to config cache")); + VIR_FREE(name); + return -1; + } + VIR_FREE(name); + return 0; +} + +static int +xenInotifyRemoveDomainConfigInfo(virConnectPtr conn, + const char *fname) { + xenUnifiedPrivatePtr priv = conn->privateData; + return priv->useXenConfigCache ? + xenXMConfigCacheRemoveFile(conn, fname) : + xenInotifyXendDomainsDirRemoveEntry(conn, fname); +} + +static int +xenInotifyAddDomainConfigInfo(virConnectPtr conn, + const char *fname) { + xenUnifiedPrivatePtr priv = conn->privateData; + return priv->useXenConfigCache ? + xenXMConfigCacheAddFile(conn, fname) : + xenInotifyXendDomainsDirAddEntry(conn, fname); +} + +static void +xenInotifyEvent(int watch ATTRIBUTE_UNUSED, + int fd, + int events ATTRIBUTE_UNUSED, + void *data) +{ + char buf[1024]; + char fname[1024]; + struct inotify_event *e; + int got; + char *tmp, *name; + virConnectPtr conn = data; + xenUnifiedPrivatePtr priv = NULL; + + DEBUG0("got inotify event"); + + if( conn && conn->privateData ) { + priv = conn->privateData; + } else { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("conn, or private data is NULL")); + return; + } + + xenUnifiedLock(priv); + +reread: + got = read(fd, buf, sizeof(buf)); + if (got == -1) { + if (errno == EINTR) + goto reread; + goto cleanup; + } + + tmp = buf; + while (got) { + if (got < sizeof(struct inotify_event)) + goto cleanup; /* bad */ + + e = (struct inotify_event *)tmp; + tmp += sizeof(struct inotify_event); + got -= sizeof(struct inotify_event); + + if (got < e->len) + goto cleanup; + + tmp += e->len; + got -= e->len; + + name = (char *)&(e->name); + + snprintf(fname, 1024, "%s/%s", + priv->configDir, name); + + if (e->mask & (IN_DELETE | IN_MOVED_FROM)) { + virDomainEventPtr event = + xenInotifyDomainEventFromFile(conn, fname, + VIR_DOMAIN_EVENT_UNDEFINED, + VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); + if (!event) + xenUnifiedDomainEventDispatch(conn->privateData, event); + else + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("looking up dom")); + + if (xenInotifyRemoveDomainConfigInfo(conn, fname) < 0 ) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Error adding file to config cache")); + goto cleanup; + } + } else if (e->mask & ( IN_CREATE | IN_CLOSE_WRITE | IN_MOVED_TO) ) { + virDomainEventPtr event; + if (xenInotifyAddDomainConfigInfo(conn, fname) < 0 ) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Error adding file to config cache")); + goto cleanup; + } + + event = xenInotifyDomainEventFromFile(conn, fname, + VIR_DOMAIN_EVENT_DEFINED, + VIR_DOMAIN_EVENT_DEFINED_ADDED); + + if (event) + xenUnifiedDomainEventDispatch(conn->privateData, event); + else + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("looking up dom")); + + } + + } + +cleanup: + xenUnifiedUnlock(priv); +} + +/** + * xenInotifyOpen: + * @conn: pointer to the connection block + * @name: URL for the target, NULL for local + * @flags: combination of virDrvOpenFlag(s) + * + * Connects and starts listening for inotify events + * + * Returns 0 or -1 in case of error. + */ +virDrvOpenStatus +xenInotifyOpen(virConnectPtr conn ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + DIR *dh; + struct dirent *ent; + char path[PATH_MAX]; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (priv->configDir) { + priv->useXenConfigCache = 1; + } else { + /* /var/lib/xend/domains//config.sxp */ + priv->configDir = LIBVIRTD_DOMAINS_DIR; + priv->useXenConfigCache = 0; + + if (VIR_ALLOC(priv->configInfoList) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to allocate configInfoList")); + return -1; + } + + /* populate initial list */ + if (!(dh = opendir(priv->configDir))) { + virReportSystemError(NULL, errno, + _("cannot open directory: %s"), + priv->configDir); + return -1; + } + while ((ent = readdir(dh))) { + if (STRPREFIX(ent->d_name, ".")) + continue; + + /* Build the full file path */ + if ((strlen(priv->configDir) + 1 + + strlen(ent->d_name) + 1) > PATH_MAX) + continue; + strcpy(path, priv->configDir); + strcat(path, "/"); + strcat(path, ent->d_name); + + if (xenInotifyAddDomainConfigInfo(conn, path) < 0 ) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Error adding file to config list")); + closedir(dh); + return -1; + } + } + closedir(dh); + } + + if ((priv->inotifyFD = inotify_init()) < 0) { + virReportSystemError(NULL, errno, + "%s", _("initializing inotify")); + return -1; + } + + DEBUG("Adding a watch on %s", priv->configDir); + if (inotify_add_watch(priv->inotifyFD, + priv->configDir, + IN_CREATE | + IN_CLOSE_WRITE | IN_DELETE | + IN_MOVED_TO | IN_MOVED_FROM) < 0) { + virReportSystemError(NULL, errno, + _("adding watch on %s"), + priv->configDir); + return -1; + } + + DEBUG0("Building initial config cache"); + if (priv->useXenConfigCache && + xenXMConfigCacheRefresh (conn) < 0) { + DEBUG("Failed to enable XM config cache %s", conn->err.message); + return -1; + } + + DEBUG0("Registering with event loop"); + /* Add the handle for monitoring */ + if ((priv->inotifyWatch = virEventAddHandle(priv->inotifyFD, VIR_EVENT_HANDLE_READABLE, + xenInotifyEvent, conn, NULL)) < 0) { + DEBUG0("Failed to add inotify handle, disabling events"); + } + + virConnectRef(conn); + return 0; +} + +/** + * xenInotifyClose: + * @conn: pointer to the connection block + * + * Close and stop listening for inotify events + * + * Returns 0 in case of success or -1 in case of error. + */ +int +xenInotifyClose(virConnectPtr conn) +{ + xenUnifiedPrivatePtr priv = conn->privateData; + + if (priv->configInfoList) + xenUnifiedDomainInfoListFree(priv->configInfoList); + + if (priv->inotifyWatch != -1) + virEventRemoveHandle(priv->inotifyWatch); + close(priv->inotifyFD); + virUnrefConnect(conn); + + return 0; +} diff --git a/src/xen/xen_inotify.h b/src/xen/xen_inotify.h new file mode 100644 index 0000000000..70bc63cbed --- /dev/null +++ b/src/xen/xen_inotify.h @@ -0,0 +1,34 @@ +/* + * xen_inofify.h: Xen notification of xml files + * + * Copyright (C) 2008 VirtualIron + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Ben Guthro + */ +#ifndef __VIR_XEN_INOTIFY_H__ +#define __VIR_XEN_INOTIFY_H__ + +#include "internal.h" +#include "driver.h" + +extern struct xenUnifiedDriver xenInotifyDriver; + +virDrvOpenStatus xenInotifyOpen (virConnectPtr conn, + virConnectAuthPtr auth, + int flags); +int xenInotifyClose (virConnectPtr conn); +#endif diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c new file mode 100644 index 0000000000..350ef8ce13 --- /dev/null +++ b/src/xen/xend_internal.c @@ -0,0 +1,5914 @@ +/* + * xend_internal.c: access to Xen though the Xen Daemon interface + * + * Copyright (C) 2005 + * + * Anthony Liguori + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.LIB in the main directory of this + * archive for more details. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "virterror_internal.h" +#include "logging.h" +#include "datatypes.h" +#include "xend_internal.h" +#include "driver.h" +#include "util.h" +#include "sexpr.h" +#include "buf.h" +#include "uuid.h" +#include "xen_driver.h" +#include "xen_hypervisor.h" +#include "xs_internal.h" /* To extract VNC port & Serial console TTY */ +#include "memory.h" + +/* required for cpumap_t */ +#include + +#define VIR_FROM_THIS VIR_FROM_XEND + +#ifndef PROXY + +/* + * The number of Xen scheduler parameters + */ +#define XEN_SCHED_SEDF_NPARAM 6 +#define XEN_SCHED_CRED_NPARAM 2 + +#endif /* PROXY */ + +#ifdef WITH_RHEL5_API +#define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 0 +#define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 2 +#else +#define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 3 +#define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 3 +#endif + + +#ifndef PROXY +static int +xenDaemonFormatSxprDisk(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainDiskDefPtr def, + virBufferPtr buf, + int hvm, + int xendConfigVersion, + int isAttach); +static int +xenDaemonFormatSxprNet(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainNetDefPtr def, + virBufferPtr buf, + int hvm, + int xendConfigVersion, + int isAttach); +static int +xenDaemonFormatSxprOnePCI(virConnectPtr conn, + virDomainHostdevDefPtr def, + virBufferPtr buf); + +static int +virDomainXMLDevID(virDomainPtr domain, + virDomainDeviceDefPtr dev, + char *class, + char *ref, + int ref_len); +#endif + +#define virXendError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_XEND, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +#define virXendErrorInt(conn, code, ival) \ + virXendError(conn, code, "%d", ival) + +/** + * do_connect: + * @xend: pointer to the Xen Daemon structure + * + * Internal routine to (re)connect to the daemon + * + * Returns the socket file descriptor or -1 in case of error + */ +static int +do_connect(virConnectPtr xend) +{ + int s; + int serrno; + int no_slow_start = 1; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) xend->privateData; + + s = socket(priv->addrfamily, SOCK_STREAM, priv->addrprotocol); + if (s == -1) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to create a socket")); + return -1; + } + + /* + * try to desactivate slow-start + */ + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start, + sizeof(no_slow_start)); + + + if (connect(s, (struct sockaddr *)&priv->addr, priv->addrlen) == -1) { + serrno = errno; + close(s); + errno = serrno; + s = -1; + + /* + * Connecting to XenD when privileged is mandatory, so log this + * error + */ + if (xenHavePrivilege()) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to connect to xend")); + } + } + + return s; +} + +/** + * wr_sync: + * @xend: the xend connection object + * @fd: the file descriptor + * @buffer: the I/O buffer + * @size: the size of the I/O + * @do_read: write operation if 0, read operation otherwise + * + * Do a synchronous read or write on the file descriptor + * + * Returns the number of bytes exchanged, or -1 in case of error + */ +static size_t +wr_sync(virConnectPtr xend, int fd, void *buffer, size_t size, int do_read) +{ + size_t offset = 0; + + while (offset < size) { + ssize_t len; + + if (do_read) { + len = read(fd, ((char *) buffer) + offset, size - offset); + } else { + len = write(fd, ((char *) buffer) + offset, size - offset); + } + + /* recoverable error, retry */ + if ((len == -1) && ((errno == EAGAIN) || (errno == EINTR))) { + continue; + } + + /* eof */ + if (len == 0) { + break; + } + + /* unrecoverable error */ + if (len == -1) { + if (do_read) + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to read from Xen Daemon")); + else + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to read from Xen Daemon")); + + return (-1); + } + + offset += len; + } + + return offset; +} + +/** + * sread: + * @xend: the xend connection object + * @fd: the file descriptor + * @buffer: the I/O buffer + * @size: the size of the I/O + * + * Internal routine to do a synchronous read + * + * Returns the number of bytes read, or -1 in case of error + */ +static ssize_t +sread(virConnectPtr xend, int fd, void *buffer, size_t size) +{ + return wr_sync(xend, fd, buffer, size, 1); +} + +/** + * swrite: + * @xend: the xend connection object + * @fd: the file descriptor + * @buffer: the I/O buffer + * @size: the size of the I/O + * + * Internal routine to do a synchronous write + * + * Returns the number of bytes written, or -1 in case of error + */ +static ssize_t +swrite(virConnectPtr xend, int fd, const void *buffer, size_t size) +{ + return wr_sync(xend, fd, (void *) buffer, size, 0); +} + +/** + * swrites: + * @xend: the xend connection object + * @fd: the file descriptor + * @string: the string to write + * + * Internal routine to do a synchronous write of a string + * + * Returns the number of bytes written, or -1 in case of error + */ +static ssize_t +swrites(virConnectPtr xend, int fd, const char *string) +{ + return swrite(xend, fd, string, strlen(string)); +} + +/** + * sreads: + * @xend: the xend connection object + * @fd: the file descriptor + * @buffer: the I/O buffer + * @n_buffer: the size of the I/O buffer + * + * Internal routine to do a synchronous read of a line + * + * Returns the number of bytes read, or -1 in case of error + */ +static ssize_t +sreads(virConnectPtr xend, int fd, char *buffer, size_t n_buffer) +{ + size_t offset; + + if (n_buffer < 1) + return (-1); + + for (offset = 0; offset < (n_buffer - 1); offset++) { + ssize_t ret; + + ret = sread(xend, fd, buffer + offset, 1); + if (ret == 0) + break; + else if (ret == -1) + return ret; + + if (buffer[offset] == '\n') { + offset++; + break; + } + } + buffer[offset] = 0; + + return offset; +} + +static int +istartswith(const char *haystack, const char *needle) +{ + return STRCASEEQLEN(haystack, needle, strlen(needle)); +} + + +/** + * xend_req: + * @xend: the xend connection object + * @fd: the file descriptor + * @content: the buffer to store the content + * @n_content: the size of the buffer + * + * Read the HTTP response from a Xen Daemon request. + * + * Returns the HTTP return code. + */ +static int +xend_req(virConnectPtr xend, int fd, char *content, size_t n_content) +{ + char buffer[4096]; + int content_length = -1; + int retcode = 0; + + while (sreads(xend, fd, buffer, sizeof(buffer)) > 0) { + if (STREQ(buffer, "\r\n")) + break; + + if (istartswith(buffer, "Content-Length: ")) + content_length = atoi(buffer + 16); + else if (istartswith(buffer, "HTTP/1.1 ")) + retcode = atoi(buffer + 9); + } + + if (content_length > -1) { + ssize_t ret; + + if ((unsigned int) content_length > (n_content + 1)) + content_length = n_content - 1; + + ret = sread(xend, fd, content, content_length); + if (ret < 0) + return -1; + + content[ret] = 0; + } else { + content[0] = 0; + } + + return retcode; +} + +/** + * xend_get: + * @xend: pointer to the Xen Daemon structure + * @path: the path used for the HTTP request + * @content: the buffer to store the content + * @n_content: the size of the buffer + * + * Do an HTTP GET RPC with the Xen Daemon + * + * Returns the HTTP return code or -1 in case or error. + */ +static int +xend_get(virConnectPtr xend, const char *path, + char *content, size_t n_content) +{ + int ret; + int s = do_connect(xend); + + if (s == -1) + return s; + + swrites(xend, s, "GET "); + swrites(xend, s, path); + swrites(xend, s, " HTTP/1.1\r\n"); + + swrites(xend, s, + "Host: localhost:8000\r\n" + "Accept-Encoding: identity\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" "\r\n"); + + ret = xend_req(xend, s, content, n_content); + close(s); + + if (((ret < 0) || (ret >= 300)) && + ((ret != 404) || (!STRPREFIX(path, "/xend/domain/")))) { + virXendError(xend, VIR_ERR_GET_FAILED, + _("%d status from xen daemon: %s:%s"), + ret, path, content); + } + + return ret; +} + +#ifndef PROXY +/** + * xend_post: + * @xend: pointer to the Xen Daemon structure + * @path: the path used for the HTTP request + * @ops: the information sent for the POST + * @content: the buffer to store the content + * @n_content: the size of the buffer + * + * Do an HTTP POST RPC with the Xen Daemon, this usually makes changes at the + * Xen level. + * + * Returns the HTTP return code or -1 in case or error. + */ +static int +xend_post(virConnectPtr xend, const char *path, const char *ops, + char *content, size_t n_content) +{ + char buffer[100]; + int ret; + int s = do_connect(xend); + + if (s == -1) + return s; + + swrites(xend, s, "POST "); + swrites(xend, s, path); + swrites(xend, s, " HTTP/1.1\r\n"); + + swrites(xend, s, + "Host: localhost:8000\r\n" + "Accept-Encoding: identity\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: "); + snprintf(buffer, sizeof(buffer), "%d", (int) strlen(ops)); + swrites(xend ,s, buffer); + swrites(xend, s, "\r\n\r\n"); + swrites(xend, s, ops); + + ret = xend_req(xend, s, content, n_content); + close(s); + + if ((ret < 0) || (ret >= 300)) { + virXendError(xend, VIR_ERR_POST_FAILED, + _("xend_post: error from xen daemon: %s"), content); + } else if ((ret == 202) && (strstr(content, "failed") != NULL)) { + virXendError(xend, VIR_ERR_POST_FAILED, + _("xend_post: error from xen daemon: %s"), content); + ret = -1; + } else if (((ret >= 200) && (ret <= 202)) && (strstr(content, "xend.err") != NULL)) { + /* This is to catch case of things like 'virsh dump Domain-0 foo' + * which returns a success code, but the word 'xend.err' + * in body to indicate error :-( + */ + virXendError(xend, VIR_ERR_POST_FAILED, + _("xend_post: error from xen daemon: %s"), content); + ret = -1; + } + + return ret; +} +#endif /* ! PROXY */ + + +/** + * http2unix: + * @xend: the xend connection object + * @ret: the http return code + * + * Convert the HTTP return code to 0/-1 and set errno if needed + * + * Return -1 in case of error code 0 otherwise + */ +static int +http2unix(virConnectPtr xend, int ret) +{ + switch (ret) { + case -1: + break; + case 200: + case 201: + case 202: + return 0; + case 404: + errno = ESRCH; + break; + case 500: + errno = EIO; + break; + default: + virXendErrorInt(xend, VIR_ERR_HTTP_ERROR, ret); + errno = EINVAL; + break; + } + return -1; +} + +#ifndef PROXY +/** + * xend_op_ext: + * @xend: pointer to the Xen Daemon structure + * @path: path for the object + * @error: buffer for the error output + * @n_error: size of @error + * @key: the key for the operation + * @ap: input values to pass to the operation + * + * internal routine to run a POST RPC operation to the Xen Daemon + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xend_op_ext(virConnectPtr xend, const char *path, char *error, + size_t n_error, const char *key, va_list ap) +{ + const char *k = key, *v; + virBuffer buf = VIR_BUFFER_INITIALIZER; + int ret; + char *content; + + while (k) { + v = va_arg(ap, const char *); + + virBufferVSprintf(&buf, "%s", k); + virBufferVSprintf(&buf, "%s", "="); + virBufferVSprintf(&buf, "%s", v); + k = va_arg(ap, const char *); + + if (k) + virBufferVSprintf(&buf, "%s", "&"); + } + + if (virBufferError(&buf)) { + virReportOOMError(NULL); + return -1; + } + + content = virBufferContentAndReset(&buf); + ret = http2unix(xend, xend_post(xend, path, content, error, n_error)); + VIR_FREE(content); + + return ret; +} + + +/** + * xend_op: + * @xend: pointer to the Xen Daemon structure + * @name: the domain name target of this operation + * @error: buffer for the error output + * @n_error: size of @error + * @key: the key for the operation + * @ap: input values to pass to the operation + * @...: input values to pass to the operation + * + * internal routine to run a POST RPC operation to the Xen Daemon targetting + * a given domain. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xend_op(virConnectPtr xend, const char *name, const char *key, ...) +{ + char buffer[1024]; + char error[1024]; + va_list ap; + int ret; + + snprintf(buffer, sizeof(buffer), "/xend/domain/%s", name); + + va_start(ap, key); + ret = xend_op_ext(xend, buffer, error, sizeof(error), key, ap); + va_end(ap); + + return ret; +} + +#endif /* ! PROXY */ + +/** + * sexpr_get: + * @xend: pointer to the Xen Daemon structure + * @fmt: format string for the path of the operation + * @...: extra data to build the path of the operation + * + * Internal routine to run a simple GET RPC operation to the Xen Daemon + * + * Returns a parsed S-Expression in case of success, NULL in case of failure + */ +static struct sexpr *sexpr_get(virConnectPtr xend, const char *fmt, ...) + ATTRIBUTE_FMT_PRINTF(2,3); + +static struct sexpr * +sexpr_get(virConnectPtr xend, const char *fmt, ...) +{ + char buffer[4096]; + char path[1024]; + va_list ap; + int ret; + + va_start(ap, fmt); + vsnprintf(path, sizeof(path), fmt, ap); + va_end(ap); + + ret = xend_get(xend, path, buffer, sizeof(buffer)); + ret = http2unix(xend ,ret); + if (ret == -1) + return NULL; + + return string2sexpr(buffer); +} + +/** + * sexpr_int: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup an int value in the S-Expression + * + * Returns the value found or 0 if not found (but may not be an error). + * This function suffers from the flaw that zero is both a correct + * return value and an error indicator: careful! + */ +static int +sexpr_int(const struct sexpr *sexpr, const char *name) +{ + const char *value = sexpr_node(sexpr, name); + + if (value) { + return strtol(value, NULL, 0); + } + return 0; +} + + +/** + * sexpr_float: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup a float value in the S-Expression + * + * Returns the value found or 0 if not found (but may not be an error) + */ +static double +sexpr_float(const struct sexpr *sexpr, const char *name) +{ + const char *value = sexpr_node(sexpr, name); + + if (value) { + return strtod(value, NULL); + } + return 0; +} + +/** + * sexpr_u64: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup a 64bits unsigned int value in the + * S-Expression + * + * Returns the value found or 0 if not found (but may not be an error) + */ +static uint64_t +sexpr_u64(const struct sexpr *sexpr, const char *name) +{ + const char *value = sexpr_node(sexpr, name); + + if (value) { + return strtoll(value, NULL, 0); + } + return 0; +} + + +/** + * sexpr_uuid: + * @ptr: where to store the UUID, incremented + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup an UUID value from the S-Expression + * + * Returns a -1 on error, 0 on success + */ +static int +sexpr_uuid(unsigned char *ptr, const struct sexpr *node, const char *path) +{ + const char *r = sexpr_node(node, path); + if (!r) + return -1; + return virUUIDParse(r, ptr); +} + + +#ifndef PROXY +/** + * urlencode: + * @string: the input URL + * + * Encode an URL see RFC 2396 and following + * + * Returns the new string or NULL in case of error. + */ +static char * +urlencode(const char *string) +{ + size_t len = strlen(string); + char *buffer; + char *ptr; + size_t i; + + if (VIR_ALLOC_N(buffer, len * 3 + 1) < 0) { + virReportOOMError(NULL); + return (NULL); + } + ptr = buffer; + for (i = 0; i < len; i++) { + switch (string[i]) { + case ' ': + case '\n': + snprintf(ptr, 4, "%%%02x", string[i]); + ptr += 3; + break; + default: + *ptr = string[i]; + ptr++; + } + } + + *ptr = 0; + + return buffer; +} +#endif /* ! PROXY */ + +/* PUBLIC FUNCTIONS */ + +/** + * xenDaemonOpen_unix: + * @conn: an existing virtual connection block + * @path: the path for the Xen Daemon socket + * + * Creates a localhost Xen Daemon connection + * Note: this doesn't try to check if the connection actually works + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenDaemonOpen_unix(virConnectPtr conn, const char *path) +{ + struct sockaddr_un *addr; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if ((conn == NULL) || (path == NULL)) + return (-1); + + memset(&priv->addr, 0, sizeof(priv->addr)); + priv->addrfamily = AF_UNIX; + /* + * This must be zero on Solaris at least for AF_UNIX (which should + * really be PF_UNIX, but doesn't matter). + */ + priv->addrprotocol = 0; + priv->addrlen = sizeof(struct sockaddr_un); + + addr = (struct sockaddr_un *)&priv->addr; + addr->sun_family = AF_UNIX; + memset(addr->sun_path, 0, sizeof(addr->sun_path)); + strncpy(addr->sun_path, path, sizeof(addr->sun_path)); + + return (0); +} + +#ifndef PROXY +/** + * xenDaemonOpen_tcp: + * @conn: an existing virtual connection block + * @host: the host name for the Xen Daemon + * @port: the port + * + * Creates a possibly remote Xen Daemon connection + * Note: this doesn't try to check if the connection actually works + * + * Returns 0 in case of success, -1 in case of error. + */ +static int +xenDaemonOpen_tcp(virConnectPtr conn, const char *host, const char *port) +{ + xenUnifiedPrivatePtr priv; + struct addrinfo *res, *r; + struct addrinfo hints; + int saved_errno = EINVAL; + int ret; + + if ((conn == NULL) || (host == NULL) || (port == NULL)) + return (-1); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + priv->addrlen = 0; + memset(&priv->addr, 0, sizeof(priv->addr)); + + // http://people.redhat.com/drepper/userapi-ipv6.html + memset (&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + + ret = getaddrinfo (host, port, &hints, &res); + if (ret != 0) { + virXendError(NULL, VIR_ERR_UNKNOWN_HOST, + _("unable to resolve hostname '%s': %s"), + host, gai_strerror (ret)); + return -1; + } + + /* Try to connect to each returned address in turn. */ + for (r = res; r; r = r->ai_next) { + int sock; + + sock = socket (r->ai_family, SOCK_STREAM, r->ai_protocol); + if (sock == -1) { + saved_errno = errno; + continue; + } + + if (connect (sock, r->ai_addr, r->ai_addrlen) == -1) { + saved_errno = errno; + close (sock); + continue; + } + + priv->addrlen = r->ai_addrlen; + priv->addrfamily = r->ai_family; + priv->addrprotocol = r->ai_protocol; + memcpy(&priv->addr, + r->ai_addr, + r->ai_addrlen); + close(sock); + break; + } + + freeaddrinfo (res); + + if (!priv->addrlen) { + /* Don't raise error when unprivileged, since proxy takes over */ + if (xenHavePrivilege()) + virReportSystemError(conn, saved_errno, + _("unable to connect to '%s:%s'"), + host, port); + return -1; + } + + return 0; +} + + +/** + * xend_wait_for_devices: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * + * Block the domain until all the virtual devices are ready. This operation + * is needed when creating a domain before resuming it. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_wait_for_devices(virConnectPtr xend, const char *name) +{ + return xend_op(xend, name, "op", "wait_for_devices", NULL); +} + + +#endif /* PROXY */ + + +/** + * xenDaemonListDomainsOld: + * @xend: pointer to the Xem Daemon block + * + * This method will return an array of names of currently running + * domains. The memory should be released will a call to free(). + * + * Returns a list of names or NULL in case of error. + */ +char ** +xenDaemonListDomainsOld(virConnectPtr xend) +{ + size_t extra = 0; + struct sexpr *root = NULL; + char **ret = NULL; + int count = 0; + int i; + char *ptr; + struct sexpr *_for_i, *node; + + root = sexpr_get(xend, "/xend/domain"); + if (root == NULL) + goto error; + + for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { + if (node->kind != SEXPR_VALUE) + continue; + extra += strlen(node->u.value) + 1; + count++; + } + + /* + * We can'tuse the normal allocation routines as we are mixing + * an array of char * at the beginning followed by an array of char + * ret points to the NULL terminated array of char * + * ptr points to the current string after that array but in the same + * allocated block + */ + if (virAlloc((void *)&ptr, + (count + 1) * sizeof(char *) + extra * sizeof(char)) < 0) + goto error; + + ret = (char **) ptr; + ptr += sizeof(char *) * (count + 1); + + i = 0; + for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { + if (node->kind != SEXPR_VALUE) + continue; + ret[i] = ptr; + strcpy(ptr, node->u.value); + ptr += strlen(node->u.value) + 1; + i++; + } + + ret[i] = NULL; + + error: + sexpr_free(root); + return ret; +} + +#ifndef PROXY +/** + * xenDaemonDomainCreateXML: + * @xend: A xend instance + * @sexpr: An S-Expr description of the domain. + * + * This method will create a domain based the passed in description. The + * domain will be paused after creation and must be unpaused with + * xenDaemonResumeDomain() to begin execution. + * This method may be deprecated once switching to XML-RPC based communcations + * with xend. + * + * Returns 0 for success, -1 (with errno) on error + */ + +int +xenDaemonDomainCreateXML(virConnectPtr xend, const char *sexpr) +{ + int ret, serrno; + char *ptr; + + ptr = urlencode(sexpr); + if (ptr == NULL) { + /* this should be caught at the interface but ... */ + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to urlencode the create S-Expr")); + return (-1); + } + + ret = xend_op(xend, "", "op", "create", "config", ptr, NULL); + + serrno = errno; + VIR_FREE(ptr); + errno = serrno; + + return ret; +} +#endif /* ! PROXY */ + +/** + * xenDaemonDomainLookupByName_ids: + * @xend: A xend instance + * @domname: The name of the domain + * @uuid: return value for the UUID if not NULL + * + * This method looks up the id of a domain + * + * Returns the id on success; -1 (with errno) on error + */ +int +xenDaemonDomainLookupByName_ids(virConnectPtr xend, const char *domname, + unsigned char *uuid) +{ + struct sexpr *root; + const char *value; + int ret = -1; + + if (uuid != NULL) + memset(uuid, 0, VIR_UUID_BUFLEN); + root = sexpr_get(xend, "/xend/domain/%s?detail=1", domname); + if (root == NULL) + goto error; + + value = sexpr_node(root, "domain/domid"); + if (value == NULL) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing domid")); + goto error; + } + ret = strtol(value, NULL, 0); + if ((ret == 0) && (value[0] != '0')) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incorrect domid not numeric")); + ret = -1; + } else if (uuid != NULL) { + if (sexpr_uuid(uuid, root, "domain/uuid") < 0) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing uuid")); + } + } + + error: + sexpr_free(root); + return (ret); +} + + +/** + * xenDaemonDomainLookupByID: + * @xend: A xend instance + * @id: The id of the domain + * @name: return value for the name if not NULL + * @uuid: return value for the UUID if not NULL + * + * This method looks up the name of a domain based on its id + * + * Returns the 0 on success; -1 (with errno) on error + */ +int +xenDaemonDomainLookupByID(virConnectPtr xend, + int id, + char **domname, + unsigned char *uuid) +{ + const char *name = NULL; + struct sexpr *root; + + memset(uuid, 0, VIR_UUID_BUFLEN); + + root = sexpr_get(xend, "/xend/domain/%d?detail=1", id); + if (root == NULL) + goto error; + + name = sexpr_node(root, "domain/name"); + if (name == NULL) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing name")); + goto error; + } + if (domname) + *domname = strdup(name); + + if (sexpr_uuid(uuid, root, "domain/uuid") < 0) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing uuid")); + goto error; + } + + sexpr_free(root); + return (0); + +error: + sexpr_free(root); + if (domname) + VIR_FREE(*domname); + return (-1); +} + + +#ifndef PROXY +static int +xend_detect_config_version(virConnectPtr conn) { + struct sexpr *root; + const char *value; + xenUnifiedPrivatePtr priv; + + if (!VIR_IS_CONNECT(conn)) { + virXendError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + root = sexpr_get(conn, "/xend/node/"); + if (root == NULL) + return (-1); + + value = sexpr_node(root, "node/xend_config_format"); + + if (value) { + priv->xendConfigVersion = strtol(value, NULL, 10); + } else { + /* Xen prior to 3.0.3 did not have the xend_config_format + field, and is implicitly version 1. */ + priv->xendConfigVersion = 1; + } + sexpr_free(root); + return (0); +} + +#endif /* PROXY */ + +/***************************************************************** + ****** + ****** Parsing of SEXPR into virDomainDef objects + ****** + *****************************************************************/ + +/** + * xenDaemonParseSxprOS + * @xend: the xend connection object + * @node: the root of the parsed S-Expression + * @def: the domain config + * @hvm: true or 1 if no contains HVM S-Expression + * @bootloader: true or 1 if a bootloader is defined + * + * Parse the xend sexp for description of os and append it to buf. + * + * Returns 0 in case of success and -1 in case of error + */ +static int +xenDaemonParseSxprOS(virConnectPtr xend, + const struct sexpr *node, + virDomainDefPtr def, + int hvm) +{ + if (hvm) { + if (sexpr_node_copy(node, "domain/image/hvm/loader", &def->os.loader) < 0) + goto no_memory; + if (def->os.loader == NULL) { + if (sexpr_node_copy(node, "domain/image/hvm/kernel", &def->os.loader) < 0) + goto no_memory; + + if (def->os.loader == NULL) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing HVM loader")); + return(-1); + } + } else { + if (sexpr_node_copy(node, "domain/image/hvm/kernel", &def->os.kernel) < 0) + goto no_memory; + if (sexpr_node_copy(node, "domain/image/hvm/ramdisk", &def->os.initrd) < 0) + goto no_memory; + if (sexpr_node_copy(node, "domain/image/hvm/args", &def->os.cmdline) < 0) + goto no_memory; + if (sexpr_node_copy(node, "domain/image/hvm/root", &def->os.root) < 0) + goto no_memory; + } + } else { + if (sexpr_node_copy(node, "domain/image/linux/kernel", &def->os.kernel) < 0) + goto no_memory; + if (sexpr_node_copy(node, "domain/image/linux/ramdisk", &def->os.initrd) < 0) + goto no_memory; + if (sexpr_node_copy(node, "domain/image/linux/args", &def->os.cmdline) < 0) + goto no_memory; + if (sexpr_node_copy(node, "domain/image/linux/root", &def->os.root) < 0) + goto no_memory; + } + + /* If HVM kenrel == loader, then old xend, so kill off kernel */ + if (hvm && + def->os.kernel && + STREQ(def->os.kernel, def->os.loader)) { + VIR_FREE(def->os.kernel); + } + + if (!def->os.kernel && + hvm) { + const char *boot = sexpr_node(node, "domain/image/hvm/boot"); + if ((boot != NULL) && (boot[0] != 0)) { + while (*boot && + def->os.nBootDevs < VIR_DOMAIN_BOOT_LAST) { + if (*boot == 'a') + def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_FLOPPY; + else if (*boot == 'c') + def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_DISK; + else if (*boot == 'd') + def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_CDROM; + else if (*boot == 'n') + def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_NET; + boot++; + } + } + } + + if (!hvm && + !def->os.kernel && + !def->os.bootloader) { + virXendError(xend, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing kernel & bootloader")); + return -1; + } + + return 0; + +no_memory: + virReportOOMError(xend); + return -1; +} + + +int +xend_parse_sexp_desc_char(virConnectPtr conn, + virBufferPtr buf, + const char *devtype, + int portNum, + const char *value, + const char *tty) +{ + const char *type; + int telnet = 0; + char *bindPort = NULL; + char *bindHost = NULL; + char *connectPort = NULL; + char *connectHost = NULL; + char *path = NULL; + int ret = -1; + + if (value[0] == '/') { + type = "dev"; + } else if (STRPREFIX(value, "null")) { + type = "null"; + value = NULL; + } else if (STRPREFIX(value, "vc")) { + type = "vc"; + value = NULL; + } else if (STRPREFIX(value, "pty")) { + type = "pty"; + value = NULL; + } else if (STRPREFIX(value, "stdio")) { + type = "stdio"; + value = NULL; + } else if (STRPREFIX(value, "file:")) { + type = "file"; + value += sizeof("file:")-1; + } else if (STRPREFIX(value, "pipe:")) { + type = "pipe"; + value += sizeof("pipe:")-1; + } else if (STRPREFIX(value, "tcp:")) { + type = "tcp"; + value += sizeof("tcp:")-1; + } else if (STRPREFIX(value, "telnet:")) { + type = "tcp"; + value += sizeof("telnet:")-1; + telnet = 1; + } else if (STRPREFIX(value, "udp:")) { + type = "udp"; + value += sizeof("udp:")-1; + } else if (STRPREFIX(value, "unix:")) { + type = "unix"; + value += sizeof("unix:")-1; + } else { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Unknown char device type")); + return -1; + } + + /* Compat with legacy syntax */ + if (STREQ(devtype, "console") && + STREQ(type, "pty") && + tty != NULL) { + virBufferVSprintf(buf, " <%s type='%s' tty='%s'>\n", + devtype, type, tty); + } else { + virBufferVSprintf(buf, " <%s type='%s'>\n", + devtype, type); + } + + if (STREQ(type, "null") || + STREQ(type, "vc") || + STREQ(type, "stdio")) { + /* no source needed */ + } else if (STREQ(type, "pty")) { + if (tty) + virBufferVSprintf(buf, " \n", + tty); + } else if (STREQ(type, "file") || + STREQ(type, "pipe")) { + virBufferVSprintf(buf, " \n", + value); + } else if (STREQ(type, "tcp")) { + const char *offset = strchr(value, ':'); + const char *offset2; + const char *mode, *protocol; + + if (offset == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed char device string")); + goto error; + } + + if (offset != value && + (bindHost = strndup(value, offset - value)) == NULL) + goto no_memory; + + offset2 = strchr(offset, ','); + if (offset2 == NULL) + bindPort = strdup(offset+1); + else + bindPort = strndup(offset+1, offset2-(offset+1)); + if (bindPort == NULL) + goto no_memory; + + if (offset2 && strstr(offset2, ",listen")) + mode = "bind"; + else + mode = "connect"; + protocol = telnet ? "telnet":"raw"; + + if (bindHost) { + virBufferVSprintf(buf, + " \n", + mode, bindHost, bindPort); + } else { + virBufferVSprintf(buf, + " \n", + mode, bindPort); + } + virBufferVSprintf(buf, + " \n", + protocol); + } else if (STREQ(type, "udp")) { + const char *offset = strchr(value, ':'); + const char *offset2, *offset3; + + if (offset == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed char device string")); + goto error; + } + + if (offset != value && + (connectHost = strndup(value, offset - value)) == NULL) + goto no_memory; + + offset2 = strchr(offset, '@'); + if (offset2 != NULL) { + if ((connectPort = strndup(offset + 1, offset2-(offset+1))) == NULL) + goto no_memory; + + offset3 = strchr(offset2, ':'); + if (offset3 == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed char device string")); + goto error; + } + + if (offset3 > (offset2 + 1) && + (bindHost = strndup(offset2 + 1, offset3 - (offset2+1))) == NULL) + goto no_memory; + + if ((bindPort = strdup(offset3 + 1)) == NULL) + goto no_memory; + } else { + if ((connectPort = strdup(offset + 1)) == NULL) + goto no_memory; + } + + if (connectPort) { + if (connectHost) { + virBufferVSprintf(buf, + " \n", + connectHost, connectPort); + } else { + virBufferVSprintf(buf, + " \n", + connectPort); + } + } + if (bindPort) { + if (bindHost) { + virBufferVSprintf(buf, + " \n", + bindHost, bindPort); + } else { + virBufferVSprintf(buf, + " \n", + bindPort); + } + } + + } else if (STREQ(type, "unix")) { + const char *offset = strchr(value, ','); + int dolisten = 0; + if (offset) + path = strndup(value, (offset - value)); + else + path = strdup(value); + if (path == NULL) + goto no_memory; + + if (offset != NULL && + strstr(offset, ",listen") != NULL) + dolisten = 1; + + virBufferVSprintf(buf, " \n", + dolisten ? "bind" : "connect", path); + } + + virBufferVSprintf(buf, " \n", + portNum); + + virBufferVSprintf(buf, " \n", + devtype); + + ret = 0; + + if (ret == -1) { +no_memory: + virReportOOMError(conn); + } + +error: + + VIR_FREE(path); + VIR_FREE(bindHost); + VIR_FREE(bindPort); + VIR_FREE(connectHost); + VIR_FREE(connectPort); + + return ret; +} + +virDomainChrDefPtr +xenDaemonParseSxprChar(virConnectPtr conn, + const char *value, + const char *tty) +{ + char prefix[10]; + char *tmp; + virDomainChrDefPtr def; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(conn); + return NULL; + } + + strncpy(prefix, value, sizeof(prefix)-1); + NUL_TERMINATE(prefix); + + if (value[0] == '/') { + def->type = VIR_DOMAIN_CHR_TYPE_DEV; + } else { + if ((tmp = strchr(prefix, ':')) != NULL) { + *tmp = '\0'; + value += (tmp - prefix) + 1; + } + + if (STREQ(prefix, "telnet")) { + def->type = VIR_DOMAIN_CHR_TYPE_TCP; + def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + } else { + if ((def->type = virDomainChrTypeFromString(prefix)) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown chr device type '%s'"), prefix); + goto error; + } + } + } + + /* Compat with legacy syntax */ + switch (def->type) { + case VIR_DOMAIN_CHR_TYPE_PTY: + if (tty != NULL && + !(def->data.file.path = strdup(tty))) + goto no_memory; + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + case VIR_DOMAIN_CHR_TYPE_PIPE: + if (!(def->data.file.path = strdup(value))) + goto no_memory; + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + { + const char *offset = strchr(value, ':'); + const char *offset2; + + if (offset == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed char device string")); + goto error; + } + + if (offset != value && + (def->data.tcp.host = strndup(value, offset - value)) == NULL) + goto no_memory; + + offset2 = strchr(offset, ','); + if (offset2 == NULL) + def->data.tcp.service = strdup(offset+1); + else + def->data.tcp.service = strndup(offset+1, offset2-(offset+1)); + if (def->data.tcp.service == NULL) + goto no_memory; + + if (offset2 && strstr(offset2, ",listen")) + def->data.tcp.listen = 1; + } + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + { + const char *offset = strchr(value, ':'); + const char *offset2, *offset3; + + if (offset == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed char device string")); + goto error; + } + + if (offset != value && + (def->data.udp.connectHost = strndup(value, offset - value)) == NULL) + goto no_memory; + + offset2 = strchr(offset, '@'); + if (offset2 != NULL) { + if ((def->data.udp.connectService = strndup(offset + 1, offset2-(offset+1))) == NULL) + goto no_memory; + + offset3 = strchr(offset2, ':'); + if (offset3 == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed char device string")); + goto error; + } + + if (offset3 > (offset2 + 1) && + (def->data.udp.bindHost = strndup(offset2 + 1, offset3 - (offset2+1))) == NULL) + goto no_memory; + + if ((def->data.udp.bindService = strdup(offset3 + 1)) == NULL) + goto no_memory; + } else { + if ((def->data.udp.connectService = strdup(offset + 1)) == NULL) + goto no_memory; + } + } + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + { + const char *offset = strchr(value, ','); + if (offset) + def->data.nix.path = strndup(value, (offset - value)); + else + def->data.nix.path = strdup(value); + if (def->data.nix.path == NULL) + goto no_memory; + + if (offset != NULL && + strstr(offset, ",listen") != NULL) + def->data.nix.listen = 1; + } + break; + } + + return def; + +no_memory: + virReportOOMError(conn); +error: + virDomainChrDefFree(def); + return NULL; +} + +/** + * xend_parse_sexp_desc_disks + * @conn: connection + * @root: root sexpr + * @xendConfigVersion: version of xend + * + * This parses out block devices from the domain sexpr + * + * Returns 0 if successful or -1 if failed. + */ +static int +xenDaemonParseSxprDisks(virConnectPtr conn, + virDomainDefPtr def, + const struct sexpr *root, + int hvm, + int xendConfigVersion) +{ + const struct sexpr *cur, *node; + virDomainDiskDefPtr disk = NULL; + + for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { + node = cur->u.s.car; + /* Normally disks are in a (device (vbd ...)) block + but blktap disks ended up in a differently named + (device (tap ....)) block.... */ + if (sexpr_lookup(node, "device/vbd") || + sexpr_lookup(node, "device/tap")) { + char *offset; + const char *src = NULL; + const char *dst = NULL; + const char *mode = NULL; + + /* Again dealing with (vbd...) vs (tap ...) differences */ + if (sexpr_lookup(node, "device/vbd")) { + src = sexpr_node(node, "device/vbd/uname"); + dst = sexpr_node(node, "device/vbd/dev"); + mode = sexpr_node(node, "device/vbd/mode"); + } else { + src = sexpr_node(node, "device/tap/uname"); + dst = sexpr_node(node, "device/tap/dev"); + mode = sexpr_node(node, "device/tap/mode"); + } + + if (VIR_ALLOC(disk) < 0) + goto no_memory; + + if (dst == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, vbd has no dev")); + goto error; + } + + if (src == NULL) { + /* There is a case without the uname to the CD-ROM device */ + offset = strchr(dst, ':'); + if (!offset || + !hvm || + STRNEQ(offset, ":cdrom")) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, vbd has no src")); + goto error; + } + } + + if (src != NULL) { + offset = strchr(src, ':'); + if (!offset) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot parse vbd filename, missing driver name")); + goto error; + } + + if (VIR_ALLOC_N(disk->driverName, (offset-src)+1) < 0) + goto no_memory; + strncpy(disk->driverName, src, (offset-src)); + disk->driverName[offset-src] = '\0'; + + src = offset + 1; + + if (STREQ (disk->driverName, "tap")) { + offset = strchr(src, ':'); + if (!offset) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot parse vbd filename, missing driver type")); + goto error; + } + + if (VIR_ALLOC_N(disk->driverType, (offset-src)+1)< 0) + goto no_memory; + strncpy(disk->driverType, src, (offset-src)); + disk->driverType[offset-src] = '\0'; + + src = offset + 1; + /* Its possible to use blktap driver for block devs + too, but kinda pointless because blkback is better, + so we assume common case here. If blktap becomes + omnipotent, we can revisit this, perhaps stat()'ing + the src file in question */ + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + } else if (STREQ(disk->driverName, "phy")) { + disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; + } else if (STREQ(disk->driverName, "file")) { + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + } + } else { + /* No CDROM media so can't really tell. We'll just + call if a FILE for now and update when media + is inserted later */ + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + } + + if (STREQLEN (dst, "ioemu:", 6)) + dst += 6; + + disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; + /* New style disk config from Xen >= 3.0.3 */ + if (xendConfigVersion > 1) { + offset = strrchr(dst, ':'); + if (offset) { + if (STREQ (offset, ":cdrom")) { + disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + } else if (STREQ (offset, ":disk")) { + /* The default anyway */ + } else { + /* Unknown, lets pretend its a disk too */ + } + offset[0] = '\0'; + } + } + + if (!(disk->dst = strdup(dst))) + goto no_memory; + if (src && + !(disk->src = strdup(src))) + goto no_memory; + + if (STRPREFIX(disk->dst, "xvd")) + disk->bus = VIR_DOMAIN_DISK_BUS_XEN; + else if (STRPREFIX(disk->dst, "hd")) + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + else if (STRPREFIX(disk->dst, "sd")) + disk->bus = VIR_DOMAIN_DISK_BUS_SCSI; + else + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + + if (mode && + strchr(mode, 'r')) + disk->readonly = 1; + if (mode && + strchr(mode, '!')) + disk->shared = 1; + + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) + goto no_memory; + + def->disks[def->ndisks++] = disk; + disk = NULL; + } + } + + return 0; + +no_memory: + virReportOOMError(conn); + +error: + virDomainDiskDefFree(disk); + return -1; +} + + +static int +xenDaemonParseSxprNets(virConnectPtr conn, + virDomainDefPtr def, + const struct sexpr *root) +{ + virDomainNetDefPtr net = NULL; + const struct sexpr *cur, *node; + const char *tmp; + int vif_index = 0; + + for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { + node = cur->u.s.car; + if (sexpr_lookup(node, "device/vif")) { + const char *tmp2, *model; + char buf[50]; + tmp2 = sexpr_node(node, "device/vif/script"); + tmp = sexpr_node(node, "device/vif/bridge"); + model = sexpr_node(node, "device/vif/model"); + + if (VIR_ALLOC(net) < 0) + goto no_memory; + + if (tmp != NULL || + (tmp2 != NULL && STREQ(tmp2, DEFAULT_VIF_SCRIPT))) { + net->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + /* XXX virtual network reverse resolve */ + + if (tmp && + !(net->data.bridge.brname = strdup(tmp))) + goto no_memory; + if (tmp2 && + net->type == VIR_DOMAIN_NET_TYPE_BRIDGE && + !(net->data.bridge.script = strdup(tmp2))) + goto no_memory; + tmp = sexpr_node(node, "device/vif/ip"); + if (tmp && + !(net->data.bridge.ipaddr = strdup(tmp))) + goto no_memory; + } else { + net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + if (tmp2 && + !(net->data.ethernet.script = strdup(tmp2))) + goto no_memory; + tmp = sexpr_node(node, "device/vif/ip"); + if (tmp && + !(net->data.ethernet.ipaddr = strdup(tmp))) + goto no_memory; + } + + tmp = sexpr_node(node, "device/vif/vifname"); + if (!tmp) { + snprintf(buf, sizeof(buf), "vif%d.%d", def->id, vif_index); + tmp = buf; + } + if (!(net->ifname = strdup(tmp))) + goto no_memory; + + tmp = sexpr_node(node, "device/vif/mac"); + if (tmp) { + unsigned int mac[6]; + if (sscanf(tmp, "%02x:%02x:%02x:%02x:%02x:%02x", + (unsigned int*)&mac[0], + (unsigned int*)&mac[1], + (unsigned int*)&mac[2], + (unsigned int*)&mac[3], + (unsigned int*)&mac[4], + (unsigned int*)&mac[5]) != 6) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("malformed mac address '%s'"), + tmp); + goto cleanup; + } + net->mac[0] = mac[0]; + net->mac[1] = mac[1]; + net->mac[2] = mac[2]; + net->mac[3] = mac[3]; + net->mac[4] = mac[4]; + net->mac[5] = mac[5]; + } + + if (model && + !(net->model = strdup(model))) + goto no_memory; + + if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0) + goto no_memory; + + def->nets[def->nnets++] = net; + vif_index++; + } + } + + return 0; + +no_memory: + virReportOOMError(conn); +cleanup: + virDomainNetDefFree(net); + return -1; +} + + +int +xenDaemonParseSxprSound(virConnectPtr conn, + virDomainDefPtr def, + const char *str) +{ + if (STREQ(str, "all")) { + int i; + + /* + * Special compatability code for Xen with a bogus + * sound=all in config. + * + * NB delibrately, don't include all possible + * sound models anymore, just the 2 that were + * historically present in Xen's QEMU. + * + * ie just es1370 + sb16. + * + * Hence use of MODEL_ES1370 + 1, instead of MODEL_LAST + */ + + if (VIR_ALLOC_N(def->sounds, + VIR_DOMAIN_SOUND_MODEL_ES1370 + 1) < 0) + goto no_memory; + + + for (i = 0 ; i < (VIR_DOMAIN_SOUND_MODEL_ES1370 + 1) ; i++) { + virDomainSoundDefPtr sound; + if (VIR_ALLOC(sound) < 0) + goto no_memory; + sound->model = i; + def->sounds[def->nsounds++] = sound; + } + } else { + char model[10]; + const char *offset = str, *offset2; + + do { + int len; + virDomainSoundDefPtr sound; + offset2 = strchr(offset, ','); + if (offset2) + len = (offset2 - offset); + else + len = strlen(offset); + if (len > (sizeof(model)-1)) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected sound model %s"), offset); + goto error; + } + strncpy(model, offset, len); + model[len] = '\0'; + + if (VIR_ALLOC(sound) < 0) + goto no_memory; + + if ((sound->model = virDomainSoundModelTypeFromString(model)) < 0) { + VIR_FREE(sound); + goto error; + } + + if (VIR_REALLOC_N(def->sounds, def->nsounds+1) < 0) { + virDomainSoundDefFree(sound); + goto no_memory; + } + + def->sounds[def->nsounds++] = sound; + offset = offset2 ? offset2 + 1 : NULL; + } while (offset); + } + + return 0; + +no_memory: + virReportOOMError(conn); +error: + return -1; +} + + +static int +xenDaemonParseSxprUSB(virConnectPtr conn, + virDomainDefPtr def, + const struct sexpr *root) +{ + struct sexpr *cur, *node; + const char *tmp; + + for (cur = sexpr_lookup(root, "domain/image/hvm"); cur && cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { + node = cur->u.s.car; + if (sexpr_lookup(node, "usbdevice")) { + tmp = sexpr_node(node, "usbdevice"); + if (tmp && *tmp) { + if (STREQ(tmp, "tablet") || + STREQ(tmp, "mouse")) { + virDomainInputDefPtr input; + if (VIR_ALLOC(input) < 0) + goto no_memory; + input->bus = VIR_DOMAIN_INPUT_BUS_USB; + if (STREQ(tmp, "tablet")) + input->type = VIR_DOMAIN_INPUT_TYPE_TABLET; + else + input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE; + + if (VIR_REALLOC_N(def->inputs, def->ninputs+1) < 0) { + VIR_FREE(input); + goto no_memory; + } + def->inputs[def->ninputs++] = input; + } else { + /* XXX Handle other non-input USB devices later */ + } + } + } + } + return 0; + +no_memory: + virReportOOMError(conn); + return -1; +} + +static int +xenDaemonParseSxprGraphicsOld(virConnectPtr conn, + virDomainDefPtr def, + const struct sexpr *root, + int hvm, + int xendConfigVersion) +{ +#ifndef PROXY + xenUnifiedPrivatePtr priv = conn->privateData; +#endif + const char *tmp; + virDomainGraphicsDefPtr graphics = NULL; + + if ((tmp = sexpr_fmt_node(root, "domain/image/%s/vnc", hvm ? "hvm" : "linux")) && + tmp[0] == '1') { + /* Graphics device (HVM, or old (pre-3.0.4) style PV VNC config) */ + int port; + const char *listenAddr = sexpr_fmt_node(root, "domain/image/%s/vnclisten", hvm ? "hvm" : "linux"); + const char *vncPasswd = sexpr_fmt_node(root, "domain/image/%s/vncpasswd", hvm ? "hvm" : "linux"); + const char *keymap = sexpr_fmt_node(root, "domain/image/%s/keymap", hvm ? "hvm" : "linux"); + const char *unused = sexpr_fmt_node(root, "domain/image/%s/vncunused", hvm ? "hvm" : "linux"); + + xenUnifiedLock(priv); + port = xenStoreDomainGetVNCPort(conn, def->id); + xenUnifiedUnlock(priv); + + if (VIR_ALLOC(graphics) < 0) + goto no_memory; + + graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; + /* For Xen >= 3.0.3, don't generate a fixed port mapping + * because it will almost certainly be wrong ! Just leave + * it as -1 which lets caller see that the VNC server isn't + * present yet. Subsquent dumps of the XML will eventually + * find the port in XenStore once VNC server has started + */ + if (port == -1 && xendConfigVersion < 2) + port = 5900 + def->id; + + if ((unused && STREQ(unused, "1")) || port == -1) + graphics->data.vnc.autoport = 1; + graphics->data.vnc.port = port; + + if (listenAddr && + !(graphics->data.vnc.listenAddr = strdup(listenAddr))) + goto no_memory; + + if (vncPasswd && + !(graphics->data.vnc.passwd = strdup(vncPasswd))) + goto no_memory; + + if (keymap && + !(graphics->data.vnc.keymap = strdup(keymap))) + goto no_memory; + + if (VIR_ALLOC_N(def->graphics, 1) < 0) + goto no_memory; + def->graphics[0] = graphics; + def->ngraphics = 1; + graphics = NULL; + } else if ((tmp = sexpr_fmt_node(root, "domain/image/%s/sdl", hvm ? "hvm" : "linux")) && + tmp[0] == '1') { + /* Graphics device (HVM, or old (pre-3.0.4) style PV sdl config) */ + const char *display = sexpr_fmt_node(root, "domain/image/%s/display", hvm ? "hvm" : "linux"); + const char *xauth = sexpr_fmt_node(root, "domain/image/%s/xauthority", hvm ? "hvm" : "linux"); + + if (VIR_ALLOC(graphics) < 0) + goto no_memory; + + graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; + if (display && + !(graphics->data.sdl.display = strdup(display))) + goto no_memory; + if (xauth && + !(graphics->data.sdl.xauth = strdup(xauth))) + goto no_memory; + + if (VIR_ALLOC_N(def->graphics, 1) < 0) + goto no_memory; + def->graphics[0] = graphics; + def->ngraphics = 1; + graphics = NULL; + } + + return 0; + +no_memory: + virReportOOMError(conn); + virDomainGraphicsDefFree(graphics); + return -1; +} + + +static int +xenDaemonParseSxprGraphicsNew(virConnectPtr conn, + virDomainDefPtr def, + const struct sexpr *root) +{ +#ifndef PROXY + xenUnifiedPrivatePtr priv = conn->privateData; +#endif + virDomainGraphicsDefPtr graphics = NULL; + const struct sexpr *cur, *node; + const char *tmp; + + /* append network devices and framebuffer */ + for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { + node = cur->u.s.car; + if (sexpr_lookup(node, "device/vfb")) { + /* New style graphics config for PV guests in >= 3.0.4, + * or for HVM guests in >= 3.0.5 */ + if (sexpr_node(node, "device/vfb/type")) { + tmp = sexpr_node(node, "device/vfb/type"); + } else if (sexpr_node(node, "device/vfb/vnc")) { + tmp = "vnc"; + } else if (sexpr_node(node, "device/vfb/sdl")) { + tmp = "sdl"; + } else { + tmp = "unknown"; + } + + if (VIR_ALLOC(graphics) < 0) + goto no_memory; + + if ((graphics->type = virDomainGraphicsTypeFromString(tmp)) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown graphics type '%s'"), tmp); + goto error; + } + + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { + const char *display = sexpr_node(node, "device/vfb/display"); + const char *xauth = sexpr_node(node, "device/vfb/xauthority"); + if (display && + !(graphics->data.sdl.display = strdup(display))) + goto no_memory; + if (xauth && + !(graphics->data.sdl.xauth = strdup(xauth))) + goto no_memory; + } else { + int port; + const char *listenAddr = sexpr_node(node, "device/vfb/vnclisten"); + const char *vncPasswd = sexpr_node(node, "device/vfb/vncpasswd"); + const char *keymap = sexpr_node(node, "device/vfb/keymap"); + const char *unused = sexpr_node(node, "device/vfb/vncunused"); + + xenUnifiedLock(priv); + port = xenStoreDomainGetVNCPort(conn, def->id); + xenUnifiedUnlock(priv); + + // Didn't find port entry in xenstore + if (port == -1) { + const char *str = sexpr_node(node, "device/vfb/vncdisplay"); + int val; + if (str != NULL && virStrToLong_i(str, NULL, 0, &val) == 0) + port = val; + } + + if ((unused && STREQ(unused, "1")) || port == -1) + graphics->data.vnc.autoport = 1; + + if (port >= 0 && port < 5900) + port += 5900; + graphics->data.vnc.port = port; + + if (listenAddr && + !(graphics->data.vnc.listenAddr = strdup(listenAddr))) + goto no_memory; + + if (vncPasswd && + !(graphics->data.vnc.passwd = strdup(vncPasswd))) + goto no_memory; + + if (keymap && + !(graphics->data.vnc.keymap = strdup(keymap))) + goto no_memory; + } + + if (VIR_ALLOC_N(def->graphics, 1) < 0) + goto no_memory; + def->graphics[0] = graphics; + def->ngraphics = 1; + graphics = NULL; + break; + } + } + + return 0; + +no_memory: + virReportOOMError(conn); +error: + virDomainGraphicsDefFree(graphics); + return -1; +} + +/** + * xenDaemonParseSxprPCI + * @conn: connection + * @root: root sexpr + * + * This parses out block devices from the domain sexpr + * + * Returns 0 if successful or -1 if failed. + */ +static int +xenDaemonParseSxprPCI(virConnectPtr conn, + virDomainDefPtr def, + const struct sexpr *root) +{ + const struct sexpr *cur, *tmp = NULL, *node; + virDomainHostdevDefPtr dev = NULL; + + /* + * With the (domain ...) block we have the following odd setup + * + * (device + * (pci + * (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0)) + * (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0)) + * ) + * ) + * + * Normally there is one (device ...) block per device, but in + * wierd world of Xen PCI, once (device ...) covers multiple + * devices. + */ + + for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { + node = cur->u.s.car; + if ((tmp = sexpr_lookup(node, "device/pci")) != NULL) + break; + } + + if (!tmp) + return 0; + + for (cur = tmp; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { + const char *domain = NULL; + const char *bus = NULL; + const char *slot = NULL; + const char *func = NULL; + int domainID; + int busID; + int slotID; + int funcID; + + node = cur->u.s.car; + if (!sexpr_lookup(node, "dev")) + continue; + + if (!(domain = sexpr_node(node, "dev/domain"))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing PCI domain")); + goto error; + } + if (!(bus = sexpr_node(node, "dev/bus"))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing PCI bus")); + goto error; + } + if (!(slot = sexpr_node(node, "dev/slot"))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing PCI slot")); + goto error; + } + if (!(func = sexpr_node(node, "dev/func"))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing PCI func")); + goto error; + } + + if (virStrToLong_i(domain, NULL, 0, &domainID) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse PCI domain '%s'"), domain); + goto error; + } + if (virStrToLong_i(bus, NULL, 0, &busID) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse PCI bus '%s'"), bus); + goto error; + } + if (virStrToLong_i(slot, NULL, 0, &slotID) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse PCI slot '%s'"), slot); + goto error; + } + if (virStrToLong_i(func, NULL, 0, &funcID) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse PCI func '%s'"), func); + goto error; + } + + if (VIR_ALLOC(dev) < 0) + goto no_memory; + + dev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + dev->managed = 0; + dev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + dev->source.subsys.u.pci.domain = domainID; + dev->source.subsys.u.pci.bus = busID; + dev->source.subsys.u.pci.slot = slotID; + dev->source.subsys.u.pci.function = funcID; + + if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { + goto no_memory; + } + + def->hostdevs[def->nhostdevs++] = dev; + } + + return 0; + +no_memory: + virReportOOMError(conn); + +error: + virDomainHostdevDefFree(dev); + return -1; +} + + +/** + * xenDaemonParseSxpr: + * @conn: the connection associated with the XML + * @root: the root of the parsed S-Expression + * @xendConfigVersion: version of xend + * @cpus: set of cpus the domain may be pinned to + * + * Parse the xend sexp description and turn it into the XML format similar + * to the one unsed for creation. + * + * Returns the 0 terminated XML string or NULL in case of error. + * the caller must free() the returned value. + */ +static virDomainDefPtr +xenDaemonParseSxpr(virConnectPtr conn, + const struct sexpr *root, + int xendConfigVersion, + const char *cpus) +{ +#ifndef PROXY + xenUnifiedPrivatePtr priv = conn->privateData; +#endif + const char *tmp; + virDomainDefPtr def; + int hvm = 0; + char *tty = NULL; + + if (VIR_ALLOC(def) < 0) + goto no_memory; + + tmp = sexpr_node(root, "domain/domid"); + if (tmp == NULL && xendConfigVersion < 3) { /* Old XenD, domid was mandatory */ + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing id")); + goto error; + } + def->virtType = VIR_DOMAIN_VIRT_XEN; + if (tmp) + def->id = sexpr_int(root, "domain/domid"); + else + def->id = -1; + + if (sexpr_node_copy(root, "domain/name", &def->name) < 0) + goto no_memory; + if (def->name == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing name")); + goto error; + } + + tmp = sexpr_node(root, "domain/uuid"); + if (tmp == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing name")); + goto error; + } + virUUIDParse(tmp, def->uuid); + + hvm = sexpr_lookup(root, "domain/image/hvm") ? 1 : 0; + if (!hvm) { + if (sexpr_node_copy(root, "domain/bootloader", + &def->os.bootloader) < 0) + goto no_memory; + + if (!def->os.bootloader && + sexpr_has(root, "domain/bootloader") && + (def->os.bootloader = strdup("")) == NULL) + goto no_memory; + + if (def->os.bootloader && + sexpr_node_copy(root, "domain/bootloader_args", + &def->os.bootloaderArgs) < 0) + goto no_memory; + } + + if (!(def->os.type = strdup(hvm ? "hvm" : "linux"))) + goto no_memory; + + if (def->id != 0) { + if (sexpr_lookup(root, "domain/image")) { + if (xenDaemonParseSxprOS(conn, root, def, hvm) < 0) + goto error; + } + } + + def->maxmem = (unsigned long) (sexpr_u64(root, "domain/maxmem") << 10); + def->memory = (unsigned long) (sexpr_u64(root, "domain/memory") << 10); + if (def->memory > def->maxmem) + def->maxmem = def->memory; + + if (cpus != NULL) { + def->cpumasklen = VIR_DOMAIN_CPUMASK_LEN; + if (VIR_ALLOC_N(def->cpumask, def->cpumasklen) < 0) { + virReportOOMError(conn); + goto error; + } + + if (virDomainCpuSetParse(conn, &cpus, + 0, def->cpumask, + def->cpumasklen) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("invalid CPU mask %s"), cpus); + goto error; + } + } + + def->vcpus = sexpr_int(root, "domain/vcpus"); + + tmp = sexpr_node(root, "domain/on_poweroff"); + if (tmp != NULL) { + if ((def->onPoweroff = virDomainLifecycleTypeFromString(tmp)) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown lifecycle type %s"), tmp); + goto error; + } + } else + def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY; + + tmp = sexpr_node(root, "domain/on_reboot"); + if (tmp != NULL) { + if ((def->onReboot = virDomainLifecycleTypeFromString(tmp)) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown lifecycle type %s"), tmp); + goto error; + } + } else + def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART; + + tmp = sexpr_node(root, "domain/on_crash"); + if (tmp != NULL) { + if ((def->onCrash = virDomainLifecycleTypeFromString(tmp)) < 0) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown lifecycle type %s"), tmp); + goto error; + } + } else + def->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY; + + + if (hvm) { + if (sexpr_int(root, "domain/image/hvm/acpi")) + def->features |= (1 << VIR_DOMAIN_FEATURE_ACPI); + if (sexpr_int(root, "domain/image/hvm/apic")) + def->features |= (1 << VIR_DOMAIN_FEATURE_APIC); + if (sexpr_int(root, "domain/image/hvm/pae")) + def->features |= (1 << VIR_DOMAIN_FEATURE_PAE); + + /* Old XenD only allows localtime here for HVM */ + if (sexpr_int(root, "domain/image/hvm/localtime")) + def->localtime = 1; + } + + /* Current XenD allows localtime here, for PV and HVM */ + if (sexpr_int(root, "domain/localtime")) + def->localtime = 1; + + if (sexpr_node_copy(root, hvm ? + "domain/image/hvm/device_model" : + "domain/image/linux/device_model", + &def->emulator) < 0) + goto no_memory; + + /* append block devices */ + if (xenDaemonParseSxprDisks(conn, def, root, hvm, xendConfigVersion) < 0) + goto error; + + if (xenDaemonParseSxprNets(conn, def, root) < 0) + goto error; + + if (xenDaemonParseSxprPCI(conn, def, root) < 0) + goto error; + + /* New style graphics device config */ + if (xenDaemonParseSxprGraphicsNew(conn, def, root) < 0) + goto error; + + /* Graphics device (HVM <= 3.0.4, or PV <= 3.0.3) vnc config */ + if ((def->ngraphics == 0) && + xenDaemonParseSxprGraphicsOld(conn, def, root, hvm, xendConfigVersion) < 0) + goto error; + + + /* Old style cdrom config from Xen <= 3.0.2 */ + if (hvm && + xendConfigVersion == 1) { + tmp = sexpr_node(root, "domain/image/hvm/cdrom"); + if ((tmp != NULL) && (tmp[0] != 0)) { + virDomainDiskDefPtr disk; + if (VIR_ALLOC(disk) < 0) + goto no_memory; + if (!(disk->src = strdup(tmp))) { + virDomainDiskDefFree(disk); + goto no_memory; + } + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + if (!(disk->dst = strdup("hdc"))) { + virDomainDiskDefFree(disk); + goto no_memory; + } + if (!(disk->driverName = strdup("file"))) { + virDomainDiskDefFree(disk); + goto no_memory; + } + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + disk->readonly = 1; + + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { + virDomainDiskDefFree(disk); + goto no_memory; + } + def->disks[def->ndisks++] = disk; + } + } + + + /* Floppy disk config */ + if (hvm) { + const char *const fds[] = { "fda", "fdb" }; + int i; + for (i = 0 ; i < ARRAY_CARDINALITY(fds) ; i++) { + tmp = sexpr_fmt_node(root, "domain/image/hvm/%s", fds[i]); + if ((tmp != NULL) && (tmp[0] != 0)) { + virDomainDiskDefPtr disk; + if (VIR_ALLOC(disk) < 0) + goto no_memory; + if (!(disk->src = strdup(tmp))) { + VIR_FREE(disk); + goto no_memory; + } + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + if (!(disk->dst = strdup(fds[i]))) { + virDomainDiskDefFree(disk); + goto no_memory; + } + if (!(disk->driverName = strdup("file"))) { + virDomainDiskDefFree(disk); + goto no_memory; + } + disk->bus = VIR_DOMAIN_DISK_BUS_FDC; + + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { + virDomainDiskDefFree(disk); + goto no_memory; + } + def->disks[def->ndisks++] = disk; + } + } + } + + /* in case of HVM we have USB device emulation */ + if (hvm && + xenDaemonParseSxprUSB(conn, def, root) < 0) + goto error; + + /* Character device config */ + xenUnifiedLock(priv); + tty = xenStoreDomainGetConsolePath(conn, def->id); + xenUnifiedUnlock(priv); + if (hvm) { + tmp = sexpr_node(root, "domain/image/hvm/serial"); + if (tmp && STRNEQ(tmp, "none")) { + virDomainChrDefPtr chr; + if ((chr = xenDaemonParseSxprChar(conn, tmp, tty)) == NULL) + goto error; + if (VIR_REALLOC_N(def->serials, def->nserials+1) < 0) { + virDomainChrDefFree(chr); + goto no_memory; + } + def->serials[def->nserials++] = chr; + } + tmp = sexpr_node(root, "domain/image/hvm/parallel"); + if (tmp && STRNEQ(tmp, "none")) { + virDomainChrDefPtr chr; + /* XXX does XenD stuff parallel port tty info into xenstore somewhere ? */ + if ((chr = xenDaemonParseSxprChar(conn, tmp, NULL)) == NULL) + goto error; + if (VIR_REALLOC_N(def->parallels, def->nparallels+1) < 0) { + virDomainChrDefFree(chr); + goto no_memory; + } + def->parallels[def->nparallels++] = chr; + } + } else { + /* Fake a paravirt console, since that's not in the sexpr */ + if (!(def->console = xenDaemonParseSxprChar(conn, "pty", tty))) + goto error; + } + VIR_FREE(tty); + + + /* Sound device config */ + if (hvm && + (tmp = sexpr_node(root, "domain/image/hvm/soundhw")) != NULL && + *tmp) { + if (xenDaemonParseSxprSound(conn, def, tmp) < 0) + goto error; + } + + return def; + +no_memory: + virReportOOMError(conn); +error: + VIR_FREE(tty); + virDomainDefFree(def); + return NULL; +} + +virDomainDefPtr +xenDaemonParseSxprString(virConnectPtr conn, + const char *sexpr, + int xendConfigVersion) +{ + struct sexpr *root = string2sexpr(sexpr); + virDomainDefPtr def; + + if (!root) + return NULL; + + def = xenDaemonParseSxpr(conn, root, xendConfigVersion, NULL); + + sexpr_free(root); + + return def; +} + + +/** + * sexpr_to_xend_domain_info: + * @root: an S-Expression describing a domain + * @info: a info data structure to fill=up + * + * Internal routine filling up the info structure with the values from + * the domain root provided. + * + * Returns 0 in case of success, -1 in case of error + */ +static int +sexpr_to_xend_domain_info(virDomainPtr domain, const struct sexpr *root, + virDomainInfoPtr info) +{ + const char *flags; + + + if ((root == NULL) || (info == NULL)) + return (-1); + + info->memory = sexpr_u64(root, "domain/memory") << 10; + info->maxMem = sexpr_u64(root, "domain/maxmem") << 10; + flags = sexpr_node(root, "domain/state"); + + if (flags) { + if (strchr(flags, 'c')) + info->state = VIR_DOMAIN_CRASHED; + else if (strchr(flags, 's')) + info->state = VIR_DOMAIN_SHUTOFF; + else if (strchr(flags, 'd')) + info->state = VIR_DOMAIN_SHUTDOWN; + else if (strchr(flags, 'p')) + info->state = VIR_DOMAIN_PAUSED; + else if (strchr(flags, 'b')) + info->state = VIR_DOMAIN_BLOCKED; + else if (strchr(flags, 'r')) + info->state = VIR_DOMAIN_RUNNING; + } else { + /* Inactive domains don't have a state reported, so + mark them SHUTOFF, rather than NOSTATE */ + if (domain->id < 0) + info->state = VIR_DOMAIN_SHUTOFF; + else + info->state = VIR_DOMAIN_NOSTATE; + } + info->cpuTime = sexpr_float(root, "domain/cpu_time") * 1000000000; + info->nrVirtCpu = sexpr_int(root, "domain/vcpus"); + return (0); +} + +/** + * sexpr_to_xend_node_info: + * @root: an S-Expression describing a domain + * @info: a info data structure to fill up + * + * Internal routine filling up the info structure with the values from + * the node root provided. + * + * Returns 0 in case of success, -1 in case of error + */ +static int +sexpr_to_xend_node_info(const struct sexpr *root, virNodeInfoPtr info) +{ + const char *machine; + + + if ((root == NULL) || (info == NULL)) + return (-1); + + machine = sexpr_node(root, "node/machine"); + if (machine == NULL) { + info->model[0] = 0; + } else { + snprintf(&info->model[0], sizeof(info->model) - 1, "%s", machine); + info->model[sizeof(info->model) - 1] = 0; + } + info->memory = (unsigned long) sexpr_u64(root, "node/total_memory") << 10; + + info->cpus = sexpr_int(root, "node/nr_cpus"); + info->mhz = sexpr_int(root, "node/cpu_mhz"); + info->nodes = sexpr_int(root, "node/nr_nodes"); + info->sockets = sexpr_int(root, "node/sockets_per_node"); + info->cores = sexpr_int(root, "node/cores_per_socket"); + info->threads = sexpr_int(root, "node/threads_per_core"); + + /* Xen 3.2.0 replaces sockets_per_node with 'nr_cpus'. + * Old Xen calculated sockets_per_node using its internal + * nr_cpus / (nodes*cores*threads), so fake it ourselves + * in the same way + */ + if (info->sockets == 0) { + int nr_cpus = sexpr_int(root, "node/nr_cpus"); + int procs = info->nodes * info->cores * info->threads; + if (procs == 0) /* Sanity check in case of Xen bugs in futures..*/ + return (-1); + info->sockets = nr_cpus / procs; + /* Should already be fine, but for further sanity make + * sure we have at least one socket + */ + if (info->sockets == 0) + info->sockets = 1; + } + return (0); +} + + +/** + * sexpr_to_xend_topology + * @root: an S-Expression describing a node + * @caps: capability info + * + * Internal routine populating capability info with + * NUMA node mapping details + * + * Does nothing when the system doesn't support NUMA (not an error). + * + * Returns 0 in case of success, -1 in case of error + */ +static int +sexpr_to_xend_topology(virConnectPtr conn, + const struct sexpr *root, + virCapsPtr caps) +{ + const char *nodeToCpu; + const char *cur; + char *cpuset = NULL; + int *cpuNums = NULL; + int cell, cpu, nb_cpus; + int n = 0; + int numCpus; + + nodeToCpu = sexpr_node(root, "node/node_to_cpu"); + if (nodeToCpu == NULL) + return 0; /* no NUMA support */ + + numCpus = sexpr_int(root, "node/nr_cpus"); + + + if (VIR_ALLOC_N(cpuset, numCpus) < 0) + goto memory_error; + if (VIR_ALLOC_N(cpuNums, numCpus) < 0) + goto memory_error; + + cur = nodeToCpu; + while (*cur != 0) { + /* + * Find the next NUMA cell described in the xend output + */ + cur = strstr(cur, "node"); + if (cur == NULL) + break; + cur += 4; + cell = virParseNumber(&cur); + if (cell < 0) + goto parse_error; + virSkipSpaces(&cur); + if (*cur != ':') + goto parse_error; + cur++; + virSkipSpaces(&cur); + if (STRPREFIX(cur, "no cpus")) { + nb_cpus = 0; + for (cpu = 0; cpu < numCpus; cpu++) + cpuset[cpu] = 0; + } else { + nb_cpus = virDomainCpuSetParse(conn, &cur, 'n', cpuset, numCpus); + if (nb_cpus < 0) + goto error; + } + + for (n = 0, cpu = 0; cpu < numCpus; cpu++) + if (cpuset[cpu] == 1) + cpuNums[n++] = cpu; + + if (virCapabilitiesAddHostNUMACell(caps, + cell, + nb_cpus, + cpuNums) < 0) + goto memory_error; + } + VIR_FREE(cpuNums); + VIR_FREE(cpuset); + return (0); + + parse_error: + virXendError(conn, VIR_ERR_XEN_CALL, "%s", _("topology syntax error")); + error: + VIR_FREE(cpuNums); + VIR_FREE(cpuset); + + return (-1); + + memory_error: + VIR_FREE(cpuNums); + VIR_FREE(cpuset); + virReportOOMError(conn); + return (-1); +} + + +#ifndef PROXY +/** + * sexpr_to_domain: + * @conn: an existing virtual connection block + * @root: an S-Expression describing a domain + * + * Internal routine returning the associated virDomainPtr for this domain + * + * Returns the domain pointer or NULL in case of error. + */ +static virDomainPtr +sexpr_to_domain(virConnectPtr conn, const struct sexpr *root) +{ + virDomainPtr ret = NULL; + unsigned char uuid[VIR_UUID_BUFLEN]; + const char *name; + const char *tmp; + xenUnifiedPrivatePtr priv; + + if ((conn == NULL) || (root == NULL)) + return(NULL); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (sexpr_uuid(uuid, root, "domain/uuid") < 0) + goto error; + name = sexpr_node(root, "domain/name"); + if (name == NULL) + goto error; + + ret = virGetDomain(conn, name, uuid); + if (ret == NULL) return NULL; + + tmp = sexpr_node(root, "domain/domid"); + /* New 3.0.4 XenD will not report a domid for inactive domains, + * so only error out for old XenD + */ + if (!tmp && priv->xendConfigVersion < 3) + goto error; + + if (tmp) + ret->id = sexpr_int(root, "domain/domid"); + else + ret->id = -1; /* An inactive domain */ + + return (ret); + +error: + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to parse Xend domain information")); + if (ret != NULL) + virUnrefDomain(ret); + return(NULL); +} +#endif /* !PROXY */ + +/***************************************************************** + ****** + ****** + ****** + ****** + Refactored + ****** + ****** + ****** + ****** + *****************************************************************/ +#ifndef PROXY +/** + * xenDaemonOpen: + * @conn: an existing virtual connection block + * @name: optional argument to select a connection type + * @flags: combination of virDrvOpenFlag(s) + * + * Creates a localhost Xen Daemon connection + * + * Returns 0 in case of success, -1 in case of error. + */ +virDrvOpenStatus +xenDaemonOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + char *port = NULL; + int ret = VIR_DRV_OPEN_ERROR; + + /* Switch on the scheme, which we expect to be NULL (file), + * "http" or "xen". + */ + if (conn->uri->scheme == NULL) { + /* It should be a file access */ + if (conn->uri->path == NULL) { + virXendError(NULL, VIR_ERR_NO_CONNECT, __FUNCTION__); + goto failed; + } + if (xenDaemonOpen_unix(conn, conn->uri->path) < 0 || + xend_detect_config_version(conn) == -1) + goto failed; + } + else if (STRCASEEQ (conn->uri->scheme, "xen")) { + /* + * try first to open the unix socket + */ + if (xenDaemonOpen_unix(conn, "/var/lib/xend/xend-socket") == 0 && + xend_detect_config_version(conn) != -1) + goto done; + + /* + * try though http on port 8000 + */ + if (xenDaemonOpen_tcp(conn, "localhost", "8000") < 0 || + xend_detect_config_version(conn) == -1) + goto failed; + } else if (STRCASEEQ (conn->uri->scheme, "http")) { + if (conn->uri->port && + virAsprintf(&port, "%d", conn->uri->port) == -1) + goto failed; + + if (xenDaemonOpen_tcp(conn, + conn->uri->server ? conn->uri->server : "localhost", + port ? port : "8000") < 0 || + xend_detect_config_version(conn) == -1) + goto failed; + } else { + virXendError(NULL, VIR_ERR_NO_CONNECT, __FUNCTION__); + goto failed; + } + + done: + ret = VIR_DRV_OPEN_SUCCESS; + +failed: + VIR_FREE(port); + return ret; +} + + +/** + * xenDaemonClose: + * @conn: an existing virtual connection block + * + * This method should be called when a connection to xend instance + * initialized with xenDaemonOpen is no longer needed + * to free the associated resources. + * + * Returns 0 in case of success, -1 in case of error + */ +int +xenDaemonClose(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + return 0; +} + +/** + * xenDaemonDomainSuspend: + * @domain: pointer to the Domain block + * + * Pause the domain, the domain is not scheduled anymore though its resources + * are preserved. Use xenDaemonDomainResume() to resume execution. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xenDaemonDomainSuspend(virDomainPtr domain) +{ + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->id < 0) { + virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("Domain %s isn't running."), domain->name); + return(-1); + } + + return xend_op(domain->conn, domain->name, "op", "pause", NULL); +} + +/** + * xenDaemonDomainResume: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * + * Resume the domain after xenDaemonDomainSuspend() has been called + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xenDaemonDomainResume(virDomainPtr domain) +{ + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->id < 0) { + virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("Domain %s isn't running."), domain->name); + return(-1); + } + + return xend_op(domain->conn, domain->name, "op", "unpause", NULL); +} + +/** + * xenDaemonDomainShutdown: + * @domain: pointer to the Domain block + * + * Shutdown the domain, the OS is requested to properly shutdown + * and the domain may ignore it. It will return immediately + * after queuing the request. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xenDaemonDomainShutdown(virDomainPtr domain) +{ + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->id < 0) { + virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("Domain %s isn't running."), domain->name); + return(-1); + } + + return xend_op(domain->conn, domain->name, "op", "shutdown", "reason", "poweroff", NULL); +} + +/** + * xenDaemonDomainReboot: + * @domain: pointer to the Domain block + * @flags: extra flags for the reboot operation, not used yet + * + * Reboot the domain, the OS is requested to properly shutdown + * and restart but the domain may ignore it. It will return immediately + * after queuing the request. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xenDaemonDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED) +{ + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->id < 0) { + virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("Domain %s isn't running."), domain->name); + return(-1); + } + + return xend_op(domain->conn, domain->name, "op", "shutdown", "reason", "reboot", NULL); +} + +/** + * xenDaemonDomainDestroy: + * @domain: pointer to the Domain block + * + * Abruptly halt the domain, the OS is not properly shutdown and the + * resources allocated for the domain are immediately freed, mounted + * filesystems will be marked as uncleanly shutdown. + * After calling this function, the domain's status will change to + * dying and will go away completely once all of the resources have been + * unmapped (usually from the backend devices). + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xenDaemonDomainDestroy(virDomainPtr domain) +{ + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->id < 0) { + virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("Domain %s isn't running."), domain->name); + return(-1); + } + + return xend_op(domain->conn, domain->name, "op", "destroy", NULL); +} + +/** + * xenDaemonDomainGetOSType: + * @domain: a domain object + * + * Get the type of domain operation system. + * + * Returns the new string or NULL in case of error, the string must be + * freed by the caller. + */ +static char * +xenDaemonDomainGetOSType(virDomainPtr domain) +{ + char *type; + struct sexpr *root; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(NULL); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id < 0 && priv->xendConfigVersion < 3) + return(NULL); + + /* can we ask for a subset ? worth it ? */ + root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); + if (root == NULL) + return(NULL); + + if (sexpr_lookup(root, "domain/image/hvm")) { + type = strdup("hvm"); + } else { + type = strdup("linux"); + } + + sexpr_free(root); + + return(type); +} + +/** + * xenDaemonDomainSave: + * @domain: pointer to the Domain block + * @filename: path for the output file + * + * This method will suspend a domain and save its memory contents to + * a file on disk. Use xenDaemonDomainRestore() to restore a domain after + * saving. + * Note that for remote Xen Daemon the file path will be interpreted in + * the remote host. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xenDaemonDomainSave(virDomainPtr domain, const char *filename) +{ + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) || + (filename == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->id < 0) { + virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("Domain %s isn't running."), domain->name); + return(-1); + } + + /* We can't save the state of Domain-0, that would mean stopping it too */ + if (domain->id == 0) { + return(-1); + } + + return xend_op(domain->conn, domain->name, "op", "save", "file", filename, NULL); +} + +/** + * xenDaemonDomainCoreDump: + * @domain: pointer to the Domain block + * @filename: path for the output file + * @flags: extra flags, currently unused + * + * This method will dump the core of a domain on a given file for analysis. + * Note that for remote Xen Daemon the file path will be interpreted in + * the remote host. + * + * Returns 0 in case of success, -1 in case of error. + */ +static int +xenDaemonDomainCoreDump(virDomainPtr domain, const char *filename, + int flags ATTRIBUTE_UNUSED) +{ + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) || + (filename == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->id < 0) { + virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, + _("Domain %s isn't running."), domain->name); + return(-1); + } + + return xend_op(domain->conn, domain->name, "op", "dump", "file", filename, + "live", "0", "crash", "0", NULL); +} + +/** + * xenDaemonDomainRestore: + * @conn: pointer to the Xem Daemon block + * @filename: path for the output file + * + * This method will restore a domain saved to disk by xenDaemonDomainSave(). + * Note that for remote Xen Daemon the file path will be interpreted in + * the remote host. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xenDaemonDomainRestore(virConnectPtr conn, const char *filename) +{ + if ((conn == NULL) || (filename == NULL)) { + /* this should be caught at the interface but ... */ + virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + return xend_op(conn, "", "op", "restore", "file", filename, NULL); +} +#endif /* !PROXY */ + +/** + * xenDaemonDomainGetMaxMemory: + * @domain: pointer to the domain block + * + * Ask the Xen Daemon for the maximum memory allowed for a domain + * + * Returns the memory size in kilobytes or 0 in case of error. + */ +unsigned long +xenDaemonDomainGetMaxMemory(virDomainPtr domain) +{ + unsigned long ret = 0; + struct sexpr *root; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id < 0 && priv->xendConfigVersion < 3) + return(-1); + + /* can we ask for a subset ? worth it ? */ + root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); + if (root == NULL) + return(0); + + ret = (unsigned long) sexpr_u64(root, "domain/memory") << 10; + sexpr_free(root); + + return(ret); +} + +#ifndef PROXY +/** + * xenDaemonDomainSetMaxMemory: + * @domain: pointer to the Domain block + * @memory: The maximum memory in kilobytes + * + * This method will set the maximum amount of memory that can be allocated to + * a domain. Please note that a domain is able to allocate up to this amount + * on its own. + * + * Returns 0 for success; -1 (with errno) on error + */ +int +xenDaemonDomainSetMaxMemory(virDomainPtr domain, unsigned long memory) +{ + char buf[1024]; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id < 0 && priv->xendConfigVersion < 3) + return(-1); + + snprintf(buf, sizeof(buf), "%lu", memory >> 10); + return xend_op(domain->conn, domain->name, "op", "maxmem_set", "memory", + buf, NULL); +} + +/** + * xenDaemonDomainSetMemory: + * @domain: pointer to the Domain block + * @memory: The target memory in kilobytes + * + * This method will set a target memory allocation for a given domain and + * request that the guest meet this target. The guest may or may not actually + * achieve this target. When this function returns, it does not signify that + * the domain has actually reached that target. + * + * Memory for a domain can only be allocated up to the maximum memory setting. + * There is no safe guard for allocations that are too small so be careful + * when using this function to reduce a domain's memory usage. + * + * Returns 0 for success; -1 (with errno) on error + */ +int +xenDaemonDomainSetMemory(virDomainPtr domain, unsigned long memory) +{ + char buf[1024]; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id < 0 && priv->xendConfigVersion < 3) + return(-1); + + snprintf(buf, sizeof(buf), "%lu", memory >> 10); + return xend_op(domain->conn, domain->name, "op", "mem_target_set", + "target", buf, NULL); +} + +#endif /* ! PROXY */ + +virDomainDefPtr +xenDaemonDomainFetch(virConnectPtr conn, + int domid, + const char *name, + const char *cpus) +{ + struct sexpr *root; + xenUnifiedPrivatePtr priv; + virDomainDefPtr def; + + if (name) + root = sexpr_get(conn, "/xend/domain/%s?detail=1", name); + else + root = sexpr_get(conn, "/xend/domain/%d?detail=1", domid); + if (root == NULL) { + virXendError (conn, VIR_ERR_XEN_CALL, + "%s", _("xenDaemonDomainFetch failed to" + " find this domain")); + return (NULL); + } + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (!(def = xenDaemonParseSxpr(conn, + root, + priv->xendConfigVersion, + cpus))) + goto cleanup; + +cleanup: + sexpr_free(root); + + return (def); +} + + +#ifndef PROXY +/** + * xenDaemonDomainDumpXML: + * @domain: a domain object + * @flags: potential dump flags + * @cpus: list of cpu the domain is pinned to. + * + * Provide an XML description of the domain. + * + * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error. + * the caller must free() the returned value. + */ +char * +xenDaemonDomainDumpXML(virDomainPtr domain, int flags, const char *cpus) +{ + xenUnifiedPrivatePtr priv; + virDomainDefPtr def; + char *xml; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(NULL); + } + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id < 0 && priv->xendConfigVersion < 3) { + // fall-through to the next driver to handle + return(NULL); + } + + if (!(def = xenDaemonDomainFetch(domain->conn, + domain->id, + domain->name, + cpus))) + return(NULL); + + xml = virDomainDefFormat(domain->conn, def, flags); + + virDomainDefFree(def); + + return xml; +} +#endif /* !PROXY */ + +/** + * xenDaemonDomainGetInfo: + * @domain: a domain object + * @info: pointer to a virDomainInfo structure allocated by the user + * + * This method looks up information about a domain and update the + * information block provided. + * + * Returns 0 in case of success, -1 in case of error + */ +int +xenDaemonDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) +{ + struct sexpr *root; + int ret; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) || + (info == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id < 0 && priv->xendConfigVersion < 3) + return(-1); + + root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); + if (root == NULL) + return (-1); + + ret = sexpr_to_xend_domain_info(domain, root, info); + sexpr_free(root); + return (ret); +} + +#ifndef PROXY +/** + * xenDaemonLookupByName: + * @conn: A xend instance + * @name: The name of the domain + * + * This method looks up information about a domain and returns + * it in the form of a struct xend_domain. This should be + * free()'d when no longer needed. + * + * Returns domain info on success; NULL (with errno) on error + */ +virDomainPtr +xenDaemonLookupByName(virConnectPtr conn, const char *domname) +{ + struct sexpr *root; + virDomainPtr ret = NULL; + + if ((conn == NULL) || (domname == NULL)) { + virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + + root = sexpr_get(conn, "/xend/domain/%s?detail=1", domname); + if (root == NULL) + goto error; + + ret = sexpr_to_domain(conn, root); + +error: + sexpr_free(root); + return(ret); +} +#endif /* ! PROXY */ + +/** + * xenDaemonNodeGetInfo: + * @conn: pointer to the Xen Daemon block + * @info: pointer to a virNodeInfo structure allocated by the user + * + * Extract hardware information about the node. + * + * Returns 0 in case of success and -1 in case of failure. + */ +int +xenDaemonNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info) { + int ret = -1; + struct sexpr *root; + + if (!VIR_IS_CONNECT(conn)) { + virXendError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if (info == NULL) { + virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + root = sexpr_get(conn, "/xend/node/"); + if (root == NULL) + return (-1); + + ret = sexpr_to_xend_node_info(root, info); + sexpr_free(root); + return (ret); +} + +/** + * xenDaemonNodeGetTopology: + * @conn: pointer to the Xen Daemon block + * @caps: capabilities info + * + * This method retrieves a node's topology information. + * + * Returns -1 in case of error, 0 otherwise. + */ +int +xenDaemonNodeGetTopology(virConnectPtr conn, + virCapsPtr caps) { + int ret = -1; + struct sexpr *root; + + if (!VIR_IS_CONNECT(conn)) { + virXendError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (caps == NULL) { + virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + root = sexpr_get(conn, "/xend/node/"); + if (root == NULL) { + return (-1); + } + + ret = sexpr_to_xend_topology(conn, root, caps); + sexpr_free(root); + return (ret); +} + +/** + * xenDaemonGetVersion: + * @conn: pointer to the Xen Daemon block + * @hvVer: return value for the version of the running hypervisor (OUT) + * + * Get the version level of the Hypervisor running. + * + * Returns -1 in case of error, 0 otherwise. if the version can't be + * extracted by lack of capacities returns 0 and @hvVer is 0, otherwise + * @hvVer value is major * 1,000,000 + minor * 1,000 + release + */ +int +xenDaemonGetVersion(virConnectPtr conn, unsigned long *hvVer) +{ + struct sexpr *root; + int major, minor; + unsigned long version; + + if (!VIR_IS_CONNECT(conn)) { + virXendError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if (hvVer == NULL) { + virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + root = sexpr_get(conn, "/xend/node/"); + if (root == NULL) + return(-1); + + major = sexpr_int(root, "node/xen_major"); + minor = sexpr_int(root, "node/xen_minor"); + sexpr_free(root); + version = major * 1000000 + minor * 1000; + *hvVer = version; + return(0); +} + +#ifndef PROXY +/** + * xenDaemonListDomains: + * @conn: pointer to the hypervisor connection + * @ids: array to collect the list of IDs of active domains + * @maxids: size of @ids + * + * Collect the list of active domains, and store their ID in @maxids + * TODO: this is quite expensive at the moment since there isn't one + * xend RPC providing both name and id for all domains. + * + * Returns the number of domain found or -1 in case of error + */ +int +xenDaemonListDomains(virConnectPtr conn, int *ids, int maxids) +{ + struct sexpr *root = NULL; + int ret = -1; + struct sexpr *_for_i, *node; + long id; + + if (maxids == 0) + return(0); + + if ((ids == NULL) || (maxids < 0)) + goto error; + root = sexpr_get(conn, "/xend/domain"); + if (root == NULL) + goto error; + + ret = 0; + + for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { + if (node->kind != SEXPR_VALUE) + continue; + id = xenDaemonDomainLookupByName_ids(conn, node->u.value, NULL); + if (id >= 0) + ids[ret++] = (int) id; + if (ret >= maxids) + break; + } + +error: + sexpr_free(root); + return(ret); +} + +/** + * xenDaemonNumOfDomains: + * @conn: pointer to the hypervisor connection + * + * Provides the number of active domains. + * + * Returns the number of domain found or -1 in case of error + */ +static int +xenDaemonNumOfDomains(virConnectPtr conn) +{ + struct sexpr *root = NULL; + int ret = -1; + struct sexpr *_for_i, *node; + + root = sexpr_get(conn, "/xend/domain"); + if (root == NULL) + goto error; + + ret = 0; + + for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { + if (node->kind != SEXPR_VALUE) + continue; + ret++; + } + +error: + sexpr_free(root); + return(ret); +} +#endif /* ! PROXY */ + +#ifndef PROXY +/** + * xenDaemonLookupByID: + * @conn: pointer to the hypervisor connection + * @id: the domain ID number + * + * Try to find a domain based on the hypervisor ID number + * + * Returns a new domain object or NULL in case of failure + */ +virDomainPtr +xenDaemonLookupByID(virConnectPtr conn, int id) { + char *name = NULL; + unsigned char uuid[VIR_UUID_BUFLEN]; + virDomainPtr ret; + + if (xenDaemonDomainLookupByID(conn, id, &name, uuid) < 0) { + goto error; + } + + ret = virGetDomain(conn, name, uuid); + if (ret == NULL) goto error; + + ret->id = id; + VIR_FREE(name); + return (ret); + + error: + VIR_FREE(name); + return (NULL); +} + +/** + * xenDaemonDomainSetVcpus: + * @domain: pointer to domain object + * @nvcpus: the new number of virtual CPUs for this domain + * + * Dynamically change the number of virtual CPUs used by the domain. + * + * Returns 0 for success; -1 (with errno) on error + */ +int +xenDaemonDomainSetVcpus(virDomainPtr domain, unsigned int vcpus) +{ + char buf[VIR_UUID_BUFLEN]; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) + || (vcpus < 1)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id < 0 && priv->xendConfigVersion < 3) + return(-1); + + snprintf(buf, sizeof(buf), "%d", vcpus); + return(xend_op(domain->conn, domain->name, "op", "set_vcpus", "vcpus", + buf, NULL)); +} + +/** + * xenDaemonDomainPinCpu: + * @domain: pointer to domain object + * @vcpu: virtual CPU number + * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) + * @maplen: length of cpumap in bytes + * + * Dynamically change the real CPUs which can be allocated to a virtual CPU. + * NOTE: The XenD cpu affinity map format changed from "[0,1,2]" to + * "0,1,2" + * the XenD cpu affinity works only after cset 19579. + * there is no fine grained xend version detection possible, so we + * use the old format for anything before version 3 + * + * Returns 0 for success; -1 (with errno) on error + */ +int +xenDaemonDomainPinVcpu(virDomainPtr domain, unsigned int vcpu, + unsigned char *cpumap, int maplen) +{ + char buf[VIR_UUID_BUFLEN], mapstr[sizeof(cpumap_t) * 64]; + int i, j; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) + || (cpumap == NULL) || (maplen < 1) || (maplen > (int)sizeof(cpumap_t))) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xendConfigVersion < 3) { + mapstr[0] = '['; + mapstr[1] = 0; + } else { + mapstr[0] = 0; + } + + /* from bit map, build character string of mapped CPU numbers */ + for (i = 0; i < maplen; i++) for (j = 0; j < 8; j++) + if (cpumap[i] & (1 << j)) { + snprintf(buf, sizeof(buf), "%d,", (8 * i) + j); + strcat(mapstr, buf); + } + if (priv->xendConfigVersion < 3) + mapstr[strlen(mapstr) - 1] = ']'; + else + mapstr[strlen(mapstr) - 1] = 0; + + snprintf(buf, sizeof(buf), "%d", vcpu); + return(xend_op(domain->conn, domain->name, "op", "pincpu", "vcpu", buf, + "cpumap", mapstr, NULL)); +} + +/** + * virDomainGetVcpus: + * @domain: pointer to domain object, or NULL for Domain0 + * @info: pointer to an array of virVcpuInfo structures (OUT) + * @maxinfo: number of structures in info array + * @cpumaps: pointer to an bit map of real CPUs for all vcpus of this domain (in 8-bit bytes) (OUT) + * If cpumaps is NULL, then no cpumap information is returned by the API. + * It's assumed there is cpumap in cpumaps array. + * The memory allocated to cpumaps must be (maxinfo * maplen) bytes + * (ie: calloc(maxinfo, maplen)). + * One cpumap inside cpumaps has the format described in virDomainPinVcpu() API. + * @maplen: number of bytes in one cpumap, from 1 up to size of CPU map in + * underlying virtualization system (Xen...). + * + * Extract information about virtual CPUs of domain, store it in info array + * and also in cpumaps if this pointer isn't NULL. + * + * Returns the number of info filled in case of success, -1 in case of failure. + */ +int +xenDaemonDomainGetVcpus(virDomainPtr domain, virVcpuInfoPtr info, int maxinfo, + unsigned char *cpumaps, int maplen) +{ + struct sexpr *root, *s, *t; + virVcpuInfoPtr ipt = info; + int nbinfo = 0, oln; + unsigned char *cpumap; + int vcpu, cpu; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) + || (info == NULL) || (maxinfo < 1)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + if (cpumaps != NULL && maplen < 1) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + + root = sexpr_get(domain->conn, "/xend/domain/%s?op=vcpuinfo", domain->name); + if (root == NULL) + return (-1); + + if (cpumaps != NULL) + memset(cpumaps, 0, maxinfo * maplen); + + /* scan the sexprs from "(vcpu (number x)...)" and get parameter values */ + for (s = root; s->kind == SEXPR_CONS; s = s->u.s.cdr) { + if ((s->u.s.car->kind == SEXPR_CONS) && + (s->u.s.car->u.s.car->kind == SEXPR_VALUE) && + STREQ(s->u.s.car->u.s.car->u.value, "vcpu")) { + t = s->u.s.car; + vcpu = ipt->number = sexpr_int(t, "vcpu/number"); + if ((oln = sexpr_int(t, "vcpu/online")) != 0) { + if (sexpr_int(t, "vcpu/running")) ipt->state = VIR_VCPU_RUNNING; + if (sexpr_int(t, "vcpu/blocked")) ipt->state = VIR_VCPU_BLOCKED; + } + else + ipt->state = VIR_VCPU_OFFLINE; + ipt->cpuTime = sexpr_float(t, "vcpu/cpu_time") * 1000000000; + ipt->cpu = oln ? sexpr_int(t, "vcpu/cpu") : -1; + + if (cpumaps != NULL && vcpu >= 0 && vcpu < maxinfo) { + cpumap = (unsigned char *) VIR_GET_CPUMAP(cpumaps, maplen, vcpu); + /* + * get sexpr from "(cpumap (x y z...))" and convert values + * to bitmap + */ + for (t = t->u.s.cdr; t->kind == SEXPR_CONS; t = t->u.s.cdr) + if ((t->u.s.car->kind == SEXPR_CONS) && + (t->u.s.car->u.s.car->kind == SEXPR_VALUE) && + STREQ(t->u.s.car->u.s.car->u.value, "cpumap") && + (t->u.s.car->u.s.cdr->kind == SEXPR_CONS)) { + for (t = t->u.s.car->u.s.cdr->u.s.car; t->kind == SEXPR_CONS; t = t->u.s.cdr) + if (t->u.s.car->kind == SEXPR_VALUE + && virStrToLong_i(t->u.s.car->u.value, NULL, 10, &cpu) == 0 + && cpu >= 0 + && (VIR_CPU_MAPLEN(cpu+1) <= maplen)) { + VIR_USE_CPU(cpumap, cpu); + } + break; + } + } + + if (++nbinfo == maxinfo) break; + ipt++; + } + } + sexpr_free(root); + return(nbinfo); +} + +/** + * xenDaemonLookupByUUID: + * @conn: pointer to the hypervisor connection + * @uuid: the raw UUID for the domain + * + * Try to lookup a domain on xend based on its UUID. + * + * Returns a new domain object or NULL in case of failure + */ +virDomainPtr +xenDaemonLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + virDomainPtr ret; + char *name = NULL; + int id = -1; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + /* Old approach for xen <= 3.0.3 */ + if (priv->xendConfigVersion < 3) { + char **names, **tmp; + unsigned char ident[VIR_UUID_BUFLEN]; + names = xenDaemonListDomainsOld(conn); + tmp = names; + + if (names == NULL) { + return (NULL); + } + while (*tmp != NULL) { + id = xenDaemonDomainLookupByName_ids(conn, *tmp, &ident[0]); + if (id >= 0) { + if (!memcmp(uuid, ident, VIR_UUID_BUFLEN)) { + name = strdup(*tmp); + break; + } + } + tmp++; + } + VIR_FREE(names); + } else { /* New approach for xen >= 3.0.4 */ + char *domname = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + struct sexpr *root = NULL; + + virUUIDFormat(uuid, uuidstr); + root = sexpr_get(conn, "/xend/domain/%s?detail=1", uuidstr); + if (root == NULL) + return (NULL); + domname = (char*)sexpr_node(root, "domain/name"); + if (sexpr_node(root, "domain/domid")) /* only active domains have domid */ + id = sexpr_int(root, "domain/domid"); + else + id = -1; + name = domname ? strdup(domname) : NULL; + sexpr_free(root); + } + + if (name == NULL) + return (NULL); + + ret = virGetDomain(conn, name, uuid); + if (ret == NULL) goto cleanup; + + ret->id = id; + + cleanup: + VIR_FREE(name); + return (ret); +} + +/** + * xenDaemonCreateXML: + * @conn: pointer to the hypervisor connection + * @xmlDesc: an XML description of the domain + * @flags: an optional set of virDomainFlags + * + * Launch a new Linux guest domain, based on an XML description similar + * to the one returned by virDomainGetXMLDesc() + * This function may requires privileged access to the hypervisor. + * + * Returns a new domain object or NULL in case of failure + */ +static virDomainPtr +xenDaemonCreateXML(virConnectPtr conn, const char *xmlDesc, + unsigned int flags ATTRIBUTE_UNUSED) +{ + int ret; + char *sexpr; + virDomainPtr dom = NULL; + xenUnifiedPrivatePtr priv; + virDomainDefPtr def; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (!(def = virDomainDefParseString(conn, + priv->caps, + xmlDesc, + VIR_DOMAIN_XML_INACTIVE))) + return (NULL); + + if (!(sexpr = xenDaemonFormatSxpr(conn, def, priv->xendConfigVersion))) { + virDomainDefFree(def); + return (NULL); + } + + ret = xenDaemonDomainCreateXML(conn, sexpr); + VIR_FREE(sexpr); + if (ret != 0) { + goto error; + } + + /* This comes before wait_for_devices, to ensure that latter + cleanup will destroy the domain upon failure */ + if (!(dom = virDomainLookupByName(conn, def->name))) + goto error; + + if (xend_wait_for_devices(conn, def->name) < 0) + goto error; + + if (xenDaemonDomainResume(dom) < 0) + goto error; + + virDomainDefFree(def); + return (dom); + + error: + /* Make sure we don't leave a still-born domain around */ + if (dom != NULL) { + xenDaemonDomainDestroy(dom); + virUnrefDomain(dom); + } + virDomainDefFree(def); + return (NULL); +} + +/** + * xenDaemonAttachDevice: + * @domain: pointer to domain object + * @xml: pointer to XML description of device + * + * Create a virtual device attachment to backend. + * XML description is translated into S-expression. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xenDaemonAttachDevice(virDomainPtr domain, const char *xml) +{ + xenUnifiedPrivatePtr priv; + char *sexpr = NULL; + int ret = -1; + virDomainDeviceDefPtr dev = NULL; + virDomainDefPtr def = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char class[8], ref[80]; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return -1; + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + /* + * on older Xen without the inactive guests management + * avoid doing this on inactive guests + */ + if ((domain->id < 0) && (priv->xendConfigVersion < 3)) + return -1; + + if (!(def = xenDaemonDomainFetch(domain->conn, + domain->id, + domain->name, + NULL))) + goto cleanup; + + if (!(dev = virDomainDeviceDefParse(domain->conn, + priv->caps, + def, xml, VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + if (xenDaemonFormatSxprDisk(domain->conn, + dev->data.disk, + &buf, + STREQ(def->os.type, "hvm") ? 1 : 0, + priv->xendConfigVersion, 1) < 0) + goto cleanup; + break; + + case VIR_DOMAIN_DEVICE_NET: + if (xenDaemonFormatSxprNet(domain->conn, + dev->data.net, + &buf, + STREQ(def->os.type, "hvm") ? 1 : 0, + priv->xendConfigVersion, 1) < 0) + goto cleanup; + break; + + case VIR_DOMAIN_DEVICE_HOSTDEV: + if (dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + if (xenDaemonFormatSxprOnePCI(domain->conn, + dev->data.hostdev, + &buf) < 0) + goto cleanup; + } else { + virXendError(domain->conn, VIR_ERR_NO_SUPPORT, "%s", + _("unsupported device type")); + goto cleanup; + } + break; + + default: + virXendError(domain->conn, VIR_ERR_NO_SUPPORT, "%s", + _("unsupported device type")); + goto cleanup; + } + + sexpr = virBufferContentAndReset(&buf); + + if (virDomainXMLDevID(domain, dev, class, ref, sizeof(ref))) { + /* device doesn't exist, define it */ + ret = xend_op(domain->conn, domain->name, "op", "device_create", + "config", sexpr, NULL); + } + else { + /* device exists, attempt to modify it */ + ret = xend_op(domain->conn, domain->name, "op", "device_configure", + "config", sexpr, "dev", ref, NULL); + } + +cleanup: + VIR_FREE(sexpr); + virDomainDefFree(def); + virDomainDeviceDefFree(dev); + return ret; +} + +/** + * xenDaemonDetachDevice: + * @domain: pointer to domain object + * @xml: pointer to XML description of device + * + * Destroy a virtual device attachment to backend. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xenDaemonDetachDevice(virDomainPtr domain, const char *xml) +{ + xenUnifiedPrivatePtr priv; + char class[8], ref[80]; + virDomainDeviceDefPtr dev = NULL; + virDomainDefPtr def = NULL; + int ret = -1; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + /* + * on older Xen without the inactive guests management + * avoid doing this on inactive guests + */ + if ((domain->id < 0) && (priv->xendConfigVersion < 3)) + return -1; + + if (!(def = xenDaemonDomainFetch(domain->conn, + domain->id, + domain->name, + NULL))) + goto cleanup; + + if (!(dev = virDomainDeviceDefParse(domain->conn, + priv->caps, + def, xml, VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (virDomainXMLDevID(domain, dev, class, ref, sizeof(ref))) + goto cleanup; + + ret = xend_op(domain->conn, domain->name, "op", "device_destroy", + "type", class, "dev", ref, "force", "0", "rm_cfg", "1", NULL); + +cleanup: + virDomainDefFree(def); + virDomainDeviceDefFree(dev); + + return ret; +} + +int +xenDaemonDomainGetAutostart(virDomainPtr domain, + int *autostart) +{ + struct sexpr *root; + const char *tmp; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + + /* xm_internal.c (the support for defined domains from /etc/xen + * config files used by old Xen) will handle this. + */ + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xendConfigVersion < 3) + return(-1); + + root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); + if (root == NULL) { + virXendError (domain->conn, VIR_ERR_XEN_CALL, + "%s", _("xenDaemonGetAutostart failed to find this domain")); + return (-1); + } + + *autostart = 0; + + tmp = sexpr_node(root, "domain/on_xend_start"); + if (tmp && STREQ(tmp, "start")) { + *autostart = 1; + } + + sexpr_free(root); + return 0; +} + +int +xenDaemonDomainSetAutostart(virDomainPtr domain, + int autostart) +{ + struct sexpr *root, *autonode; + const char *autostr; + char buf[4096]; + int ret = -1; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INTERNAL_ERROR, + __FUNCTION__); + return (-1); + } + + /* xm_internal.c (the support for defined domains from /etc/xen + * config files used by old Xen) will handle this. + */ + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xendConfigVersion < 3) + return(-1); + + root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); + if (root == NULL) { + virXendError (domain->conn, VIR_ERR_XEN_CALL, + "%s", _("xenDaemonSetAutostart failed to find this domain")); + return (-1); + } + + autostr = sexpr_node(root, "domain/on_xend_start"); + if (autostr) { + if (!STREQ(autostr, "ignore") && !STREQ(autostr, "start")) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("unexpected value from on_xend_start")); + goto error; + } + + // Change the autostart value in place, then define the new sexpr + autonode = sexpr_lookup(root, "domain/on_xend_start"); + VIR_FREE(autonode->u.s.car->u.value); + autonode->u.s.car->u.value = (autostart ? strdup("start") + : strdup("ignore")); + if (!(autonode->u.s.car->u.value)) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("no memory")); + goto error; + } + + if (sexpr2string(root, buf, sizeof(buf)) == 0) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("sexpr2string failed")); + goto error; + } + if (xend_op(domain->conn, "", "op", "new", "config", buf, NULL) != 0) { + virXendError(domain->conn, VIR_ERR_XEN_CALL, + "%s", _("Failed to redefine sexpr")); + goto error; + } + } else { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("on_xend_start not present in sexpr")); + goto error; + } + + ret = 0; + error: + sexpr_free(root); + return ret; +} + +int +xenDaemonDomainMigratePrepare (virConnectPtr dconn, + char **cookie ATTRIBUTE_UNUSED, + int *cookielen ATTRIBUTE_UNUSED, + const char *uri_in, + char **uri_out, + unsigned long flags ATTRIBUTE_UNUSED, + const char *dname ATTRIBUTE_UNUSED, + unsigned long resource ATTRIBUTE_UNUSED) +{ + int r; + char hostname [HOST_NAME_MAX+1]; + + /* If uri_in is NULL, get the current hostname as a best guess + * of how the source host should connect to us. Note that caller + * deallocates this string. + */ + if (uri_in == NULL) { + r = gethostname (hostname, HOST_NAME_MAX+1); + if (r == -1) { + virReportSystemError(dconn, errno, + _("unable to resolve name %s"), hostname); + return -1; + } + *uri_out = strdup (hostname); + if (*uri_out == NULL) { + virReportOOMError(dconn); + return -1; + } + } + + return 0; +} + +int +xenDaemonDomainMigratePerform (virDomainPtr domain, + const char *cookie ATTRIBUTE_UNUSED, + int cookielen ATTRIBUTE_UNUSED, + const char *uri, + unsigned long flags, + const char *dname, + unsigned long bandwidth) +{ + /* Upper layers have already checked domain. */ + virConnectPtr conn = domain->conn; + /* NB: Passing port=0 to xend means it ignores + * the port. However this is somewhat specific to + * the internals of the xend Python code. (XXX). + */ + char port[16] = "0"; + char live[2] = "0"; + int ret; + char *p, *hostname = NULL; + + /* Xen doesn't support renaming domains during migration. */ + if (dname) { + virXendError (conn, VIR_ERR_NO_SUPPORT, + "%s", _("xenDaemonDomainMigrate: Xen does not support" + " renaming domains during migration")); + return -1; + } + + /* Xen (at least up to 3.1.0) takes a resource parameter but + * ignores it. + */ + if (bandwidth) { + virXendError (conn, VIR_ERR_NO_SUPPORT, + "%s", _("xenDaemonDomainMigrate: Xen does not support" + " bandwidth limits during migration")); + return -1; + } + + /* Check the flags. */ + if ((flags & VIR_MIGRATE_LIVE)) { + strcpy (live, "1"); + flags &= ~VIR_MIGRATE_LIVE; + } + if (flags != 0) { + virXendError (conn, VIR_ERR_NO_SUPPORT, + "%s", _("xenDaemonDomainMigrate: unsupported flag")); + return -1; + } + + /* Set hostname and port. + * + * URI is non-NULL (guaranteed by caller). We expect either + * "hostname", "hostname:port" or "xenmigr://hostname[:port]/". + */ + if (strstr (uri, "//")) { /* Full URI. */ + xmlURIPtr uriptr = xmlParseURI (uri); + if (!uriptr) { + virXendError (conn, VIR_ERR_INVALID_ARG, + "%s", _("xenDaemonDomainMigrate: invalid URI")); + return -1; + } + if (uriptr->scheme && STRCASENEQ (uriptr->scheme, "xenmigr")) { + virXendError (conn, VIR_ERR_INVALID_ARG, + "%s", _("xenDaemonDomainMigrate: only xenmigr://" + " migrations are supported by Xen")); + xmlFreeURI (uriptr); + return -1; + } + if (!uriptr->server) { + virXendError (conn, VIR_ERR_INVALID_ARG, + "%s", _("xenDaemonDomainMigrate: a hostname must be" + " specified in the URI")); + xmlFreeURI (uriptr); + return -1; + } + hostname = strdup (uriptr->server); + if (!hostname) { + virReportOOMError (conn); + xmlFreeURI (uriptr); + return -1; + } + if (uriptr->port) + snprintf (port, sizeof port, "%d", uriptr->port); + xmlFreeURI (uriptr); + } + else if ((p = strrchr (uri, ':')) != NULL) { /* "hostname:port" */ + int port_nr, n; + + if (sscanf (p+1, "%d", &port_nr) != 1) { + virXendError (conn, VIR_ERR_INVALID_ARG, + "%s", _("xenDaemonDomainMigrate: invalid port number")); + return -1; + } + snprintf (port, sizeof port, "%d", port_nr); + + /* Get the hostname. */ + n = p - uri; /* n = Length of hostname in bytes. */ + hostname = strdup (uri); + if (!hostname) { + virReportOOMError (conn); + return -1; + } + hostname[n] = '\0'; + } + else { /* "hostname" (or IP address) */ + hostname = strdup (uri); + if (!hostname) { + virReportOOMError (conn); + return -1; + } + } + + DEBUG("hostname = %s, port = %s", hostname, port); + + /* Make the call. */ + ret = xend_op (domain->conn, domain->name, + "op", "migrate", + "destination", hostname, + "live", live, + "port", port, + "node", "-1", + "ssl", "0", + "resource", "0", /* required, xend ignores it */ + NULL); + VIR_FREE (hostname); + + DEBUG0("migration done"); + + return ret; +} + +virDomainPtr xenDaemonDomainDefineXML(virConnectPtr conn, const char *xmlDesc) { + int ret; + char *sexpr; + virDomainPtr dom; + xenUnifiedPrivatePtr priv; + virDomainDefPtr def; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (priv->xendConfigVersion < 3) + return(NULL); + + if (!(def = virDomainDefParseString(conn, priv->caps, xmlDesc, + VIR_DOMAIN_XML_INACTIVE))) { + virXendError(conn, VIR_ERR_XML_ERROR, + "%s", _("failed to parse domain description")); + return (NULL); + } + + if (!(sexpr = xenDaemonFormatSxpr(conn, def, priv->xendConfigVersion))) { + virXendError(conn, VIR_ERR_XML_ERROR, + "%s", _("failed to build sexpr")); + goto error; + } + + DEBUG("Defining w/ sexpr: \n%s", sexpr); + + ret = xend_op(conn, "", "op", "new", "config", sexpr, NULL); + VIR_FREE(sexpr); + if (ret != 0) { + virXendError(conn, VIR_ERR_XEN_CALL, + _("Failed to create inactive domain %s\n"), def->name); + goto error; + } + + dom = virDomainLookupByName(conn, def->name); + if (dom == NULL) { + goto error; + } + virDomainDefFree(def); + return (dom); + + error: + virDomainDefFree(def); + return (NULL); +} +int xenDaemonDomainCreate(virDomainPtr domain) +{ + xenUnifiedPrivatePtr priv; + int ret; + virDomainPtr tmp; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (priv->xendConfigVersion < 3) + return(-1); + + ret = xend_op(domain->conn, domain->name, "op", "start", NULL); + + if (ret != -1) { + /* Need to force a refresh of this object's ID */ + tmp = virDomainLookupByName(domain->conn, domain->name); + if (tmp) { + domain->id = tmp->id; + virDomainFree(tmp); + } + } + return ret; +} + +int xenDaemonDomainUndefine(virDomainPtr domain) +{ + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (priv->xendConfigVersion < 3) + return(-1); + + return xend_op(domain->conn, domain->name, "op", "delete", NULL); +} + +/** + * xenDaemonNumOfDomains: + * @conn: pointer to the hypervisor connection + * + * Provides the number of active domains. + * + * Returns the number of domain found or -1 in case of error + */ +static int +xenDaemonNumOfDefinedDomains(virConnectPtr conn) +{ + struct sexpr *root = NULL; + int ret = -1; + struct sexpr *_for_i, *node; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + /* xm_internal.c (the support for defined domains from /etc/xen + * config files used by old Xen) will handle this. + */ + if (priv->xendConfigVersion < 3) + return(-1); + + root = sexpr_get(conn, "/xend/domain?state=halted"); + if (root == NULL) + goto error; + + ret = 0; + + for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { + if (node->kind != SEXPR_VALUE) + continue; + ret++; + } + +error: + sexpr_free(root); + return(ret); +} + +static int +xenDaemonListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) { + struct sexpr *root = NULL; + int ret = -1; + struct sexpr *_for_i, *node; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (priv->xendConfigVersion < 3) + return(-1); + + if ((names == NULL) || (maxnames < 0)) + goto error; + if (maxnames == 0) + return(0); + + root = sexpr_get(conn, "/xend/domain?state=halted"); + if (root == NULL) + goto error; + + ret = 0; + + for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { + if (node->kind != SEXPR_VALUE) + continue; + + names[ret++] = strdup(node->u.value); + if (ret >= maxnames) + break; + } + +error: + sexpr_free(root); + return(ret); +} + +/** + * xenDaemonGetSchedulerType: + * @domain: pointer to the Domain block + * @nparams: give a number of scheduler parameters + * + * Get the scheduler type of Xen + * + * Returns a scheduler name (credit or sedf) which must be freed by the + * caller or NULL in case of failure + */ +static char * +xenDaemonGetSchedulerType(virDomainPtr domain, int *nparams) +{ + xenUnifiedPrivatePtr priv; + struct sexpr *root; + const char *ret = NULL; + char *schedulertype = NULL; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) + || (nparams == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return NULL; + } + + /* Support only xendConfigVersion >=4 */ + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xendConfigVersion < 4) { + virXendError (domain->conn, VIR_ERR_NO_SUPPORT, + "%s", _("unsupported in xendConfigVersion < 4")); + return NULL; + } + + root = sexpr_get(domain->conn, "/xend/node/"); + if (root == NULL) + return NULL; + + /* get xen_scheduler from xend/node */ + ret = sexpr_node(root, "node/xen_scheduler"); + if (ret == NULL){ + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("node information incomplete, missing scheduler name")); + goto error; + } + if (STREQ (ret, "credit")) { + schedulertype = strdup("credit"); + if (schedulertype == NULL){ + virXendError(domain->conn, VIR_ERR_SYSTEM_ERROR, "%s", _("strdup failed")); + goto error; + } + *nparams = XEN_SCHED_CRED_NPARAM; + } else if (STREQ (ret, "sedf")) { + schedulertype = strdup("sedf"); + if (schedulertype == NULL){ + virXendError(domain->conn, VIR_ERR_SYSTEM_ERROR, "%s", _("strdup failed")); + goto error; + } + *nparams = XEN_SCHED_SEDF_NPARAM; + } else { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("Unknown scheduler")); + goto error; + } + +error: + sexpr_free(root); + return schedulertype; + +} + +static const char *str_weight = "weight"; +static const char *str_cap = "cap"; + +/** + * xenDaemonGetSchedulerParameters: + * @domain: pointer to the Domain block + * @params: pointer to scheduler parameters + * This memory area must be allocated by the caller + * @nparams: a number of scheduler parameters which should be same as a + * given number from xenDaemonGetSchedulerType() + * + * Get the scheduler parameters + * + * Returns 0 or -1 in case of failure + */ +static int +xenDaemonGetSchedulerParameters(virDomainPtr domain, + virSchedParameterPtr params, int *nparams) +{ + xenUnifiedPrivatePtr priv; + struct sexpr *root; + char *sched_type = NULL; + int sched_nparam = 0; + int ret = -1; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) + || (params == NULL) || (nparams == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + + /* Support only xendConfigVersion >=4 */ + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xendConfigVersion < 4) { + virXendError (domain->conn, VIR_ERR_NO_SUPPORT, + "%s", _("unsupported in xendConfigVersion < 4")); + return (-1); + } + + /* look up the information by domain name */ + root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); + if (root == NULL) + return (-1); + + /* get the scheduler type */ + sched_type = xenDaemonGetSchedulerType(domain, &sched_nparam); + if (sched_type == NULL) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to get a scheduler name")); + goto error; + } + + switch (sched_nparam){ + case XEN_SCHED_SEDF_NPARAM: + /* TODO: Implement for Xen/SEDF */ + TODO + goto error; + case XEN_SCHED_CRED_NPARAM: + /* get cpu_weight/cpu_cap from xend/domain */ + if (sexpr_node(root, "domain/cpu_weight") == NULL) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing cpu_weight")); + goto error; + } + if (sexpr_node(root, "domain/cpu_cap") == NULL) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing cpu_cap")); + goto error; + } + + strncpy (params[0].field, str_weight, VIR_DOMAIN_SCHED_FIELD_LENGTH); + params[0].field[VIR_DOMAIN_SCHED_FIELD_LENGTH-1] = '\0'; + params[0].type = VIR_DOMAIN_SCHED_FIELD_UINT; + params[0].value.ui = sexpr_int(root, "domain/cpu_weight"); + + strncpy (params[1].field, str_cap, VIR_DOMAIN_SCHED_FIELD_LENGTH); + params[1].field[VIR_DOMAIN_SCHED_FIELD_LENGTH-1] = '\0'; + params[1].type = VIR_DOMAIN_SCHED_FIELD_UINT; + params[1].value.ui = sexpr_int(root, "domain/cpu_cap"); + *nparams = XEN_SCHED_CRED_NPARAM; + ret = 0; + break; + default: + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("Unknown scheduler")); + goto error; + } + +error: + sexpr_free(root); + VIR_FREE(sched_type); + return (ret); +} + +/** + * xenDaemonSetSchedulerParameters: + * @domain: pointer to the Domain block + * @params: pointer to scheduler parameters + * @nparams: a number of scheduler setting parameters + * + * Set the scheduler parameters + * + * Returns 0 or -1 in case of failure + */ +static int +xenDaemonSetSchedulerParameters(virDomainPtr domain, + virSchedParameterPtr params, int nparams) +{ + xenUnifiedPrivatePtr priv; + struct sexpr *root; + char *sched_type = NULL; + int i; + int sched_nparam = 0; + int ret = -1; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) + || (params == NULL)) { + virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + + /* Support only xendConfigVersion >=4 and active domains */ + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xendConfigVersion < 4) { + virXendError (domain->conn, VIR_ERR_NO_SUPPORT, + "%s", _("unsupported in xendConfigVersion < 4")); + return (-1); + } + + /* look up the information by domain name */ + root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); + if (root == NULL) + return (-1); + + /* get the scheduler type */ + sched_type = xenDaemonGetSchedulerType(domain, &sched_nparam); + if (sched_type == NULL) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to get a scheduler name")); + goto error; + } + + switch (sched_nparam){ + case XEN_SCHED_SEDF_NPARAM: + /* TODO: Implement for Xen/SEDF */ + TODO + goto error; + case XEN_SCHED_CRED_NPARAM: { + char buf_weight[VIR_UUID_BUFLEN]; + char buf_cap[VIR_UUID_BUFLEN]; + const char *weight = NULL; + const char *cap = NULL; + + /* get the scheduler parameters */ + memset(&buf_weight, 0, VIR_UUID_BUFLEN); + memset(&buf_cap, 0, VIR_UUID_BUFLEN); + for (i = 0; i < nparams; i++) { + if (STREQ (params[i].field, str_weight) && + params[i].type == VIR_DOMAIN_SCHED_FIELD_UINT) { + snprintf(buf_weight, sizeof(buf_weight), "%u", params[i].value.ui); + } else if (STREQ (params[i].field, str_cap) && + params[i].type == VIR_DOMAIN_SCHED_FIELD_UINT) { + snprintf(buf_cap, sizeof(buf_cap), "%u", params[i].value.ui); + } else { + virXendError(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + } + + /* if not get the scheduler parameter, set the current setting */ + if (strlen(buf_weight) == 0) { + weight = sexpr_node(root, "domain/cpu_weight"); + if (weight == NULL) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing cpu_weight")); + goto error; + } + snprintf(buf_weight, sizeof(buf_weight), "%s", weight); + } + if (strlen(buf_cap) == 0) { + cap = sexpr_node(root, "domain/cpu_cap"); + if (cap == NULL) { + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("domain information incomplete, missing cpu_cap")); + goto error; + } + snprintf(buf_cap, sizeof(buf_cap), "%s", cap); + } + + ret = xend_op(domain->conn, domain->name, "op", + "domain_sched_credit_set", "weight", buf_weight, + "cap", buf_cap, NULL); + break; + } + default: + virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("Unknown scheduler")); + goto error; + } + +error: + sexpr_free(root); + VIR_FREE(sched_type); + return (ret); +} + +/** + * xenDaemonDomainBlockPeek: + * @dom: domain object + * @path: path to the file or device + * @offset: offset + * @size: size + * @buffer: return buffer + * + * Returns 0 if successful, -1 if error, -2 if declined. + */ +int +xenDaemonDomainBlockPeek (virDomainPtr domain, const char *path, + unsigned long long offset, size_t size, + void *buffer) +{ + xenUnifiedPrivatePtr priv; + struct sexpr *root = NULL; + int fd = -1, ret = -1; + int found = 0, i; + virDomainDefPtr def; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id < 0 && priv->xendConfigVersion < 3) + return -2; /* Decline, allow XM to handle it. */ + + /* Security check: The path must correspond to a block device. */ + if (domain->id > 0) + root = sexpr_get (domain->conn, "/xend/domain/%d?detail=1", + domain->id); + else if (domain->id < 0) + root = sexpr_get (domain->conn, "/xend/domain/%s?detail=1", + domain->name); + else { + /* This call always fails for dom0. */ + virXendError (domain->conn, VIR_ERR_NO_SUPPORT, + "%s", _("domainBlockPeek is not supported for dom0")); + return -1; + } + + if (!root) { + virXendError (domain->conn, VIR_ERR_XEN_CALL, __FUNCTION__); + return -1; + } + + if (!(def = xenDaemonParseSxpr(domain->conn, root, priv->xendConfigVersion, NULL))) + goto cleanup; + + for (i = 0 ; i < def->ndisks ; i++) { + if (def->disks[i]->src && + STREQ(def->disks[i]->src, path)) { + found = 1; + break; + } + } + if (!found) { + virXendError (domain->conn, VIR_ERR_INVALID_ARG, + _("%s: invalid path"), path); + goto cleanup; + } + + /* The path is correct, now try to open it and get its size. */ + fd = open (path, O_RDONLY); + if (fd == -1) { + virReportSystemError(domain->conn, errno, + _("failed to open for reading: %s"), + path); + goto cleanup; + } + + /* Seek and read. */ + /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should + * be 64 bits on all platforms. + */ + if (lseek (fd, offset, SEEK_SET) == (off_t) -1 || + saferead (fd, buffer, size) == (ssize_t) -1) { + virReportSystemError(domain->conn, errno, + _("failed to lseek or read from file: %s"), + path); + goto cleanup; + } + + ret = 0; + cleanup: + if (fd >= 0) close (fd); + sexpr_free(root); + virDomainDefFree(def); + return ret; +} + +struct xenUnifiedDriver xenDaemonDriver = { + xenDaemonOpen, /* open */ + xenDaemonClose, /* close */ + xenDaemonGetVersion, /* version */ + NULL, /* hostname */ + xenDaemonNodeGetInfo, /* nodeGetInfo */ + NULL, /* getCapabilities */ + xenDaemonListDomains, /* listDomains */ + xenDaemonNumOfDomains, /* numOfDomains */ + xenDaemonCreateXML, /* domainCreateXML */ + xenDaemonDomainSuspend, /* domainSuspend */ + xenDaemonDomainResume, /* domainResume */ + xenDaemonDomainShutdown, /* domainShutdown */ + xenDaemonDomainReboot, /* domainReboot */ + xenDaemonDomainDestroy, /* domainDestroy */ + xenDaemonDomainGetOSType, /* domainGetOSType */ + xenDaemonDomainGetMaxMemory, /* domainGetMaxMemory */ + xenDaemonDomainSetMaxMemory, /* domainSetMaxMemory */ + xenDaemonDomainSetMemory, /* domainMaxMemory */ + xenDaemonDomainGetInfo, /* domainGetInfo */ + xenDaemonDomainSave, /* domainSave */ + xenDaemonDomainRestore, /* domainRestore */ + xenDaemonDomainCoreDump, /* domainCoreDump */ + xenDaemonDomainSetVcpus, /* domainSetVcpus */ + xenDaemonDomainPinVcpu, /* domainPinVcpu */ + xenDaemonDomainGetVcpus, /* domainGetVcpus */ + NULL, /* domainGetMaxVcpus */ + xenDaemonListDefinedDomains, /* listDefinedDomains */ + xenDaemonNumOfDefinedDomains,/* numOfDefinedDomains */ + xenDaemonDomainCreate, /* domainCreate */ + xenDaemonDomainDefineXML, /* domainDefineXML */ + xenDaemonDomainUndefine, /* domainUndefine */ + xenDaemonAttachDevice, /* domainAttachDevice */ + xenDaemonDetachDevice, /* domainDetachDevice */ + xenDaemonDomainGetAutostart, /* domainGetAutostart */ + xenDaemonDomainSetAutostart, /* domainSetAutostart */ + xenDaemonGetSchedulerType, /* domainGetSchedulerType */ + xenDaemonGetSchedulerParameters, /* domainGetSchedulerParameters */ + xenDaemonSetSchedulerParameters, /* domainSetSchedulerParameters */ +}; + +/************************************************************************ + * * + * Converter functions to go from the XML tree to an S-Expr for Xen * + * * + ************************************************************************/ + + +/** + * virtDomainParseXMLGraphicsDescVFB: + * @conn: pointer to the hypervisor connection + * @node: node containing graphics description + * @buf: a buffer for the result S-Expr + * + * Parse the graphics part of the XML description and add it to the S-Expr + * in buf. This is a temporary interface as the S-Expr interface will be + * replaced by XML-RPC in the future. However the XML format should stay + * valid over time. + * + * Returns 0 in case of success, -1 in case of error + */ +static int +xenDaemonFormatSxprGraphicsNew(virConnectPtr conn, + virDomainGraphicsDefPtr def, + virBufferPtr buf) +{ + if (def->type != VIR_DOMAIN_GRAPHICS_TYPE_SDL && + def->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected graphics type %d"), + def->type); + return -1; + } + + virBufferAddLit(buf, "(device (vkbd))"); + virBufferAddLit(buf, "(device (vfb "); + + if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { + virBufferAddLit(buf, "(type sdl)"); + if (def->data.sdl.display) + virBufferVSprintf(buf, "(display '%s')", def->data.sdl.display); + if (def->data.sdl.xauth) + virBufferVSprintf(buf, "(xauthority '%s')", def->data.sdl.xauth); + } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + virBufferAddLit(buf, "(type vnc)"); + if (def->data.vnc.autoport) { + virBufferAddLit(buf, "(vncunused 1)"); + } else { + virBufferAddLit(buf, "(vncunused 0)"); + virBufferVSprintf(buf, "(vncdisplay %d)", def->data.vnc.port-5900); + } + + if (def->data.vnc.listenAddr) + virBufferVSprintf(buf, "(vnclisten '%s')", def->data.vnc.listenAddr); + if (def->data.vnc.passwd) + virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.passwd); + if (def->data.vnc.keymap) + virBufferVSprintf(buf, "(keymap '%s')", def->data.vnc.keymap); + } + + virBufferAddLit(buf, "))"); + + return 0; +} + + +static int +xenDaemonFormatSxprGraphicsOld(virConnectPtr conn, + virDomainGraphicsDefPtr def, + virBufferPtr buf, + int xendConfigVersion) +{ + if (def->type != VIR_DOMAIN_GRAPHICS_TYPE_SDL && + def->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected graphics type %d"), + def->type); + return -1; + } + + if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { + virBufferAddLit(buf, "(sdl 1)"); + if (def->data.sdl.display) + virBufferVSprintf(buf, "(display '%s')", def->data.sdl.display); + if (def->data.sdl.xauth) + virBufferVSprintf(buf, "(xauthority '%s')", def->data.sdl.xauth); + } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + virBufferAddLit(buf, "(vnc 1)"); + if (xendConfigVersion >= 2) { + if (def->data.vnc.autoport) { + virBufferAddLit(buf, "(vncunused 1)"); + } else { + virBufferAddLit(buf, "(vncunused 0)"); + virBufferVSprintf(buf, "(vncdisplay %d)", def->data.vnc.port-5900); + } + + if (def->data.vnc.listenAddr) + virBufferVSprintf(buf, "(vnclisten '%s')", def->data.vnc.listenAddr); + if (def->data.vnc.passwd) + virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.passwd); + if (def->data.vnc.keymap) + virBufferVSprintf(buf, "(keymap '%s')", def->data.vnc.keymap); + + } + } + + return 0; +} + +int +xenDaemonFormatSxprChr(virConnectPtr conn, + virDomainChrDefPtr def, + virBufferPtr buf) +{ + const char *type = virDomainChrTypeToString(def->type); + + if (!type) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("unexpected chr device type")); + return -1; + } + + switch (def->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + case VIR_DOMAIN_CHR_TYPE_STDIO: + case VIR_DOMAIN_CHR_TYPE_VC: + case VIR_DOMAIN_CHR_TYPE_PTY: + virBufferVSprintf(buf, "%s", type); + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + case VIR_DOMAIN_CHR_TYPE_PIPE: + virBufferVSprintf(buf, "%s:%s", type, def->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + virBufferVSprintf(buf, "%s", def->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + virBufferVSprintf(buf, "%s:%s:%s%s", + (def->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW ? + "tcp" : "telnet"), + (def->data.tcp.host ? def->data.tcp.host : ""), + (def->data.tcp.service ? def->data.tcp.service : ""), + (def->data.tcp.listen ? ",listen" : "")); + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + virBufferVSprintf(buf, "%s:%s:%s@%s:%s", type, + (def->data.udp.connectHost ? def->data.udp.connectHost : ""), + (def->data.udp.connectService ? def->data.udp.connectService : ""), + (def->data.udp.bindHost ? def->data.udp.bindHost : ""), + (def->data.udp.bindService ? def->data.udp.bindService : "")); + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + virBufferVSprintf(buf, "%s:%s%s", type, + def->data.nix.path, + def->data.nix.listen ? ",listen" : ""); + break; + } + + if (virBufferError(buf)) + return -1; + + return 0; +} + + +/** + * virDomainParseXMLDiskDesc: + * @node: node containing disk description + * @conn: pointer to the hypervisor connection + * @buf: a buffer for the result S-Expr + * @xendConfigVersion: xend configuration file format + * + * Parse the one disk in the XML description and add it to the S-Expr in buf + * This is a temporary interface as the S-Expr interface + * will be replaced by XML-RPC in the future. However the XML format should + * stay valid over time. + * + * Returns 0 in case of success, -1 in case of error. + */ +static int +xenDaemonFormatSxprDisk(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainDiskDefPtr def, + virBufferPtr buf, + int hvm, + int xendConfigVersion, + int isAttach) +{ + /* Xend (all versions) put the floppy device config + * under the hvm (image (os)) block + */ + if (hvm && + def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) { + if (isAttach) { + virXendError(conn, VIR_ERR_INVALID_ARG, + _("Cannot directly attach floppy %s"), def->src); + return -1; + } + return 0; + } + + /* Xend <= 3.0.2 doesn't include cdrom config here */ + if (hvm && + def->device == VIR_DOMAIN_DISK_DEVICE_CDROM && + xendConfigVersion == 1) { + if (isAttach) { + virXendError(conn, VIR_ERR_INVALID_ARG, + _("Cannot directly attach CDROM %s"), def->src); + return -1; + } + return 0; + } + + if (!isAttach) + virBufferAddLit(buf, "(device "); + + /* Normally disks are in a (device (vbd ...)) block + * but blktap disks ended up in a differently named + * (device (tap ....)) block.... */ + if (def->driverName && + STREQ(def->driverName, "tap")) { + virBufferAddLit(buf, "(tap "); + } else { + virBufferAddLit(buf, "(vbd "); + } + + if (hvm) { + /* Xend <= 3.0.2 wants a ioemu: prefix on devices for HVM */ + if (xendConfigVersion == 1) + virBufferVSprintf(buf, "(dev 'ioemu:%s')", def->dst); + else /* But newer does not */ + virBufferVSprintf(buf, "(dev '%s:%s')", def->dst, + def->device == VIR_DOMAIN_DISK_DEVICE_CDROM ? + "cdrom" : "disk"); + } else if (def->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + virBufferVSprintf(buf, "(dev '%s:cdrom')", def->dst); + } else { + virBufferVSprintf(buf, "(dev '%s')", def->dst); + } + + if (def->src) { + if (def->driverName) { + if (STREQ(def->driverName, "tap")) { + virBufferVSprintf(buf, "(uname '%s:%s:%s')", + def->driverName, + def->driverType ? def->driverType : "aio", + def->src); + } else { + virBufferVSprintf(buf, "(uname '%s:%s')", + def->driverName, + def->src); + } + } else { + if (def->type == VIR_DOMAIN_DISK_TYPE_FILE) { + virBufferVSprintf(buf, "(uname 'file:%s')", def->src); + } else { + if (def->src[0] == '/') + virBufferVSprintf(buf, "(uname 'phy:%s')", def->src); + else + virBufferVSprintf(buf, "(uname 'phy:/dev/%s')", def->src); + } + } + } + + if (def->readonly) + virBufferAddLit(buf, "(mode 'r')"); + else if (def->shared) + virBufferAddLit(buf, "(mode 'w!')"); + else + virBufferAddLit(buf, "(mode 'w')"); + + if (!isAttach) + virBufferAddLit(buf, ")"); + + virBufferAddLit(buf, ")"); + + return 0; +} + +/** + * xenDaemonFormatSxprNet + * @conn: pointer to the hypervisor connection + * @node: node containing the interface description + * @buf: a buffer for the result S-Expr + * @xendConfigVersion: xend configuration file format + * + * Parse the one interface the XML description and add it to the S-Expr in buf + * This is a temporary interface as the S-Expr interface + * will be replaced by XML-RPC in the future. However the XML format should + * stay valid over time. + * + * Returns 0 in case of success, -1 in case of error. + */ +static int +xenDaemonFormatSxprNet(virConnectPtr conn, + virDomainNetDefPtr def, + virBufferPtr buf, + int hvm, + int xendConfigVersion, + int isAttach) +{ + const char *script = DEFAULT_VIF_SCRIPT; + + if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE && + def->type != VIR_DOMAIN_NET_TYPE_NETWORK && + def->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported network type %d"), def->type); + return -1; + } + + if (!isAttach) + virBufferAddLit(buf, "(device "); + + virBufferAddLit(buf, "(vif "); + + virBufferVSprintf(buf, + "(mac '%02x:%02x:%02x:%02x:%02x:%02x')", + def->mac[0], def->mac[1], def->mac[2], + def->mac[3], def->mac[4], def->mac[5]); + + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + virBufferVSprintf(buf, "(bridge '%s')", def->data.bridge.brname); + if (def->data.bridge.script) + script = def->data.bridge.script; + + virBufferVSprintf(buf, "(script '%s')", script); + if (def->data.bridge.ipaddr != NULL) + virBufferVSprintf(buf, "(ip '%s')", def->data.bridge.ipaddr); + break; + + case VIR_DOMAIN_NET_TYPE_NETWORK: + { + virNetworkPtr network = + virNetworkLookupByName(conn, def->data.network.name); + char *bridge; + + if (!network) { + virXendError(conn, VIR_ERR_NO_NETWORK, "%s", + def->data.network.name); + return -1; + } + + bridge = virNetworkGetBridgeName(network); + virNetworkFree(network); + if (!bridge) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("network %s is not active"), + def->data.network.name); + return -1; + } + virBufferVSprintf(buf, "(bridge '%s')", bridge); + virBufferVSprintf(buf, "(script '%s')", script); + VIR_FREE(bridge); + } + break; + + case VIR_DOMAIN_NET_TYPE_ETHERNET: + if (def->data.ethernet.script) + virBufferVSprintf(buf, "(script '%s')", def->data.ethernet.script); + if (def->data.ethernet.ipaddr != NULL) + virBufferVSprintf(buf, "(ip '%s')", def->data.ethernet.ipaddr); + break; + } + + if (def->ifname != NULL && + !STRPREFIX(def->ifname, "vif")) + virBufferVSprintf(buf, "(vifname '%s')", def->ifname); + + if (def->model != NULL) + virBufferVSprintf(buf, "(model '%s')", def->model); + + /* + * apparently (type ioemu) breaks paravirt drivers on HVM so skip this + * from Xen 3.1.0 + */ + if (hvm && xendConfigVersion <= XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU) + virBufferAddLit(buf, "(type ioemu)"); + + if (!isAttach) + virBufferAddLit(buf, ")"); + + virBufferAddLit(buf, ")"); + + return 0; +} + + +static void +xenDaemonFormatSxprPCI(virDomainHostdevDefPtr def, + virBufferPtr buf) +{ + virBufferVSprintf(buf, "(dev (domain 0x%04x)(bus 0x%02x)(slot 0x%02x)(func 0x%x))", + def->source.subsys.u.pci.domain, + def->source.subsys.u.pci.bus, + def->source.subsys.u.pci.slot, + def->source.subsys.u.pci.function); +} + +static int +xenDaemonFormatSxprOnePCI(virConnectPtr conn, + virDomainHostdevDefPtr def, + virBufferPtr buf) +{ + if (def->managed) { + virXendError(conn, VIR_ERR_NO_SUPPORT, "%s", + _("managed PCI devices not supported with XenD")); + return -1; + } + + virBufferAddLit(buf, "(pci "); + xenDaemonFormatSxprPCI(def, buf); + virBufferAddLit(buf, ")"); + + return 0; +} + +static int +xenDaemonFormatSxprAllPCI(virConnectPtr conn, + virDomainDefPtr def, + virBufferPtr buf) +{ + int hasPCI = 0; + int i; + + for (i = 0 ; i < def->nhostdevs ; i++) + if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + hasPCI = 1; + + if (!hasPCI) + return 0; + + /* + * With the (domain ...) block we have the following odd setup + * + * (device + * (pci + * (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0)) + * (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0)) + * ) + * ) + * + * Normally there is one (device ...) block per device, but in + * wierd world of Xen PCI, once (device ...) covers multiple + * devices. + */ + + virBufferAddLit(buf, "(device (pci "); + for (i = 0 ; i < def->nhostdevs ; i++) { + if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + if (def->hostdevs[i]->managed) { + virXendError(conn, VIR_ERR_NO_SUPPORT, "%s", + _("managed PCI devices not supported with XenD")); + return -1; + } + + xenDaemonFormatSxprPCI(def->hostdevs[i], buf); + } + } + virBufferAddLit(buf, "))"); + + return 0; +} + +int +xenDaemonFormatSxprSound(virConnectPtr conn, + virDomainDefPtr def, + virBufferPtr buf) +{ + const char *str; + int i; + + for (i = 0 ; i < def->nsounds ; i++) { + if (!(str = virDomainSoundModelTypeToString(def->sounds[i]->model))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected sound model %d"), + def->sounds[i]->model); + return -1; + } + virBufferVSprintf(buf, "%s%s", i ? "," : "", str); + } + + if (virBufferError(buf)) + return -1; + + return 0; +} + + +static int +xenDaemonFormatSxprInput(virConnectPtr conn, + virDomainInputDefPtr input, + virBufferPtr buf) +{ + if (input->bus != VIR_DOMAIN_INPUT_BUS_USB) + return 0; + + if (input->type != VIR_DOMAIN_INPUT_TYPE_MOUSE && + input->type != VIR_DOMAIN_INPUT_TYPE_TABLET) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected input type %d"), input->type); + return -1; + } + + virBufferVSprintf(buf, "(usbdevice %s)", + input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? + "mouse" : "tablet"); + + return 0; +} + + +/** + * xenDaemonFormatSxpr: + * @conn: pointer to the hypervisor connection + * @def: domain config definition + * @xendConfigVersion: xend configuration file format + * + * Generate an SEXPR representing the domain configuration. + * + * Returns the 0 terminatedi S-Expr string or NULL in case of error. + * the caller must free() the returned value. + */ +char * +xenDaemonFormatSxpr(virConnectPtr conn, + virDomainDefPtr def, + int xendConfigVersion) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + const char *tmp; + int hvm = 0, i; + + virBufferAddLit(&buf, "(vm "); + virBufferVSprintf(&buf, "(name '%s')", def->name); + virBufferVSprintf(&buf, "(memory %lu)(maxmem %lu)", + def->memory/1024, def->maxmem/1024); + virBufferVSprintf(&buf, "(vcpus %lu)", def->vcpus); + + if (def->cpumask) { + char *ranges = virDomainCpuSetFormat(conn, def->cpumask, def->cpumasklen); + if (ranges == NULL) + goto error; + virBufferVSprintf(&buf, "(cpus '%s')", ranges); + VIR_FREE(ranges); + } + + virUUIDFormat(def->uuid, uuidstr); + virBufferVSprintf(&buf, "(uuid '%s')", uuidstr); + + if (def->os.bootloader) { + if (def->os.bootloader[0]) + virBufferVSprintf(&buf, "(bootloader '%s')", def->os.bootloader); + else + virBufferAddLit(&buf, "(bootloader)"); + + if (def->os.bootloaderArgs) + virBufferVSprintf(&buf, "(bootloader_args '%s')", def->os.bootloaderArgs); + } + + if (!(tmp = virDomainLifecycleTypeToString(def->onPoweroff))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected lifecycle value %d"), def->onPoweroff); + goto error; + } + virBufferVSprintf(&buf, "(on_poweroff '%s')", tmp); + + if (!(tmp = virDomainLifecycleTypeToString(def->onReboot))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected lifecycle value %d"), def->onReboot); + goto error; + } + virBufferVSprintf(&buf, "(on_reboot '%s')", tmp); + + if (!(tmp = virDomainLifecycleTypeToString(def->onCrash))) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected lifecycle value %d"), def->onCrash); + goto error; + } + virBufferVSprintf(&buf, "(on_crash '%s')", tmp); + + /* Set localtime here for current XenD (both PV & HVM) */ + if (def->localtime) + virBufferAddLit(&buf, "(localtime 1)"); + + if (!def->os.bootloader) { + if (STREQ(def->os.type, "hvm")) + hvm = 1; + + if (hvm) + virBufferAddLit(&buf, "(image (hvm "); + else + virBufferAddLit(&buf, "(image (linux "); + + if (hvm && + def->os.loader == NULL) { + virXendError(conn, VIR_ERR_INTERNAL_ERROR, + "%s",_("no HVM domain loader")); + goto error; + } + + if (def->os.kernel) + virBufferVSprintf(&buf, "(kernel '%s')", def->os.kernel); + if (def->os.initrd) + virBufferVSprintf(&buf, "(ramdisk '%s')", def->os.initrd); + if (def->os.root) + virBufferVSprintf(&buf, "(root '%s')", def->os.root); + if (def->os.cmdline) + virBufferVSprintf(&buf, "(args '%s')", def->os.cmdline); + + if (hvm) { + char bootorder[VIR_DOMAIN_BOOT_LAST+1]; + if (def->os.kernel) + virBufferVSprintf(&buf, "(loader '%s')", def->os.loader); + else + virBufferVSprintf(&buf, "(kernel '%s')", def->os.loader); + + virBufferVSprintf(&buf, "(vcpus %lu)", def->vcpus); + + for (i = 0 ; i < def->os.nBootDevs ; i++) { + switch (def->os.bootDevs[i]) { + case VIR_DOMAIN_BOOT_FLOPPY: + bootorder[i] = 'a'; + break; + default: + case VIR_DOMAIN_BOOT_DISK: + bootorder[i] = 'c'; + break; + case VIR_DOMAIN_BOOT_CDROM: + bootorder[i] = 'd'; + break; + case VIR_DOMAIN_BOOT_NET: + bootorder[i] = 'n'; + break; + } + } + if (def->os.nBootDevs == 0) { + bootorder[0] = 'c'; + bootorder[1] = '\0'; + } else { + bootorder[def->os.nBootDevs] = '\0'; + } + virBufferVSprintf(&buf, "(boot %s)", bootorder); + + /* some disk devices are defined here */ + for (i = 0 ; i < def->ndisks ; i++) { + switch (def->disks[i]->device) { + case VIR_DOMAIN_DISK_DEVICE_CDROM: + /* Only xend <= 3.0.2 wants cdrom config here */ + if (xendConfigVersion != 1) + break; + if (!STREQ(def->disks[i]->dst, "hdc") || + def->disks[i]->src == NULL) + break; + + virBufferVSprintf(&buf, "(cdrom '%s')", + def->disks[i]->src); + break; + + case VIR_DOMAIN_DISK_DEVICE_FLOPPY: + /* all xend versions define floppies here */ + virBufferVSprintf(&buf, "(%s '%s')", def->disks[i]->dst, + def->disks[i]->src); + break; + + default: + break; + } + } + + if (def->features & (1 << VIR_DOMAIN_FEATURE_ACPI)) + virBufferAddLit(&buf, "(acpi 1)"); + if (def->features & (1 << VIR_DOMAIN_FEATURE_APIC)) + virBufferAddLit(&buf, "(apic 1)"); + if (def->features & (1 << VIR_DOMAIN_FEATURE_PAE)) + virBufferAddLit(&buf, "(pae 1)"); + + virBufferAddLit(&buf, "(usb 1)"); + + for (i = 0 ; i < def->ninputs ; i++) + if (xenDaemonFormatSxprInput(conn, def->inputs[i], &buf) < 0) + goto error; + + if (def->parallels) { + virBufferAddLit(&buf, "(parallel "); + if (xenDaemonFormatSxprChr(conn, def->parallels[0], &buf) < 0) + goto error; + virBufferAddLit(&buf, ")"); + } else { + virBufferAddLit(&buf, "(parallel none)"); + } + if (def->serials) { + virBufferAddLit(&buf, "(serial "); + if (xenDaemonFormatSxprChr(conn, def->serials[0], &buf) < 0) + goto error; + virBufferAddLit(&buf, ")"); + } else { + virBufferAddLit(&buf, "(serial none)"); + } + + /* Set localtime here to keep old XenD happy for HVM */ + if (def->localtime) + virBufferAddLit(&buf, "(localtime 1)"); + + if (def->sounds) { + virBufferAddLit(&buf, "(soundhw '"); + if (xenDaemonFormatSxprSound(conn, def, &buf) < 0) + goto error; + virBufferAddLit(&buf, "')"); + } + } + + /* get the device emulation model */ + if (def->emulator && (hvm || xendConfigVersion >= 3)) + virBufferVSprintf(&buf, "(device_model '%s')", def->emulator); + + + /* PV graphics for xen <= 3.0.4, or HVM graphics for xen <= 3.1.0 */ + if ((!hvm && xendConfigVersion < XEND_CONFIG_MIN_VERS_PVFB_NEWCONF) || + (hvm && xendConfigVersion < 4)) { + if ((def->ngraphics == 1) && + xenDaemonFormatSxprGraphicsOld(conn, def->graphics[0], + &buf, xendConfigVersion) < 0) + goto error; + } + + virBufferAddLit(&buf, "))"); + } + + for (i = 0 ; i < def->ndisks ; i++) + if (xenDaemonFormatSxprDisk(conn, def->disks[i], + &buf, hvm, xendConfigVersion, 0) < 0) + goto error; + + for (i = 0 ; i < def->nnets ; i++) + if (xenDaemonFormatSxprNet(conn, def->nets[i], + &buf, hvm, xendConfigVersion, 0) < 0) + goto error; + + if (xenDaemonFormatSxprAllPCI(conn, def, &buf) < 0) + goto error; + + /* New style PV graphics config xen >= 3.0.4, + * or HVM graphics config xen >= 3.0.5 */ + if ((xendConfigVersion >= XEND_CONFIG_MIN_VERS_PVFB_NEWCONF && !hvm) || + (xendConfigVersion >= 4 && hvm)) { + if ((def->ngraphics == 1) && + xenDaemonFormatSxprGraphicsNew(conn, def->graphics[0], &buf) < 0) + goto error; + } + + virBufferAddLit(&buf, ")"); /* closes (vm */ + + if (virBufferError(&buf)) { + virReportOOMError(conn); + return NULL; + } + + return virBufferContentAndReset(&buf); + +error: + tmp = virBufferContentAndReset(&buf); + VIR_FREE(tmp); + return NULL; +} + + +/** + * virDomainXMLDevID: + * @domain: pointer to domain object + * @dev: pointer to device config object + * @class: Xen device class "vbd" or "vif" (OUT) + * @ref: Xen device reference (OUT) + * + * Set class according to XML root, and: + * - if disk, copy in ref the target name from description + * - if network, get MAC address from description, scan XenStore and + * copy in ref the corresponding vif number. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +virDomainXMLDevID(virDomainPtr domain, + virDomainDeviceDefPtr dev, + char *class, + char *ref, + int ref_len) +{ + xenUnifiedPrivatePtr priv = domain->conn->privateData; + char *xref; + + if (dev->type == VIR_DOMAIN_DEVICE_DISK) { + if (dev->data.disk->driverName && + STREQ(dev->data.disk->driverName, "tap")) + strcpy(class, "tap"); + else + strcpy(class, "vbd"); + + if (dev->data.disk->dst == NULL) + return -1; + xenUnifiedLock(priv); + xref = xenStoreDomainGetDiskID(domain->conn, domain->id, + dev->data.disk->dst); + xenUnifiedUnlock(priv); + if (xref == NULL) + return -1; + + strncpy(ref, xref, ref_len); + free(xref); + ref[ref_len - 1] = '\0'; + } else if (dev->type == VIR_DOMAIN_DEVICE_NET) { + char mac[30]; + virDomainNetDefPtr def = dev->data.net; + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + def->mac[0], def->mac[1], def->mac[2], + def->mac[3], def->mac[4], def->mac[5]); + + strcpy(class, "vif"); + + xenUnifiedLock(priv); + xref = xenStoreDomainGetNetworkID(domain->conn, domain->id, + mac); + xenUnifiedUnlock(priv); + if (xref == NULL) + return -1; + + strncpy(ref, xref, ref_len); + free(xref); + ref[ref_len - 1] = '\0'; + } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV && + dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + } else { + virXendError(NULL, VIR_ERR_NO_SUPPORT, + "%s", _("hotplug of device type not supported")); + return -1; + } + + return 0; +} + +#endif /* ! PROXY */ diff --git a/src/xen/xend_internal.h b/src/xen/xend_internal.h new file mode 100644 index 0000000000..9d2571bd16 --- /dev/null +++ b/src/xen/xend_internal.h @@ -0,0 +1,191 @@ +/* + * xend_internal.h + * + * Copyright (C) 2005,2006 + * + * Anthony Liguori + * Daniel Veillard + * + * Copyright 2006-2008 Red Hat + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#ifndef __XEND_INTERNAL_H_ +#define __XEND_INTERNAL_H_ + +#include +#include +#include +#include + +#include "internal.h" +#include "capabilities.h" +#include "domain_conf.h" +#include "driver.h" +#include "buf.h" + +#ifdef __sun +#define DEFAULT_VIF_SCRIPT "vif-vnic" +#else +#define DEFAULT_VIF_SCRIPT "vif-bridge" +#endif + +int +xenDaemonOpen_unix(virConnectPtr conn, const char *path); + +/** + * \brief Blocks until a domain's devices are initialized + * \param xend A xend instance + * \param name The domain's name + * \return 0 for success; -1 (with errno) on error + * + * xen_create() returns after a domain has been allocated including + * its memory. This does not guarentee, though, that the devices + * have come up properly. For instance, if you create a VBD with an + * invalid filename, the error won't occur until after this function + * returns. + */ + int xend_wait_for_devices(virConnectPtr xend, const char *name); + + +/** + * \brief Create a new domain + * \param xend A xend instance + * \param sexpr An S-Expr defining the domain + * \return 0 for success; -1 (with errno) on error + * + * This method will create a domain based the passed in description. The + * domain will be paused after creation and must be unpaused with + * xenDaemonResumeDomain() to begin execution. + */ +int xenDaemonDomainCreateXML(virConnectPtr xend, const char *sexpr); + +/** + * \brief Lookup the id of a domain + * \param xend A xend instance + * \param name The name of the domain + * \param uuid pointer to store a copy of the uuid + * \return the id number on success; -1 (with errno) on error + * + * This method looks up the ids of a domain + */ +int xenDaemonDomainLookupByName_ids(virConnectPtr xend, + const char *name, unsigned char *uuid); + + +/** + * \brief Lookup the name of a domain + * \param xend A xend instance + * \param id The id of the domain + * \param name pointer to store a copy of the name + * \param uuid pointer to store a copy of the uuid + * + * This method looks up the name/uuid of a domain + */ +int xenDaemonDomainLookupByID(virConnectPtr xend, + int id, + char **name, unsigned char *uuid); + + +virDomainDefPtr +xenDaemonDomainFetch(virConnectPtr xend, + int domid, + const char *name, + const char *cpus); + + int xend_parse_sexp_desc_char(virConnectPtr conn, + virBufferPtr buf, + const char *devtype, + int portNum, + const char *value, + const char *tty); + +virDomainDefPtr +xenDaemonParseSxprString(virConnectPtr conn, + const char *sexpr, + int xendConfigVersion); + +int +xenDaemonParseSxprSound(virConnectPtr conn, + virDomainDefPtr def, + const char *str); +virDomainChrDefPtr +xenDaemonParseSxprChar(virConnectPtr conn, + const char *value, + const char *tty); + +int +xenDaemonFormatSxprChr(virConnectPtr conn, + virDomainChrDefPtr def, + virBufferPtr buf); +int +xenDaemonFormatSxprSound(virConnectPtr conn, + virDomainDefPtr def, + virBufferPtr buf); + +char * +xenDaemonFormatSxpr(virConnectPtr conn, + virDomainDefPtr def, + int xendConfigVersion); + + int is_sound_model_valid(const char *model); + int is_sound_model_conflict(const char *model, const char *soundstr); + + +/* refactored ones */ +virDrvOpenStatus xenDaemonOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags); +int xenDaemonClose(virConnectPtr conn); +int xenDaemonGetVersion(virConnectPtr conn, unsigned long *hvVer); +int xenDaemonNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info); +int xenDaemonNodeGetTopology(virConnectPtr conn, virCapsPtr caps); +int xenDaemonDomainSuspend(virDomainPtr domain); +int xenDaemonDomainResume(virDomainPtr domain); +int xenDaemonDomainShutdown(virDomainPtr domain); +int xenDaemonDomainReboot(virDomainPtr domain, unsigned int flags); +int xenDaemonDomainDestroy(virDomainPtr domain); +int xenDaemonDomainSave(virDomainPtr domain, const char *filename); +int xenDaemonDomainRestore(virConnectPtr conn, const char *filename); +int xenDaemonDomainSetMemory(virDomainPtr domain, unsigned long memory); +int xenDaemonDomainSetMaxMemory(virDomainPtr domain, unsigned long memory); +int xenDaemonDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info); +char *xenDaemonDomainDumpXML(virDomainPtr domain, int flags, const char *cpus); +unsigned long xenDaemonDomainGetMaxMemory(virDomainPtr domain); +char **xenDaemonListDomainsOld(virConnectPtr xend); + +virDomainPtr xenDaemonDomainDefineXML(virConnectPtr xend, const char *sexpr); +int xenDaemonDomainCreate(virDomainPtr domain); +int xenDaemonDomainUndefine(virDomainPtr domain); + +int xenDaemonDomainSetVcpus (virDomainPtr domain, + unsigned int vcpus); +int xenDaemonDomainPinVcpu (virDomainPtr domain, + unsigned int vcpu, + unsigned char *cpumap, + int maplen); +int xenDaemonDomainGetVcpus (virDomainPtr domain, + virVcpuInfoPtr info, + int maxinfo, + unsigned char *cpumaps, + int maplen); +int xenDaemonDomainGetAutostart (virDomainPtr dom, + int *autostart); +int xenDaemonDomainSetAutostart (virDomainPtr domain, + int autostart); + +/* xen_unified calls through here. */ +extern struct xenUnifiedDriver xenDaemonDriver; +int xenDaemonInit (void); + +virDomainPtr xenDaemonLookupByID(virConnectPtr conn, int id); +virDomainPtr xenDaemonLookupByUUID(virConnectPtr conn, const unsigned char *uuid); +virDomainPtr xenDaemonLookupByName(virConnectPtr conn, const char *domname); +int xenDaemonDomainMigratePrepare (virConnectPtr dconn, char **cookie, int *cookielen, const char *uri_in, char **uri_out, unsigned long flags, const char *dname, unsigned long resource); +int xenDaemonDomainMigratePerform (virDomainPtr domain, const char *cookie, int cookielen, const char *uri, unsigned long flags, const char *dname, unsigned long resource); + +int xenDaemonDomainBlockPeek (virDomainPtr domain, const char *path, unsigned long long offset, size_t size, void *buffer); +int xenDaemonListDomains(virConnectPtr conn, int *ids, int maxids); + +#endif /* __XEND_INTERNAL_H_ */ diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c new file mode 100644 index 0000000000..960b17af3d --- /dev/null +++ b/src/xen/xm_internal.c @@ -0,0 +1,3066 @@ +/* + * xm_internal.h: helper routines for dealing with inactive domains + * + * Copyright (C) 2006-2007, 2009 Red Hat + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "virterror_internal.h" +#include "datatypes.h" +#include "xm_internal.h" +#include "xen_driver.h" +#include "xend_internal.h" +#include "hash.h" +#include "buf.h" +#include "uuid.h" +#include "util.h" +#include "memory.h" +#include "logging.h" + +#define VIR_FROM_THIS VIR_FROM_XENXM + +#ifdef WITH_RHEL5_API +#define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 0 +#define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 2 +#else +#define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 3 +#define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 3 +#endif + +/* The true Xen limit varies but so far is always way + less than 1024, which is the Linux kernel limit according + to sched.h, so we'll match that for now */ +#define XEN_MAX_PHYSICAL_CPU 1024 + +static int xenXMConfigSetString(virConfPtr conf, const char *setting, + const char *str); +char * xenXMAutoAssignMac(void); +static int xenXMDomainAttachDevice(virDomainPtr domain, const char *xml); +static int xenXMDomainDetachDevice(virDomainPtr domain, const char *xml); + +#define XM_REFRESH_INTERVAL 10 + +#define XM_CONFIG_DIR "/etc/xen" +#define XM_EXAMPLE_PREFIX "xmexample" +#define XEND_CONFIG_FILE "xend-config.sxp" +#define XEND_PCI_CONFIG_PREFIX "xend-pci-" +#define QEMU_IF_SCRIPT "qemu-ifup" +#define XM_XML_ERROR "Invalid xml" + +struct xenUnifiedDriver xenXMDriver = { + xenXMOpen, /* open */ + xenXMClose, /* close */ + NULL, /* version */ + NULL, /* hostname */ + NULL, /* nodeGetInfo */ + NULL, /* getCapabilities */ + NULL, /* listDomains */ + NULL, /* numOfDomains */ + NULL, /* domainCreateXML */ + NULL, /* domainSuspend */ + NULL, /* domainResume */ + NULL, /* domainShutdown */ + NULL, /* domainReboot */ + NULL, /* domainDestroy */ + NULL, /* domainGetOSType */ + xenXMDomainGetMaxMemory, /* domainGetMaxMemory */ + xenXMDomainSetMaxMemory, /* domainSetMaxMemory */ + xenXMDomainSetMemory, /* domainMaxMemory */ + xenXMDomainGetInfo, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + NULL, /* domainCoreDump */ + xenXMDomainSetVcpus, /* domainSetVcpus */ + xenXMDomainPinVcpu, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + NULL, /* domainGetMaxVcpus */ + xenXMListDefinedDomains, /* listDefinedDomains */ + xenXMNumOfDefinedDomains, /* numOfDefinedDomains */ + xenXMDomainCreate, /* domainCreate */ + xenXMDomainDefineXML, /* domainDefineXML */ + xenXMDomainUndefine, /* domainUndefine */ + xenXMDomainAttachDevice, /* domainAttachDevice */ + xenXMDomainDetachDevice, /* domainDetachDevice */ + NULL, /* domainGetAutostart */ + NULL, /* domainSetAutostart */ + NULL, /* domainGetSchedulerType */ + NULL, /* domainGetSchedulerParameters */ + NULL, /* domainSetSchedulerParameters */ +}; + +#define xenXMError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_XENXM, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +#ifndef WITH_XEN_INOTIFY +static int xenInotifyActive(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + return 0; +} +#else +static int xenInotifyActive(virConnectPtr conn) +{ + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + return priv->inotifyWatch > 0; +} +#endif + +/* Convenience method to grab a int from the config file object */ +static int xenXMConfigGetBool(virConnectPtr conn, + virConfPtr conf, + const char *name, + int *value, + int def) { + virConfValuePtr val; + + *value = 0; + if (!(val = virConfGetValue(conf, name))) { + *value = def; + return 0; + } + + if (val->type == VIR_CONF_LONG) { + *value = val->l ? 1 : 0; + } else if (val->type == VIR_CONF_STRING) { + if (!val->str) { + *value = def; + } + *value = STREQ(val->str, "1") ? 1 : 0; + } else { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("config value %s was malformed"), name); + return -1; + } + return 0; +} + + +/* Convenience method to grab a int from the config file object */ +static int xenXMConfigGetULong(virConnectPtr conn, + virConfPtr conf, + const char *name, + unsigned long *value, + int def) { + virConfValuePtr val; + + *value = 0; + if (!(val = virConfGetValue(conf, name))) { + *value = def; + return 0; + } + + if (val->type == VIR_CONF_LONG) { + *value = val->l; + } else if (val->type == VIR_CONF_STRING) { + char *ret; + if (!val->str) { + *value = def; + } + *value = strtol(val->str, &ret, 10); + if (ret == val->str) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("config value %s was malformed"), name); + return -1; + } + } else { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("config value %s was malformed"), name); + return -1; + } + return 0; +} + + +/* Convenience method to grab a string from the config file object */ +static int xenXMConfigGetString(virConnectPtr conn, + virConfPtr conf, + const char *name, + const char **value, + const char *def) { + virConfValuePtr val; + + *value = NULL; + if (!(val = virConfGetValue(conf, name))) { + *value = def; + return 0; + } + + if (val->type != VIR_CONF_STRING) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("config value %s was malformed"), name); + return -1; + } + if (!val->str) + *value = def; + else + *value = val->str; + return 0; +} + +static int xenXMConfigCopyStringInternal(virConnectPtr conn, + virConfPtr conf, + const char *name, + char **value, + int allowMissing) { + virConfValuePtr val; + + *value = NULL; + if (!(val = virConfGetValue(conf, name))) { + if (allowMissing) + return 0; + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("config value %s was missing"), name); + return -1; + } + + if (val->type != VIR_CONF_STRING) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("config value %s was not a string"), name); + return -1; + } + if (!val->str) { + if (allowMissing) + return 0; + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("config value %s was missing"), name); + return -1; + } + + if (!(*value = strdup(val->str))) { + virReportOOMError(conn); + return -1; + } + + return 0; +} + + +static int xenXMConfigCopyString(virConnectPtr conn, + virConfPtr conf, + const char *name, + char **value) { + return xenXMConfigCopyStringInternal(conn, conf, name, value, 0); +} + +static int xenXMConfigCopyStringOpt(virConnectPtr conn, + virConfPtr conf, + const char *name, + char **value) { + return xenXMConfigCopyStringInternal(conn, conf, name, value, 1); +} + + +/* Convenience method to grab a string UUID from the config file object */ +static int xenXMConfigGetUUID(virConfPtr conf, const char *name, unsigned char *uuid) { + virConfValuePtr val; + if (!uuid || !name || !conf) + return (-1); + if (!(val = virConfGetValue(conf, name))) { + return (-1); + } + + if (val->type != VIR_CONF_STRING) + return (-1); + if (!val->str) + return (-1); + + if (virUUIDParse(val->str, uuid) < 0) + return (-1); + + return (0); +} + + +/* Release memory associated with a cached config object */ +static void xenXMConfigFree(void *payload, const char *key ATTRIBUTE_UNUSED) { + xenXMConfCachePtr entry = (xenXMConfCachePtr)payload; + virDomainDefFree(entry->def); + VIR_FREE(entry); +} + +struct xenXMConfigReaperData { + xenUnifiedPrivatePtr priv; + time_t now; +}; + +/* Remove any configs which were not refreshed recently */ +static int xenXMConfigReaper(const void *payload, const char *key ATTRIBUTE_UNUSED, const void *data) { + const struct xenXMConfigReaperData *args = data; + xenXMConfCachePtr entry = (xenXMConfCachePtr)payload; + + /* We're going to purge this config file, so check if it + is currently mapped as owner of a named domain. */ + if (entry->refreshedAt != args->now) { + const char *olddomname = entry->def->name; + char *nameowner = (char *)virHashLookup(args->priv->nameConfigMap, olddomname); + if (nameowner && STREQ(nameowner, key)) { + virHashRemoveEntry(args->priv->nameConfigMap, olddomname, NULL); + } + return (1); + } + return (0); +} + + +static virDomainDefPtr +xenXMConfigReadFile(virConnectPtr conn, const char *filename) { + virConfPtr conf; + virDomainDefPtr def; + + if (!(conf = virConfReadFile(filename, 0))) + return NULL; + + def = xenXMDomainConfigParse(conn, conf); + virConfFree(conf); + + return def; +} + +static int +xenXMConfigSaveFile(virConnectPtr conn, const char *filename, virDomainDefPtr def) { + virConfPtr conf; + int ret; + + if (!(conf = xenXMDomainConfigFormat(conn, def))) + return -1; + + ret = virConfWriteFile(filename, conf); + virConfFree(conf); + return ret; +} + + +/* + * Caller must hold the lock on 'conn->privateData' before + * calling this funtion + */ +int +xenXMConfigCacheRemoveFile(virConnectPtr conn, + const char *filename) +{ + xenUnifiedPrivatePtr priv = conn->privateData; + xenXMConfCachePtr entry; + + entry = virHashLookup(priv->configCache, filename); + if (!entry) { + DEBUG("No config entry for %s", filename); + return 0; + } + + virHashRemoveEntry(priv->nameConfigMap, entry->def->name, NULL); + virHashRemoveEntry(priv->configCache, filename, xenXMConfigFree); + DEBUG("Removed %s %s", entry->def->name, filename); + return 0; +} + + +/* + * Caller must hold the lock on 'conn->privateData' before + * calling this funtion + */ +int +xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename) +{ + xenUnifiedPrivatePtr priv = conn->privateData; + xenXMConfCachePtr entry; + struct stat st; + int newborn = 0; + time_t now = time(NULL); + + DEBUG("Adding file %s", filename); + + /* Get modified time */ + if ((stat(filename, &st) < 0)) { + virReportSystemError(conn, errno, + _("cannot stat: %s"), + filename); + return -1; + } + + /* Ignore zero length files, because inotify fires before + any content has actually been created */ + if (st.st_size == 0) { + DEBUG("Ignoring zero length file %s", filename); + return -1; + } + + /* If we already have a matching entry and it is not + modified, then carry on to next one*/ + if ((entry = virHashLookup(priv->configCache, filename))) { + char *nameowner; + + if (entry->refreshedAt >= st.st_mtime) { + entry->refreshedAt = now; + /* return success if up-to-date */ + return 0; + } + + /* If we currently own the name, then release it and + re-acquire it later - just in case it was renamed */ + nameowner = (char *)virHashLookup(priv->nameConfigMap, entry->def->name); + if (nameowner && STREQ(nameowner, filename)) { + virHashRemoveEntry(priv->nameConfigMap, entry->def->name, NULL); + } + + /* Clear existing config entry which needs refresh */ + virDomainDefFree(entry->def); + entry->def = NULL; + } else { /* Completely new entry */ + newborn = 1; + if (VIR_ALLOC(entry) < 0) { + virReportOOMError(conn); + return -1; + } + memcpy(entry->filename, filename, PATH_MAX); + } + entry->refreshedAt = now; + + if (!(entry->def = xenXMConfigReadFile(conn, entry->filename))) { + DEBUG("Failed to read %s", entry->filename); + if (!newborn) + virHashRemoveEntry(priv->configCache, filename, NULL); + VIR_FREE(entry); + return -1; + } + + /* If its a completely new entry, it must be stuck into + the cache (refresh'd entries are already registered) */ + if (newborn) { + if (virHashAddEntry(priv->configCache, entry->filename, entry) < 0) { + virDomainDefFree(entry->def); + VIR_FREE(entry); + xenXMError (conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("xenXMConfigCacheRefresh: virHashAddEntry")); + return -1; + } + } + + /* See if we need to map this config file in as the primary owner + * of the domain in question + */ + if (!virHashLookup(priv->nameConfigMap, entry->def->name)) { + if (virHashAddEntry(priv->nameConfigMap, entry->def->name, entry->filename) < 0) { + virHashRemoveEntry(priv->configCache, filename, NULL); + virDomainDefFree(entry->def); + VIR_FREE(entry); + } + } + DEBUG("Added config %s %s", entry->def->name, filename); + + return 0; +} + +/* This method is called by various methods to scan /etc/xen + * (or whatever directory was set by LIBVIRT_XM_CONFIG_DIR + * environment variable) and process any domain configs. It + * has rate-limited so never rescans more frequently than + * once every X seconds + * + * Caller must hold the lock on 'conn->privateData' before + * calling this funtion + */ +int xenXMConfigCacheRefresh (virConnectPtr conn) { + xenUnifiedPrivatePtr priv = conn->privateData; + DIR *dh; + struct dirent *ent; + time_t now = time(NULL); + int ret = -1; + struct xenXMConfigReaperData args; + + if (now == ((time_t)-1)) { + virReportSystemError(conn, errno, + "%s", _("cannot get time of day")); + return (-1); + } + + /* Rate limit re-scans */ + if ((now - priv->lastRefresh) < XM_REFRESH_INTERVAL) + return (0); + + priv->lastRefresh = now; + + /* Process the files in the config dir */ + if (!(dh = opendir(priv->configDir))) { + virReportSystemError(conn, errno, + _("cannot read directory %s"), + priv->configDir); + return (-1); + } + + while ((ent = readdir(dh))) { + struct stat st; + char path[PATH_MAX]; + + /* + * Skip a bunch of crufty files that clearly aren't config files + */ + + /* Like 'dot' files... */ + if (STRPREFIX(ent->d_name, ".")) + continue; + /* ...and the XenD server config file */ + if (STRPREFIX(ent->d_name, XEND_CONFIG_FILE)) + continue; + /* ...and random PCI config cruft */ + if (STRPREFIX(ent->d_name, XEND_PCI_CONFIG_PREFIX)) + continue; + /* ...and the example domain configs */ + if (STRPREFIX(ent->d_name, XM_EXAMPLE_PREFIX)) + continue; + /* ...and the QEMU networking script */ + if (STRPREFIX(ent->d_name, QEMU_IF_SCRIPT)) + continue; + + /* ...and editor backups */ + if (ent->d_name[0] == '#') + continue; + if (ent->d_name[strlen(ent->d_name)-1] == '~') + continue; + + /* Build the full file path */ + if ((strlen(priv->configDir) + 1 + strlen(ent->d_name) + 1) > PATH_MAX) + continue; + strcpy(path, priv->configDir); + strcat(path, "/"); + strcat(path, ent->d_name); + + /* Skip anything which isn't a file (takes care of scripts/ subdir */ + if ((stat(path, &st) < 0) || + (!S_ISREG(st.st_mode))) { + continue; + } + + /* If we already have a matching entry and it is not + modified, then carry on to next one*/ + if (xenXMConfigCacheAddFile(conn, path) < 0) { + /* Ignoring errors, since alot of stuff goes wrong in /etc/xen */ + } + } + + /* Reap all entries which were not changed, by comparing + their refresh timestamp - the timestamp should match + 'now' if they were refreshed. If timestamp doesn't match + then the config is no longer on disk */ + args.now = now; + args.priv = priv; + virHashRemoveSet(priv->configCache, xenXMConfigReaper, xenXMConfigFree, &args); + ret = 0; + + if (dh) + closedir(dh); + + return (ret); +} + + +/* + * The XM driver keeps a cache of config files as virDomainDefPtr + * objects in the xenUnifiedPrivatePtr. Optionally inotify lets + * us watch for changes (see separate driver), otherwise we poll + * every few seconds + */ +virDrvOpenStatus +xenXMOpen (virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + xenUnifiedPrivatePtr priv = conn->privateData; + + priv->configDir = XM_CONFIG_DIR; + + priv->configCache = virHashCreate(50); + if (!priv->configCache) + return (-1); + priv->nameConfigMap = virHashCreate(50); + if (!priv->nameConfigMap) { + virHashFree(priv->configCache, NULL); + priv->configCache = NULL; + return (-1); + } + /* Force the cache to be reloaded next time that + * xenXMConfigCacheRefresh is called. + */ + priv->lastRefresh = 0; + + return (0); +} + +/* + * Free the cached config files associated with this + * connection + */ +int xenXMClose(virConnectPtr conn) { + xenUnifiedPrivatePtr priv = conn->privateData; + + virHashFree(priv->nameConfigMap, NULL); + virHashFree(priv->configCache, xenXMConfigFree); + + return (0); +} + +/* + * Since these are all offline domains, we only return info about + * VCPUs and memory. + */ +int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) { + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + if (domain->id != -1) + return (-1); + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto error; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto error; + + memset(info, 0, sizeof(virDomainInfo)); + info->maxMem = entry->def->maxmem; + info->memory = entry->def->memory; + info->nrVirtCpu = entry->def->vcpus; + info->state = VIR_DOMAIN_SHUTOFF; + info->cpuTime = 0; + + xenUnifiedUnlock(priv); + return (0); + +error: + xenUnifiedUnlock(priv); + return -1; +} + +#define MAX_VFB 1024 +/* + * Turn a config record into a lump of XML describing the + * domain, suitable for later feeding for virDomainCreateXML + */ +virDomainDefPtr +xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { + const char *str; + int hvm = 0; + int val; + virConfValuePtr list; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + virDomainDefPtr def = NULL; + virDomainDiskDefPtr disk = NULL; + virDomainNetDefPtr net = NULL; + virDomainGraphicsDefPtr graphics = NULL; + virDomainHostdevDefPtr hostdev = NULL; + int i; + const char *defaultArch, *defaultMachine; + + if (VIR_ALLOC(def) < 0) + return NULL; + + def->virtType = VIR_DOMAIN_VIRT_XEN; + def->id = -1; + + if (xenXMConfigCopyString(conn, conf, "name", &def->name) < 0) + goto cleanup; + if (xenXMConfigGetUUID(conf, "uuid", def->uuid) < 0) + goto cleanup; + + + if ((xenXMConfigGetString(conn, conf, "builder", &str, "linux") == 0) && + STREQ(str, "hvm")) + hvm = 1; + + if (!(def->os.type = strdup(hvm ? "hvm" : "xen"))) + goto no_memory; + + defaultArch = virCapabilitiesDefaultGuestArch(priv->caps, def->os.type, virDomainVirtTypeToString(def->virtType)); + if (defaultArch == NULL) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("no supported architecture for os type '%s'"), + def->os.type); + goto cleanup; + } + if (!(def->os.arch = strdup(defaultArch))) + goto no_memory; + + defaultMachine = virCapabilitiesDefaultGuestMachine(priv->caps, + def->os.type, + def->os.arch); + if (defaultMachine != NULL) { + if (!(def->os.machine = strdup(defaultMachine))) + goto no_memory; + } + + if (hvm) { + const char *boot; + if (xenXMConfigCopyString(conn, conf, "kernel", &def->os.loader) < 0) + goto cleanup; + + if (xenXMConfigGetString(conn, conf, "boot", &boot, "c") < 0) + goto cleanup; + + for (i = 0 ; i < VIR_DOMAIN_BOOT_LAST && boot[i] ; i++) { + switch (*boot) { + case 'a': + def->os.bootDevs[i] = VIR_DOMAIN_BOOT_FLOPPY; + break; + case 'd': + def->os.bootDevs[i] = VIR_DOMAIN_BOOT_CDROM; + break; + case 'n': + def->os.bootDevs[i] = VIR_DOMAIN_BOOT_NET; + break; + case 'c': + default: + def->os.bootDevs[i] = VIR_DOMAIN_BOOT_DISK; + break; + } + def->os.nBootDevs++; + } + } else { + if (xenXMConfigCopyStringOpt(conn, conf, "bootloader", &def->os.bootloader) < 0) + goto cleanup; + if (xenXMConfigCopyStringOpt(conn, conf, "bootargs", &def->os.bootloaderArgs) < 0) + goto cleanup; + + if (xenXMConfigCopyStringOpt(conn, conf, "kernel", &def->os.kernel) < 0) + goto cleanup; + if (xenXMConfigCopyStringOpt(conn, conf, "ramdisk", &def->os.initrd) < 0) + goto cleanup; + if (xenXMConfigCopyStringOpt(conn, conf, "extra", &def->os.cmdline) < 0) + goto cleanup; + } + + if (xenXMConfigGetULong(conn, conf, "memory", &def->memory, MIN_XEN_GUEST_SIZE * 2) < 0) + goto cleanup; + + if (xenXMConfigGetULong(conn, conf, "maxmem", &def->maxmem, def->memory) < 0) + goto cleanup; + + def->memory *= 1024; + def->maxmem *= 1024; + + + if (xenXMConfigGetULong(conn, conf, "vcpus", &def->vcpus, 1) < 0) + goto cleanup; + + if (xenXMConfigGetString(conn, conf, "cpus", &str, NULL) < 0) + goto cleanup; + if (str) { + def->cpumasklen = 4096; + if (VIR_ALLOC_N(def->cpumask, def->cpumasklen) < 0) + goto no_memory; + + if (virDomainCpuSetParse(conn, &str, 0, + def->cpumask, def->cpumasklen) < 0) + goto cleanup; + } + + + if (xenXMConfigGetString(conn, conf, "on_poweroff", &str, "destroy") < 0) + goto cleanup; + if ((def->onPoweroff = virDomainLifecycleTypeFromString(str)) < 0) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected value %s for on_poweroff"), str); + goto cleanup; + } + + if (xenXMConfigGetString(conn, conf, "on_reboot", &str, "restart") < 0) + goto cleanup; + if ((def->onReboot = virDomainLifecycleTypeFromString(str)) < 0) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected value %s for on_reboot"), str); + goto cleanup; + } + + if (xenXMConfigGetString(conn, conf, "on_crash", &str, "restart") < 0) + goto cleanup; + if ((def->onCrash = virDomainLifecycleTypeFromString(str)) < 0) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected value %s for on_crash"), str); + goto cleanup; + } + + + + if (hvm) { + if (xenXMConfigGetBool(conn, conf, "pae", &val, 0) < 0) + goto cleanup; + else if (val) + def->features |= (1 << VIR_DOMAIN_FEATURE_PAE); + if (xenXMConfigGetBool(conn, conf, "acpi", &val, 0) < 0) + goto cleanup; + else if (val) + def->features |= (1 << VIR_DOMAIN_FEATURE_ACPI); + if (xenXMConfigGetBool(conn, conf, "apic", &val, 0) < 0) + goto cleanup; + else if (val) + def->features |= (1 << VIR_DOMAIN_FEATURE_APIC); + } + if (xenXMConfigGetBool(conn, conf, "localtime", &def->localtime, 0) < 0) + goto cleanup; + + if (xenXMConfigCopyStringOpt(conn, conf, "device_model", &def->emulator) < 0) + goto cleanup; + + list = virConfGetValue(conf, "disk"); + if (list && list->type == VIR_CONF_LIST) { + list = list->list; + while (list) { + char *head; + char *offset; + char *tmp; + + if ((list->type != VIR_CONF_STRING) || (list->str == NULL)) + goto skipdisk; + head = list->str; + + if (VIR_ALLOC(disk) < 0) + goto no_memory; + + /* + * Disks have 3 components, SOURCE,DEST-DEVICE,MODE + * eg, phy:/dev/HostVG/XenGuest1,xvda,w + * The SOURCE is usually prefixed with a driver type, + * and optionally driver sub-type + * The DEST-DEVICE is optionally post-fixed with disk type + */ + + /* Extract the source file path*/ + if (!(offset = strchr(head, ','))) + goto skipdisk; + if ((offset - head) >= (PATH_MAX-1)) + goto skipdisk; + + if (offset == head) { + disk->src = NULL; /* No source file given, eg CDROM with no media */ + } else { + if (VIR_ALLOC_N(disk->src, (offset - head) + 1) < 0) + goto no_memory; + strncpy(disk->src, head, (offset - head)); + disk->src[(offset-head)] = '\0'; + } + head = offset + 1; + + /* Remove legacy ioemu: junk */ + if (STRPREFIX(head, "ioemu:")) + head = head + 6; + + /* Extract the dest device name */ + if (!(offset = strchr(head, ','))) + goto skipdisk; + if (VIR_ALLOC_N(disk->dst, (offset - head) + 1) < 0) + goto no_memory; + strncpy(disk->dst, head, (offset - head)); + disk->dst[(offset-head)] = '\0'; + head = offset + 1; + + + /* Extract source driver type */ + if (disk->src) { + /* The main type phy:, file:, tap: ... */ + if ((tmp = strchr(disk->src, ':')) != NULL) { + if (VIR_ALLOC_N(disk->driverName, (tmp - disk->src) + 1) < 0) + goto no_memory; + strncpy(disk->driverName, disk->src, (tmp - disk->src)); + disk->driverName[tmp - disk->src] = '\0'; + + /* Strip the prefix we found off the source file name */ + memmove(disk->src, disk->src+(tmp-disk->src)+1, + strlen(disk->src)-(tmp-disk->src)); + } + + /* And the sub-type for tap:XXX: type */ + if (disk->driverName && + STREQ(disk->driverName, "tap")) { + if (!(tmp = strchr(disk->src, ':'))) + goto skipdisk; + if (VIR_ALLOC_N(disk->driverType, (tmp - disk->src) + 1) < 0) + goto no_memory; + strncpy(disk->driverType, disk->src, (tmp - disk->src)); + disk->driverType[tmp - disk->src] = '\0'; + + /* Strip the prefix we found off the source file name */ + memmove(disk->src, disk->src+(tmp-disk->src)+1, + strlen(disk->src)-(tmp-disk->src)); + } + } + + /* No source, or driver name, so fix to phy: */ + if (!disk->driverName && + !(disk->driverName = strdup("phy"))) + goto no_memory; + + + /* phy: type indicates a block device */ + disk->type = STREQ(disk->driverName, "phy") ? + VIR_DOMAIN_DISK_TYPE_BLOCK : VIR_DOMAIN_DISK_TYPE_FILE; + + /* Check for a :cdrom/:disk postfix */ + disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; + if ((tmp = strchr(disk->dst, ':')) != NULL) { + if (STREQ(tmp, ":cdrom")) + disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + tmp[0] = '\0'; + } + + if (STRPREFIX(disk->dst, "xvd") || !hvm) { + disk->bus = VIR_DOMAIN_DISK_BUS_XEN; + } else if (STRPREFIX(disk->dst, "sd")) { + disk->bus = VIR_DOMAIN_DISK_BUS_SCSI; + } else { + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + } + + if (STREQ(head, "r") || + STREQ(head, "ro")) + disk->readonly = 1; + else if ((STREQ(head, "w!")) || + (STREQ(head, "!"))) + disk->shared = 1; + + /* Maintain list in sorted order according to target device name */ + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) + goto no_memory; + def->disks[def->ndisks++] = disk; + disk = NULL; + + skipdisk: + list = list->next; + virDomainDiskDefFree(disk); + } + } + + if (hvm && priv->xendConfigVersion == 1) { + if (xenXMConfigGetString(conn, conf, "cdrom", &str, NULL) < 0) + goto cleanup; + if (str) { + if (VIR_ALLOC(disk) < 0) + goto no_memory; + + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + if (!(disk->driverName = strdup("file"))) + goto no_memory; + if (!(disk->src = strdup(str))) + goto no_memory; + if (!(disk->dst = strdup("hdc"))) + goto no_memory; + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + disk->readonly = 1; + + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) + goto no_memory; + def->disks[def->ndisks++] = disk; + disk = NULL; + } + } + + list = virConfGetValue(conf, "vif"); + if (list && list->type == VIR_CONF_LIST) { + list = list->list; + while (list) { + char script[PATH_MAX]; + char model[10]; + char ip[16]; + char mac[18]; + char bridge[50]; + char vifname[50]; + char *key; + + bridge[0] = '\0'; + mac[0] = '\0'; + script[0] = '\0'; + ip[0] = '\0'; + model[0] = '\0'; + vifname[0] = '\0'; + + if ((list->type != VIR_CONF_STRING) || (list->str == NULL)) + goto skipnic; + + key = list->str; + while (key) { + char *data; + char *nextkey = strchr(key, ','); + + if (!(data = strchr(key, '='))) + goto skipnic; + data++; + + if (STRPREFIX(key, "mac=")) { + int len = nextkey ? (nextkey - data) : 17; + if (len > 17) + len = 17; + strncpy(mac, data, len); + mac[len] = '\0'; + } else if (STRPREFIX(key, "bridge=")) { + int len = nextkey ? (nextkey - data) : sizeof(bridge)-1; + if (len > (sizeof(bridge)-1)) + len = sizeof(bridge)-1; + strncpy(bridge, data, len); + bridge[len] = '\0'; + } else if (STRPREFIX(key, "script=")) { + int len = nextkey ? (nextkey - data) : PATH_MAX-1; + if (len > (PATH_MAX-1)) + len = PATH_MAX-1; + strncpy(script, data, len); + script[len] = '\0'; + } else if (STRPREFIX(key, "model=")) { + int len = nextkey ? (nextkey - data) : sizeof(model)-1; + if (len > (sizeof(model)-1)) + len = sizeof(model)-1; + strncpy(model, data, len); + model[len] = '\0'; + } else if (STRPREFIX(key, "vifname=")) { + int len = nextkey ? (nextkey - data) : sizeof(vifname)-1; + if (len > (sizeof(vifname)-1)) + len = sizeof(vifname)-1; + strncpy(vifname, data, len); + vifname[len] = '\0'; + } else if (STRPREFIX(key, "ip=")) { + int len = nextkey ? (nextkey - data) : 15; + if (len > 15) + len = 15; + strncpy(ip, data, len); + ip[len] = '\0'; + } + + while (nextkey && (nextkey[0] == ',' || + nextkey[0] == ' ' || + nextkey[0] == '\t')) + nextkey++; + key = nextkey; + } + + if (VIR_ALLOC(net) < 0) + goto cleanup; + + if (mac[0]) { + unsigned int rawmac[6]; + sscanf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", + (unsigned int*)&rawmac[0], + (unsigned int*)&rawmac[1], + (unsigned int*)&rawmac[2], + (unsigned int*)&rawmac[3], + (unsigned int*)&rawmac[4], + (unsigned int*)&rawmac[5]); + net->mac[0] = rawmac[0]; + net->mac[1] = rawmac[1]; + net->mac[2] = rawmac[2]; + net->mac[3] = rawmac[3]; + net->mac[4] = rawmac[4]; + net->mac[5] = rawmac[5]; + } + + if (bridge[0] || STREQ(script, "vif-bridge") || + STREQ(script, "vif-vnic")) { + net->type = VIR_DOMAIN_NET_TYPE_BRIDGE; + } else { + net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + } + + if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { + if (bridge[0] && + !(net->data.bridge.brname = strdup(bridge))) + goto no_memory; + if (script[0] && + !(net->data.bridge.script = strdup(script))) + goto no_memory; + if (ip[0] && + !(net->data.bridge.ipaddr = strdup(ip))) + goto no_memory; + } else { + if (script[0] && + !(net->data.ethernet.script = strdup(script))) + goto no_memory; + if (ip[0] && + !(net->data.ethernet.ipaddr = strdup(ip))) + goto no_memory; + } + if (model[0] && + !(net->model = strdup(model))) + goto no_memory; + + if (vifname[0] && + !(net->ifname = strdup(vifname))) + goto no_memory; + + if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) + goto no_memory; + def->nets[def->nnets++] = net; + net = NULL; + + skipnic: + list = list->next; + virDomainNetDefFree(net); + } + } + + list = virConfGetValue(conf, "pci"); + if (list && list->type == VIR_CONF_LIST) { + list = list->list; + while (list) { + char domain[5]; + char bus[3]; + char slot[3]; + char func[2]; + char *key, *nextkey; + int domainID; + int busID; + int slotID; + int funcID; + + domain[0] = bus[0] = slot[0] = func[0] = '\0'; + + if ((list->type != VIR_CONF_STRING) || (list->str == NULL)) + goto skippci; + + /* pci=['0000:00:1b.0','0000:00:13.0'] */ + if (!(key = list->str)) + goto skippci; + if (!(nextkey = strchr(key, ':'))) + goto skippci; + + if ((nextkey - key) > (sizeof(domain)-1)) + goto skippci; + + strncpy(domain, key, sizeof(domain)); + domain[sizeof(domain)-1] = '\0'; + + key = nextkey + 1; + if (!(nextkey = strchr(key, ':'))) + goto skippci; + + strncpy(bus, key, sizeof(bus)); + bus[sizeof(bus)-1] = '\0'; + + key = nextkey + 1; + if (!(nextkey = strchr(key, '.'))) + goto skippci; + + strncpy(slot, key, sizeof(slot)); + slot[sizeof(slot)-1] = '\0'; + + key = nextkey + 1; + if (strlen(key) != 1) + goto skippci; + + strncpy(func, key, sizeof(func)); + func[sizeof(func)-1] = '\0'; + + if (virStrToLong_i(domain, NULL, 16, &domainID) < 0) + goto skippci; + if (virStrToLong_i(bus, NULL, 16, &busID) < 0) + goto skippci; + if (virStrToLong_i(slot, NULL, 16, &slotID) < 0) + goto skippci; + if (virStrToLong_i(func, NULL, 16, &funcID) < 0) + goto skippci; + + if (VIR_ALLOC(hostdev) < 0) + goto cleanup; + + hostdev->managed = 0; + hostdev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + hostdev->source.subsys.u.pci.domain = domainID; + hostdev->source.subsys.u.pci.bus = busID; + hostdev->source.subsys.u.pci.slot = slotID; + hostdev->source.subsys.u.pci.function = funcID; + + if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) + goto no_memory; + def->hostdevs[def->nhostdevs++] = hostdev; + hostdev = NULL; + + skippci: + list = list->next; + } + } + + if (hvm) { + if (xenXMConfigGetString(conn, conf, "usbdevice", &str, NULL) < 0) + goto cleanup; + if (str && + (STREQ(str, "tablet") || + STREQ(str, "mouse"))) { + virDomainInputDefPtr input; + if (VIR_ALLOC(input) < 0) + goto no_memory; + input->bus = VIR_DOMAIN_INPUT_BUS_USB; + input->type = STREQ(str, "tablet") ? + VIR_DOMAIN_INPUT_TYPE_TABLET : + VIR_DOMAIN_INPUT_TYPE_MOUSE; + if (VIR_ALLOC_N(def->inputs, 1) < 0) { + virDomainInputDefFree(input); + goto no_memory; + } + def->inputs[0] = input; + def->ninputs = 1; + } + } + + /* HVM guests, or old PV guests use this config format */ + if (hvm || priv->xendConfigVersion < 3) { + if (xenXMConfigGetBool(conn, conf, "vnc", &val, 0) < 0) + goto cleanup; + + if (val) { + if (VIR_ALLOC(graphics) < 0) + goto no_memory; + graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; + if (xenXMConfigGetBool(conn, conf, "vncunused", &val, 1) < 0) + goto cleanup; + graphics->data.vnc.autoport = val ? 1 : 0; + + if (!graphics->data.vnc.autoport) { + unsigned long vncdisplay; + if (xenXMConfigGetULong(conn, conf, "vncdisplay", &vncdisplay, 0) < 0) + goto cleanup; + graphics->data.vnc.port = (int)vncdisplay + 5900; + } + if (xenXMConfigCopyStringOpt(conn, conf, "vnclisten", &graphics->data.vnc.listenAddr) < 0) + goto cleanup; + if (xenXMConfigCopyStringOpt(conn, conf, "vncpasswd", &graphics->data.vnc.passwd) < 0) + goto cleanup; + if (xenXMConfigCopyStringOpt(conn, conf, "keymap", &graphics->data.vnc.keymap) < 0) + goto cleanup; + + if (VIR_ALLOC_N(def->graphics, 1) < 0) + goto no_memory; + def->graphics[0] = graphics; + def->ngraphics = 1; + graphics = NULL; + } else { + if (xenXMConfigGetBool(conn, conf, "sdl", &val, 0) < 0) + goto cleanup; + if (val) { + if (VIR_ALLOC(graphics) < 0) + goto no_memory; + graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; + if (xenXMConfigCopyStringOpt(conn, conf, "display", &graphics->data.sdl.display) < 0) + goto cleanup; + if (xenXMConfigCopyStringOpt(conn, conf, "xauthority", &graphics->data.sdl.xauth) < 0) + goto cleanup; + if (VIR_ALLOC_N(def->graphics, 1) < 0) + goto no_memory; + def->graphics[0] = graphics; + def->ngraphics = 1; + graphics = NULL; + } + } + } + + if (!hvm && def->graphics == NULL) { /* New PV guests use this format */ + list = virConfGetValue(conf, "vfb"); + if (list && list->type == VIR_CONF_LIST && + list->list && list->list->type == VIR_CONF_STRING && + list->list->str) { + char vfb[MAX_VFB]; + char *key = vfb; + strncpy(vfb, list->list->str, MAX_VFB-1); + vfb[MAX_VFB-1] = '\0'; + + if (VIR_ALLOC(graphics) < 0) + goto no_memory; + + if (strstr(key, "type=sdl")) + graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; + else + graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; + + while (key) { + char *data; + char *nextkey = strchr(key, ','); + char *end = nextkey; + if (nextkey) { + *end = '\0'; + nextkey++; + } + + if (!(data = strchr(key, '='))) + break; + + if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + if (STRPREFIX(key, "vncunused=")) { + if (STREQ(key + 10, "1")) + graphics->data.vnc.autoport = 1; + } else if (STRPREFIX(key, "vnclisten=")) { + if (!(graphics->data.vnc.listenAddr = strdup(key + 10))) + goto no_memory; + } else if (STRPREFIX(key, "vncpasswd=")) { + if (!(graphics->data.vnc.passwd = strdup(key + 10))) + goto no_memory; + } else if (STRPREFIX(key, "keymap=")) { + if (!(graphics->data.vnc.keymap = strdup(key + 7))) + goto no_memory; + } else if (STRPREFIX(key, "vncdisplay=")) { + graphics->data.vnc.port = strtol(key+11, NULL, 10) + 5900; + } + } else { + if (STRPREFIX(key, "display=")) { + if (!(graphics->data.sdl.display = strdup(key + 8))) + goto no_memory; + } else if (STRPREFIX(key, "xauthority=")) { + if (!(graphics->data.sdl.xauth = strdup(key + 11))) + goto no_memory; + } + } + + while (nextkey && (nextkey[0] == ',' || + nextkey[0] == ' ' || + nextkey[0] == '\t')) + nextkey++; + key = nextkey; + } + if (VIR_ALLOC_N(def->graphics, 1) < 0) + goto no_memory; + def->graphics[0] = graphics; + def->ngraphics = 1; + graphics = NULL; + } + } + + if (hvm) { + virDomainChrDefPtr chr = NULL; + + if (xenXMConfigGetString(conn, conf, "parallel", &str, NULL) < 0) + goto cleanup; + if (str && STRNEQ(str, "none") && + !(chr = xenDaemonParseSxprChar(conn, str, NULL))) + goto cleanup; + + if (chr) { + if (VIR_ALLOC_N(def->parallels, 1) < 0) { + virDomainChrDefFree(chr); + goto no_memory; + } + def->parallels[0] = chr; + def->nparallels++; + chr = NULL; + } + + if (xenXMConfigGetString(conn, conf, "serial", &str, NULL) < 0) + goto cleanup; + if (str && STRNEQ(str, "none") && + !(chr = xenDaemonParseSxprChar(conn, str, NULL))) + goto cleanup; + + if (chr) { + if (VIR_ALLOC_N(def->serials, 1) < 0) { + virDomainChrDefFree(chr); + goto no_memory; + } + def->serials[0] = chr; + def->nserials++; + } + } else { + if (!(def->console = xenDaemonParseSxprChar(conn, "pty", NULL))) + goto cleanup; + } + + if (hvm) { + if (xenXMConfigGetString(conn, conf, "soundhw", &str, NULL) < 0) + goto cleanup; + + if (str && + xenDaemonParseSxprSound(conn, def, str) < 0) + goto cleanup; + } + + return def; + +no_memory: + virReportOOMError(conn); + /* fallthrough */ +cleanup: + virDomainGraphicsDefFree(graphics); + virDomainNetDefFree(net); + virDomainDiskDefFree(disk); + virDomainDefFree(def); + return NULL; +} + + +/* + * Turn a config record into a lump of XML describing the + * domain, suitable for later feeding for virDomainCreateXML + */ +char *xenXMDomainDumpXML(virDomainPtr domain, int flags) { + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + char *ret = NULL; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(NULL); + } + if (domain->id != -1) + return (NULL); + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto cleanup; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + + ret = virDomainDefFormat(domain->conn, entry->def, flags); + +cleanup: + xenUnifiedUnlock(priv); + return ret; +} + + +/* + * Update amount of memory in the config file + */ +int xenXMDomainSetMemory(virDomainPtr domain, unsigned long memory) { + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + int ret = -1; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + if (domain->id != -1) + return (-1); + if (memory < 1024 * MIN_XEN_GUEST_SIZE) + return (-1); + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto cleanup; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + + entry->def->memory = memory; + if (entry->def->memory > entry->def->maxmem) + entry->def->memory = entry->def->maxmem; + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) + goto cleanup; + ret = 0; + +cleanup: + xenUnifiedUnlock(priv); + return ret; +} + +/* + * Update maximum memory limit in config + */ +int xenXMDomainSetMaxMemory(virDomainPtr domain, unsigned long memory) { + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + int ret = -1; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + if (domain->id != -1) + return (-1); + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto cleanup; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + + entry->def->maxmem = memory; + if (entry->def->memory > entry->def->maxmem) + entry->def->memory = entry->def->maxmem; + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) + goto cleanup; + ret = 0; + +cleanup: + xenUnifiedUnlock(priv); + return ret; +} + +/* + * Get max memory limit from config + */ +unsigned long xenXMDomainGetMaxMemory(virDomainPtr domain) { + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + unsigned long ret = 0; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (0); + } + if (domain->id != -1) + return (0); + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto cleanup; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + + ret = entry->def->maxmem; + +cleanup: + xenUnifiedUnlock(priv); + return ret; +} + +/* + * Set the VCPU count in config + */ +int xenXMDomainSetVcpus(virDomainPtr domain, unsigned int vcpus) { + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + int ret = -1; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + if (domain->id != -1) + return (-1); + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto cleanup; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + + entry->def->vcpus = vcpus; + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) + goto cleanup; + ret = 0; + +cleanup: + xenUnifiedUnlock(priv); + return ret; +} + +/** + * xenXMDomainPinVcpu: + * @domain: pointer to domain object + * @vcpu: virtual CPU number (reserved) + * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) + * @maplen: length of cpumap in bytes + * + * Set the vcpu affinity in config + * + * Returns 0 for success; -1 (with errno) on error + */ +int xenXMDomainPinVcpu(virDomainPtr domain, + unsigned int vcpu ATTRIBUTE_UNUSED, + unsigned char *cpumap, int maplen) +{ + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + virBuffer mapbuf = VIR_BUFFER_INITIALIZER; + char *mapstr = NULL, *mapsave = NULL; + int i, j, n, comma = 0; + int ret = -1; + char *cpuset = NULL; + int maxcpu = XEN_MAX_PHYSICAL_CPU; + + if (domain == NULL || domain->conn == NULL || domain->name == NULL + || cpumap == NULL || maplen < 1 || maplen > (int)sizeof(cpumap_t)) { + xenXMError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG, + __FUNCTION__); + return -1; + } + if (domain->conn->flags & VIR_CONNECT_RO) { + xenXMError (domain->conn, VIR_ERR_INVALID_ARG, + "%s", _("read only connection")); + return -1; + } + if (domain->id != -1) { + xenXMError (domain->conn, VIR_ERR_INVALID_ARG, + "%s", _("not inactive domain")); + return -1; + } + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) { + xenXMError (domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("virHashLookup")); + goto cleanup; + } + if (!(entry = virHashLookup(priv->configCache, filename))) { + xenXMError (domain->conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("can't retrieve config file for domain")); + goto cleanup; + } + + /* from bit map, build character string of mapped CPU numbers */ + for (i = 0; i < maplen; i++) + for (j = 0; j < 8; j++) + if ((cpumap[i] & (1 << j))) { + n = i*8 + j; + + if (comma) + virBufferAddLit (&mapbuf, ","); + comma = 1; + + virBufferVSprintf (&mapbuf, "%d", n); + } + + if (virBufferError(&mapbuf)) { + virReportOOMError(domain->conn); + goto cleanup; + } + + mapstr = virBufferContentAndReset(&mapbuf); + mapsave = mapstr; + + if (VIR_ALLOC_N(cpuset, maxcpu) < 0) { + virReportOOMError(domain->conn); + goto cleanup; + } + if (virDomainCpuSetParse(domain->conn, + (const char **)&mapstr, 0, + cpuset, maxcpu) < 0) + goto cleanup; + + VIR_FREE(entry->def->cpumask); + entry->def->cpumask = cpuset; + entry->def->cpumasklen = maxcpu; + cpuset = NULL; + + if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(mapsave); + VIR_FREE(cpuset); + xenUnifiedUnlock(priv); + return (ret); +} + +/* + * Find an inactive domain based on its name + */ +virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname) { + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + virDomainPtr ret = NULL; + + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (domname == NULL) { + xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + priv = conn->privateData; + xenUnifiedLock(priv); + + if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) + goto cleanup; + + if (!(filename = virHashLookup(priv->nameConfigMap, domname))) + goto cleanup; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + + if (!(ret = virGetDomain(conn, domname, entry->def->uuid))) + goto cleanup; + + /* Ensure its marked inactive, because may be cached + handle to a previously active domain */ + ret->id = -1; + +cleanup: + xenUnifiedUnlock(priv); + return (ret); +} + + +/* + * Hash table iterator to search for a domain based on UUID + */ +static int xenXMDomainSearchForUUID(const void *payload, const char *name ATTRIBUTE_UNUSED, const void *data) { + const unsigned char *wantuuid = (const unsigned char *)data; + const xenXMConfCachePtr entry = (const xenXMConfCachePtr)payload; + + if (!memcmp(entry->def->uuid, wantuuid, VIR_UUID_BUFLEN)) + return (1); + + return (0); +} + +/* + * Find an inactive domain based on its UUID + */ +virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) { + xenUnifiedPrivatePtr priv; + xenXMConfCachePtr entry; + virDomainPtr ret = NULL; + + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (uuid == NULL) { + xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + + priv = conn->privateData; + xenUnifiedLock(priv); + + if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) + goto cleanup; + + if (!(entry = virHashSearch(priv->configCache, xenXMDomainSearchForUUID, (const void *)uuid))) + goto cleanup; + + if (!(ret = virGetDomain(conn, entry->def->name, uuid))) + goto cleanup; + + /* Ensure its marked inactive, because may be cached + handle to a previously active domain */ + ret->id = -1; + +cleanup: + xenUnifiedUnlock(priv); + return (ret); +} + + +/* + * Start a domain from an existing defined config file + */ +int xenXMDomainCreate(virDomainPtr domain) { + char *sexpr; + int ret = -1; + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + + if (domain->id != -1) + return (-1); + + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto error; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto error; + + if (!(sexpr = xenDaemonFormatSxpr(domain->conn, entry->def, priv->xendConfigVersion))) + goto error; + + ret = xenDaemonDomainCreateXML(domain->conn, sexpr); + VIR_FREE(sexpr); + if (ret != 0) + goto error; + + if ((ret = xenDaemonDomainLookupByName_ids(domain->conn, domain->name, + entry->def->uuid)) < 0) + goto error; + domain->id = ret; + + if (xend_wait_for_devices(domain->conn, domain->name) < 0) + goto error; + + if (xenDaemonDomainResume(domain) < 0) + goto error; + + xenUnifiedUnlock(priv); + return (0); + + error: + if (domain->id != -1) { + xenDaemonDomainDestroy(domain); + domain->id = -1; + } + xenUnifiedUnlock(priv); + return (-1); +} + + +static +int xenXMConfigSetInt(virConfPtr conf, const char *setting, long l) { + virConfValuePtr value = NULL; + + if (VIR_ALLOC(value) < 0) + return -1; + + value->type = VIR_CONF_LONG; + value->next = NULL; + value->l = l; + + return virConfSetValue(conf, setting, value); +} + + +static +int xenXMConfigSetString(virConfPtr conf, const char *setting, const char *str) { + virConfValuePtr value = NULL; + + if (VIR_ALLOC(value) < 0) + return -1; + + value->type = VIR_CONF_STRING; + value->next = NULL; + if (!(value->str = strdup(str))) { + VIR_FREE(value); + return -1; + } + + return virConfSetValue(conf, setting, value); +} + + +static int xenXMDomainConfigFormatDisk(virConnectPtr conn, + virConfValuePtr list, + virDomainDiskDefPtr disk, + int hvm, + int xendConfigVersion) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virConfValuePtr val, tmp; + char *str; + + if(disk->src) { + if (disk->driverName) { + virBufferVSprintf(&buf, "%s:", disk->driverName); + if (STREQ(disk->driverName, "tap")) + virBufferVSprintf(&buf, "%s:", disk->driverType ? disk->driverType : "aio"); + } else { + virBufferVSprintf(&buf, "%s:", + disk->type == VIR_DOMAIN_DISK_TYPE_FILE ? + "file" : "phy"); + } + virBufferVSprintf(&buf, "%s", disk->src); + } + virBufferAddLit(&buf, ","); + if (hvm && xendConfigVersion == 1) + virBufferAddLit(&buf, "ioemu:"); + + virBufferVSprintf(&buf, "%s", disk->dst); + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) + virBufferAddLit(&buf, ":cdrom"); + + if (disk->readonly) + virBufferAddLit(&buf, ",r"); + else if (disk->shared) + virBufferAddLit(&buf, ",!"); + else + virBufferAddLit(&buf, ",w"); + + if (virBufferError(&buf)) { + virReportOOMError(conn); + return -1; + } + + if (VIR_ALLOC(val) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + val->type = VIR_CONF_STRING; + val->str = virBufferContentAndReset(&buf); + tmp = list->list; + while (tmp && tmp->next) + tmp = tmp->next; + if (tmp) + tmp->next = val; + else + list->list = val; + + return 0; + +cleanup: + str = virBufferContentAndReset(&buf); + VIR_FREE(str); + return -1; +} + +static int xenXMDomainConfigFormatNet(virConnectPtr conn, + virConfValuePtr list, + virDomainNetDefPtr net, + int hvm) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virConfValuePtr val, tmp; + char *str; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + virBufferVSprintf(&buf, "mac=%02x:%02x:%02x:%02x:%02x:%02x", + net->mac[0], net->mac[1], + net->mac[2], net->mac[3], + net->mac[4], net->mac[5]); + + switch (net->type) { + case VIR_DOMAIN_NET_TYPE_BRIDGE: + virBufferVSprintf(&buf, ",bridge=%s", net->data.bridge.brname); + if (net->data.bridge.ipaddr) + virBufferVSprintf(&buf, ",ip=%s", net->data.bridge.ipaddr); + virBufferVSprintf(&buf, ",script=%s", DEFAULT_VIF_SCRIPT); + break; + + case VIR_DOMAIN_NET_TYPE_ETHERNET: + if (net->data.ethernet.script) + virBufferVSprintf(&buf, ",script=%s", net->data.ethernet.script); + if (net->data.ethernet.ipaddr) + virBufferVSprintf(&buf, ",ip=%s", net->data.ethernet.ipaddr); + break; + + case VIR_DOMAIN_NET_TYPE_NETWORK: + { + virNetworkPtr network = virNetworkLookupByName(conn, net->data.network.name); + char *bridge; + if (!network) { + xenXMError(conn, VIR_ERR_NO_NETWORK, "%s", + net->data.network.name); + return -1; + } + bridge = virNetworkGetBridgeName(network); + virNetworkFree(network); + if (!bridge) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("network %s is not active"), + net->data.network.name); + return -1; + } + + virBufferVSprintf(&buf, ",bridge=%s", bridge); + virBufferVSprintf(&buf, ",script=%s", DEFAULT_VIF_SCRIPT); + } + break; + + default: + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported network type %d"), + net->type); + goto cleanup; + } + + if (hvm && priv->xendConfigVersion <= XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU) + virBufferAddLit(&buf, ",type=ioemu"); + + if (net->model) + virBufferVSprintf(&buf, ",model=%s", + net->model); + + if (net->ifname) + virBufferVSprintf(&buf, ",vifname=%s", + net->ifname); + + if (virBufferError(&buf)) + goto cleanup; + + if (VIR_ALLOC(val) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + val->type = VIR_CONF_STRING; + val->str = virBufferContentAndReset(&buf); + tmp = list->list; + while (tmp && tmp->next) + tmp = tmp->next; + if (tmp) + tmp->next = val; + else + list->list = val; + + return 0; + +cleanup: + str = virBufferContentAndReset(&buf); + VIR_FREE(str); + return -1; +} + + + +static int +xenXMDomainConfigFormatPCI(virConnectPtr conn, + virConfPtr conf, + virDomainDefPtr def) +{ + + virConfValuePtr pciVal = NULL; + int hasPCI = 0; + int i; + + for (i = 0 ; i < def->nhostdevs ; i++) + if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + hasPCI = 1; + + if (!hasPCI) + return 0; + + if (VIR_ALLOC(pciVal) < 0) + return -1; + + pciVal->type = VIR_CONF_LIST; + pciVal->list = NULL; + + for (i = 0 ; i < def->nhostdevs ; i++) { + if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + virConfValuePtr val, tmp; + char *buf; + + if (virAsprintf(&buf, "%04x:%02x:%02x.%x", + def->hostdevs[i]->source.subsys.u.pci.domain, + def->hostdevs[i]->source.subsys.u.pci.bus, + def->hostdevs[i]->source.subsys.u.pci.slot, + def->hostdevs[i]->source.subsys.u.pci.function) < 0) + goto error; + + if (VIR_ALLOC(val) < 0) { + VIR_FREE(buf); + virReportOOMError(conn); + goto error; + } + val->type = VIR_CONF_STRING; + val->str = buf; + tmp = pciVal->list; + while (tmp && tmp->next) + tmp = tmp->next; + if (tmp) + tmp->next = val; + else + pciVal->list = val; + } + } + + if (pciVal->list != NULL) { + int ret = virConfSetValue(conf, "pci", pciVal); + pciVal = NULL; + if (ret < 0) + return -1; + } + VIR_FREE(pciVal); + + return 0; + +error: + virConfFreeValue(pciVal); + return -1; +} + + +virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, + virDomainDefPtr def) { + virConfPtr conf = NULL; + int hvm = 0, i; + xenUnifiedPrivatePtr priv; + char *cpus = NULL; + const char *lifecycle; + char uuid[VIR_UUID_STRING_BUFLEN]; + virConfValuePtr diskVal = NULL; + virConfValuePtr netVal = NULL; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (!(conf = virConfNew())) + goto cleanup; + + + if (xenXMConfigSetString(conf, "name", def->name) < 0) + goto no_memory; + + virUUIDFormat(def->uuid, uuid); + if (xenXMConfigSetString(conf, "uuid", uuid) < 0) + goto no_memory; + + if (xenXMConfigSetInt(conf, "maxmem", def->maxmem / 1024) < 0) + goto no_memory; + + if (xenXMConfigSetInt(conf, "memory", def->memory / 1024) < 0) + goto no_memory; + + if (xenXMConfigSetInt(conf, "vcpus", def->vcpus) < 0) + goto no_memory; + + if (def->cpumask && + !(cpus = virDomainCpuSetFormat(conn, def->cpumask, def->cpumasklen)) < 0) + goto cleanup; + + if (cpus && + xenXMConfigSetString(conf, "cpus", cpus) < 0) + goto no_memory; + VIR_FREE(cpus); + + hvm = STREQ(def->os.type, "hvm") ? 1 : 0; + + if (hvm) { + char boot[VIR_DOMAIN_BOOT_LAST+1]; + if (xenXMConfigSetString(conf, "builder", "hvm") < 0) + goto no_memory; + + if (def->os.loader && + xenXMConfigSetString(conf, "kernel", def->os.loader) < 0) + goto no_memory; + + for (i = 0 ; i < def->os.nBootDevs ; i++) { + switch (def->os.bootDevs[i]) { + case VIR_DOMAIN_BOOT_FLOPPY: + boot[i] = 'a'; + break; + case VIR_DOMAIN_BOOT_CDROM: + boot[i] = 'd'; + break; + case VIR_DOMAIN_BOOT_NET: + boot[i] = 'n'; + break; + case VIR_DOMAIN_BOOT_DISK: + default: + boot[i] = 'c'; + break; + } + } + if (!def->os.nBootDevs) { + boot[0] = 'c'; + boot[1] = '\0'; + } else { + boot[def->os.nBootDevs] = '\0'; + } + + if (xenXMConfigSetString(conf, "boot", boot) < 0) + goto no_memory; + + if (xenXMConfigSetInt(conf, "pae", + (def->features & + (1 << VIR_DOMAIN_FEATURE_PAE)) ? 1 : 0) < 0) + goto no_memory; + + if (xenXMConfigSetInt(conf, "acpi", + (def->features & + (1 << VIR_DOMAIN_FEATURE_ACPI)) ? 1 : 0) < 0) + goto no_memory; + + if (xenXMConfigSetInt(conf, "apic", + (def->features & + (1 << VIR_DOMAIN_FEATURE_APIC)) ? 1 : 0) < 0) + goto no_memory; + + + if (xenXMConfigSetInt(conf, "localtime", def->localtime ? 1 : 0) < 0) + goto no_memory; + + if (priv->xendConfigVersion == 1) { + for (i = 0 ; i < def->ndisks ; i++) { + if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_CDROM && + def->disks[i]->dst && + STREQ(def->disks[i]->dst, "hdc") && + def->disks[i]->src) { + if (xenXMConfigSetString(conf, "cdrom", + def->disks[i]->src) < 0) + goto no_memory; + break; + } + } + } + + /* XXX floppy disks */ + } else { + if (def->os.bootloader && + xenXMConfigSetString(conf, "bootloader", def->os.bootloader) < 0) + goto no_memory; + if (def->os.bootloaderArgs && + xenXMConfigSetString(conf, "bootloader_args", def->os.bootloaderArgs) < 0) + goto no_memory; + if (def->os.kernel && + xenXMConfigSetString(conf, "kernel", def->os.kernel) < 0) + goto no_memory; + if (def->os.initrd && + xenXMConfigSetString(conf, "ramdisk", def->os.initrd) < 0) + goto no_memory; + if (def->os.cmdline && + xenXMConfigSetString(conf, "extra", def->os.cmdline) < 0) + goto no_memory; + + } + + if (!(lifecycle = virDomainLifecycleTypeToString(def->onPoweroff))) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected lifecycle action %d"), def->onPoweroff); + goto cleanup; + } + if (xenXMConfigSetString(conf, "on_poweroff", lifecycle) < 0) + goto no_memory; + + + if (!(lifecycle = virDomainLifecycleTypeToString(def->onReboot))) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected lifecycle action %d"), def->onReboot); + goto cleanup; + } + if (xenXMConfigSetString(conf, "on_reboot", lifecycle) < 0) + goto no_memory; + + + if (!(lifecycle = virDomainLifecycleTypeToString(def->onCrash))) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected lifecycle action %d"), def->onCrash); + goto cleanup; + } + if (xenXMConfigSetString(conf, "on_crash", lifecycle) < 0) + goto no_memory; + + + + if (hvm) { + if (def->emulator && + xenXMConfigSetString(conf, "device_model", def->emulator) < 0) + goto no_memory; + + for (i = 0 ; i < def->ninputs ; i++) { + if (def->inputs[i]->bus == VIR_DOMAIN_INPUT_BUS_USB) { + if (xenXMConfigSetInt(conf, "usb", 1) < 0) + goto no_memory; + if (xenXMConfigSetString(conf, "usbdevice", + def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? + "mouse" : "tablet") < 0) + goto no_memory; + break; + } + } + } + + if (def->ngraphics == 1) { + if (priv->xendConfigVersion < (hvm ? 4 : XEND_CONFIG_MIN_VERS_PVFB_NEWCONF)) { + if (def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { + if (xenXMConfigSetInt(conf, "sdl", 1) < 0) + goto no_memory; + if (xenXMConfigSetInt(conf, "vnc", 0) < 0) + goto no_memory; + if (def->graphics[0]->data.sdl.display && + xenXMConfigSetString(conf, "display", + def->graphics[0]->data.sdl.display) < 0) + goto no_memory; + if (def->graphics[0]->data.sdl.xauth && + xenXMConfigSetString(conf, "xauthority", + def->graphics[0]->data.sdl.xauth) < 0) + goto no_memory; + } else { + if (xenXMConfigSetInt(conf, "sdl", 0) < 0) + goto no_memory; + if (xenXMConfigSetInt(conf, "vnc", 1) < 0) + goto no_memory; + if (xenXMConfigSetInt(conf, "vncunused", + def->graphics[0]->data.vnc.autoport ? 1 : 0) < 0) + goto no_memory; + if (!def->graphics[0]->data.vnc.autoport && + xenXMConfigSetInt(conf, "vncdisplay", + def->graphics[0]->data.vnc.port - 5900) < 0) + goto no_memory; + if (def->graphics[0]->data.vnc.listenAddr && + xenXMConfigSetString(conf, "vnclisten", + def->graphics[0]->data.vnc.listenAddr) < 0) + goto no_memory; + if (def->graphics[0]->data.vnc.passwd && + xenXMConfigSetString(conf, "vncpasswd", + def->graphics[0]->data.vnc.passwd) < 0) + goto no_memory; + if (def->graphics[0]->data.vnc.keymap && + xenXMConfigSetString(conf, "keymap", + def->graphics[0]->data.vnc.keymap) < 0) + goto no_memory; + } + } else { + virConfValuePtr vfb, disp; + char *vfbstr = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + if (def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { + virBufferAddLit(&buf, "type=sdl"); + if (def->graphics[0]->data.sdl.display) + virBufferVSprintf(&buf, ",display=%s", + def->graphics[0]->data.sdl.display); + if (def->graphics[0]->data.sdl.xauth) + virBufferVSprintf(&buf, ",xauthority=%s", + def->graphics[0]->data.sdl.xauth); + } else { + virBufferAddLit(&buf, "type=vnc"); + virBufferVSprintf(&buf, ",vncunused=%d", + def->graphics[0]->data.vnc.autoport ? 1 : 0); + if (!def->graphics[0]->data.vnc.autoport) + virBufferVSprintf(&buf, ",vncdisplay=%d", + def->graphics[0]->data.vnc.port - 5900); + if (def->graphics[0]->data.vnc.listenAddr) + virBufferVSprintf(&buf, ",vnclisten=%s", + def->graphics[0]->data.vnc.listenAddr); + if (def->graphics[0]->data.vnc.passwd) + virBufferVSprintf(&buf, ",vncpasswd=%s", + def->graphics[0]->data.vnc.passwd); + if (def->graphics[0]->data.vnc.keymap) + virBufferVSprintf(&buf, ",keymap=%s", + def->graphics[0]->data.vnc.keymap); + } + if (virBufferError(&buf)) + goto no_memory; + + vfbstr = virBufferContentAndReset(&buf); + + if (VIR_ALLOC(vfb) < 0) { + VIR_FREE(vfbstr); + goto no_memory; + } + + if (VIR_ALLOC(disp) < 0) { + VIR_FREE(vfb); + VIR_FREE(vfbstr); + goto no_memory; + } + + vfb->type = VIR_CONF_LIST; + vfb->list = disp; + disp->type = VIR_CONF_STRING; + disp->str = vfbstr; + + if (virConfSetValue(conf, "vfb", vfb) < 0) + goto no_memory; + } + } + + /* analyze of the devices */ + if (VIR_ALLOC(diskVal) < 0) + goto no_memory; + diskVal->type = VIR_CONF_LIST; + diskVal->list = NULL; + + for (i = 0 ; i < def->ndisks ; i++) { + if (priv->xendConfigVersion == 1 && + def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_CDROM && + def->disks[i]->dst && + STREQ(def->disks[i]->dst, "hdc")) { + continue; + } + if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) + continue; + + if (xenXMDomainConfigFormatDisk(conn, diskVal, def->disks[i], + hvm, priv->xendConfigVersion) < 0) + goto cleanup; + } + if (diskVal->list != NULL) { + int ret = virConfSetValue(conf, "disk", diskVal); + diskVal = NULL; + if (ret < 0) + goto no_memory; + } + VIR_FREE(diskVal); + + if (VIR_ALLOC(netVal) < 0) + goto no_memory; + netVal->type = VIR_CONF_LIST; + netVal->list = NULL; + + for (i = 0 ; i < def->nnets ; i++) { + if (xenXMDomainConfigFormatNet(conn, netVal, + def->nets[i], + hvm) < 0) + goto cleanup; + } + if (netVal->list != NULL) { + int ret = virConfSetValue(conf, "vif", netVal); + netVal = NULL; + if (ret < 0) + goto no_memory; + } + VIR_FREE(netVal); + + if (xenXMDomainConfigFormatPCI(conn, conf, def) < 0) + goto cleanup; + + if (hvm) { + if (def->nparallels) { + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *str; + int ret; + + ret = xenDaemonFormatSxprChr(conn, def->parallels[0], &buf); + str = virBufferContentAndReset(&buf); + if (ret == 0) + ret = xenXMConfigSetString(conf, "parallel", str); + VIR_FREE(str); + if (ret < 0) + goto no_memory; + } else { + if (xenXMConfigSetString(conf, "parallel", "none") < 0) + goto no_memory; + } + + if (def->nserials) { + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *str; + int ret; + + ret = xenDaemonFormatSxprChr(conn, def->serials[0], &buf); + str = virBufferContentAndReset(&buf); + if (ret == 0) + ret = xenXMConfigSetString(conf, "serial", str); + VIR_FREE(str); + if (ret < 0) + goto no_memory; + } else { + if (xenXMConfigSetString(conf, "serial", "none") < 0) + goto no_memory; + } + + + if (def->sounds) { + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *str = NULL; + int ret = xenDaemonFormatSxprSound(conn, + def, + &buf); + str = virBufferContentAndReset(&buf); + if (ret == 0) + ret = xenXMConfigSetString(conf, "soundhw", str); + + VIR_FREE(str); + if (ret < 0) + goto no_memory; + } + } + + return conf; + +no_memory: + virReportOOMError(conn); + +cleanup: + virConfFreeValue(diskVal); + virConfFreeValue(netVal); + VIR_FREE(cpus); + if (conf) + virConfFree(conf); + return (NULL); +} + +/* + * Create a config file for a domain, based on an XML + * document describing its config + */ +virDomainPtr xenXMDomainDefineXML(virConnectPtr conn, const char *xml) { + virDomainPtr ret; + virDomainPtr olddomain; + char filename[PATH_MAX]; + const char * oldfilename; + virDomainDefPtr def = NULL; + xenXMConfCachePtr entry = NULL; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + if (xml == NULL) { + xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (NULL); + } + if (conn->flags & VIR_CONNECT_RO) + return (NULL); + + xenUnifiedLock(priv); + + if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) { + xenUnifiedUnlock(priv); + return (NULL); + } + + if (!(def = virDomainDefParseString(conn, priv->caps, xml, + VIR_DOMAIN_XML_INACTIVE))) { + xenUnifiedUnlock(priv); + return (NULL); + } + + if (virHashLookup(priv->nameConfigMap, def->name)) { + /* domain exists, we will overwrite it */ + + if (!(oldfilename = (char *)virHashLookup(priv->nameConfigMap, def->name))) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("can't retrieve config filename for domain to overwrite")); + goto error; + } + + if (!(entry = virHashLookup(priv->configCache, oldfilename))) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("can't retrieve config entry for domain to overwrite")); + goto error; + } + + /* XXX wtf.com is this line for - it appears to be amemory leak */ + if (!(olddomain = virGetDomain(conn, def->name, entry->def->uuid))) + goto error; + + /* Remove the name -> filename mapping */ + if (virHashRemoveEntry(priv->nameConfigMap, def->name, NULL) < 0) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to remove old domain from config map")); + goto error; + } + + /* Remove the config record itself */ + if (virHashRemoveEntry(priv->configCache, oldfilename, xenXMConfigFree) < 0) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to remove old domain from config map")); + goto error; + } + + entry = NULL; + } + + if ((strlen(priv->configDir) + 1 + strlen(def->name) + 1) > PATH_MAX) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("config file name is too long")); + goto error; + } + + strcpy(filename, priv->configDir); + strcat(filename, "/"); + strcat(filename, def->name); + + if (xenXMConfigSaveFile(conn, filename, def) < 0) + goto error; + + if (VIR_ALLOC(entry) < 0) { + virReportOOMError(conn); + goto error; + } + + if ((entry->refreshedAt = time(NULL)) == ((time_t)-1)) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to get current time")); + goto error; + } + + memmove(entry->filename, filename, PATH_MAX); + entry->def = def; + + if (virHashAddEntry(priv->configCache, filename, entry) < 0) { + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to store config file handle")); + goto error; + } + + if (virHashAddEntry(priv->nameConfigMap, def->name, entry->filename) < 0) { + virHashRemoveEntry(priv->configCache, filename, NULL); + xenXMError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to store config file handle")); + goto error; + } + + ret = virGetDomain(conn, def->name, def->uuid); + xenUnifiedUnlock(priv); + return (ret); + + error: + VIR_FREE(entry); + virDomainDefFree(def); + xenUnifiedUnlock(priv); + return (NULL); +} + +/* + * Delete a domain from disk + */ +int xenXMDomainUndefine(virDomainPtr domain) { + xenUnifiedPrivatePtr priv; + const char *filename; + xenXMConfCachePtr entry; + int ret = -1; + + if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return (-1); + } + + if (domain->id != -1) + return (-1); + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto cleanup; + + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + + if (unlink(entry->filename) < 0) + goto cleanup; + + /* Remove the name -> filename mapping */ + if (virHashRemoveEntry(priv->nameConfigMap, domain->name, NULL) < 0) + goto cleanup; + + /* Remove the config record itself */ + if (virHashRemoveEntry(priv->configCache, entry->filename, xenXMConfigFree) < 0) + goto cleanup; + + ret = 0; + +cleanup: + xenUnifiedUnlock(priv); + return ret; +} + +struct xenXMListIteratorContext { + virConnectPtr conn; + int max; + int count; + char ** names; +}; + +static void xenXMListIterator(const void *payload ATTRIBUTE_UNUSED, const char *name, const void *data) { + struct xenXMListIteratorContext *ctx = (struct xenXMListIteratorContext *)data; + virDomainPtr dom = NULL; + + if (ctx->count == ctx->max) + return; + + dom = xenDaemonLookupByName(ctx->conn, name); + if (!dom) { + ctx->names[ctx->count] = strdup(name); + ctx->count++; + } else { + virDomainFree(dom); + } +} + + +/* + * List all defined domains, filtered to remove any which + * are currently running + */ +int xenXMListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) { + xenUnifiedPrivatePtr priv; + struct xenXMListIteratorContext ctx; + int ret = -1; + + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + priv = conn->privateData; + xenUnifiedLock(priv); + + if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) + goto cleanup; + + if (maxnames > virHashSize(priv->configCache)) + maxnames = virHashSize(priv->configCache); + + ctx.conn = conn; + ctx.count = 0; + ctx.max = maxnames; + ctx.names = names; + + virHashForEach(priv->nameConfigMap, xenXMListIterator, &ctx); + ret = ctx.count; + +cleanup: + xenUnifiedUnlock(priv); + return ret; +} + +/* + * Return the maximum number of defined domains - not filtered + * based on number running + */ +int xenXMNumOfDefinedDomains(virConnectPtr conn) { + xenUnifiedPrivatePtr priv; + int ret = -1; + + if (!VIR_IS_CONNECT(conn)) { + xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + priv = conn->privateData; + xenUnifiedLock(priv); + + if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) + goto cleanup; + + ret = virHashSize(priv->nameConfigMap); + +cleanup: + xenUnifiedUnlock(priv); + return ret; +} + + +/** + * xenXMDomainAttachDevice: + * @domain: pointer to domain object + * @xml: pointer to XML description of device + * + * Create a virtual device attachment to backend. + * XML description is translated into config file. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xenXMDomainAttachDevice(virDomainPtr domain, const char *xml) { + const char *filename = NULL; + xenXMConfCachePtr entry = NULL; + int ret = -1; + virDomainDeviceDefPtr dev = NULL; + virDomainDefPtr def; + xenUnifiedPrivatePtr priv; + + if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return -1; + } + + if (domain->conn->flags & VIR_CONNECT_RO) + return -1; + if (domain->id != -1) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto cleanup; + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + def = entry->def; + + if (!(dev = virDomainDeviceDefParse(domain->conn, + priv->caps, + entry->def, + xml, VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + { + if (virDomainDiskInsert(def, dev->data.disk) < 0) { + virReportOOMError(domain->conn); + goto cleanup; + } + dev->data.disk = NULL; + } + break; + + case VIR_DOMAIN_DEVICE_NET: + { + if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) { + virReportOOMError(domain->conn); + goto cleanup; + } + def->nets[def->nnets++] = dev->data.net; + dev->data.net = NULL; + break; + } + + default: + xenXMError(domain->conn, VIR_ERR_XML_ERROR, + "%s", _("unknown device")); + goto cleanup; + } + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainDeviceDefFree(dev); + xenUnifiedUnlock(priv); + return ret; +} + + +/** + * xenXMDomainDetachDevice: + * @domain: pointer to domain object + * @xml: pointer to XML description of device + * + * Destroy a virtual device attachment to backend. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xenXMDomainDetachDevice(virDomainPtr domain, const char *xml) { + const char *filename = NULL; + xenXMConfCachePtr entry = NULL; + virDomainDeviceDefPtr dev = NULL; + virDomainDefPtr def; + int ret = -1; + int i; + xenUnifiedPrivatePtr priv; + + if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) { + xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return -1; + } + + + if (domain->conn->flags & VIR_CONNECT_RO) + return -1; + if (domain->id != -1) + return -1; + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + xenUnifiedLock(priv); + + if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) + goto cleanup; + if (!(entry = virHashLookup(priv->configCache, filename))) + goto cleanup; + def = entry->def; + + if (!(dev = virDomainDeviceDefParse(domain->conn, + priv->caps, + entry->def, + xml, VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + { + for (i = 0 ; i < def->ndisks ; i++) { + if (def->disks[i]->dst && + dev->data.disk->dst && + STREQ(def->disks[i]->dst, dev->data.disk->dst)) { + virDomainDiskDefFree(def->disks[i]); + if (i < (def->ndisks - 1)) + memmove(def->disks + i, + def->disks + i + 1, + sizeof(*def->disks) * + (def->ndisks - (i + 1))); + break; + } + } + break; + } + + case VIR_DOMAIN_DEVICE_NET: + { + for (i = 0 ; i < def->nnets ; i++) { + if (!memcmp(def->nets[i]->mac, + dev->data.net->mac, + sizeof(def->nets[i]->mac))) { + virDomainNetDefFree(def->nets[i]); + if (i < (def->nnets - 1)) + memmove(def->nets + i, + def->nets + i + 1, + sizeof(*def->nets) * + (def->nnets - (i + 1))); + break; + } + } + break; + } + default: + xenXMError(domain->conn, VIR_ERR_XML_ERROR, + "%s", _("unknown device")); + goto cleanup; + } + + /* If this fails, should we try to undo our changes to the + * in-memory representation of the config file. I say not! + */ + if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virDomainDeviceDefFree(dev); + xenUnifiedUnlock(priv); + return (ret); +} + +int +xenXMDomainBlockPeek (virDomainPtr dom, + const char *path ATTRIBUTE_UNUSED, + unsigned long long offset ATTRIBUTE_UNUSED, + size_t size ATTRIBUTE_UNUSED, + void *buffer ATTRIBUTE_UNUSED) +{ + xenXMError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +static char *xenXMAutostartLinkName(virDomainPtr dom) +{ + char *ret; + virAsprintf(&ret, "/etc/xen/auto/%s", dom->name); + return ret; +} + +static char *xenXMDomainConfigName(virDomainPtr dom) +{ + char *ret; + virAsprintf(&ret, "/etc/xen/%s", dom->name); + return ret; +} + +int xenXMDomainGetAutostart(virDomainPtr dom, int *autostart) +{ + char *linkname = xenXMAutostartLinkName(dom); + char *config = xenXMDomainConfigName(dom); + int ret = -1; + + if (!linkname || !config) { + virReportOOMError(dom->conn); + goto cleanup; + } + + *autostart = virFileLinkPointsTo(linkname, config); + if (*autostart < 0) { + virReportSystemError(dom->conn, errno, + _("cannot check link %s points to config %s"), + linkname, config); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(linkname); + VIR_FREE(config); + return ret; +} + + +int xenXMDomainSetAutostart(virDomainPtr dom, int autostart) +{ + char *linkname = xenXMAutostartLinkName(dom); + char *config = xenXMDomainConfigName(dom); + int ret = -1; + + if (!linkname || !config) { + virReportOOMError(dom->conn); + goto cleanup; + } + + if (autostart) { + if (symlink(config, linkname) < 0 && + errno != EEXIST) { + virReportSystemError(dom->conn, errno, + _("failed to create link %s to %s"), + config, linkname); + goto cleanup; + } + } else { + if (unlink(linkname) < 0 && + errno != ENOENT) { + virReportSystemError(dom->conn, errno, + _("failed to remove link %s"), + linkname); + goto cleanup; + } + } + ret = 0; + +cleanup: + VIR_FREE(linkname); + VIR_FREE(config); + + return ret; +} diff --git a/src/xen/xm_internal.h b/src/xen/xm_internal.h new file mode 100644 index 0000000000..7d52ac8c95 --- /dev/null +++ b/src/xen/xm_internal.h @@ -0,0 +1,69 @@ +/* + * xm_internal.h: helper routines for dealing with inactive domains + * + * Copyright (C) 2006-2007 Red Hat + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + * + */ + +#ifndef _LIBVIRT_XM_INTERNAL_H_ +#define _LIBVIRT_XM_INTERNAL_H_ + +#include "internal.h" +#include "driver.h" +#include "conf.h" +#include "domain_conf.h" + +extern struct xenUnifiedDriver xenXMDriver; + +int xenXMConfigCacheRefresh (virConnectPtr conn); +int xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename); +int xenXMConfigCacheRemoveFile(virConnectPtr conn, const char *filename); + +virDrvOpenStatus xenXMOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags); +int xenXMClose(virConnectPtr conn); +const char *xenXMGetType(virConnectPtr conn); +int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info); +char *xenXMDomainDumpXML(virDomainPtr domain, int flags); +int xenXMDomainSetMemory(virDomainPtr domain, unsigned long memory); +int xenXMDomainSetMaxMemory(virDomainPtr domain, unsigned long memory); +unsigned long xenXMDomainGetMaxMemory(virDomainPtr domain); +int xenXMDomainSetVcpus(virDomainPtr domain, unsigned int vcpus); +int xenXMDomainPinVcpu(virDomainPtr domain, unsigned int vcpu, + unsigned char *cpumap, int maplen); +virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname); +virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn, + const unsigned char *uuid); + +int xenXMListDefinedDomains(virConnectPtr conn, char ** const names, int maxnames); +int xenXMNumOfDefinedDomains(virConnectPtr conn); + +int xenXMDomainCreate(virDomainPtr domain); +virDomainPtr xenXMDomainDefineXML(virConnectPtr con, const char *xml); +int xenXMDomainUndefine(virDomainPtr domain); + +virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, virDomainDefPtr def); +virDomainDefPtr xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf); + +int xenXMDomainBlockPeek (virDomainPtr dom, const char *path, unsigned long long offset, size_t size, void *buffer); + +int xenXMDomainGetAutostart(virDomainPtr dom, int *autostart); +int xenXMDomainSetAutostart(virDomainPtr dom, int autostart); + +#endif diff --git a/src/xen/xs_internal.c b/src/xen/xs_internal.c new file mode 100644 index 0000000000..494728d639 --- /dev/null +++ b/src/xen/xs_internal.c @@ -0,0 +1,1407 @@ +/* + * xs_internal.c: access to Xen Store + * + * Copyright (C) 2006, 2009 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Veillard + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include "virterror_internal.h" +#include "datatypes.h" +#include "driver.h" +#include "memory.h" +#include "event.h" +#include "logging.h" +#include "uuid.h" +#include "xen_driver.h" +#include "xs_internal.h" +#include "xen_hypervisor.h" + +#define VIR_FROM_THIS VIR_FROM_XEN + +#ifndef PROXY +static char *xenStoreDomainGetOSType(virDomainPtr domain); +static void xenStoreWatchEvent(int watch, int fd, int events, void *data); +static void xenStoreWatchListFree(xenStoreWatchListPtr list); + +struct xenUnifiedDriver xenStoreDriver = { + xenStoreOpen, /* open */ + xenStoreClose, /* close */ + NULL, /* version */ + NULL, /* hostname */ + NULL, /* nodeGetInfo */ + NULL, /* getCapabilities */ + xenStoreListDomains, /* listDomains */ + NULL, /* numOfDomains */ + NULL, /* domainCreateXML */ + NULL, /* domainSuspend */ + NULL, /* domainResume */ + xenStoreDomainShutdown, /* domainShutdown */ + xenStoreDomainReboot, /* domainReboot */ + NULL, /* domainDestroy */ + xenStoreDomainGetOSType, /* domainGetOSType */ + xenStoreDomainGetMaxMemory, /* domainGetMaxMemory */ + NULL, /* domainSetMaxMemory */ + xenStoreDomainSetMemory, /* domainSetMemory */ + xenStoreGetDomainInfo, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + NULL, /* domainCoreDump */ + NULL, /* domainSetVcpus */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + NULL, /* domainGetMaxVcpus */ + NULL, /* listDefinedDomains */ + NULL, /* numOfDefinedDomains */ + NULL, /* domainCreate */ + NULL, /* domainDefineXML */ + NULL, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainDetachDevice */ + NULL, /* domainGetAutostart */ + NULL, /* domainSetAutostart */ + NULL, /* domainGetSchedulerType */ + NULL, /* domainGetSchedulerParameters */ + NULL, /* domainSetSchedulerParameters */ +}; + +#endif /* ! PROXY */ + +#define virXenStoreError(conn, code, fmt...) \ + virReportErrorHelper(NULL, VIR_FROM_XENSTORE, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +/************************************************************************ + * * + * Helper internal APIs * + * * + ************************************************************************/ +#ifndef PROXY +/** + * virConnectDoStoreList: + * @conn: pointer to the hypervisor connection + * @path: the absolute path of the directory in the store to list + * @nb: OUT pointer to the number of items found + * + * Internal API querying the Xenstore for a list + * + * Returns a string which must be freed by the caller or NULL in case of error + */ +static char ** +virConnectDoStoreList(virConnectPtr conn, const char *path, + unsigned int *nb) +{ + xenUnifiedPrivatePtr priv; + + if (conn == NULL) + return NULL; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL || path == NULL || nb == NULL) + return (NULL); + + return xs_directory (priv->xshandle, 0, path, nb); +} +#endif /* ! PROXY */ + +/** + * virDomainDoStoreQuery: + * @conn: pointer to the hypervisor connection + * @domid: id of the domain + * @path: the relative path of the data in the store to retrieve + * + * Internal API querying the Xenstore for a string value. + * + * Returns a string which must be freed by the caller or NULL in case of error + */ +static char * +virDomainDoStoreQuery(virConnectPtr conn, int domid, const char *path) +{ + char s[256]; + unsigned int len = 0; + xenUnifiedPrivatePtr priv; + + if (!conn) + return NULL; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return (NULL); + + snprintf(s, 255, "/local/domain/%d/%s", domid, path); + s[255] = 0; + + return xs_read(priv->xshandle, 0, &s[0], &len); +} + +#ifndef PROXY +/** + * virDomainDoStoreWrite: + * @domain: a domain object + * @path: the relative path of the data in the store to retrieve + * + * Internal API setting up a string value in the Xenstore + * Requires write access to the XenStore + * + * Returns 0 in case of success, -1 in case of failure + */ +static int +virDomainDoStoreWrite(virDomainPtr domain, const char *path, + const char *value) +{ + char s[256]; + xenUnifiedPrivatePtr priv; + int ret = -1; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) + return (-1); + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xshandle == NULL) + return (-1); + if (domain->conn->flags & VIR_CONNECT_RO) + return (-1); + + snprintf(s, 255, "/local/domain/%d/%s", domain->id, path); + s[255] = 0; + + if (xs_write(priv->xshandle, 0, &s[0], value, strlen(value))) + ret = 0; + + return (ret); +} + +/** + * virDomainGetVM: + * @domain: a domain object + * + * Internal API extracting a xenstore vm path. + * + * Returns the new string or NULL in case of error + */ +static char * +virDomainGetVM(virDomainPtr domain) +{ + char *vm; + char query[200]; + unsigned int len; + xenUnifiedPrivatePtr priv; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) + return (NULL); + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xshandle == NULL) + return (NULL); + + snprintf(query, 199, "/local/domain/%d/vm", virDomainGetID(domain)); + query[199] = 0; + + vm = xs_read(priv->xshandle, 0, &query[0], &len); + + return (vm); +} + +/** + * virDomainGetVMInfo: + * @domain: a domain object + * @vm: the xenstore vm path + * @name: the value's path + * + * Internal API extracting one information the device used + * by the domain from xensttore + * + * Returns the new string or NULL in case of error + */ +static char * +virDomainGetVMInfo(virDomainPtr domain, const char *vm, const char *name) +{ + char s[256]; + char *ret = NULL; + unsigned int len = 0; + xenUnifiedPrivatePtr priv; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) + return (NULL); + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xshandle == NULL) + return (NULL); + + snprintf(s, 255, "%s/%s", vm, name); + s[255] = 0; + + ret = xs_read(priv->xshandle, 0, &s[0], &len); + + return (ret); +} + +#endif /* ! PROXY */ + +/************************************************************************ + * * + * Canonical internal APIs * + * * + ************************************************************************/ +/** + * xenStoreOpen: + * @conn: pointer to the connection block + * @name: URL for the target, NULL for local + * @flags: combination of virDrvOpenFlag(s) + * + * Connects to the Xen hypervisor. + * + * Returns 0 or -1 in case of error. + */ +virDrvOpenStatus +xenStoreOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + +#ifdef PROXY + priv->xshandle = xs_daemon_open_readonly(); +#else + if (flags & VIR_CONNECT_RO) + priv->xshandle = xs_daemon_open_readonly(); + else + priv->xshandle = xs_daemon_open(); +#endif /* ! PROXY */ + + if (priv->xshandle == NULL) { + /* + * not being able to connect via the socket as an unprivileged + * user is rather normal, this should fallback to the proxy (or + * remote) mechanism. + */ + if (xenHavePrivilege()) { + virXenStoreError(NULL, VIR_ERR_NO_XEN, + "%s", _("failed to connect to Xen Store")); + } + return (-1); + } + +#ifndef PROXY + /* Init activeDomainList */ + if (VIR_ALLOC(priv->activeDomainList) < 0) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to allocate activeDomainList")); + return -1; + } + + /* Init watch list before filling in domInfoList, + so we can know if it is the first time through + when the callback fires */ + if ( VIR_ALLOC(priv->xsWatchList) < 0 ) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to allocate xsWatchList")); + return -1; + } + + /* This will get called once at start */ + if ( xenStoreAddWatch(conn, "@releaseDomain", + "releaseDomain", xenStoreDomainReleased, priv) < 0 ) + { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("adding watch @releaseDomain")); + return -1; + } + + /* The initial call of this will fill domInfoList */ + if( xenStoreAddWatch(conn, "@introduceDomain", + "introduceDomain", xenStoreDomainIntroduced, priv) < 0 ) + { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("adding watch @introduceDomain")); + return -1; + } + + /* Add an event handle */ + if ((priv->xsWatch = virEventAddHandle(xs_fileno(priv->xshandle), + VIR_EVENT_HANDLE_READABLE, + xenStoreWatchEvent, + conn, + NULL)) < 0) + DEBUG0("Failed to add event handle, disabling events\n"); + +#endif //PROXY + return 0; +} + +/** + * xenStoreClose: + * @conn: pointer to the connection block + * + * Close the connection to the Xen hypervisor. + * + * Returns 0 in case of success or -1 in case of error. + */ +int +xenStoreClose(virConnectPtr conn) +{ + xenUnifiedPrivatePtr priv; + + if (conn == NULL) { + virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) conn->privateData; + +#ifndef PROXY + if (xenStoreRemoveWatch(conn, "@introduceDomain", "introduceDomain") < 0) { + DEBUG0("Warning, could not remove @introduceDomain watch"); + /* not fatal */ + } + + if (xenStoreRemoveWatch(conn, "@releaseDomain", "releaseDomain") < 0) { + DEBUG0("Warning, could not remove @releaseDomain watch"); + /* not fatal */ + } + + xenStoreWatchListFree(priv->xsWatchList); + priv->xsWatchList = NULL; + xenUnifiedDomainInfoListFree(priv->activeDomainList); + priv->activeDomainList = NULL; +#endif + if (priv->xshandle == NULL) + return(-1); + + if (priv->xsWatch != -1) + virEventRemoveHandle(priv->xsWatch); + xs_daemon_close(priv->xshandle); + priv->xshandle = NULL; + + return (0); +} + +#ifndef PROXY +/** + * xenStoreGetDomainInfo: + * @domain: pointer to the domain block + * @info: the place where information should be stored + * + * Do an hypervisor call to get the related set of domain information. + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenStoreGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info) +{ + char *tmp, **tmp2; + unsigned int nb_vcpus; + char request[200]; + xenUnifiedPrivatePtr priv; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) + return (-1); + + if ((domain == NULL) || (domain->conn == NULL) || (info == NULL)) { + virXenStoreError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) domain->conn->privateData; + if (priv->xshandle == NULL) + return(-1); + + if (domain->id == -1) + return(-1); + + tmp = virDomainDoStoreQuery(domain->conn, domain->id, "running"); + if (tmp != NULL) { + if (tmp[0] == '1') + info->state = VIR_DOMAIN_RUNNING; + free(tmp); + } else { + info->state = VIR_DOMAIN_NOSTATE; + } + tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target"); + if (tmp != NULL) { + info->memory = atol(tmp); + info->maxMem = atol(tmp); + free(tmp); + } else { + info->memory = 0; + info->maxMem = 0; + } +#if 0 + /* doesn't seems to work */ + tmp = virDomainDoStoreQuery(domain->conn, domain->id, "cpu_time"); + if (tmp != NULL) { + info->cpuTime = atol(tmp); + free(tmp); + } else { + info->cpuTime = 0; + } +#endif + snprintf(request, 199, "/local/domain/%d/cpu", domain->id); + request[199] = 0; + tmp2 = virConnectDoStoreList(domain->conn, request, &nb_vcpus); + if (tmp2 != NULL) { + info->nrVirtCpu = nb_vcpus; + free(tmp2); + } + return (0); +} + +/** + * xenStoreDomainSetMemory: + * @domain: pointer to the domain block + * @memory: the max memory size in kilobytes. + * + * Change the maximum amount of memory allowed in the xen store + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenStoreDomainSetMemory(virDomainPtr domain, unsigned long memory) +{ + int ret; + char value[20]; + + if ((domain == NULL) || (domain->conn == NULL) || + (memory < 1024 * MIN_XEN_GUEST_SIZE)) { + virXenStoreError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + if (domain->id == -1) + return(-1); + if ((domain->id == 0) && (memory < (2 * MIN_XEN_GUEST_SIZE * 1024))) + return(-1); + snprintf(value, 19, "%lu", memory); + value[19] = 0; + ret = virDomainDoStoreWrite(domain, "memory/target", &value[0]); + if (ret < 0) + return (-1); + return (0); +} + +/** + * xenStoreDomainGetMaxMemory: + * @domain: pointer to the domain block + * + * Ask the xenstore for the maximum memory allowed for a domain + * + * Returns the memory size in kilobytes or 0 in case of error. + */ +unsigned long +xenStoreDomainGetMaxMemory(virDomainPtr domain) +{ + char *tmp; + unsigned long ret = 0; + xenUnifiedPrivatePtr priv; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) + return (ret); + if (domain->id == -1) + return(-1); + + priv = domain->conn->privateData; + xenUnifiedLock(priv); + tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target"); + if (tmp != NULL) { + ret = (unsigned long) atol(tmp); + VIR_FREE(tmp); + } + xenUnifiedUnlock(priv); + return(ret); +} + +/** + * xenStoreNumOfDomains: + * @conn: pointer to the hypervisor connection + * + * Provides the number of active domains. + * + * Returns the number of domain found or -1 in case of error + */ +int +xenStoreNumOfDomains(virConnectPtr conn) +{ + unsigned int num; + char **idlist; + int ret = -1; + xenUnifiedPrivatePtr priv; + + if (conn == NULL) { + virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return -1; + } + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) { + virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num); + if (idlist) { + free(idlist); + ret = num; + } + return(ret); +} + +/** + * xenStoreDoListDomains: + * @conn: pointer to the hypervisor connection + * @ids: array to collect the list of IDs of active domains + * @maxids: size of @ids + * + * Internal API: collect the list of active domains, and store + * their ID in @maxids. The driver lock must be held. + * + * Returns the number of domain found or -1 in case of error + */ +static int +xenStoreDoListDomains(xenUnifiedPrivatePtr priv, int *ids, int maxids) +{ + char **idlist = NULL, *endptr; + unsigned int num, i; + int ret = -1; + long id; + + if (priv->xshandle == NULL) + goto out; + + idlist = xs_directory (priv->xshandle, 0, "/local/domain", &num); + if (idlist == NULL) + goto out; + + for (ret = 0, i = 0; (i < num) && (ret < maxids); i++) { + id = strtol(idlist[i], &endptr, 10); + if ((endptr == idlist[i]) || (*endptr != 0)) + goto out; + ids[ret++] = (int) id; + } + +out: + VIR_FREE (idlist); + return ret; +} + +/** + * xenStoreListDomains: + * @conn: pointer to the hypervisor connection + * @ids: array to collect the list of IDs of active domains + * @maxids: size of @ids + * + * Collect the list of active domains, and store their ID in @maxids + * + * Returns the number of domain found or -1 in case of error + */ +int +xenStoreListDomains(virConnectPtr conn, int *ids, int maxids) +{ + xenUnifiedPrivatePtr priv; + int ret; + + if ((conn == NULL) || (ids == NULL)) { + virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + + priv = (xenUnifiedPrivatePtr) conn->privateData; + + xenUnifiedLock(priv); + ret = xenStoreDoListDomains(priv, ids, maxids); + xenUnifiedUnlock(priv); + + return(ret); +} + +/** + * xenStoreLookupByName: + * @conn: A xend instance + * @name: The name of the domain + * + * Try to lookup a domain on the Xen Store based on its name. + * + * Returns a new domain object or NULL in case of failure + */ +virDomainPtr +xenStoreLookupByName(virConnectPtr conn, const char *name) +{ + virDomainPtr ret = NULL; + unsigned int num, i, len; + long id = -1; + char **idlist = NULL, *endptr; + char prop[200], *tmp; + int found = 0; + struct xend_domain *xenddomain = NULL; + xenUnifiedPrivatePtr priv; + + if ((conn == NULL) || (name == NULL)) { + virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(NULL); + } + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return(NULL); + + idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num); + if (idlist == NULL) + goto done; + + for (i = 0; i < num; i++) { + id = strtol(idlist[i], &endptr, 10); + if ((endptr == idlist[i]) || (*endptr != 0)) { + goto done; + } +#if 0 + if (virConnectCheckStoreID(conn, (int) id) < 0) + continue; +#endif + snprintf(prop, 199, "/local/domain/%s/name", idlist[i]); + prop[199] = 0; + tmp = xs_read(priv->xshandle, 0, prop, &len); + if (tmp != NULL) { + found = STREQ (name, tmp); + free(tmp); + if (found) + break; + } + } + if (!found) + goto done; + + ret = virGetDomain(conn, name, NULL); + if (ret == NULL) + goto done; + + ret->id = id; + +done: + free(xenddomain); + free(idlist); + + return(ret); +} + +/** + * xenStoreDomainShutdown: + * @domain: pointer to the Domain block + * + * Shutdown the domain, the OS is requested to properly shutdown + * and the domain may ignore it. It will return immediately + * after queuing the request. + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenStoreDomainShutdown(virDomainPtr domain) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL)) { + virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + if (domain->id == -1 || domain->id == 0) + return(-1); + /* + * this is very hackish, the domU kernel probes for a special + * node in the xenstore and launch the shutdown command if found. + */ + priv = domain->conn->privateData; + xenUnifiedLock(priv); + ret = virDomainDoStoreWrite(domain, "control/shutdown", "poweroff"); + xenUnifiedUnlock(priv); + return ret; +} + +/** + * xenStoreDomainReboot: + * @domain: pointer to the Domain block + * @flags: extra flags for the reboot operation, not used yet + * + * Reboot the domain, the OS is requested to properly shutdown + * and reboot but the domain may ignore it. It will return immediately + * after queuing the request. + * + * Returns 0 in case of success, -1 in case of error. + */ +int +xenStoreDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED) +{ + int ret; + xenUnifiedPrivatePtr priv; + + if ((domain == NULL) || (domain->conn == NULL)) { + virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(-1); + } + if (domain->id == -1 || domain->id == 0) + return(-1); + /* + * this is very hackish, the domU kernel probes for a special + * node in the xenstore and launch the shutdown command if found. + */ + priv = domain->conn->privateData; + xenUnifiedLock(priv); + ret = virDomainDoStoreWrite(domain, "control/shutdown", "reboot"); + xenUnifiedUnlock(priv); + return ret; +} + +/* + * xenStoreDomainGetOSType: + * @domain: a domain object + * + * Get the type of domain operation system. + * + * Returns the new string or NULL in case of error, the string must be + * freed by the caller. + */ +static char * +xenStoreDomainGetOSType(virDomainPtr domain) { + char *vm, *str = NULL; + + if ((domain == NULL) || (domain->conn == NULL)) { + virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, + __FUNCTION__); + return(NULL); + } + + vm = virDomainGetVM(domain); + if (vm) { + xenUnifiedPrivatePtr priv = domain->conn->privateData; + xenUnifiedLock(priv); + str = virDomainGetVMInfo(domain, vm, "image/ostype"); + xenUnifiedUnlock(priv); + VIR_FREE(vm); + } + + return (str); +} +#endif /* ! PROXY */ + +/** + * xenStoreDomainGetVNCPort: + * @conn: the hypervisor connection + * @domid: id of the domain + * + * Return the port number on which the domain is listening for VNC + * connections. + * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + * + * Returns the port number, -1 in case of error + */ +int xenStoreDomainGetVNCPort(virConnectPtr conn, int domid) { + char *tmp; + int ret = -1; + + tmp = virDomainDoStoreQuery(conn, domid, "console/vnc-port"); + if (tmp != NULL) { + char *end; + ret = strtol(tmp, &end, 10); + if (ret == 0 && end == tmp) + ret = -1; + free(tmp); + } + return(ret); +} + +/** + * xenStoreDomainGetConsolePath: + * @conn: the hypervisor connection + * @domid: id of the domain + * + * Return the path to the psuedo TTY on which the guest domain's + * serial console is attached. + * + * Returns the path to the serial console. It is the callers + * responsibilty to free() the return string. Returns NULL + * on error + * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ +char * xenStoreDomainGetConsolePath(virConnectPtr conn, int domid) { + return virDomainDoStoreQuery(conn, domid, "console/tty"); +} + +#ifdef PROXY +/* + * xenStoreDomainGetOSTypeID: + * @conn: pointer to the connection. + * @id: the domain id + * + * Get the type of domain operation system. + * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + * + * Returns the new string or NULL in case of error, the string must be + * freed by the caller. + */ +char * +xenStoreDomainGetOSTypeID(virConnectPtr conn, int id) { + char *vm, *str = NULL; + char query[200]; + unsigned int len; + xenUnifiedPrivatePtr priv; + + if (id < 0) + return(NULL); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return (NULL); + + snprintf(query, 199, "/local/domain/%d/vm", id); + query[199] = 0; + + vm = xs_read(priv->xshandle, 0, &query[0], &len); + + if (vm) { + snprintf(query, 199, "%s/image/ostype", vm); + str = xs_read(priv->xshandle, 0, &query[0], &len); + free(vm); + } + if (str == NULL) + str = strdup("linux"); + + + return (str); +} +#endif /* PROXY */ + +/* + * xenStoreDomainGetNetworkID: + * @conn: pointer to the connection. + * @id: the domain id + * @mac: the mac address + * + * Get the reference (i.e. the string number) for the device on that domain + * which uses the given mac address + * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + * + * Returns the new string or NULL in case of error, the string must be + * freed by the caller. + */ +char * +xenStoreDomainGetNetworkID(virConnectPtr conn, int id, const char *mac) { + char dir[80], path[128], **list = NULL, *val = NULL; + unsigned int len, i, num; + char *ret = NULL; + xenUnifiedPrivatePtr priv; + + if (id < 0) + return(NULL); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return (NULL); + if (mac == NULL) + return (NULL); + + snprintf(dir, sizeof(dir), "/local/domain/0/backend/vif/%d", id); + list = xs_directory(priv->xshandle, 0, dir, &num); + if (list == NULL) + return(NULL); + for (i = 0; i < num; i++) { + snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "mac"); + if ((val = xs_read(priv->xshandle, 0, path, &len)) == NULL) + break; + + bool match = (virMacAddrCompare(val, mac) == 0); + + VIR_FREE(val); + + if (match) { + ret = strdup(list[i]); + break; + } + } + + VIR_FREE(list); + return(ret); +} + +/* + * xenStoreDomainGetDiskID: + * @conn: pointer to the connection. + * @id: the domain id + * @dev: the virtual block device name + * + * Get the reference (i.e. the string number) for the device on that domain + * which uses the given virtual block device name + * + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + * + * Returns the new string or NULL in case of error, the string must be + * freed by the caller. + */ +char * +xenStoreDomainGetDiskID(virConnectPtr conn, int id, const char *dev) { + char dir[80], path[128], **list = NULL, *val = NULL; + unsigned int devlen, len, i, num; + char *ret = NULL; + xenUnifiedPrivatePtr priv; + + if (id < 0) + return(NULL); + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return (NULL); + if (dev == NULL) + return (NULL); + devlen = strlen(dev); + if (devlen <= 0) + return (NULL); + + snprintf(dir, sizeof(dir), "/local/domain/0/backend/vbd/%d", id); + list = xs_directory(priv->xshandle, 0, dir, &num); + if (list != NULL) { + for (i = 0; i < num; i++) { + snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "dev"); + val = xs_read(priv->xshandle, 0, path, &len); + if (val == NULL) + break; + if ((devlen != len) || memcmp(val, dev, len)) { + free (val); + } else { + ret = strdup(list[i]); + free (val); + free (list); + return (ret); + } + } + free (list); + } + snprintf(dir, sizeof(dir), "/local/domain/0/backend/tap/%d", id); + list = xs_directory(priv->xshandle, 0, dir, &num); + if (list != NULL) { + for (i = 0; i < num; i++) { + snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "dev"); + val = xs_read(priv->xshandle, 0, path, &len); + if (val == NULL) + break; + if ((devlen != len) || memcmp(val, dev, len)) { + free (val); + } else { + ret = strdup(list[i]); + free (val); + free (list); + return (ret); + } + } + free (list); + } + return (NULL); +} + +/* + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ +char *xenStoreDomainGetName(virConnectPtr conn, + int id) { + char prop[200]; + xenUnifiedPrivatePtr priv; + unsigned int len; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return(NULL); + + snprintf(prop, 199, "/local/domain/%d/name", id); + prop[199] = 0; + return xs_read(priv->xshandle, 0, prop, &len); +} + +#ifndef PROXY +/* + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ +int xenStoreDomainGetUUID(virConnectPtr conn, + int id, + unsigned char *uuid) { + char prop[200]; + xenUnifiedPrivatePtr priv; + unsigned int len; + char *uuidstr; + int ret = 0; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return -1; + + snprintf(prop, 199, "/local/domain/%d/vm", id); + prop[199] = 0; + // This will return something like + // /vm/00000000-0000-0000-0000-000000000000 + uuidstr = xs_read(priv->xshandle, 0, prop, &len); + + // remove "/vm/" + ret = virUUIDParse(uuidstr + 4, uuid); + + VIR_FREE(uuidstr); + + return ret; +} + +static void +xenStoreWatchListFree(xenStoreWatchListPtr list) +{ + int i; + for (i=0; icount; i++) { + VIR_FREE(list->watches[i]->path); + VIR_FREE(list->watches[i]->token); + VIR_FREE(list->watches[i]); + } + VIR_FREE(list); +} + +/* + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ +int xenStoreAddWatch(virConnectPtr conn, + const char *path, + const char *token, + xenStoreWatchCallback cb, + void *opaque) +{ + xenStoreWatchPtr watch; + int n; + xenStoreWatchListPtr list; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (priv->xshandle == NULL) + return -1; + + list = priv->xsWatchList; + if(!list) + return -1; + + /* check if we already have this callback on our list */ + for (n=0; n < list->count; n++) { + if( STREQ(list->watches[n]->path, path) && + STREQ(list->watches[n]->token, token)) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("watch already tracked")); + return -1; + } + } + + if (VIR_ALLOC(watch) < 0) + return -1; + watch->path = strdup(path); + watch->token = strdup(token); + watch->cb = cb; + watch->opaque = opaque; + + /* Make space on list */ + n = list->count; + if (VIR_REALLOC_N(list->watches, n + 1) < 0) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("reallocating list")); + VIR_FREE(watch); + return -1; + } + + list->watches[n] = watch; + list->count++; + + conn->refs++; + + return xs_watch(priv->xshandle, watch->path, watch->token); +} + +/* + * The caller must hold the lock on the privateData + * associated with the 'conn' parameter. + */ +int xenStoreRemoveWatch(virConnectPtr conn, + const char *path, + const char *token) +{ + int i; + xenStoreWatchListPtr list; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if (priv->xshandle == NULL) + return -1; + + list = priv->xsWatchList; + if(!list) + return -1; + + for (i = 0 ; i < list->count ; i++) { + if( STREQ(list->watches[i]->path, path) && + STREQ(list->watches[i]->token, token)) { + + if (!xs_unwatch(priv->xshandle, + list->watches[i]->path, + list->watches[i]->token)) + { + DEBUG0("WARNING: Could not remove watch"); + /* Not fatal, continue */ + } + + VIR_FREE(list->watches[i]->path); + VIR_FREE(list->watches[i]->token); + VIR_FREE(list->watches[i]); + + if (i < (list->count - 1)) + memmove(list->watches + i, + list->watches + i + 1, + sizeof(*(list->watches)) * + (list->count - (i + 1))); + + if (VIR_REALLOC_N(list->watches, + list->count - 1) < 0) { + ; /* Failure to reduce memory allocation isn't fatal */ + } + list->count--; + virUnrefConnect(conn); + return 0; + } + } + return -1; +} + +static xenStoreWatchPtr +xenStoreFindWatch(xenStoreWatchListPtr list, + const char *path, + const char *token) +{ + int i; + for (i = 0 ; i < list->count ; i++) + if( STREQ(path, list->watches[i]->path) && + STREQ(token, list->watches[i]->token) ) + return list->watches[i]; + + return NULL; +} + +static void +xenStoreWatchEvent(int watch ATTRIBUTE_UNUSED, + int fd ATTRIBUTE_UNUSED, + int events, + void *data) +{ + char **event; + char *path; + char *token; + unsigned int stringCount; + xenStoreWatchPtr sw; + + virConnectPtr conn = data; + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if(!priv) return; + + /* only set a watch on read and write events */ + if (events & (VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP)) return; + + xenUnifiedLock(priv); + + if(!priv->xshandle) + goto cleanup; + + event = xs_read_watch(priv->xshandle, &stringCount); + if (!event) + goto cleanup; + + path = event[XS_WATCH_PATH]; + token = event[XS_WATCH_TOKEN]; + + sw = xenStoreFindWatch(priv->xsWatchList, path, token); + if( sw ) + sw->cb(conn, path, token, sw->opaque); + VIR_FREE(event); + +cleanup: + xenUnifiedUnlock(priv); +} + + +/* + * The domain callback for the @introduceDomain watch + * + * The lock on 'priv' is held when calling this + */ +int xenStoreDomainIntroduced(virConnectPtr conn, + const char *path ATTRIBUTE_UNUSED, + const char *token ATTRIBUTE_UNUSED, + void *opaque) +{ + int i, j, found, missing = 0, retries = 20; + int new_domain_cnt; + int *new_domids; + int nread; + + xenUnifiedPrivatePtr priv = opaque; + +retry: + new_domain_cnt = xenStoreNumOfDomains(conn); + if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) { + virReportOOMError(NULL); + return -1; + } + nread = xenStoreDoListDomains(priv, new_domids, new_domain_cnt); + if (nread != new_domain_cnt) { + // mismatch. retry this read + VIR_FREE(new_domids); + goto retry; + } + + missing = 0; + for (i=0 ; i < new_domain_cnt ; i++) { + found = 0; + for (j = 0 ; j < priv->activeDomainList->count ; j++) { + if (priv->activeDomainList->doms[j]->id == new_domids[i]) { + found = 1; + break; + } + } + + if (!found) { + virDomainEventPtr event; + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + + if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { + missing = 1; + continue; + } + if (xenStoreDomainGetUUID(conn, new_domids[i], uuid) < 0) { + missing = 1; + VIR_FREE(name); + continue; + } + + event = virDomainEventNew(new_domids[i], name, uuid, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_STARTED_BOOTED); + if (event) + xenUnifiedDomainEventDispatch(priv, event); + + /* Add to the list */ + xenUnifiedAddDomainInfo(priv->activeDomainList, + new_domids[i], name, uuid); + + VIR_FREE(name); + } + } + VIR_FREE(new_domids); + + if (missing && retries--) { + DEBUG0("Some domains were missing, trying again"); + usleep(100 * 1000); + goto retry; + } + return 0; +} + +/* + * The domain callback for the @destroyDomain watch + * + * The lock on 'priv' is held when calling this + */ +int xenStoreDomainReleased(virConnectPtr conn, + const char *path ATTRIBUTE_UNUSED, + const char *token ATTRIBUTE_UNUSED, + void *opaque) +{ + int i, j, found, removed, retries = 20; + int new_domain_cnt; + int *new_domids; + int nread; + + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) opaque; + + if(!priv->activeDomainList->count) return 0; + +retry: + new_domain_cnt = xenStoreNumOfDomains(conn); + + if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) { + virReportOOMError(NULL); + return -1; + } + nread = xenStoreDoListDomains(priv, new_domids, new_domain_cnt); + if (nread != new_domain_cnt) { + // mismatch. retry this read + VIR_FREE(new_domids); + goto retry; + } + + removed = 0; + for (j=0 ; j < priv->activeDomainList->count ; j++) { + found = 0; + for (i=0 ; i < new_domain_cnt ; i++) { + if (priv->activeDomainList->doms[j]->id == new_domids[i]) { + found = 1; + break; + } + } + + if (!found) { + virDomainEventPtr event = + virDomainEventNew(-1, + priv->activeDomainList->doms[j]->name, + priv->activeDomainList->doms[j]->uuid, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN); + if (event) + xenUnifiedDomainEventDispatch(priv, event); + + /* Remove from the list */ + xenUnifiedRemoveDomainInfo(priv->activeDomainList, + priv->activeDomainList->doms[j]->id, + priv->activeDomainList->doms[j]->name, + priv->activeDomainList->doms[j]->uuid); + + removed = 1; + } + } + + VIR_FREE(new_domids); + + if (!removed && retries--) { + DEBUG0("No domains removed, retrying"); + usleep(100 * 1000); + goto retry; + } + return 0; +} + +#endif //PROXY diff --git a/src/xen/xs_internal.h b/src/xen/xs_internal.h new file mode 100644 index 0000000000..29e680fb07 --- /dev/null +++ b/src/xen/xs_internal.h @@ -0,0 +1,101 @@ +/* + * xs_internal.h: internal API for access to XenStore + * + * Copyright (C) 2006 Red Hat, Inc. + * + * See COPYING.LIB for the License of this software + * + * Daniel Veillard + */ + +#ifndef __VIR_XS_INTERNAL_H__ +#define __VIR_XS_INTERNAL_H__ + +#include "internal.h" +#include "driver.h" + +extern struct xenUnifiedDriver xenStoreDriver; +int xenStoreInit (void); + +virDrvOpenStatus xenStoreOpen (virConnectPtr conn, + virConnectAuthPtr auth, + int flags); +int xenStoreClose (virConnectPtr conn); +int xenStoreGetDomainInfo (virDomainPtr domain, + virDomainInfoPtr info); +int xenStoreNumOfDomains (virConnectPtr conn); +int xenStoreListDomains (virConnectPtr conn, + int *ids, + int maxids); +virDomainPtr xenStoreLookupByName(virConnectPtr conn, + const char *name); +unsigned long xenStoreGetMaxMemory (virDomainPtr domain); +int xenStoreDomainSetMemory (virDomainPtr domain, + unsigned long memory); +unsigned long xenStoreDomainGetMaxMemory(virDomainPtr domain); +int xenStoreDomainShutdown (virDomainPtr domain); +int xenStoreDomainReboot (virDomainPtr domain, + unsigned int flags); + +/* those are entry point for the proxy */ +int xenStoreDomainGetVNCPort(virConnectPtr conn, + int domid); +char * xenStoreDomainGetConsolePath(virConnectPtr conn, + int domid); +char * xenStoreDomainGetOSTypeID(virConnectPtr conn, + int id); +char * xenStoreDomainGetNetworkID(virConnectPtr conn, + int id, + const char *mac); +char * xenStoreDomainGetDiskID(virConnectPtr conn, + int id, + const char *dev); +char * xenStoreDomainGetName(virConnectPtr conn, + int id); +int xenStoreDomainGetUUID(virConnectPtr conn, + int id, + unsigned char *uuid); + +typedef int (*xenStoreWatchCallback)(virConnectPtr conn, + const char *path, + const char *token, + void *opaque); + +struct _xenStoreWatch { + char *path; + char *token; + xenStoreWatchCallback cb; + void *opaque; +}; +typedef struct _xenStoreWatch xenStoreWatch; +typedef xenStoreWatch *xenStoreWatchPtr; + +struct _xenStoreWatchList { + unsigned int count; + xenStoreWatchPtr *watches; +}; +typedef struct _xenStoreWatchList xenStoreWatchList; +typedef xenStoreWatchList *xenStoreWatchListPtr; + + +int xenStoreAddWatch(virConnectPtr conn, + const char *path, + const char *token, + xenStoreWatchCallback cb, + void *opaque); +int xenStoreRemoveWatch(virConnectPtr conn, + const char *path, + const char *token); + +/* domain events */ +int xenStoreDomainIntroduced(virConnectPtr conn, + const char *path, + const char *token, + void *opaque); +int xenStoreDomainReleased(virConnectPtr conn, + const char *path, + const char *token, + void *opaque); + +int xenStoreDomainEventEmitted(virDomainEventType evt); +#endif /* __VIR_XS_INTERNAL_H__ */ diff --git a/src/xen_inotify.c b/src/xen_inotify.c deleted file mode 100644 index e312b9eb1c..0000000000 --- a/src/xen_inotify.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * xen_inofify.c: Xen notification of xml file activity in the - * following dirs: - * /etc/xen - * /var/lib/xend/domains - * - * Copyright (C) 2008 VirtualIron - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Ben Guthro - */ -#include -#include -#include - -#include "virterror_internal.h" -#include "datatypes.h" -#include "driver.h" -#include "memory.h" -#include "event.h" -#include "xen_unified.h" -#include "conf.h" -#include "domain_conf.h" -#include "xen_inotify.h" -#include "xend_internal.h" -#include "logging.h" -#include "uuid.h" - -#include "xm_internal.h" /* for xenXMDomainConfigParse */ - -#define VIR_FROM_THIS VIR_FROM_XEN_INOTIFY - -#define virXenInotifyError(conn, code, fmt...) \ - virReportErrorHelper(NULL, VIR_FROM_XEN_INOTIFY, code, __FILE__, \ - __FUNCTION__, __LINE__, fmt) - -#define LIBVIRTD_DOMAINS_DIR "/var/lib/xend/domains" - -struct xenUnifiedDriver xenInotifyDriver = { - xenInotifyOpen, /* open */ - xenInotifyClose, /* close */ - NULL, /* version */ - NULL, /* hostname */ - NULL, /* nodeGetInfo */ - NULL, /* getCapabilities */ - NULL, /* listDomains */ - NULL, /* numOfDomains */ - NULL, /* domainCreateLinux */ - NULL, /* domainSuspend */ - NULL, /* domainResume */ - NULL, /* domainShutdown */ - NULL, /* domainReboot */ - NULL, /* domainDestroy */ - NULL, /* domainGetOSType */ - NULL, /* domainGetMaxMemory */ - NULL, /* domainSetMaxMemory */ - NULL, /* domainSetMemory */ - NULL, /* domainGetInfo */ - NULL, /* domainSave */ - NULL, /* domainRestore */ - NULL, /* domainCoreDump */ - NULL, /* domainSetVcpus */ - NULL, /* domainPinVcpu */ - NULL, /* domainGetVcpus */ - NULL, /* domainGetMaxVcpus */ - NULL, /* listDefinedDomains */ - NULL, /* numOfDefinedDomains */ - NULL, /* domainCreate */ - NULL, /* domainDefineXML */ - NULL, /* domainUndefine */ - NULL, /* domainAttachDevice */ - NULL, /* domainDetachDevice */ - NULL, /* domainGetAutostart */ - NULL, /* domainSetAutostart */ - NULL, /* domainGetSchedulerType */ - NULL, /* domainGetSchedulerParameters */ - NULL, /* domainSetSchedulerParameters */ -}; - -static int -xenInotifyXenCacheLookup(virConnectPtr conn, - const char *filename, - char **name, unsigned char *uuid) { - xenUnifiedPrivatePtr priv = conn->privateData; - xenXMConfCachePtr entry; - - if (!(entry = virHashLookup(priv->configCache, filename))) { - DEBUG("No config found for %s", filename); - return -1; - } - - *name = strdup(entry->def->name); - memcpy(uuid, entry->def->uuid, VIR_UUID_BUFLEN); - - if (!*name) { - DEBUG0("Error getting dom from def"); - return -1; - } - return 0; -} - -static int -xenInotifyXendDomainsDirLookup(virConnectPtr conn, const char *filename, - char **name, unsigned char *uuid) { - int i; - virDomainPtr dom; - const char *uuid_str; - unsigned char rawuuid[VIR_UUID_BUFLEN]; - xenUnifiedPrivatePtr priv = conn->privateData; - - /* xend is managing domains. we will get - * a filename in the manner: - * /var/lib/xend/domains// - */ - uuid_str = filename + strlen(LIBVIRTD_DOMAINS_DIR) + 1; - - if (virUUIDParse(uuid_str, rawuuid) < 0) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - _("parsing uuid %s"), uuid_str); - return -1; - } - /* call directly into xend here, as driver may not yet - be set during open while we are building our - initial list of domains */ - DEBUG("Looking for dom with uuid: %s", uuid_str); - /* XXX Should not have to go via a virDomainPtr obj instance */ - if(!(dom = xenDaemonLookupByUUID(conn, rawuuid))) { - /* If we are here, the domain has gone away. - search for, and create a domain from the stored - list info */ - for (i = 0 ; i < priv->configInfoList->count ; i++) { - if (!memcmp(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) { - *name = strdup(priv->configInfoList->doms[i]->name); - if (!*name) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - _("finding dom for %s"), uuid_str); - return -1; - } - memcpy(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN); - DEBUG0("Found dom on list"); - return 0; - } - } - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("finding dom on config list")); - return -1; - } - - if (!(*name = strdup(dom->name))) - return -1; - memcpy(uuid, dom->uuid, VIR_UUID_BUFLEN); - virDomainFree(dom); - /* succeeded too find domain by uuid */ - return 0; -} - -static int -xenInotifyDomainLookup(virConnectPtr conn, - const char *filename, - char **name, unsigned char *uuid) { - xenUnifiedPrivatePtr priv = conn->privateData; - if (priv->useXenConfigCache) - return xenInotifyXenCacheLookup(conn, filename, name, uuid); - else - return xenInotifyXendDomainsDirLookup(conn, filename, name, uuid); -} - -static virDomainEventPtr -xenInotifyDomainEventFromFile(virConnectPtr conn, - const char *filename, - int type, int detail) { - virDomainEventPtr event; - char *name = NULL; - unsigned char uuid[VIR_UUID_BUFLEN]; - - if (xenInotifyDomainLookup(conn, filename, &name, uuid) < 0) - return NULL; - - event = virDomainEventNew(-1, name, uuid, type, detail); - VIR_FREE(name); - return event; -} - -static int -xenInotifyXendDomainsDirRemoveEntry(virConnectPtr conn, - const char *fname) { - xenUnifiedPrivatePtr priv = conn->privateData; - const char *uuidstr = fname + strlen(LIBVIRTD_DOMAINS_DIR) + 1; - unsigned char uuid[VIR_UUID_BUFLEN]; - int i; - - if (virUUIDParse(uuidstr, uuid) < 0) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - _("parsing uuid %s"), uuidstr); - return -1; - } - - /* match and remove on uuid */ - for (i = 0 ; i < priv->configInfoList->count ; i++) { - if (!memcmp(uuid, priv->configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) { - VIR_FREE(priv->configInfoList->doms[i]->name); - VIR_FREE(priv->configInfoList->doms[i]); - - if (i < (priv->configInfoList->count - 1)) - memmove(priv->configInfoList->doms + i, - priv->configInfoList->doms + i + 1, - sizeof(*(priv->configInfoList->doms)) * - (priv->configInfoList->count - (i + 1))); - - if (VIR_REALLOC_N(priv->configInfoList->doms, - priv->configInfoList->count - 1) < 0) { - ; /* Failure to reduce memory allocation isn't fatal */ - } - priv->configInfoList->count--; - return 0; - } - } - return -1; -} - -static int -xenInotifyXendDomainsDirAddEntry(virConnectPtr conn, - const char *fname) { - char *name = NULL; - unsigned char uuid[VIR_UUID_BUFLEN]; - xenUnifiedPrivatePtr priv = conn->privateData; - - if (xenInotifyDomainLookup(conn, fname, &name, uuid) < 0) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Error looking up domain")); - return -1; - } - - if (xenUnifiedAddDomainInfo(priv->configInfoList, - -1, name, uuid) < 0) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Error adding file to config cache")); - VIR_FREE(name); - return -1; - } - VIR_FREE(name); - return 0; -} - -static int -xenInotifyRemoveDomainConfigInfo(virConnectPtr conn, - const char *fname) { - xenUnifiedPrivatePtr priv = conn->privateData; - return priv->useXenConfigCache ? - xenXMConfigCacheRemoveFile(conn, fname) : - xenInotifyXendDomainsDirRemoveEntry(conn, fname); -} - -static int -xenInotifyAddDomainConfigInfo(virConnectPtr conn, - const char *fname) { - xenUnifiedPrivatePtr priv = conn->privateData; - return priv->useXenConfigCache ? - xenXMConfigCacheAddFile(conn, fname) : - xenInotifyXendDomainsDirAddEntry(conn, fname); -} - -static void -xenInotifyEvent(int watch ATTRIBUTE_UNUSED, - int fd, - int events ATTRIBUTE_UNUSED, - void *data) -{ - char buf[1024]; - char fname[1024]; - struct inotify_event *e; - int got; - char *tmp, *name; - virConnectPtr conn = data; - xenUnifiedPrivatePtr priv = NULL; - - DEBUG0("got inotify event"); - - if( conn && conn->privateData ) { - priv = conn->privateData; - } else { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("conn, or private data is NULL")); - return; - } - - xenUnifiedLock(priv); - -reread: - got = read(fd, buf, sizeof(buf)); - if (got == -1) { - if (errno == EINTR) - goto reread; - goto cleanup; - } - - tmp = buf; - while (got) { - if (got < sizeof(struct inotify_event)) - goto cleanup; /* bad */ - - e = (struct inotify_event *)tmp; - tmp += sizeof(struct inotify_event); - got -= sizeof(struct inotify_event); - - if (got < e->len) - goto cleanup; - - tmp += e->len; - got -= e->len; - - name = (char *)&(e->name); - - snprintf(fname, 1024, "%s/%s", - priv->configDir, name); - - if (e->mask & (IN_DELETE | IN_MOVED_FROM)) { - virDomainEventPtr event = - xenInotifyDomainEventFromFile(conn, fname, - VIR_DOMAIN_EVENT_UNDEFINED, - VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); - if (!event) - xenUnifiedDomainEventDispatch(conn->privateData, event); - else - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("looking up dom")); - - if (xenInotifyRemoveDomainConfigInfo(conn, fname) < 0 ) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Error adding file to config cache")); - goto cleanup; - } - } else if (e->mask & ( IN_CREATE | IN_CLOSE_WRITE | IN_MOVED_TO) ) { - virDomainEventPtr event; - if (xenInotifyAddDomainConfigInfo(conn, fname) < 0 ) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Error adding file to config cache")); - goto cleanup; - } - - event = xenInotifyDomainEventFromFile(conn, fname, - VIR_DOMAIN_EVENT_DEFINED, - VIR_DOMAIN_EVENT_DEFINED_ADDED); - - if (event) - xenUnifiedDomainEventDispatch(conn->privateData, event); - else - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("looking up dom")); - - } - - } - -cleanup: - xenUnifiedUnlock(priv); -} - -/** - * xenInotifyOpen: - * @conn: pointer to the connection block - * @name: URL for the target, NULL for local - * @flags: combination of virDrvOpenFlag(s) - * - * Connects and starts listening for inotify events - * - * Returns 0 or -1 in case of error. - */ -virDrvOpenStatus -xenInotifyOpen(virConnectPtr conn ATTRIBUTE_UNUSED, - virConnectAuthPtr auth ATTRIBUTE_UNUSED, - int flags ATTRIBUTE_UNUSED) -{ - DIR *dh; - struct dirent *ent; - char path[PATH_MAX]; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (priv->configDir) { - priv->useXenConfigCache = 1; - } else { - /* /var/lib/xend/domains//config.sxp */ - priv->configDir = LIBVIRTD_DOMAINS_DIR; - priv->useXenConfigCache = 0; - - if (VIR_ALLOC(priv->configInfoList) < 0) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to allocate configInfoList")); - return -1; - } - - /* populate initial list */ - if (!(dh = opendir(priv->configDir))) { - virReportSystemError(NULL, errno, - _("cannot open directory: %s"), - priv->configDir); - return -1; - } - while ((ent = readdir(dh))) { - if (STRPREFIX(ent->d_name, ".")) - continue; - - /* Build the full file path */ - if ((strlen(priv->configDir) + 1 + - strlen(ent->d_name) + 1) > PATH_MAX) - continue; - strcpy(path, priv->configDir); - strcat(path, "/"); - strcat(path, ent->d_name); - - if (xenInotifyAddDomainConfigInfo(conn, path) < 0 ) { - virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("Error adding file to config list")); - closedir(dh); - return -1; - } - } - closedir(dh); - } - - if ((priv->inotifyFD = inotify_init()) < 0) { - virReportSystemError(NULL, errno, - "%s", _("initializing inotify")); - return -1; - } - - DEBUG("Adding a watch on %s", priv->configDir); - if (inotify_add_watch(priv->inotifyFD, - priv->configDir, - IN_CREATE | - IN_CLOSE_WRITE | IN_DELETE | - IN_MOVED_TO | IN_MOVED_FROM) < 0) { - virReportSystemError(NULL, errno, - _("adding watch on %s"), - priv->configDir); - return -1; - } - - DEBUG0("Building initial config cache"); - if (priv->useXenConfigCache && - xenXMConfigCacheRefresh (conn) < 0) { - DEBUG("Failed to enable XM config cache %s", conn->err.message); - return -1; - } - - DEBUG0("Registering with event loop"); - /* Add the handle for monitoring */ - if ((priv->inotifyWatch = virEventAddHandle(priv->inotifyFD, VIR_EVENT_HANDLE_READABLE, - xenInotifyEvent, conn, NULL)) < 0) { - DEBUG0("Failed to add inotify handle, disabling events"); - } - - virConnectRef(conn); - return 0; -} - -/** - * xenInotifyClose: - * @conn: pointer to the connection block - * - * Close and stop listening for inotify events - * - * Returns 0 in case of success or -1 in case of error. - */ -int -xenInotifyClose(virConnectPtr conn) -{ - xenUnifiedPrivatePtr priv = conn->privateData; - - if (priv->configInfoList) - xenUnifiedDomainInfoListFree(priv->configInfoList); - - if (priv->inotifyWatch != -1) - virEventRemoveHandle(priv->inotifyWatch); - close(priv->inotifyFD); - virUnrefConnect(conn); - - return 0; -} diff --git a/src/xen_inotify.h b/src/xen_inotify.h deleted file mode 100644 index 70bc63cbed..0000000000 --- a/src/xen_inotify.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * xen_inofify.h: Xen notification of xml files - * - * Copyright (C) 2008 VirtualIron - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Ben Guthro - */ -#ifndef __VIR_XEN_INOTIFY_H__ -#define __VIR_XEN_INOTIFY_H__ - -#include "internal.h" -#include "driver.h" - -extern struct xenUnifiedDriver xenInotifyDriver; - -virDrvOpenStatus xenInotifyOpen (virConnectPtr conn, - virConnectAuthPtr auth, - int flags); -int xenInotifyClose (virConnectPtr conn); -#endif diff --git a/src/xen_internal.c b/src/xen_internal.c deleted file mode 100644 index 30fd8bd195..0000000000 --- a/src/xen_internal.c +++ /dev/null @@ -1,3457 +0,0 @@ -/* - * xen_internal.c: direct access to Xen hypervisor level - * - * Copyright (C) 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. - * - * See COPYING.LIB for the License of this software - * - * Daniel Veillard - */ - -#include - -#include -#include -/* required for uint8_t, uint32_t, etc ... */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __sun -#include - -#include - -#ifndef PRIV_XVM_CONTROL -#define PRIV_XVM_CONTROL ((const char *)"xvm_control") -#endif - -#endif /* __sun */ - -/* required for dom0_getdomaininfo_t */ -#include -#include -#ifdef HAVE_XEN_LINUX_PRIVCMD_H -#include -#else -#ifdef HAVE_XEN_SYS_PRIVCMD_H -#include -#endif -#endif - -/* required for shutdown flags */ -#include - -#include "virterror_internal.h" -#include "logging.h" -#include "datatypes.h" -#include "driver.h" -#include "util.h" -#include "xen_unified.h" -#include "xen_internal.h" -#include "xs_internal.h" -#include "stats_linux.h" -#include "xend_internal.h" -#include "buf.h" -#include "capabilities.h" -#include "memory.h" - -#define VIR_FROM_THIS VIR_FROM_XEN - -/* - * so far there is 2 versions of the structures usable for doing - * hypervisor calls. - */ -/* the old one */ -typedef struct v0_hypercall_struct { - unsigned long op; - unsigned long arg[5]; -} v0_hypercall_t; - -#ifdef __linux__ -#define XEN_V0_IOCTL_HYPERCALL_CMD \ - _IOC(_IOC_NONE, 'P', 0, sizeof(v0_hypercall_t)) -/* the new one */ -typedef struct v1_hypercall_struct -{ - uint64_t op; - uint64_t arg[5]; -} v1_hypercall_t; -#define XEN_V1_IOCTL_HYPERCALL_CMD \ - _IOC(_IOC_NONE, 'P', 0, sizeof(v1_hypercall_t)) -typedef v1_hypercall_t hypercall_t; -#elif defined(__sun) -typedef privcmd_hypercall_t hypercall_t; -#else -#error "unsupported platform" -#endif - -#ifndef __HYPERVISOR_sysctl -#define __HYPERVISOR_sysctl 35 -#endif -#ifndef __HYPERVISOR_domctl -#define __HYPERVISOR_domctl 36 -#endif - -#ifdef WITH_RHEL5_API -#define SYS_IFACE_MIN_VERS_NUMA 3 -#else -#define SYS_IFACE_MIN_VERS_NUMA 4 -#endif - -static int xen_ioctl_hypercall_cmd = 0; -static int initialized = 0; -static int in_init = 0; -static int hv_version = 0; -static int hypervisor_version = 2; -static int sys_interface_version = -1; -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 *flags_pae_re = "^flags[[:blank:]]+:.* pae[[:space:]]"; -static regex_t flags_pae_rec; -static const char *xen_cap_re = "(xen|hvm)-[[:digit:]]+\\.[[:digit:]]+-(x86_32|x86_64|ia64|powerpc64)(p|be)?"; -static regex_t xen_cap_rec; - -/* - * The content of the structures for a getdomaininfolist system hypercall - */ -#ifndef DOMFLAGS_DYING -#define DOMFLAGS_DYING (1<<0) /* Domain is scheduled to die. */ -#define DOMFLAGS_HVM (1<<1) /* Domain is HVM */ -#define DOMFLAGS_SHUTDOWN (1<<2) /* The guest OS has shut down. */ -#define DOMFLAGS_PAUSED (1<<3) /* Currently paused by control software. */ -#define DOMFLAGS_BLOCKED (1<<4) /* Currently blocked pending an event. */ -#define DOMFLAGS_RUNNING (1<<5) /* Domain is currently running. */ -#define DOMFLAGS_CPUMASK 255 /* CPU to which this domain is bound. */ -#define DOMFLAGS_CPUSHIFT 8 -#define DOMFLAGS_SHUTDOWNMASK 255 /* DOMFLAGS_SHUTDOWN guest-supplied code. */ -#define DOMFLAGS_SHUTDOWNSHIFT 16 -#endif - -/* - * These flags explain why a system is in the state of "shutdown". Normally, - * They are defined in xen/sched.h - */ -#ifndef SHUTDOWN_poweroff -#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ -#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ -#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ -#define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ -#endif - -#define XEN_V0_OP_GETDOMAININFOLIST 38 -#define XEN_V1_OP_GETDOMAININFOLIST 38 -#define XEN_V2_OP_GETDOMAININFOLIST 6 - -struct xen_v0_getdomaininfo { - domid_t domain; /* the domain number */ - uint32_t flags; /* flags, see before */ - uint64_t tot_pages; /* total number of pages used */ - uint64_t max_pages; /* maximum number of pages allowed */ - unsigned long shared_info_frame; /* MFN of shared_info struct */ - uint64_t cpu_time; /* CPU time used */ - uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ - uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ - uint32_t ssidref; - xen_domain_handle_t handle; -}; -typedef struct xen_v0_getdomaininfo xen_v0_getdomaininfo; - -struct xen_v2_getdomaininfo { - domid_t domain; /* the domain number */ - uint32_t flags; /* flags, see before */ - uint64_t tot_pages; /* total number of pages used */ - uint64_t max_pages; /* maximum number of pages allowed */ - uint64_t shared_info_frame; /* MFN of shared_info struct */ - uint64_t cpu_time; /* CPU time used */ - uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ - uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ - uint32_t ssidref; - xen_domain_handle_t handle; -}; -typedef struct xen_v2_getdomaininfo xen_v2_getdomaininfo; - - -/* As of Hypervisor Call v2, DomCtl v5 we are now 8-byte aligned - even on 32-bit archs when dealing with uint64_t */ -#define ALIGN_64 __attribute__((aligned(8))) - -struct xen_v2d5_getdomaininfo { - domid_t domain; /* the domain number */ - uint32_t flags; /* flags, see before */ - uint64_t tot_pages ALIGN_64; /* total number of pages used */ - uint64_t max_pages ALIGN_64; /* maximum number of pages allowed */ - uint64_t shared_info_frame ALIGN_64; /* MFN of shared_info struct */ - uint64_t cpu_time ALIGN_64; /* CPU time used */ - uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ - uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ - uint32_t ssidref; - xen_domain_handle_t handle; -}; -typedef struct xen_v2d5_getdomaininfo xen_v2d5_getdomaininfo; - -union xen_getdomaininfo { - struct xen_v0_getdomaininfo v0; - struct xen_v2_getdomaininfo v2; - struct xen_v2d5_getdomaininfo v2d5; -}; -typedef union xen_getdomaininfo xen_getdomaininfo; - -union xen_getdomaininfolist { - struct xen_v0_getdomaininfo *v0; - struct xen_v2_getdomaininfo *v2; - struct xen_v2d5_getdomaininfo *v2d5; -}; -typedef union xen_getdomaininfolist xen_getdomaininfolist; - - -struct xen_v2_getschedulerid { - uint32_t sched_id; /* Get Scheduler ID from Xen */ -}; -typedef struct xen_v2_getschedulerid xen_v2_getschedulerid; - - -union xen_getschedulerid { - struct xen_v2_getschedulerid *v2; -}; -typedef union xen_getschedulerid xen_getschedulerid; - -struct xen_v2s4_availheap { - uint32_t min_bitwidth; /* Smallest address width (zero if don't care). */ - uint32_t max_bitwidth; /* Largest address width (zero if don't care). */ - int32_t node; /* NUMA node (-1 for sum across all nodes). */ - uint64_t avail_bytes; /* Bytes available in the specified region. */ -}; - -typedef struct xen_v2s4_availheap xen_v2s4_availheap; - -struct xen_v2s5_availheap { - uint32_t min_bitwidth; /* Smallest address width (zero if don't care). */ - uint32_t max_bitwidth; /* Largest address width (zero if don't care). */ - int32_t node; /* NUMA node (-1 for sum across all nodes). */ - uint64_t avail_bytes ALIGN_64; /* Bytes available in the specified region. */ -}; - -typedef struct xen_v2s5_availheap xen_v2s5_availheap; - - -#define XEN_GETDOMAININFOLIST_ALLOC(domlist, size) \ - (hypervisor_version < 2 ? \ - (VIR_ALLOC_N(domlist.v0, (size)) == 0) : \ - (dom_interface_version < 5 ? \ - (VIR_ALLOC_N(domlist.v2, (size)) == 0) : \ - (VIR_ALLOC_N(domlist.v2d5, (size)) == 0))) - -#define XEN_GETDOMAININFOLIST_FREE(domlist) \ - (hypervisor_version < 2 ? \ - VIR_FREE(domlist.v0) : \ - (dom_interface_version < 5 ? \ - VIR_FREE(domlist.v2) : \ - VIR_FREE(domlist.v2d5))) - -#define XEN_GETDOMAININFOLIST_CLEAR(domlist, size) \ - (hypervisor_version < 2 ? \ - memset(domlist.v0, 0, sizeof(*domlist.v0) * size) : \ - (dom_interface_version < 5 ? \ - memset(domlist.v2, 0, sizeof(*domlist.v2) * size) : \ - memset(domlist.v2d5, 0, sizeof(*domlist.v2d5) * size))) - -#define XEN_GETDOMAININFOLIST_DOMAIN(domlist, n) \ - (hypervisor_version < 2 ? \ - domlist.v0[n].domain : \ - (dom_interface_version < 5 ? \ - domlist.v2[n].domain : \ - domlist.v2d5[n].domain)) - -#define XEN_GETDOMAININFOLIST_UUID(domlist, n) \ - (hypervisor_version < 2 ? \ - domlist.v0[n].handle : \ - (dom_interface_version < 5 ? \ - domlist.v2[n].handle : \ - domlist.v2d5[n].handle)) - -#define XEN_GETDOMAININFOLIST_DATA(domlist) \ - (hypervisor_version < 2 ? \ - (void*)(domlist->v0) : \ - (dom_interface_version < 5 ? \ - (void*)(domlist->v2) : \ - (void*)(domlist->v2d5))) - -#define XEN_GETDOMAININFO_SIZE \ - (hypervisor_version < 2 ? \ - sizeof(xen_v0_getdomaininfo) : \ - (dom_interface_version < 5 ? \ - sizeof(xen_v2_getdomaininfo) : \ - sizeof(xen_v2d5_getdomaininfo))) - -#define XEN_GETDOMAININFO_CLEAR(dominfo) \ - (hypervisor_version < 2 ? \ - memset(&(dominfo.v0), 0, sizeof(xen_v0_getdomaininfo)) : \ - (dom_interface_version < 5 ? \ - memset(&(dominfo.v2), 0, sizeof(xen_v2_getdomaininfo)) : \ - memset(&(dominfo.v2d5), 0, sizeof(xen_v2d5_getdomaininfo)))) - -#define XEN_GETDOMAININFO_DOMAIN(dominfo) \ - (hypervisor_version < 2 ? \ - dominfo.v0.domain : \ - (dom_interface_version < 5 ? \ - dominfo.v2.domain : \ - dominfo.v2d5.domain)) - -#define XEN_GETDOMAININFO_CPUTIME(dominfo) \ - (hypervisor_version < 2 ? \ - dominfo.v0.cpu_time : \ - (dom_interface_version < 5 ? \ - dominfo.v2.cpu_time : \ - dominfo.v2d5.cpu_time)) - -#define XEN_GETDOMAININFO_CPUCOUNT(dominfo) \ - (hypervisor_version < 2 ? \ - dominfo.v0.nr_online_vcpus : \ - (dom_interface_version < 5 ? \ - dominfo.v2.nr_online_vcpus : \ - dominfo.v2d5.nr_online_vcpus)) - -#define XEN_GETDOMAININFO_MAXCPUID(dominfo) \ - (hypervisor_version < 2 ? \ - dominfo.v0.max_vcpu_id : \ - (dom_interface_version < 5 ? \ - dominfo.v2.max_vcpu_id : \ - dominfo.v2d5.max_vcpu_id)) - -#define XEN_GETDOMAININFO_FLAGS(dominfo) \ - (hypervisor_version < 2 ? \ - dominfo.v0.flags : \ - (dom_interface_version < 5 ? \ - dominfo.v2.flags : \ - dominfo.v2d5.flags)) - -#define XEN_GETDOMAININFO_TOT_PAGES(dominfo) \ - (hypervisor_version < 2 ? \ - dominfo.v0.tot_pages : \ - (dom_interface_version < 5 ? \ - dominfo.v2.tot_pages : \ - dominfo.v2d5.tot_pages)) - -#define XEN_GETDOMAININFO_MAX_PAGES(dominfo) \ - (hypervisor_version < 2 ? \ - dominfo.v0.max_pages : \ - (dom_interface_version < 5 ? \ - dominfo.v2.max_pages : \ - dominfo.v2d5.max_pages)) - -#define XEN_GETDOMAININFO_UUID(dominfo) \ - (hypervisor_version < 2 ? \ - dominfo.v0.handle : \ - (dom_interface_version < 5 ? \ - dominfo.v2.handle : \ - dominfo.v2d5.handle)) - - -static int -lock_pages(void *addr, size_t len) -{ -#ifdef __linux__ - return (mlock(addr, len)); -#elif defined(__sun) - return (0); -#endif -} - -static int -unlock_pages(void *addr, size_t len) -{ -#ifdef __linux__ - return (munlock(addr, len)); -#elif defined(__sun) - return (0); -#endif -} - - -struct xen_v0_getdomaininfolistop { - domid_t first_domain; - uint32_t max_domains; - struct xen_v0_getdomaininfo *buffer; - uint32_t num_domains; -}; -typedef struct xen_v0_getdomaininfolistop xen_v0_getdomaininfolistop; - - -struct xen_v2_getdomaininfolistop { - domid_t first_domain; - uint32_t max_domains; - struct xen_v2_getdomaininfo *buffer; - uint32_t num_domains; -}; -typedef struct xen_v2_getdomaininfolistop xen_v2_getdomaininfolistop; - -/* As of HV version 2, sysctl version 3 the *buffer pointer is 64-bit aligned */ -struct xen_v2s3_getdomaininfolistop { - domid_t first_domain; - uint32_t max_domains; -#ifdef __BIG_ENDIAN__ - struct { - int __pad[(sizeof (long long) - sizeof (struct xen_v2d5_getdomaininfo *)) / sizeof (int)]; - struct xen_v2d5_getdomaininfo *v; - } buffer; -#else - union { - struct xen_v2d5_getdomaininfo *v; - uint64_t pad ALIGN_64; - } buffer; -#endif - uint32_t num_domains; -}; -typedef struct xen_v2s3_getdomaininfolistop xen_v2s3_getdomaininfolistop; - - - -struct xen_v0_domainop { - domid_t domain; -}; -typedef struct xen_v0_domainop xen_v0_domainop; - -/* - * The information for a destroydomain system hypercall - */ -#define XEN_V0_OP_DESTROYDOMAIN 9 -#define XEN_V1_OP_DESTROYDOMAIN 9 -#define XEN_V2_OP_DESTROYDOMAIN 2 - -/* - * The information for a pausedomain system hypercall - */ -#define XEN_V0_OP_PAUSEDOMAIN 10 -#define XEN_V1_OP_PAUSEDOMAIN 10 -#define XEN_V2_OP_PAUSEDOMAIN 3 - -/* - * The information for an unpausedomain system hypercall - */ -#define XEN_V0_OP_UNPAUSEDOMAIN 11 -#define XEN_V1_OP_UNPAUSEDOMAIN 11 -#define XEN_V2_OP_UNPAUSEDOMAIN 4 - -/* - * The information for an setmaxmem system hypercall - */ -#define XEN_V0_OP_SETMAXMEM 28 -#define XEN_V1_OP_SETMAXMEM 28 -#define XEN_V2_OP_SETMAXMEM 11 - -struct xen_v0_setmaxmem { - domid_t domain; - uint64_t maxmem; -}; -typedef struct xen_v0_setmaxmem xen_v0_setmaxmem; -typedef struct xen_v0_setmaxmem xen_v1_setmaxmem; - -struct xen_v2_setmaxmem { - uint64_t maxmem; -}; -typedef struct xen_v2_setmaxmem xen_v2_setmaxmem; - -struct xen_v2d5_setmaxmem { - uint64_t maxmem ALIGN_64; -}; -typedef struct xen_v2d5_setmaxmem xen_v2d5_setmaxmem; - -/* - * The information for an setmaxvcpu system hypercall - */ -#define XEN_V0_OP_SETMAXVCPU 41 -#define XEN_V1_OP_SETMAXVCPU 41 -#define XEN_V2_OP_SETMAXVCPU 15 - -struct xen_v0_setmaxvcpu { - domid_t domain; - uint32_t maxvcpu; -}; -typedef struct xen_v0_setmaxvcpu xen_v0_setmaxvcpu; -typedef struct xen_v0_setmaxvcpu xen_v1_setmaxvcpu; - -struct xen_v2_setmaxvcpu { - uint32_t maxvcpu; -}; -typedef struct xen_v2_setmaxvcpu xen_v2_setmaxvcpu; - -/* - * The information for an setvcpumap system hypercall - * Note that between 1 and 2 the limitation to 64 physical CPU was lifted - * hence the difference in structures - */ -#define XEN_V0_OP_SETVCPUMAP 20 -#define XEN_V1_OP_SETVCPUMAP 20 -#define XEN_V2_OP_SETVCPUMAP 9 - -struct xen_v0_setvcpumap { - domid_t domain; - uint32_t vcpu; - cpumap_t cpumap; -}; -typedef struct xen_v0_setvcpumap xen_v0_setvcpumap; -typedef struct xen_v0_setvcpumap xen_v1_setvcpumap; - -struct xen_v2_cpumap { - uint8_t *bitmap; - uint32_t nr_cpus; -}; -struct xen_v2_setvcpumap { - uint32_t vcpu; - struct xen_v2_cpumap cpumap; -}; -typedef struct xen_v2_setvcpumap xen_v2_setvcpumap; - -/* HV version 2, Dom version 5 requires 64-bit alignment */ -struct xen_v2d5_cpumap { -#ifdef __BIG_ENDIAN__ - struct { - int __pad[(sizeof (long long) - sizeof (uint8_t *)) / sizeof (int)]; - uint8_t *v; - } bitmap; -#else - union { - uint8_t *v; - uint64_t pad ALIGN_64; - } bitmap; -#endif - uint32_t nr_cpus; -}; -struct xen_v2d5_setvcpumap { - uint32_t vcpu; - struct xen_v2d5_cpumap cpumap; -}; -typedef struct xen_v2d5_setvcpumap xen_v2d5_setvcpumap; - -/* - * The information for an vcpuinfo system hypercall - */ -#define XEN_V0_OP_GETVCPUINFO 43 -#define XEN_V1_OP_GETVCPUINFO 43 -#define XEN_V2_OP_GETVCPUINFO 14 - -struct xen_v0_vcpuinfo { - domid_t domain; /* owner's domain */ - uint32_t vcpu; /* the vcpu number */ - uint8_t online; /* seen as on line */ - uint8_t blocked; /* blocked on event */ - uint8_t running; /* scheduled on CPU */ - uint64_t cpu_time; /* nanosecond of CPU used */ - uint32_t cpu; /* current mapping */ - cpumap_t cpumap; /* deprecated in V2 */ -}; -typedef struct xen_v0_vcpuinfo xen_v0_vcpuinfo; -typedef struct xen_v0_vcpuinfo xen_v1_vcpuinfo; - -struct xen_v2_vcpuinfo { - uint32_t vcpu; /* the vcpu number */ - uint8_t online; /* seen as on line */ - uint8_t blocked; /* blocked on event */ - uint8_t running; /* scheduled on CPU */ - uint64_t cpu_time; /* nanosecond of CPU used */ - uint32_t cpu; /* current mapping */ -}; -typedef struct xen_v2_vcpuinfo xen_v2_vcpuinfo; - -struct xen_v2d5_vcpuinfo { - uint32_t vcpu; /* the vcpu number */ - uint8_t online; /* seen as on line */ - uint8_t blocked; /* blocked on event */ - uint8_t running; /* scheduled on CPU */ - uint64_t cpu_time ALIGN_64; /* nanosecond of CPU used */ - uint32_t cpu; /* current mapping */ -}; -typedef struct xen_v2d5_vcpuinfo xen_v2d5_vcpuinfo; - -/* - * from V2 the pinning of a vcpu is read with a separate call - */ -#define XEN_V2_OP_GETVCPUMAP 25 -typedef struct xen_v2_setvcpumap xen_v2_getvcpumap; -typedef struct xen_v2d5_setvcpumap xen_v2d5_getvcpumap; - -/* - * from V2 we get the scheduler information - */ -#define XEN_V2_OP_GETSCHEDULERID 4 - -/* - * from V2 we get the available heap information - */ -#define XEN_V2_OP_GETAVAILHEAP 9 - -/* - * from V2 we get the scheduler parameter - */ -#define XEN_V2_OP_SCHEDULER 16 -/* Scheduler types. */ -#define XEN_SCHEDULER_SEDF 4 -#define XEN_SCHEDULER_CREDIT 5 -/* get/set scheduler parameters */ -#define XEN_DOMCTL_SCHEDOP_putinfo 0 -#define XEN_DOMCTL_SCHEDOP_getinfo 1 - -struct xen_v2_setschedinfo { - uint32_t sched_id; - uint32_t cmd; - union { - struct xen_domctl_sched_sedf { - uint64_t period ALIGN_64; - uint64_t slice ALIGN_64; - uint64_t latency ALIGN_64; - uint32_t extratime; - uint32_t weight; - } sedf; - struct xen_domctl_sched_credit { - uint16_t weight; - uint16_t cap; - } credit; - } u; -}; -typedef struct xen_v2_setschedinfo xen_v2_setschedinfo; -typedef struct xen_v2_setschedinfo xen_v2_getschedinfo; - - -/* - * The hypercall operation structures also have changed on - * changeset 86d26e6ec89b - */ -/* the old structure */ -struct xen_op_v0 { - uint32_t cmd; - uint32_t interface_version; - union { - xen_v0_getdomaininfolistop getdomaininfolist; - xen_v0_domainop domain; - xen_v0_setmaxmem setmaxmem; - xen_v0_setmaxvcpu setmaxvcpu; - xen_v0_setvcpumap setvcpumap; - xen_v0_vcpuinfo getvcpuinfo; - uint8_t padding[128]; - } u; -}; -typedef struct xen_op_v0 xen_op_v0; -typedef struct xen_op_v0 xen_op_v1; - -/* the new structure for systems operations */ -struct xen_op_v2_sys { - uint32_t cmd; - uint32_t interface_version; - union { - xen_v2_getdomaininfolistop getdomaininfolist; - xen_v2s3_getdomaininfolistop getdomaininfolists3; - xen_v2_getschedulerid getschedulerid; - xen_v2s4_availheap availheap; - xen_v2s5_availheap availheap5; - uint8_t padding[128]; - } u; -}; -typedef struct xen_op_v2_sys xen_op_v2_sys; - -/* the new structure for domains operation */ -struct xen_op_v2_dom { - uint32_t cmd; - uint32_t interface_version; - domid_t domain; - union { - xen_v2_setmaxmem setmaxmem; - xen_v2d5_setmaxmem setmaxmemd5; - xen_v2_setmaxvcpu setmaxvcpu; - xen_v2_setvcpumap setvcpumap; - xen_v2d5_setvcpumap setvcpumapd5; - xen_v2_vcpuinfo getvcpuinfo; - xen_v2d5_vcpuinfo getvcpuinfod5; - xen_v2_getvcpumap getvcpumap; - xen_v2d5_getvcpumap getvcpumapd5; - xen_v2_setschedinfo setschedinfo; - xen_v2_getschedinfo getschedinfo; - uint8_t padding[128]; - } u; -}; -typedef struct xen_op_v2_dom xen_op_v2_dom; - - -#ifdef __linux__ -#define XEN_HYPERVISOR_SOCKET "/proc/xen/privcmd" -#define HYPERVISOR_CAPABILITIES "/sys/hypervisor/properties/capabilities" -#elif defined(__sun) -#define XEN_HYPERVISOR_SOCKET "/dev/xen/privcmd" -#else -#error "unsupported platform" -#endif - -#ifndef PROXY -static unsigned long xenHypervisorGetMaxMemory(virDomainPtr domain); -#endif - -#ifndef PROXY -struct xenUnifiedDriver xenHypervisorDriver = { - xenHypervisorOpen, /* open */ - xenHypervisorClose, /* close */ - xenHypervisorGetVersion, /* version */ - NULL, /* hostname */ - NULL, /* nodeGetInfo */ - xenHypervisorGetCapabilities, /* getCapabilities */ - xenHypervisorListDomains, /* listDomains */ - xenHypervisorNumOfDomains, /* numOfDomains */ - NULL, /* domainCreateXML */ - xenHypervisorPauseDomain, /* domainSuspend */ - xenHypervisorResumeDomain, /* domainResume */ - NULL, /* domainShutdown */ - NULL, /* domainReboot */ - xenHypervisorDestroyDomain, /* domainDestroy */ - xenHypervisorDomainGetOSType, /* domainGetOSType */ - xenHypervisorGetMaxMemory, /* domainGetMaxMemory */ - xenHypervisorSetMaxMemory, /* domainSetMaxMemory */ - NULL, /* domainSetMemory */ - xenHypervisorGetDomainInfo, /* domainGetInfo */ - NULL, /* domainSave */ - NULL, /* domainRestore */ - NULL, /* domainCoreDump */ - xenHypervisorSetVcpus, /* domainSetVcpus */ - xenHypervisorPinVcpu, /* domainPinVcpu */ - xenHypervisorGetVcpus, /* domainGetVcpus */ - xenHypervisorGetVcpuMax, /* domainGetMaxVcpus */ - NULL, /* listDefinedDomains */ - NULL, /* numOfDefinedDomains */ - NULL, /* domainCreate */ - NULL, /* domainDefineXML */ - NULL, /* domainUndefine */ - NULL, /* domainAttachDevice */ - NULL, /* domainDetachDevice */ - NULL, /* domainGetAutostart */ - NULL, /* domainSetAutostart */ - xenHypervisorGetSchedulerType, /* domainGetSchedulerType */ - xenHypervisorGetSchedulerParameters, /* domainGetSchedulerParameters */ - xenHypervisorSetSchedulerParameters, /* domainSetSchedulerParameters */ -}; -#endif /* !PROXY */ - -#define virXenError(conn, code, fmt...) \ - if (in_init == 0) \ - virReportErrorHelper(conn, VIR_FROM_XEN, code, __FILE__, \ - __FUNCTION__, __LINE__, fmt) - -#ifndef PROXY - -/** - * virXenErrorFunc: - * @conn: connection, if known - * @error: the error number - * @func: the function failing - * @info: extra information string - * @value: extra information number - * - * Handle an error at the xend daemon interface - */ -static void -virXenErrorFunc(virConnectPtr conn, - virErrorNumber error, const char *func, const char *info, - int value) -{ - char fullinfo[1000]; - const char *errmsg; - - if ((error == VIR_ERR_OK) || (in_init != 0)) - return; - - - errmsg =virErrorMsg(error, info); - if (func != NULL) { - snprintf(fullinfo, 999, "%s: %s", func, info); - fullinfo[999] = 0; - virRaiseError(conn, NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR, - errmsg, fullinfo, NULL, value, 0, errmsg, fullinfo, - value); - } else { - virRaiseError(conn, NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR, - errmsg, info, NULL, value, 0, errmsg, info, - value); - } -} - -#endif /* PROXY */ - -/** - * xenHypervisorDoV0Op: - * @handle: the handle to the Xen hypervisor - * @op: pointer to the hypervisor operation structure - * - * Do an hypervisor operation though the old interface, - * this leads to an hypervisor call through ioctl. - * - * Returns 0 in case of success and -1 in case of error. - */ -static int -xenHypervisorDoV0Op(int handle, xen_op_v0 * op) -{ - int ret; - v0_hypercall_t hc; - - memset(&hc, 0, sizeof(hc)); - op->interface_version = hv_version << 8; - hc.op = __HYPERVISOR_dom0_op; - hc.arg[0] = (unsigned long) op; - - if (lock_pages(op, sizeof(dom0_op_t)) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); - return (-1); - } - - ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); - if (ret < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " ioctl %d", - xen_ioctl_hypercall_cmd); - } - - if (unlock_pages(op, sizeof(dom0_op_t)) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " releasing"); - ret = -1; - } - - if (ret < 0) - return (-1); - - return (0); -} -/** - * xenHypervisorDoV1Op: - * @handle: the handle to the Xen hypervisor - * @op: pointer to the hypervisor operation structure - * - * Do an hypervisor v1 operation, this leads to an hypervisor call through - * ioctl. - * - * Returns 0 in case of success and -1 in case of error. - */ -static int -xenHypervisorDoV1Op(int handle, xen_op_v1* op) -{ - int ret; - hypercall_t hc; - - memset(&hc, 0, sizeof(hc)); - op->interface_version = DOM0_INTERFACE_VERSION; - hc.op = __HYPERVISOR_dom0_op; - hc.arg[0] = (unsigned long) op; - - if (lock_pages(op, sizeof(dom0_op_t)) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); - return (-1); - } - - ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); - if (ret < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " ioctl %d", - xen_ioctl_hypercall_cmd); - } - - if (unlock_pages(op, sizeof(dom0_op_t)) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " releasing"); - ret = -1; - } - - if (ret < 0) - return (-1); - - return (0); -} - -/** - * xenHypervisorDoV2Sys: - * @handle: the handle to the Xen hypervisor - * @op: pointer to the hypervisor operation structure - * - * Do an hypervisor v2 system operation, this leads to an hypervisor - * call through ioctl. - * - * Returns 0 in case of success and -1 in case of error. - */ -static int -xenHypervisorDoV2Sys(int handle, xen_op_v2_sys* op) -{ - int ret; - hypercall_t hc; - - memset(&hc, 0, sizeof(hc)); - op->interface_version = sys_interface_version; - hc.op = __HYPERVISOR_sysctl; - hc.arg[0] = (unsigned long) op; - - if (lock_pages(op, sizeof(dom0_op_t)) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); - return (-1); - } - - ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); - if (ret < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " sys ioctl %d", - xen_ioctl_hypercall_cmd); - } - - if (unlock_pages(op, sizeof(dom0_op_t)) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " releasing"); - ret = -1; - } - - if (ret < 0) - return (-1); - - return (0); -} - -/** - * xenHypervisorDoV2Dom: - * @handle: the handle to the Xen hypervisor - * @op: pointer to the hypervisor domain operation structure - * - * Do an hypervisor v2 domain operation, this leads to an hypervisor - * call through ioctl. - * - * Returns 0 in case of success and -1 in case of error. - */ -static int -xenHypervisorDoV2Dom(int handle, xen_op_v2_dom* op) -{ - int ret; - hypercall_t hc; - - memset(&hc, 0, sizeof(hc)); - op->interface_version = dom_interface_version; - hc.op = __HYPERVISOR_domctl; - hc.arg[0] = (unsigned long) op; - - if (lock_pages(op, sizeof(dom0_op_t)) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); - return (-1); - } - - ret = ioctl(handle, xen_ioctl_hypercall_cmd, (unsigned long) &hc); - if (ret < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " ioctl %d", - xen_ioctl_hypercall_cmd); - } - - if (unlock_pages(op, sizeof(dom0_op_t)) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " releasing"); - ret = -1; - } - - if (ret < 0) - return (-1); - - return (0); -} - -/** - * virXen_getdomaininfolist: - * @handle: the hypervisor handle - * @first_domain: first domain in the range - * @maxids: maximum number of domains to list - * @dominfos: output structures - * - * Do a low level hypercall to list existing domains information - * - * Returns the number of domains or -1 in case of failure - */ -static int -virXen_getdomaininfolist(int handle, int first_domain, int maxids, - xen_getdomaininfolist *dominfos) -{ - int ret = -1; - - if (lock_pages(XEN_GETDOMAININFOLIST_DATA(dominfos), - XEN_GETDOMAININFO_SIZE * maxids) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); - return (-1); - } - if (hypervisor_version > 1) { - xen_op_v2_sys op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_GETDOMAININFOLIST; - - if (sys_interface_version < 3) { - op.u.getdomaininfolist.first_domain = (domid_t) first_domain; - op.u.getdomaininfolist.max_domains = maxids; - op.u.getdomaininfolist.buffer = dominfos->v2; - op.u.getdomaininfolist.num_domains = maxids; - } else { - op.u.getdomaininfolists3.first_domain = (domid_t) first_domain; - op.u.getdomaininfolists3.max_domains = maxids; - op.u.getdomaininfolists3.buffer.v = dominfos->v2d5; - op.u.getdomaininfolists3.num_domains = maxids; - } - ret = xenHypervisorDoV2Sys(handle, &op); - - if (ret == 0) { - if (sys_interface_version < 3) - ret = op.u.getdomaininfolist.num_domains; - else - ret = op.u.getdomaininfolists3.num_domains; - } - } else if (hypervisor_version == 1) { - xen_op_v1 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V1_OP_GETDOMAININFOLIST; - op.u.getdomaininfolist.first_domain = (domid_t) first_domain; - op.u.getdomaininfolist.max_domains = maxids; - op.u.getdomaininfolist.buffer = dominfos->v0; - op.u.getdomaininfolist.num_domains = maxids; - ret = xenHypervisorDoV1Op(handle, &op); - if (ret == 0) - ret = op.u.getdomaininfolist.num_domains; - } else if (hypervisor_version == 0) { - xen_op_v0 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V0_OP_GETDOMAININFOLIST; - op.u.getdomaininfolist.first_domain = (domid_t) first_domain; - op.u.getdomaininfolist.max_domains = maxids; - op.u.getdomaininfolist.buffer = dominfos->v0; - op.u.getdomaininfolist.num_domains = maxids; - ret = xenHypervisorDoV0Op(handle, &op); - if (ret == 0) - ret = op.u.getdomaininfolist.num_domains; - } - if (unlock_pages(XEN_GETDOMAININFOLIST_DATA(dominfos), - XEN_GETDOMAININFO_SIZE * maxids) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " release"); - ret = -1; - } - return(ret); -} - -static int -virXen_getdomaininfo(int handle, int first_domain, - xen_getdomaininfo *dominfo) { - xen_getdomaininfolist dominfos; - - if (hypervisor_version < 2) { - dominfos.v0 = &(dominfo->v0); - } else { - dominfos.v2 = &(dominfo->v2); - } - - return virXen_getdomaininfolist(handle, first_domain, 1, &dominfos); -} - - -#ifndef PROXY -/** - * xenHypervisorGetSchedulerType: - * @domain: pointer to the Xen Hypervisor block - * @nparams:give a number of scheduler parameters. - * - * Do a low level hypercall to get scheduler type - * - * Returns scheduler name or NULL in case of failure - */ -char * -xenHypervisorGetSchedulerType(virDomainPtr domain, int *nparams) -{ - char *schedulertype = NULL; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL)) { - virXenErrorFunc(NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "domain or conn is NULL", 0); - return NULL; - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0) { - virXenErrorFunc(domain->conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "priv->handle invalid", 0); - return NULL; - } - if (domain->id < 0) { - virXenError(domain->conn, VIR_ERR_OPERATION_INVALID, - "%s", _("domain is not running")); - return NULL; - } - - /* - * Support only dom_interface_version >=5 - * (Xen3.1.0 or later) - * TODO: check on Xen 3.0.3 - */ - if (dom_interface_version < 5) { - virXenErrorFunc(domain->conn, VIR_ERR_NO_XEN, __FUNCTION__, - "unsupported in dom interface < 5", 0); - return NULL; - } - - if (hypervisor_version > 1) { - xen_op_v2_sys op; - int ret; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_GETSCHEDULERID; - ret = xenHypervisorDoV2Sys(priv->handle, &op); - if (ret < 0) - return(NULL); - - switch (op.u.getschedulerid.sched_id){ - case XEN_SCHEDULER_SEDF: - schedulertype = strdup("sedf"); - if (nparams) - *nparams = 6; - break; - case XEN_SCHEDULER_CREDIT: - schedulertype = strdup("credit"); - if (nparams) - *nparams = 2; - break; - default: - break; - } - } - - return schedulertype; -} - -static const char *str_weight = "weight"; -static const char *str_cap = "cap"; - -/** - * xenHypervisorGetSchedulerParameters: - * @domain: pointer to the Xen Hypervisor block - * @params: pointer to scheduler parameters. - * This memory area should be allocated before calling. - * @nparams:this parameter should be same as - * a given number of scheduler parameters. - * from xenHypervisorGetSchedulerType(). - * - * Do a low level hypercall to get scheduler parameters - * - * Returns 0 or -1 in case of failure - */ -int -xenHypervisorGetSchedulerParameters(virDomainPtr domain, - virSchedParameterPtr params, int *nparams) -{ - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL)) { - virXenErrorFunc(NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "domain or conn is NULL", 0); - return -1; - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0) { - virXenErrorFunc(domain->conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "priv->handle invalid", 0); - return -1; - } - if (domain->id < 0) { - virXenError(domain->conn, VIR_ERR_OPERATION_INVALID, - "%s", _("domain is not running")); - return -1; - } - - /* - * Support only dom_interface_version >=5 - * (Xen3.1.0 or later) - * TODO: check on Xen 3.0.3 - */ - if (dom_interface_version < 5) { - virXenErrorFunc(domain->conn, VIR_ERR_NO_XEN, __FUNCTION__, - "unsupported in dom interface < 5", 0); - return -1; - } - - if (hypervisor_version > 1) { - xen_op_v2_sys op_sys; - xen_op_v2_dom op_dom; - int ret; - - memset(&op_sys, 0, sizeof(op_sys)); - op_sys.cmd = XEN_V2_OP_GETSCHEDULERID; - ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); - if (ret < 0) - return -1; - - switch (op_sys.u.getschedulerid.sched_id){ - case XEN_SCHEDULER_SEDF: - /* TODO: Implement for Xen/SEDF */ - TODO - return(-1); - case XEN_SCHEDULER_CREDIT: - if (*nparams < 2) - return(-1); - memset(&op_dom, 0, sizeof(op_dom)); - op_dom.cmd = XEN_V2_OP_SCHEDULER; - op_dom.domain = (domid_t) domain->id; - op_dom.u.getschedinfo.sched_id = XEN_SCHEDULER_CREDIT; - op_dom.u.getschedinfo.cmd = XEN_DOMCTL_SCHEDOP_getinfo; - ret = xenHypervisorDoV2Dom(priv->handle, &op_dom); - if (ret < 0) - return(-1); - - strncpy (params[0].field, str_weight, VIR_DOMAIN_SCHED_FIELD_LENGTH); - params[0].field[VIR_DOMAIN_SCHED_FIELD_LENGTH-1] = '\0'; - params[0].type = VIR_DOMAIN_SCHED_FIELD_UINT; - params[0].value.ui = op_dom.u.getschedinfo.u.credit.weight; - - strncpy (params[1].field, str_cap, VIR_DOMAIN_SCHED_FIELD_LENGTH); - params[1].field[VIR_DOMAIN_SCHED_FIELD_LENGTH-1] = '\0'; - params[1].type = VIR_DOMAIN_SCHED_FIELD_UINT; - params[1].value.ui = op_dom.u.getschedinfo.u.credit.cap; - - *nparams = 2; - break; - default: - virXenErrorFunc(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "Unknown scheduler", op_sys.u.getschedulerid.sched_id); - return -1; - } - } - - return 0; -} - -/** - * xenHypervisorSetSchedulerParameters: - * @domain: pointer to the Xen Hypervisor block - * @nparams:give a number of scheduler setting parameters . - * - * Do a low level hypercall to set scheduler parameters - * - * Returns 0 or -1 in case of failure - */ -int -xenHypervisorSetSchedulerParameters(virDomainPtr domain, - virSchedParameterPtr params, int nparams) -{ - int i; - unsigned int val; - xenUnifiedPrivatePtr priv; - char buf[256]; - - if ((domain == NULL) || (domain->conn == NULL)) { - virXenErrorFunc (NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "domain or conn is NULL", 0); - return -1; - } - - if ((nparams == 0) || (params == NULL)) { - virXenErrorFunc (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "Noparameters given", 0); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0) { - virXenErrorFunc(domain->conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "priv->handle invalid", 0); - return -1; - } - if (domain->id < 0) { - virXenError(domain->conn, VIR_ERR_OPERATION_INVALID, - "%s", _("domain is not running")); - return -1; - } - - /* - * Support only dom_interface_version >=5 - * (Xen3.1.0 or later) - * TODO: check on Xen 3.0.3 - */ - if (dom_interface_version < 5) { - virXenErrorFunc(domain->conn, VIR_ERR_NO_XEN, __FUNCTION__, - "unsupported in dom interface < 5", 0); - return -1; - } - - if (hypervisor_version > 1) { - xen_op_v2_sys op_sys; - xen_op_v2_dom op_dom; - int ret; - - memset(&op_sys, 0, sizeof(op_sys)); - op_sys.cmd = XEN_V2_OP_GETSCHEDULERID; - ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); - if (ret == -1) return -1; - - switch (op_sys.u.getschedulerid.sched_id){ - case XEN_SCHEDULER_SEDF: - /* TODO: Implement for Xen/SEDF */ - TODO - return(-1); - case XEN_SCHEDULER_CREDIT: { - memset(&op_dom, 0, sizeof(op_dom)); - op_dom.cmd = XEN_V2_OP_SCHEDULER; - op_dom.domain = (domid_t) domain->id; - op_dom.u.getschedinfo.sched_id = XEN_SCHEDULER_CREDIT; - op_dom.u.getschedinfo.cmd = XEN_DOMCTL_SCHEDOP_putinfo; - - /* - * credit scheduler parameters - * following values do not change the parameters - */ - op_dom.u.getschedinfo.u.credit.weight = 0; - op_dom.u.getschedinfo.u.credit.cap = (uint16_t)~0U; - - for (i = 0; i < nparams; i++) { - memset(&buf, 0, sizeof(buf)); - if (STREQ (params[i].field, str_weight) && - params[i].type == VIR_DOMAIN_SCHED_FIELD_UINT) { - val = params[i].value.ui; - if ((val < 1) || (val > USHRT_MAX)) { - snprintf(buf, sizeof(buf), _("Credit scheduler weight parameter (%d) is out of range (1-65535)"), val); - virXenErrorFunc (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, buf, val); - return(-1); - } - op_dom.u.getschedinfo.u.credit.weight = val; - } else if (STREQ (params[i].field, str_cap) && - params[i].type == VIR_DOMAIN_SCHED_FIELD_UINT) { - val = params[i].value.ui; - if (val > USHRT_MAX) { - snprintf(buf, sizeof(buf), _("Credit scheduler cap parameter (%d) is out of range (0-65535)"), val); - virXenErrorFunc (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, buf, val); - return(-1); - } - op_dom.u.getschedinfo.u.credit.cap = val; - } else { - virXenErrorFunc (domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "Credit scheduler accepts 'cap' and 'weight' integer parameters", - 0); - return(-1); - } - } - - ret = xenHypervisorDoV2Dom(priv->handle, &op_dom); - if (ret < 0) - return -1; - break; - } - default: - virXenErrorFunc(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "Unknown scheduler", op_sys.u.getschedulerid.sched_id); - return -1; - } - } - - return 0; -} - - -int -xenHypervisorDomainBlockStats (virDomainPtr dom, - const char *path, - struct _virDomainBlockStats *stats) -{ -#ifdef __linux__ - xenUnifiedPrivatePtr priv; - int ret; - - priv = (xenUnifiedPrivatePtr) dom->conn->privateData; - xenUnifiedLock(priv); - /* Need to lock because it hits the xenstore handle :-( */ - ret = xenLinuxDomainBlockStats (priv, dom, path, stats); - xenUnifiedUnlock(priv); - return ret; -#else - virXenErrorFunc (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__, - "block statistics not supported on this platform", - dom->id); - return -1; -#endif -} - -/* Paths have the form vif. (this interface checks that - * is the real domain ID and returns an error if not). - * - * In future we may allow you to query bridge stats (virbrX or - * xenbrX), but that will probably be through a separate - * virNetwork interface, as yet not decided. - */ -int -xenHypervisorDomainInterfaceStats (virDomainPtr dom, - const char *path, - struct _virDomainInterfaceStats *stats) -{ -#ifdef __linux__ - int rqdomid, device; - - /* Verify that the vif requested is one belonging to the current - * domain. - */ - if (sscanf (path, "vif%d.%d", &rqdomid, &device) != 2) { - virXenErrorFunc (dom->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "invalid path, should be vif..", 0); - return -1; - } - if (rqdomid != dom->id) { - virXenErrorFunc (dom->conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "invalid path, vif should match this domain ID", 0); - return -1; - } - - return linuxDomainInterfaceStats (dom->conn, path, stats); -#else - virXenErrorFunc (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__, - "/proc/net/dev: Interface not found", 0); - return -1; -#endif -} - -/** - * virXen_pausedomain: - * @handle: the hypervisor handle - * @id: the domain id - * - * Do a low level hypercall to pause the domain - * - * Returns 0 or -1 in case of failure - */ -static int -virXen_pausedomain(int handle, int id) -{ - int ret = -1; - - if (hypervisor_version > 1) { - xen_op_v2_dom op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_PAUSEDOMAIN; - op.domain = (domid_t) id; - ret = xenHypervisorDoV2Dom(handle, &op); - } else if (hypervisor_version == 1) { - xen_op_v1 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V1_OP_PAUSEDOMAIN; - op.u.domain.domain = (domid_t) id; - ret = xenHypervisorDoV1Op(handle, &op); - } else if (hypervisor_version == 0) { - xen_op_v0 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V0_OP_PAUSEDOMAIN; - op.u.domain.domain = (domid_t) id; - ret = xenHypervisorDoV0Op(handle, &op); - } - return(ret); -} - -/** - * virXen_unpausedomain: - * @handle: the hypervisor handle - * @id: the domain id - * - * Do a low level hypercall to unpause the domain - * - * Returns 0 or -1 in case of failure - */ -static int -virXen_unpausedomain(int handle, int id) -{ - int ret = -1; - - if (hypervisor_version > 1) { - xen_op_v2_dom op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_UNPAUSEDOMAIN; - op.domain = (domid_t) id; - ret = xenHypervisorDoV2Dom(handle, &op); - } else if (hypervisor_version == 1) { - xen_op_v1 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V1_OP_UNPAUSEDOMAIN; - op.u.domain.domain = (domid_t) id; - ret = xenHypervisorDoV1Op(handle, &op); - } else if (hypervisor_version == 0) { - xen_op_v0 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V0_OP_UNPAUSEDOMAIN; - op.u.domain.domain = (domid_t) id; - ret = xenHypervisorDoV0Op(handle, &op); - } - return(ret); -} - -/** - * virXen_destroydomain: - * @handle: the hypervisor handle - * @id: the domain id - * - * Do a low level hypercall to destroy the domain - * - * Returns 0 or -1 in case of failure - */ -static int -virXen_destroydomain(int handle, int id) -{ - int ret = -1; - - if (hypervisor_version > 1) { - xen_op_v2_dom op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_DESTROYDOMAIN; - op.domain = (domid_t) id; - ret = xenHypervisorDoV2Dom(handle, &op); - } else if (hypervisor_version == 1) { - xen_op_v1 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V1_OP_DESTROYDOMAIN; - op.u.domain.domain = (domid_t) id; - ret = xenHypervisorDoV1Op(handle, &op); - } else if (hypervisor_version == 0) { - xen_op_v0 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V0_OP_DESTROYDOMAIN; - op.u.domain.domain = (domid_t) id; - ret = xenHypervisorDoV0Op(handle, &op); - } - return(ret); -} - -/** - * virXen_setmaxmem: - * @handle: the hypervisor handle - * @id: the domain id - * @memory: the amount of memory in kilobytes - * - * Do a low level hypercall to change the max memory amount - * - * Returns 0 or -1 in case of failure - */ -static int -virXen_setmaxmem(int handle, int id, unsigned long memory) -{ - int ret = -1; - - if (hypervisor_version > 1) { - xen_op_v2_dom op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_SETMAXMEM; - op.domain = (domid_t) id; - if (dom_interface_version < 5) - op.u.setmaxmem.maxmem = memory; - else - op.u.setmaxmemd5.maxmem = memory; - ret = xenHypervisorDoV2Dom(handle, &op); - } else if (hypervisor_version == 1) { - xen_op_v1 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V1_OP_SETMAXMEM; - op.u.setmaxmem.domain = (domid_t) id; - op.u.setmaxmem.maxmem = memory; - ret = xenHypervisorDoV1Op(handle, &op); - } else if (hypervisor_version == 0) { - xen_op_v0 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V0_OP_SETMAXMEM; - op.u.setmaxmem.domain = (domid_t) id; - op.u.setmaxmem.maxmem = memory; - ret = xenHypervisorDoV0Op(handle, &op); - } - return(ret); -} - -/** - * virXen_setmaxvcpus: - * @handle: the hypervisor handle - * @id: the domain id - * @vcpus: the numbers of vcpus - * - * Do a low level hypercall to change the max vcpus amount - * - * Returns 0 or -1 in case of failure - */ -static int -virXen_setmaxvcpus(int handle, int id, unsigned int vcpus) -{ - int ret = -1; - - if (hypervisor_version > 1) { - xen_op_v2_dom op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_SETMAXVCPU; - op.domain = (domid_t) id; - op.u.setmaxvcpu.maxvcpu = vcpus; - ret = xenHypervisorDoV2Dom(handle, &op); - } else if (hypervisor_version == 1) { - xen_op_v1 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V1_OP_SETMAXVCPU; - op.u.setmaxvcpu.domain = (domid_t) id; - op.u.setmaxvcpu.maxvcpu = vcpus; - ret = xenHypervisorDoV1Op(handle, &op); - } else if (hypervisor_version == 0) { - xen_op_v0 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V0_OP_SETMAXVCPU; - op.u.setmaxvcpu.domain = (domid_t) id; - op.u.setmaxvcpu.maxvcpu = vcpus; - ret = xenHypervisorDoV0Op(handle, &op); - } - return(ret); -} - -/** - * virXen_setvcpumap: - * @handle: the hypervisor handle - * @id: the domain id - * @vcpu: the vcpu to map - * @cpumap: the bitmap for this vcpu - * @maplen: the size of the bitmap in bytes - * - * Do a low level hypercall to change the pinning for vcpu - * - * Returns 0 or -1 in case of failure - */ -static int -virXen_setvcpumap(int handle, int id, unsigned int vcpu, - unsigned char * cpumap, int maplen) -{ - int ret = -1; - unsigned char *new = NULL; - unsigned char *bitmap = NULL; - uint32_t nr_cpus; - - if (hypervisor_version > 1) { - xen_op_v2_dom op; - - if (lock_pages(cpumap, maplen) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); - return (-1); - } - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_SETVCPUMAP; - op.domain = (domid_t) id; - - /* The allocated memory to cpumap must be 'sizeof(uint64_t)' byte * - * for Xen, and also nr_cpus must be 'sizeof(uint64_t) * 8' */ - if (maplen < 8) { - if (VIR_ALLOC_N(new, sizeof(uint64_t)) < 0) { - virReportOOMError(NULL); - return (-1); - } - memcpy(new, cpumap, maplen); - bitmap = new; - nr_cpus = sizeof(uint64_t) * 8; - } else { - bitmap = cpumap; - nr_cpus = maplen * 8; - } - - if (dom_interface_version < 5) { - op.u.setvcpumap.vcpu = vcpu; - op.u.setvcpumap.cpumap.bitmap = bitmap; - op.u.setvcpumap.cpumap.nr_cpus = nr_cpus; - } else { - op.u.setvcpumapd5.vcpu = vcpu; - op.u.setvcpumapd5.cpumap.bitmap.v = bitmap; - op.u.setvcpumapd5.cpumap.nr_cpus = nr_cpus; - } - ret = xenHypervisorDoV2Dom(handle, &op); - VIR_FREE(new); - - if (unlock_pages(cpumap, maplen) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " release"); - ret = -1; - } - } else { - cpumap_t xen_cpumap; /* limited to 64 CPUs in old hypervisors */ - uint64_t *pm = &xen_cpumap; - int j; - - if ((maplen > (int)sizeof(cpumap_t)) || (sizeof(cpumap_t) & 7)) - return (-1); - - memset(pm, 0, sizeof(cpumap_t)); - for (j = 0; j < maplen; j++) - *(pm + (j / 8)) |= cpumap[j] << (8 * (j & 7)); - - if (hypervisor_version == 1) { - xen_op_v1 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V1_OP_SETVCPUMAP; - op.u.setvcpumap.domain = (domid_t) id; - op.u.setvcpumap.vcpu = vcpu; - op.u.setvcpumap.cpumap = xen_cpumap; - ret = xenHypervisorDoV1Op(handle, &op); - } else if (hypervisor_version == 0) { - xen_op_v0 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V0_OP_SETVCPUMAP; - op.u.setvcpumap.domain = (domid_t) id; - op.u.setvcpumap.vcpu = vcpu; - op.u.setvcpumap.cpumap = xen_cpumap; - ret = xenHypervisorDoV0Op(handle, &op); - } - } - return(ret); -} -#endif /* !PROXY*/ - -/** - * virXen_getvcpusinfo: - * @handle: the hypervisor handle - * @id: the domain id - * @vcpu: the vcpu to map - * @cpumap: the bitmap for this vcpu - * @maplen: the size of the bitmap in bytes - * - * Do a low level hypercall to change the pinning for vcpu - * - * Returns 0 or -1 in case of failure - */ -static int -virXen_getvcpusinfo(int handle, int id, unsigned int vcpu, virVcpuInfoPtr ipt, - unsigned char *cpumap, int maplen) -{ - int ret = -1; - - if (hypervisor_version > 1) { - xen_op_v2_dom op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_GETVCPUINFO; - op.domain = (domid_t) id; - if (dom_interface_version < 5) - op.u.getvcpuinfo.vcpu = (uint16_t) vcpu; - else - op.u.getvcpuinfod5.vcpu = (uint16_t) vcpu; - ret = xenHypervisorDoV2Dom(handle, &op); - - if (ret < 0) - return(-1); - ipt->number = vcpu; - if (dom_interface_version < 5) { - if (op.u.getvcpuinfo.online) { - if (op.u.getvcpuinfo.running) - ipt->state = VIR_VCPU_RUNNING; - if (op.u.getvcpuinfo.blocked) - ipt->state = VIR_VCPU_BLOCKED; - } else - ipt->state = VIR_VCPU_OFFLINE; - - ipt->cpuTime = op.u.getvcpuinfo.cpu_time; - ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; - } else { - if (op.u.getvcpuinfod5.online) { - if (op.u.getvcpuinfod5.running) - ipt->state = VIR_VCPU_RUNNING; - if (op.u.getvcpuinfod5.blocked) - ipt->state = VIR_VCPU_BLOCKED; - } else - ipt->state = VIR_VCPU_OFFLINE; - - ipt->cpuTime = op.u.getvcpuinfod5.cpu_time; - ipt->cpu = op.u.getvcpuinfod5.online ? (int)op.u.getvcpuinfod5.cpu : -1; - } - if ((cpumap != NULL) && (maplen > 0)) { - if (lock_pages(cpumap, maplen) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " locking"); - return (-1); - } - memset(cpumap, 0, maplen); - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V2_OP_GETVCPUMAP; - op.domain = (domid_t) id; - if (dom_interface_version < 5) { - op.u.getvcpumap.vcpu = vcpu; - op.u.getvcpumap.cpumap.bitmap = cpumap; - op.u.getvcpumap.cpumap.nr_cpus = maplen * 8; - } else { - op.u.getvcpumapd5.vcpu = vcpu; - op.u.getvcpumapd5.cpumap.bitmap.v = cpumap; - op.u.getvcpumapd5.cpumap.nr_cpus = maplen * 8; - } - ret = xenHypervisorDoV2Dom(handle, &op); - if (unlock_pages(cpumap, maplen) < 0) { - virXenError(NULL, VIR_ERR_XEN_CALL, " release"); - ret = -1; - } - } - } else { - int mapl = maplen; - int cpu; - - if (maplen > (int)sizeof(cpumap_t)) - mapl = (int)sizeof(cpumap_t); - - if (hypervisor_version == 1) { - xen_op_v1 op; - - memset(&op, 0, sizeof(op)); - op.cmd = XEN_V1_OP_GETVCPUINFO; - op.u.getvcpuinfo.domain = (domid_t) id; - op.u.getvcpuinfo.vcpu = vcpu; - ret = xenHypervisorDoV1Op(handle, &op); - if (ret < 0) - return(-1); - ipt->number = vcpu; - if (op.u.getvcpuinfo.online) { - if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING; - if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED; - } - else ipt->state = VIR_VCPU_OFFLINE; - ipt->cpuTime = op.u.getvcpuinfo.cpu_time; - ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; - if ((cpumap != NULL) && (maplen > 0)) { - for (cpu = 0; cpu < (mapl * 8); cpu++) { - if (op.u.getvcpuinfo.cpumap & ((uint64_t)1<number = vcpu; - if (op.u.getvcpuinfo.online) { - if (op.u.getvcpuinfo.running) ipt->state = VIR_VCPU_RUNNING; - if (op.u.getvcpuinfo.blocked) ipt->state = VIR_VCPU_BLOCKED; - } - else ipt->state = VIR_VCPU_OFFLINE; - ipt->cpuTime = op.u.getvcpuinfo.cpu_time; - ipt->cpu = op.u.getvcpuinfo.online ? (int)op.u.getvcpuinfo.cpu : -1; - if ((cpumap != NULL) && (maplen > 0)) { - for (cpu = 0; cpu < (mapl * 8); cpu++) { - if (op.u.getvcpuinfo.cpumap & ((uint64_t)1<privateData; - - if (initialized == 0) - if (xenHypervisorInit() == -1) - return -1; - - priv->handle = -1; - - ret = open(XEN_HYPERVISOR_SOCKET, O_RDWR); - if (ret < 0) { - virXenError(conn, VIR_ERR_NO_XEN, "%s", XEN_HYPERVISOR_SOCKET); - return (-1); - } - - priv->handle = ret; - - return(0); -} - -/** - * xenHypervisorClose: - * @conn: pointer to the connection block - * - * Close the connection to the Xen hypervisor. - * - * Returns 0 in case of success or -1 in case of error. - */ -int -xenHypervisorClose(virConnectPtr conn) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if (conn == NULL) - return (-1); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (priv->handle < 0) - return -1; - - ret = close(priv->handle); - if (ret < 0) - return (-1); - - return (0); -} - - -/** - * xenHypervisorGetVersion: - * @conn: pointer to the connection block - * @hvVer: where to store the version - * - * Call the hypervisor to extracts his own internal API version - * - * Returns 0 in case of success, -1 in case of error - */ -int -xenHypervisorGetVersion(virConnectPtr conn, unsigned long *hvVer) -{ - xenUnifiedPrivatePtr priv; - - if (conn == NULL) - return -1; - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->handle < 0 || hvVer == NULL) - return (-1); - *hvVer = (hv_version >> 16) * 1000000 + (hv_version & 0xFFFF) * 1000; - return(0); -} - -struct guest_arch { - const char *model; - int bits; - int hvm; - int pae; - int nonpae; - int ia64_be; -}; - - -static virCapsPtr -xenHypervisorBuildCapabilities(virConnectPtr conn, - const char *hostmachine, - int host_pae, - const char *hvm_type, - struct guest_arch *guest_archs, - int nr_guest_archs) { - virCapsPtr caps; - int i; - int hv_major = hv_version >> 16; - int hv_minor = hv_version & 0xFFFF; - - if ((caps = virCapabilitiesNew(hostmachine, 1, 1)) == NULL) - goto no_memory; - - virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x00, 0x16, 0x3e }); - - if (hvm_type && STRNEQ(hvm_type, "") && - virCapabilitiesAddHostFeature(caps, hvm_type) < 0) - goto no_memory; - if (host_pae && - virCapabilitiesAddHostFeature(caps, "pae") < 0) - goto no_memory; - - - if (virCapabilitiesAddHostMigrateTransport(caps, - "xenmigr") < 0) - goto no_memory; - - - if (sys_interface_version >= SYS_IFACE_MIN_VERS_NUMA) { - if (xenDaemonNodeGetTopology(conn, caps) != 0) { - virCapabilitiesFree(caps); - return NULL; - } - } - - for (i = 0; i < nr_guest_archs; ++i) { - virCapsGuestPtr guest; - char const *const xen_machines[] = {guest_archs[i].hvm ? "xenfv" : "xenpv"}; - virCapsGuestMachinePtr *machines; - - if ((machines = virCapabilitiesAllocMachines(xen_machines, 1)) == NULL) - goto no_memory; - - if ((guest = virCapabilitiesAddGuest(caps, - guest_archs[i].hvm ? "hvm" : "xen", - guest_archs[i].model, - guest_archs[i].bits, - (STREQ(hostmachine, "x86_64") ? - "/usr/lib64/xen/bin/qemu-dm" : - "/usr/lib/xen/bin/qemu-dm"), - (guest_archs[i].hvm ? - "/usr/lib/xen/boot/hvmloader" : - NULL), - 1, - machines)) == NULL) { - virCapabilitiesFreeMachines(machines, 1); - goto no_memory; - } - machines = NULL; - - if (virCapabilitiesAddGuestDomain(guest, - "xen", - NULL, - NULL, - 0, - NULL) == NULL) - goto no_memory; - - if (guest_archs[i].pae && - virCapabilitiesAddGuestFeature(guest, - "pae", - 1, - 0) == NULL) - goto no_memory; - - if (guest_archs[i].nonpae && - virCapabilitiesAddGuestFeature(guest, - "nonpae", - 1, - 0) == NULL) - goto no_memory; - - if (guest_archs[i].ia64_be && - virCapabilitiesAddGuestFeature(guest, - "ia64_be", - 1, - 0) == NULL) - goto no_memory; - - if (guest_archs[i].hvm) { - if (virCapabilitiesAddGuestFeature(guest, - "acpi", - 1, 1) == NULL) - goto no_memory; - - // In Xen 3.1.0, APIC is always on and can't be toggled - if (virCapabilitiesAddGuestFeature(guest, - "apic", - 1, - (hv_major > 3 && - hv_minor > 0 ? - 0 : 1)) == NULL) - goto no_memory; - } - } - - return caps; - - no_memory: - virCapabilitiesFree(caps); - return NULL; -} - -#ifdef __sun - -static int -get_cpu_flags(virConnectPtr conn, const char **hvm, int *pae, int *longmode) -{ - struct { - uint32_t r_eax, r_ebx, r_ecx, r_edx; - } regs; - - char tmpbuf[20]; - int ret = 0; - int fd; - - /* returns -1, errno 22 if in 32-bit mode */ - *longmode = (sysinfo(SI_ARCHITECTURE_64, tmpbuf, sizeof(tmpbuf)) != -1); - - if ((fd = open("/dev/cpu/self/cpuid", O_RDONLY)) == -1 || - pread(fd, ®s, sizeof(regs), 0) != sizeof(regs)) { - char ebuf[1024]; - virXenError(conn, VIR_ERR_SYSTEM_ERROR, - "couldn't read CPU flags: %s", virStrerror(errno, ebuf, sizeof ebuf)); - goto out; - } - - *pae = 0; - *hvm = ""; - - if (STREQLEN((const char *)®s.r_ebx, "AuthcAMDenti", 12)) { - if (pread(fd, ®s, sizeof (regs), 0x80000001) == sizeof (regs)) { - /* Read secure virtual machine bit (bit 2 of ECX feature ID) */ - if ((regs.r_ecx >> 2) & 1) { - *hvm = "svm"; - } - if ((regs.r_edx >> 6) & 1) - *pae = 1; - } - } else if (STREQLEN((const char *)®s.r_ebx, "GenuntelineI", 12)) { - if (pread(fd, ®s, sizeof (regs), 0x00000001) == sizeof (regs)) { - /* Read VMXE feature bit (bit 5 of ECX feature ID) */ - if ((regs.r_ecx >> 5) & 1) - *hvm = "vmx"; - if ((regs.r_edx >> 6) & 1) - *pae = 1; - } - } - - ret = 1; - -out: - if (fd != -1) - close(fd); - return ret; -} - -static virCapsPtr -xenHypervisorMakeCapabilitiesSunOS(virConnectPtr conn) -{ - struct guest_arch guest_arches[32]; - int i = 0; - virCapsPtr caps = NULL; - struct utsname utsname; - int pae, longmode; - const char *hvm; - - if (!get_cpu_flags(conn, &hvm, &pae, &longmode)) - return NULL; - - /* Really, this never fails - look at the man-page. */ - uname (&utsname); - - guest_arches[i].model = "i686"; - guest_arches[i].bits = 32; - guest_arches[i].hvm = 0; - guest_arches[i].pae = pae; - guest_arches[i].nonpae = !pae; - guest_arches[i].ia64_be = 0; - i++; - - if (longmode) { - guest_arches[i].model = "x86_64"; - guest_arches[i].bits = 64; - guest_arches[i].hvm = 0; - guest_arches[i].pae = 0; - guest_arches[i].nonpae = 0; - guest_arches[i].ia64_be = 0; - i++; - } - - if (hvm[0] != '\0') { - guest_arches[i].model = "i686"; - guest_arches[i].bits = 32; - guest_arches[i].hvm = 1; - guest_arches[i].pae = pae; - guest_arches[i].nonpae = 1; - guest_arches[i].ia64_be = 0; - i++; - - if (longmode) { - guest_arches[i].model = "x86_64"; - guest_arches[i].bits = 64; - guest_arches[i].hvm = 1; - guest_arches[i].pae = 0; - guest_arches[i].nonpae = 0; - guest_arches[i].ia64_be = 0; - i++; - } - } - - if ((caps = xenHypervisorBuildCapabilities(conn, - utsname.machine, - pae, hvm, - guest_arches, i)) == NULL) - virReportOOMError(NULL); - - return caps; -} - -#endif /* __sun */ - -/** - * xenHypervisorMakeCapabilitiesInternal: - * @conn: pointer to the connection block - * @cpuinfo: file handle containing /proc/cpuinfo data, or NULL - * @capabilities: file handle containing /sys/hypervisor/properties/capabilities data, or NULL - * - * Return the capabilities of this hypervisor. - */ -virCapsPtr -xenHypervisorMakeCapabilitiesInternal(virConnectPtr conn, - const char *hostmachine, - FILE *cpuinfo, FILE *capabilities) -{ - char line[1024], *str, *token; - regmatch_t subs[4]; - char *saveptr = NULL; - int i; - - char hvm_type[4] = ""; /* "vmx" or "svm" (or "" if not in CPU). */ - int host_pae = 0; - struct guest_arch guest_archs[32]; - int nr_guest_archs = 0; - virCapsPtr caps = NULL; - - memset(guest_archs, 0, sizeof(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) - */ - if (cpuinfo) { - while (fgets (line, sizeof line, cpuinfo)) { - if (regexec (&flags_hvm_rec, line, sizeof(subs)/sizeof(regmatch_t), subs, 0) == 0 - && subs[0].rm_so != -1) { - strncpy (hvm_type, - &line[subs[1].rm_so], subs[1].rm_eo-subs[1].rm_so+1); - hvm_type[subs[1].rm_eo-subs[1].rm_so] = '\0'; - } else if (regexec (&flags_pae_rec, line, 0, NULL, 0) == 0) - host_pae = 1; - } - } - - /* 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 - */ - - /* Expecting one line in this file - ignore any more. */ - if ((capabilities) && (fgets (line, sizeof line, capabilities))) { - /* 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 < sizeof guest_archs / sizeof guest_archs[0] - && (token = strtok_r (str, " ", &saveptr)) != NULL; - str = NULL) { - - if (regexec (&xen_cap_rec, token, sizeof subs / sizeof subs[0], - subs, 0) == 0) { - int hvm = STRPREFIX(&token[subs[1].rm_so], "hvm"); - const char *model; - int bits, pae = 0, nonpae = 0, ia64_be = 0; - - if (STRPREFIX(&token[subs[2].rm_so], "x86_32")) { - model = "i686"; - bits = 32; - if (subs[3].rm_so != -1 && - STRPREFIX(&token[subs[3].rm_so], "p")) - pae = 1; - else - nonpae = 1; - } - else if (STRPREFIX(&token[subs[2].rm_so], "x86_64")) { - model = "x86_64"; - bits = 64; - } - else if (STRPREFIX(&token[subs[2].rm_so], "ia64")) { - model = "ia64"; - bits = 64; - if (subs[3].rm_so != -1 && - STRPREFIX(&token[subs[3].rm_so], "be")) - ia64_be = 1; - } - else if (STRPREFIX(&token[subs[2].rm_so], "powerpc64")) { - model = "ppc64"; - bits = 64; - } else { - /* XXX surely no other Xen archs exist */ - continue; - } - - /* Search for existing matching (model,hvm) tuple */ - for (i = 0 ; i < nr_guest_archs ; i++) { - if (STREQ(guest_archs[i].model, model) && - guest_archs[i].hvm == hvm) { - break; - } - } - - /* Too many arch flavours - highly unlikely ! */ - if (i >= ARRAY_CARDINALITY(guest_archs)) - continue; - /* Didn't find a match, so create a new one */ - if (i == nr_guest_archs) - nr_guest_archs++; - - guest_archs[i].model = model; - guest_archs[i].bits = bits; - guest_archs[i].hvm = hvm; - - /* Careful not to overwrite a previous positive - setting with a negative one here - some archs - can do both pae & non-pae, but Xen reports - separately capabilities so we're merging archs */ - if (pae) - guest_archs[i].pae = pae; - if (nonpae) - guest_archs[i].nonpae = nonpae; - if (ia64_be) - guest_archs[i].ia64_be = ia64_be; - } - } - } - - if ((caps = xenHypervisorBuildCapabilities(conn, - hostmachine, - host_pae, - hvm_type, - guest_archs, - nr_guest_archs)) == NULL) - goto no_memory; - - return caps; - - no_memory: - virReportOOMError(NULL); - virCapabilitiesFree(caps); - return NULL; -} - -/** - * xenHypervisorMakeCapabilities: - * - * Return the capabilities of this hypervisor. - */ -virCapsPtr -xenHypervisorMakeCapabilities(virConnectPtr conn) -{ -#ifdef __sun - return xenHypervisorMakeCapabilitiesSunOS(conn); -#else - virCapsPtr caps; - FILE *cpuinfo, *capabilities; - struct utsname utsname; - - /* Really, this never fails - look at the man-page. */ - uname (&utsname); - - cpuinfo = fopen ("/proc/cpuinfo", "r"); - if (cpuinfo == NULL) { - if (errno != ENOENT) { - virReportSystemError(conn, errno, - _("cannot read file %s"), - "/proc/cpuinfo"); - return NULL; - } - } - - capabilities = fopen ("/sys/hypervisor/properties/capabilities", "r"); - if (capabilities == NULL) { - if (errno != ENOENT) { - fclose(cpuinfo); - virReportSystemError(conn, errno, - _("cannot read file %s"), - "/sys/hypervisor/properties/capabilities"); - return NULL; - } - } - - caps = xenHypervisorMakeCapabilitiesInternal(conn, - utsname.machine, - cpuinfo, - capabilities); - - if (cpuinfo) - fclose(cpuinfo); - if (capabilities) - fclose(capabilities); - - return caps; -#endif /* __sun */ -} - - - -/** - * xenHypervisorGetCapabilities: - * @conn: pointer to the connection block - * - * Return the capabilities of this hypervisor. - */ -char * -xenHypervisorGetCapabilities (virConnectPtr conn) -{ - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - char *xml; - - if (!(xml = virCapabilitiesFormatXML(priv->caps))) { - virReportOOMError(conn); - return NULL; - } - - return xml; -} - - -/** - * xenHypervisorNumOfDomains: - * @conn: pointer to the connection block - * - * Provides the number of active domains. - * - * Returns the number of domain found or -1 in case of error - */ -int -xenHypervisorNumOfDomains(virConnectPtr conn) -{ - xen_getdomaininfolist dominfos; - int ret, nbids; - static int last_maxids = 2; - int maxids = last_maxids; - xenUnifiedPrivatePtr priv; - - if (conn == NULL) - return -1; - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->handle < 0) - return (-1); - - retry: - if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) { - virReportOOMError(conn); - return(-1); - } - - XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids); - - ret = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos); - - XEN_GETDOMAININFOLIST_FREE(dominfos); - - if (ret < 0) - return (-1); - - nbids = ret; - /* Can't possibly have more than 65,000 concurrent guests - * so limit how many times we try, to avoid increasing - * without bound & thus allocating all of system memory ! - * XXX I'll regret this comment in a few years time ;-) - */ - if (nbids == maxids) { - if (maxids < 65000) { - last_maxids *= 2; - maxids *= 2; - goto retry; - } - nbids = -1; - } - if ((nbids < 0) || (nbids > maxids)) - return(-1); - return(nbids); -} - -/** - * xenHypervisorListDomains: - * @conn: pointer to the connection block - * @ids: array to collect the list of IDs of active domains - * @maxids: size of @ids - * - * Collect the list of active domains, and store their ID in @maxids - * - * Returns the number of domain found or -1 in case of error - */ -int -xenHypervisorListDomains(virConnectPtr conn, int *ids, int maxids) -{ - xen_getdomaininfolist dominfos; - int ret, nbids, i; - xenUnifiedPrivatePtr priv; - - if (conn == NULL) - return -1; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->handle < 0 || - (ids == NULL) || (maxids < 0)) - return (-1); - - if (maxids == 0) - return(0); - - if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) { - virReportOOMError(conn); - return(-1); - } - - XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids); - memset(ids, 0, maxids * sizeof(int)); - - ret = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos); - - if (ret < 0) { - XEN_GETDOMAININFOLIST_FREE(dominfos); - return (-1); - } - - nbids = ret; - if ((nbids < 0) || (nbids > maxids)) { - XEN_GETDOMAININFOLIST_FREE(dominfos); - return(-1); - } - - for (i = 0;i < nbids;i++) { - ids[i] = XEN_GETDOMAININFOLIST_DOMAIN(dominfos, i); - } - - XEN_GETDOMAININFOLIST_FREE(dominfos); - return (nbids); -} - - -#ifndef PROXY -char * -xenHypervisorDomainGetOSType (virDomainPtr dom) -{ - xenUnifiedPrivatePtr priv; - xen_getdomaininfo dominfo; - - priv = (xenUnifiedPrivatePtr) dom->conn->privateData; - if (priv->handle < 0) - return (NULL); - - /* HV's earlier than 3.1.0 don't include the HVM flags in guests status*/ - if (hypervisor_version < 2 || - dom_interface_version < 4) - return (NULL); - - XEN_GETDOMAININFO_CLEAR(dominfo); - - if (virXen_getdomaininfo(priv->handle, dom->id, &dominfo) < 0) - return (NULL); - - if (XEN_GETDOMAININFO_DOMAIN(dominfo) != dom->id) - return (NULL); - - if (XEN_GETDOMAININFO_FLAGS(dominfo) & DOMFLAGS_HVM) - return strdup("hvm"); - return strdup("linux"); -} - -virDomainPtr -xenHypervisorLookupDomainByID(virConnectPtr conn, - int id) -{ - xenUnifiedPrivatePtr priv; - xen_getdomaininfo dominfo; - virDomainPtr ret; - char *name; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->handle < 0) - return (NULL); - - XEN_GETDOMAININFO_CLEAR(dominfo); - - if (virXen_getdomaininfo(priv->handle, id, &dominfo) < 0) - return (NULL); - - if (XEN_GETDOMAININFO_DOMAIN(dominfo) != id) - return (NULL); - - xenUnifiedLock(priv); - name = xenStoreDomainGetName(conn, id); - xenUnifiedUnlock(priv); - if (!name) - return (NULL); - - ret = virGetDomain(conn, name, XEN_GETDOMAININFO_UUID(dominfo)); - if (ret) - ret->id = id; - VIR_FREE(name); - return ret; -} - - -virDomainPtr -xenHypervisorLookupDomainByUUID(virConnectPtr conn, - const unsigned char *uuid) -{ - xen_getdomaininfolist dominfos; - xenUnifiedPrivatePtr priv; - virDomainPtr ret; - char *name; - int maxids = 100, nids, i, id; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->handle < 0) - return (NULL); - - retry: - if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) { - virReportOOMError(conn); - return(NULL); - } - - XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids); - - nids = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos); - - if (nids < 0) { - XEN_GETDOMAININFOLIST_FREE(dominfos); - return (NULL); - } - - /* Can't possibly have more than 65,000 concurrent guests - * so limit how many times we try, to avoid increasing - * without bound & thus allocating all of system memory ! - * XXX I'll regret this comment in a few years time ;-) - */ - if (nids == maxids) { - XEN_GETDOMAININFOLIST_FREE(dominfos); - if (maxids < 65000) { - maxids *= 2; - goto retry; - } - return (NULL); - } - - id = -1; - for (i = 0 ; i < nids ; i++) { - if (memcmp(XEN_GETDOMAININFOLIST_UUID(dominfos, i), uuid, VIR_UUID_BUFLEN) == 0) { - id = XEN_GETDOMAININFOLIST_DOMAIN(dominfos, i); - break; - } - } - XEN_GETDOMAININFOLIST_FREE(dominfos); - - if (id == -1) - return (NULL); - - xenUnifiedLock(priv); - name = xenStoreDomainGetName(conn, id); - xenUnifiedUnlock(priv); - if (!name) - return (NULL); - - ret = virGetDomain(conn, name, uuid); - if (ret) - ret->id = id; - VIR_FREE(name); - return ret; -} -#endif - -/** - * xenHypervisorGetMaxVcpus: - * - * Returns the maximum of CPU defined by Xen. - */ -int -xenHypervisorGetMaxVcpus(virConnectPtr conn, - const char *type ATTRIBUTE_UNUSED) -{ - xenUnifiedPrivatePtr priv; - - if (conn == NULL) - return -1; - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->handle < 0) - return (-1); - - return MAX_VIRT_CPUS; -} - -/** - * xenHypervisorGetDomMaxMemory: - * @conn: connection data - * @id: domain id - * - * Retrieve the maximum amount of physical memory allocated to a - * domain. - * - * Returns the memory size in kilobytes or 0 in case of error. - */ -unsigned long -xenHypervisorGetDomMaxMemory(virConnectPtr conn, int id) -{ - xenUnifiedPrivatePtr priv; - xen_getdomaininfo dominfo; - int ret; - - if (conn == NULL) - return 0; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->handle < 0) - return 0; - - if (kb_per_pages == 0) { - kb_per_pages = sysconf(_SC_PAGESIZE) / 1024; - if (kb_per_pages <= 0) - kb_per_pages = 4; - } - - XEN_GETDOMAININFO_CLEAR(dominfo); - - ret = virXen_getdomaininfo(priv->handle, id, &dominfo); - - if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != id)) - return (0); - - return((unsigned long) XEN_GETDOMAININFO_MAX_PAGES(dominfo) * kb_per_pages); -} - -#ifndef PROXY -/** - * xenHypervisorGetMaxMemory: - * @domain: a domain object or NULL - * - * Retrieve the maximum amount of physical memory allocated to a - * domain. If domain is NULL, then this get the amount of memory reserved - * to Domain0 i.e. the domain where the application runs. - * - * Returns the memory size in kilobytes or 0 in case of error. - */ -static unsigned long -xenHypervisorGetMaxMemory(virDomainPtr domain) -{ - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL)) - return 0; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || domain->id < 0) - return (0); - - return(xenHypervisorGetDomMaxMemory(domain->conn, domain->id)); -} -#endif - -/** - * xenHypervisorGetDomInfo: - * @conn: connection data - * @id: the domain ID - * @info: the place where information should be stored - * - * Do an hypervisor call to get the related set of domain information. - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenHypervisorGetDomInfo(virConnectPtr conn, int id, virDomainInfoPtr info) -{ - xenUnifiedPrivatePtr priv; - xen_getdomaininfo dominfo; - int ret; - uint32_t domain_flags, domain_state, domain_shutdown_cause; - - if (kb_per_pages == 0) { - kb_per_pages = sysconf(_SC_PAGESIZE) / 1024; - if (kb_per_pages <= 0) - kb_per_pages = 4; - } - - if (conn == NULL) - return -1; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->handle < 0 || info == NULL) - return (-1); - - memset(info, 0, sizeof(virDomainInfo)); - XEN_GETDOMAININFO_CLEAR(dominfo); - - ret = virXen_getdomaininfo(priv->handle, id, &dominfo); - - if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != id)) - return (-1); - - domain_flags = XEN_GETDOMAININFO_FLAGS(dominfo); - domain_flags &= ~DOMFLAGS_HVM; /* Mask out HVM flags */ - domain_state = domain_flags & 0xFF; /* Mask out high bits */ - switch (domain_state) { - case DOMFLAGS_DYING: - info->state = VIR_DOMAIN_SHUTDOWN; - break; - case DOMFLAGS_SHUTDOWN: - /* The domain is shutdown. Determine the cause. */ - domain_shutdown_cause = domain_flags >> DOMFLAGS_SHUTDOWNSHIFT; - switch (domain_shutdown_cause) { - case SHUTDOWN_crash: - info->state = VIR_DOMAIN_CRASHED; - break; - default: - info->state = VIR_DOMAIN_SHUTOFF; - } - break; - case DOMFLAGS_PAUSED: - info->state = VIR_DOMAIN_PAUSED; - break; - case DOMFLAGS_BLOCKED: - info->state = VIR_DOMAIN_BLOCKED; - break; - case DOMFLAGS_RUNNING: - info->state = VIR_DOMAIN_RUNNING; - break; - default: - info->state = VIR_DOMAIN_NOSTATE; - } - - /* - * the API brings back the cpu time in nanoseconds, - * convert to microseconds, same thing convert to - * kilobytes from page counts - */ - info->cpuTime = XEN_GETDOMAININFO_CPUTIME(dominfo); - info->memory = XEN_GETDOMAININFO_TOT_PAGES(dominfo) * kb_per_pages; - info->maxMem = XEN_GETDOMAININFO_MAX_PAGES(dominfo); - if(info->maxMem != UINT_MAX) - info->maxMem *= kb_per_pages; - info->nrVirtCpu = XEN_GETDOMAININFO_CPUCOUNT(dominfo); - return (0); -} - -/** - * xenHypervisorGetDomainInfo: - * @domain: pointer to the domain block - * @info: the place where information should be stored - * - * Do an hypervisor call to get the related set of domain information. - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenHypervisorGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info) -{ - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL)) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || info == NULL || - (domain->id < 0)) - return (-1); - - return(xenHypervisorGetDomInfo(domain->conn, domain->id, info)); - -} - -#ifndef PROXY -/** - * xenHypervisorNodeGetCellsFreeMemory: - * @conn: pointer to the hypervisor connection - * @freeMems: pointer to the array of unsigned long long - * @startCell: index of first cell to return freeMems info on. - * @maxCells: Maximum number of cells for which freeMems information can - * be returned. - * - * This call returns the amount of free memory in one or more NUMA cells. - * The @freeMems array must be allocated by the caller and will be filled - * with the amount of free memory in kilobytes for each cell requested, - * starting with startCell (in freeMems[0]), up to either - * (startCell + maxCells), or the number of additional cells in the node, - * whichever is smaller. - * - * Returns the number of entries filled in freeMems, or -1 in case of error. - */ -int -xenHypervisorNodeGetCellsFreeMemory(virConnectPtr conn, unsigned long long *freeMems, - int startCell, int maxCells) -{ - xen_op_v2_sys op_sys; - int i, j, ret; - xenUnifiedPrivatePtr priv; - - if (conn == NULL) { - virXenErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "invalid argument", 0); - return -1; - } - - priv = conn->privateData; - - if (priv->nbNodeCells < 0) { - virXenErrorFunc (conn, VIR_ERR_XEN_CALL, __FUNCTION__, - "cannot determine actual number of cells",0); - return(-1); - } - - if ((maxCells < 1) || (startCell >= priv->nbNodeCells)) { - virXenErrorFunc (conn, VIR_ERR_INVALID_ARG, __FUNCTION__, - "invalid argument", 0); - return -1; - } - - /* - * Support only sys_interface_version >=4 - */ - if (sys_interface_version < SYS_IFACE_MIN_VERS_NUMA) { - virXenErrorFunc (conn, VIR_ERR_XEN_CALL, __FUNCTION__, - "unsupported in sys interface < 4", 0); - return -1; - } - - if (priv->handle < 0) { - virXenErrorFunc (conn, VIR_ERR_INTERNAL_ERROR, __FUNCTION__, - "priv->handle invalid", 0); - return -1; - } - - memset(&op_sys, 0, sizeof(op_sys)); - op_sys.cmd = XEN_V2_OP_GETAVAILHEAP; - - for (i = startCell, j = 0;(i < priv->nbNodeCells) && (j < maxCells);i++,j++) { - if (sys_interface_version >= 5) - op_sys.u.availheap5.node = i; - else - op_sys.u.availheap.node = i; - ret = xenHypervisorDoV2Sys(priv->handle, &op_sys); - if (ret < 0) { - return(-1); - } - if (sys_interface_version >= 5) - freeMems[j] = op_sys.u.availheap5.avail_bytes; - else - freeMems[j] = op_sys.u.availheap.avail_bytes; - } - return (j); -} - - -/** - * xenHypervisorPauseDomain: - * @domain: pointer to the domain block - * - * Do an hypervisor call to pause the given domain - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenHypervisorPauseDomain(virDomainPtr domain) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL)) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || domain->id < 0) - return (-1); - - ret = virXen_pausedomain(priv->handle, domain->id); - if (ret < 0) - return (-1); - return (0); -} - -/** - * xenHypervisorResumeDomain: - * @domain: pointer to the domain block - * - * Do an hypervisor call to resume the given domain - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenHypervisorResumeDomain(virDomainPtr domain) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL)) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || domain->id < 0) - return (-1); - - ret = virXen_unpausedomain(priv->handle, domain->id); - if (ret < 0) - return (-1); - return (0); -} - -/** - * xenHypervisorDestroyDomain: - * @domain: pointer to the domain block - * - * Do an hypervisor call to destroy the given domain - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenHypervisorDestroyDomain(virDomainPtr domain) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if (domain == NULL || domain->conn == NULL) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || domain->id < 0) - return (-1); - - ret = virXen_destroydomain(priv->handle, domain->id); - if (ret < 0) - return (-1); - return (0); -} - -/** - * xenHypervisorSetMaxMemory: - * @domain: pointer to the domain block - * @memory: the max memory size in kilobytes. - * - * Do an hypervisor call to change the maximum amount of memory used - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenHypervisorSetMaxMemory(virDomainPtr domain, unsigned long memory) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if (domain == NULL || domain->conn == NULL) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || domain->id < 0) - return (-1); - - ret = virXen_setmaxmem(priv->handle, domain->id, memory); - if (ret < 0) - return (-1); - return (0); -} -#endif /* PROXY */ - -#ifndef PROXY -/** - * xenHypervisorSetVcpus: - * @domain: pointer to domain object - * @nvcpus: the new number of virtual CPUs for this domain - * - * Dynamically change the number of virtual CPUs used by the domain. - * - * Returns 0 in case of success, -1 in case of failure. - */ - -int -xenHypervisorSetVcpus(virDomainPtr domain, unsigned int nvcpus) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if (domain == NULL || domain->conn == NULL) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || domain->id < 0 || nvcpus < 1) - return (-1); - - ret = virXen_setmaxvcpus(priv->handle, domain->id, nvcpus); - if (ret < 0) - return (-1); - return (0); -} - -/** - * xenHypervisorPinVcpu: - * @domain: pointer to domain object - * @vcpu: virtual CPU number - * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) - * @maplen: length of cpumap in bytes - * - * Dynamically change the real CPUs which can be allocated to a virtual CPU. - * - * Returns 0 in case of success, -1 in case of failure. - */ - -int -xenHypervisorPinVcpu(virDomainPtr domain, unsigned int vcpu, - unsigned char *cpumap, int maplen) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if (domain == NULL || domain->conn == NULL) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || (domain->id < 0) || - (cpumap == NULL) || (maplen < 1)) - return (-1); - - ret = virXen_setvcpumap(priv->handle, domain->id, vcpu, - cpumap, maplen); - if (ret < 0) - return (-1); - return (0); -} -#endif - -/** - * virDomainGetVcpus: - * @domain: pointer to domain object, or NULL for Domain0 - * @info: pointer to an array of virVcpuInfo structures (OUT) - * @maxinfo: number of structures in info array - * @cpumaps: pointer to an bit map of real CPUs for all vcpus of this domain (in 8-bit bytes) (OUT) - * If cpumaps is NULL, then no cpumap information is returned by the API. - * It's assumed there is cpumap in cpumaps array. - * The memory allocated to cpumaps must be (maxinfo * maplen) bytes - * (ie: calloc(maxinfo, maplen)). - * One cpumap inside cpumaps has the format described in virDomainPinVcpu() API. - * @maplen: number of bytes in one cpumap, from 1 up to size of CPU map in - * underlying virtualization system (Xen...). - * - * Extract information about virtual CPUs of domain, store it in info array - * and also in cpumaps if this pointer isn't NULL. - * - * Returns the number of info filled in case of success, -1 in case of failure. - */ -#ifndef PROXY -int -xenHypervisorGetVcpus(virDomainPtr domain, virVcpuInfoPtr info, int maxinfo, - unsigned char *cpumaps, int maplen) -{ - xen_getdomaininfo dominfo; - int ret; - xenUnifiedPrivatePtr priv; - virVcpuInfoPtr ipt; - int nbinfo, i; - - if (domain == NULL || domain->conn == NULL) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0 || (domain->id < 0) || - (info == NULL) || (maxinfo < 1) || - (sizeof(cpumap_t) & 7)) - return (-1); - if ((cpumaps != NULL) && (maplen < 1)) - return -1; - - /* first get the number of virtual CPUs in this domain */ - XEN_GETDOMAININFO_CLEAR(dominfo); - ret = virXen_getdomaininfo(priv->handle, domain->id, - &dominfo); - - if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != domain->id)) - return (-1); - nbinfo = XEN_GETDOMAININFO_CPUCOUNT(dominfo) + 1; - if (nbinfo > maxinfo) nbinfo = maxinfo; - - if (cpumaps != NULL) - memset(cpumaps, 0, maxinfo * maplen); - - for (i = 0, ipt = info; i < nbinfo; i++, ipt++) { - if ((cpumaps != NULL) && (i < maxinfo)) { - ret = virXen_getvcpusinfo(priv->handle, domain->id, i, - ipt, - (unsigned char *)VIR_GET_CPUMAP(cpumaps, maplen, i), - maplen); - if (ret < 0) - return(-1); - } else { - ret = virXen_getvcpusinfo(priv->handle, domain->id, i, - ipt, NULL, 0); - if (ret < 0) - return(-1); - } - } - return nbinfo; -} -#endif /* PROXY */ - -/** - * xenHypervisorGetVcpuMax: - * - * Returns the maximum number of virtual CPUs supported for - * the guest VM. If the guest is inactive, this is the maximum - * of CPU defined by Xen. If the guest is running this reflect - * the maximum number of virtual CPUs the guest was booted with. - */ -int -xenHypervisorGetVcpuMax(virDomainPtr domain) -{ - xen_getdomaininfo dominfo; - int ret; - int maxcpu; - xenUnifiedPrivatePtr priv; - - if (domain == NULL || domain->conn == NULL) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->handle < 0) - return (-1); - - /* inactive domain */ - if (domain->id < 0) { - maxcpu = MAX_VIRT_CPUS; - } else { - XEN_GETDOMAININFO_CLEAR(dominfo); - ret = virXen_getdomaininfo(priv->handle, domain->id, - &dominfo); - - if ((ret < 0) || (XEN_GETDOMAININFO_DOMAIN(dominfo) != domain->id)) - return (-1); - maxcpu = XEN_GETDOMAININFO_MAXCPUID(dominfo) + 1; - } - - return maxcpu; -} - -/** - * xenHavePrivilege() - * - * Return true if the current process should be able to connect to Xen. - */ -int -xenHavePrivilege() -{ -#ifdef __sun - return priv_ineffect (PRIV_XVM_CONTROL); -#else - return access(XEN_HYPERVISOR_SOCKET, R_OK) == 0; -#endif -} diff --git a/src/xen_internal.h b/src/xen_internal.h deleted file mode 100644 index 766f676836..0000000000 --- a/src/xen_internal.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * xen_internal.h: internal API for direct access to Xen hypervisor level - * - * Copyright (C) 2005 Red Hat, Inc. - * - * See COPYING.LIB for the License of this software - * - * Daniel Veillard - */ - -#ifndef __VIR_XEN_INTERNAL_H__ -#define __VIR_XEN_INTERNAL_H__ - -#include - -#include "internal.h" -#include "capabilities.h" -#include "driver.h" - -extern struct xenUnifiedDriver xenHypervisorDriver; -int xenHypervisorInit (void); - -virCapsPtr xenHypervisorMakeCapabilities (virConnectPtr conn); - -/* The following calls are made directly by the Xen proxy: */ - -virDomainPtr - xenHypervisorLookupDomainByID (virConnectPtr conn, - int id); -virDomainPtr - xenHypervisorLookupDomainByUUID (virConnectPtr conn, - const unsigned char *uuid); -char * - xenHypervisorDomainGetOSType (virDomainPtr dom); - -virDrvOpenStatus - xenHypervisorOpen (virConnectPtr conn, - virConnectAuthPtr auth, - int flags); -int xenHypervisorClose (virConnectPtr conn); -int xenHypervisorGetVersion (virConnectPtr conn, - unsigned long *hvVer); -virCapsPtr - xenHypervisorMakeCapabilitiesInternal(virConnectPtr conn, - const char *hostmachine, - FILE *cpuinfo, - FILE *capabilities); -char * - xenHypervisorGetCapabilities (virConnectPtr conn); -unsigned long - xenHypervisorGetDomMaxMemory (virConnectPtr conn, - int id); -int xenHypervisorNumOfDomains (virConnectPtr conn); -int xenHypervisorListDomains (virConnectPtr conn, - int *ids, - int maxids); -int xenHypervisorGetMaxVcpus (virConnectPtr conn, - const char *type); -int xenHypervisorDestroyDomain (virDomainPtr domain); -int xenHypervisorResumeDomain (virDomainPtr domain); -int xenHypervisorPauseDomain (virDomainPtr domain); -int xenHypervisorGetDomainInfo (virDomainPtr domain, - virDomainInfoPtr info); -int xenHypervisorGetDomInfo (virConnectPtr conn, - int id, - virDomainInfoPtr info); -int xenHypervisorSetMaxMemory (virDomainPtr domain, - unsigned long memory); -int xenHypervisorCheckID (virConnectPtr conn, - int id); -int xenHypervisorSetVcpus (virDomainPtr domain, - unsigned int nvcpus); -int xenHypervisorPinVcpu (virDomainPtr domain, - unsigned int vcpu, - unsigned char *cpumap, - int maplen); -int xenHypervisorGetVcpus (virDomainPtr domain, - virVcpuInfoPtr info, - int maxinfo, - unsigned char *cpumaps, - int maplen); -int xenHypervisorGetVcpuMax (virDomainPtr domain); - -char * xenHypervisorGetSchedulerType (virDomainPtr domain, - int *nparams); - -int xenHypervisorGetSchedulerParameters(virDomainPtr domain, - virSchedParameterPtr params, - int *nparams); - -int xenHypervisorSetSchedulerParameters(virDomainPtr domain, - virSchedParameterPtr params, - int nparams); - -int xenHypervisorDomainBlockStats (virDomainPtr domain, - const char *path, - struct _virDomainBlockStats *stats); -int xenHypervisorDomainInterfaceStats (virDomainPtr domain, - const char *path, - struct _virDomainInterfaceStats *stats); - -int xenHypervisorNodeGetCellsFreeMemory(virConnectPtr conn, - unsigned long long *freeMems, - int startCell, - int maxCells); - -int xenHavePrivilege(void); - -#endif /* __VIR_XEN_INTERNAL_H__ */ diff --git a/src/xen_unified.c b/src/xen_unified.c deleted file mode 100644 index dfa9ca57c1..0000000000 --- a/src/xen_unified.c +++ /dev/null @@ -1,1917 +0,0 @@ -/* - * xen_unified.c: Unified Xen driver. - * - * Copyright (C) 2007, 2008, 2009 Red Hat, Inc. - * - * See COPYING.LIB for the License of this software - * - * Richard W.M. Jones - */ - -#include - -/* Note: - * - * This driver provides a unified interface to the five - * separate underlying Xen drivers (xen_internal, proxy_internal, - * xend_internal, xs_internal and xm_internal). Historically - * the body of libvirt.c handled the five Xen drivers, - * and contained Xen-specific code. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "virterror_internal.h" -#include "logging.h" -#include "datatypes.h" -#include "xen_unified.h" - -#include "xen_internal.h" -#include "proxy_internal.h" -#include "xend_internal.h" -#include "xs_internal.h" -#include "xm_internal.h" -#if WITH_XEN_INOTIFY -#include "xen_inotify.h" -#endif -#include "xml.h" -#include "util.h" -#include "memory.h" -#include "node_device_conf.h" -#include "pci.h" - -#define VIR_FROM_THIS VIR_FROM_XEN - -static int -xenUnifiedNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info); -static int -xenUnifiedDomainGetMaxVcpus (virDomainPtr dom); -static int -xenUnifiedDomainGetVcpus (virDomainPtr dom, - virVcpuInfoPtr info, int maxinfo, - unsigned char *cpumaps, int maplen); - -/* The five Xen drivers below us. */ -static struct xenUnifiedDriver const * const drivers[XEN_UNIFIED_NR_DRIVERS] = { - [XEN_UNIFIED_HYPERVISOR_OFFSET] = &xenHypervisorDriver, - [XEN_UNIFIED_PROXY_OFFSET] = &xenProxyDriver, - [XEN_UNIFIED_XEND_OFFSET] = &xenDaemonDriver, - [XEN_UNIFIED_XS_OFFSET] = &xenStoreDriver, - [XEN_UNIFIED_XM_OFFSET] = &xenXMDriver, -#if WITH_XEN_INOTIFY - [XEN_UNIFIED_INOTIFY_OFFSET] = &xenInotifyDriver, -#endif -}; - -static int inside_daemon; - -#define xenUnifiedError(conn, code, fmt...) \ - virReportErrorHelper(conn, VIR_FROM_XEN, code, __FILE__, \ - __FUNCTION__, __LINE__, fmt) - -/** - * xenNumaInit: - * @conn: pointer to the hypervisor connection - * - * Initializer for previous variables. We currently assume that - * the number of physical CPU and the number of NUMA cell is fixed - * until reboot which might be false in future Xen implementations. - */ -static void -xenNumaInit(virConnectPtr conn) { - virNodeInfo nodeInfo; - xenUnifiedPrivatePtr priv; - int ret; - - ret = xenUnifiedNodeGetInfo(conn, &nodeInfo); - if (ret < 0) - return; - - priv = conn->privateData; - - priv->nbNodeCells = nodeInfo.nodes; - priv->nbNodeCpus = nodeInfo.cpus; -} - - -/** - * xenDomainUsedCpus: - * @dom: the domain - * - * Analyze which set of CPUs are used by the domain and - * return a string providing the ranges. - * - * Returns the string which needs to be freed by the caller or - * NULL if the domain uses all CPU or in case of error. - */ -char * -xenDomainUsedCpus(virDomainPtr dom) -{ - char *res = NULL; - int ncpus; - int nb_vcpu; - char *cpulist = NULL; - unsigned char *cpumap = NULL; - size_t cpumaplen; - int nb = 0; - int n, m; - virVcpuInfoPtr cpuinfo = NULL; - virNodeInfo nodeinfo; - xenUnifiedPrivatePtr priv; - - if (!VIR_IS_CONNECTED_DOMAIN(dom)) - return (NULL); - - priv = dom->conn->privateData; - - if (priv->nbNodeCpus <= 0) - return(NULL); - nb_vcpu = xenUnifiedDomainGetMaxVcpus(dom); - if (nb_vcpu <= 0) - return(NULL); - if (xenUnifiedNodeGetInfo(dom->conn, &nodeinfo) < 0) - return(NULL); - - if (VIR_ALLOC_N(cpulist, priv->nbNodeCpus) < 0) - goto done; - if (VIR_ALLOC_N(cpuinfo, nb_vcpu) < 0) - goto done; - cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo)); - if (xalloc_oversized(nb_vcpu, cpumaplen) || - VIR_ALLOC_N(cpumap, nb_vcpu * cpumaplen) < 0) - goto done; - - if ((ncpus = xenUnifiedDomainGetVcpus(dom, cpuinfo, nb_vcpu, - cpumap, cpumaplen)) >= 0) { - for (n = 0 ; n < ncpus ; n++) { - for (m = 0 ; m < priv->nbNodeCpus; m++) { - if ((cpulist[m] == 0) && - (VIR_CPU_USABLE(cpumap, cpumaplen, n, m))) { - cpulist[m] = 1; - nb++; - /* if all CPU are used just return NULL */ - if (nb == priv->nbNodeCpus) - goto done; - - } - } - } - res = virDomainCpuSetFormat(dom->conn, cpulist, priv->nbNodeCpus); - } - -done: - VIR_FREE(cpulist); - VIR_FREE(cpumap); - VIR_FREE(cpuinfo); - return(res); -} - -#ifdef WITH_LIBVIRTD - -static int -xenInitialize (int privileged ATTRIBUTE_UNUSED) -{ - inside_daemon = 1; - return 0; -} - -static virStateDriver state_driver = { - .initialize = xenInitialize, -}; - -#endif - -/*----- Dispatch functions. -----*/ - -/* These dispatch functions follow the model used historically - * by libvirt.c -- trying each low-level Xen driver in turn - * until one succeeds. However since we know what low-level - * drivers can perform which functions, it is probably better - * in future to optimise these dispatch functions to just call - * the single function (or small number of appropriate functions) - * in the low level drivers directly. - */ - -static int -xenUnifiedProbe (void) -{ -#ifdef __linux__ - if (virFileExists("/proc/xen")) - return 1; -#endif -#ifdef __sun - FILE *fh; - - if (fh = fopen("/dev/xen/domcaps", "r")) { - fclose(fh); - return 1; - } -#endif - return 0; -} - -static virDrvOpenStatus -xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags) -{ - int i, ret = VIR_DRV_OPEN_DECLINED; - xenUnifiedPrivatePtr priv; - virDomainEventCallbackListPtr cbList; - -#ifdef __sun - /* - * Only the libvirtd instance can open this driver. - * Everything else falls back to the remote driver. - */ - if (!inside_daemon) - return VIR_DRV_OPEN_DECLINED; -#endif - - if (conn->uri == NULL) { - if (!xenUnifiedProbe()) - return VIR_DRV_OPEN_DECLINED; - - conn->uri = xmlParseURI("xen:///"); - if (!conn->uri) { - virReportOOMError (NULL); - return VIR_DRV_OPEN_ERROR; - } - } else { - if (conn->uri->scheme) { - /* Decline any scheme which isn't "xen://" or "http://". */ - if (STRCASENEQ(conn->uri->scheme, "xen") && - STRCASENEQ(conn->uri->scheme, "http")) - return VIR_DRV_OPEN_DECLINED; - - - /* Return an error if the path isn't '' or '/' */ - if (conn->uri->path && - STRNEQ(conn->uri->path, "") && - STRNEQ(conn->uri->path, "/")) { - xenUnifiedError(NULL, VIR_ERR_INTERNAL_ERROR, - _("unexpected Xen URI path '%s', try xen:///"), - conn->uri->path); - return VIR_DRV_OPEN_ERROR; - } - - /* Decline any xen:// URI with a server specified, allowing remote - * driver to handle, but keep any http:/// URIs */ - if (STRCASEEQ(conn->uri->scheme, "xen") && - conn->uri->server) - return VIR_DRV_OPEN_DECLINED; - } else { - /* Special case URI for Xen driver only: - * - * Treat a plain path as a Xen UNIX socket path, and give - * error unless path is absolute - */ - if (!conn->uri->path || conn->uri->path[0] != '/') { - xenUnifiedError(NULL, VIR_ERR_INTERNAL_ERROR, - _("unexpected Xen URI path '%s', try ///var/lib/xen/xend-socket"), - NULLSTR(conn->uri->path)); - return VIR_DRV_OPEN_ERROR; - } - } - } - - /* We now know the URI is definitely for this driver, so beyond - * here, don't return DECLINED, always use ERROR */ - - /* Allocate per-connection private data. */ - if (VIR_ALLOC(priv) < 0) { - virReportOOMError (NULL); - return VIR_DRV_OPEN_ERROR; - } - if (virMutexInit(&priv->lock) < 0) { - xenUnifiedError (NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot initialise mutex")); - VIR_FREE(priv); - return VIR_DRV_OPEN_ERROR; - } - - /* Allocate callback list */ - if (VIR_ALLOC(cbList) < 0) { - virReportOOMError (NULL); - virMutexDestroy(&priv->lock); - VIR_FREE(priv); - return VIR_DRV_OPEN_ERROR; - } - conn->privateData = priv; - - priv->domainEventCallbacks = cbList; - - priv->handle = -1; - priv->xendConfigVersion = -1; - priv->xshandle = NULL; - priv->proxy = -1; - - - /* Hypervisor is only run with privilege & required to succeed */ - if (xenHavePrivilege()) { - DEBUG0("Trying hypervisor sub-driver"); - if (drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->open(conn, auth, flags) == - VIR_DRV_OPEN_SUCCESS) { - DEBUG0("Activated hypervisor sub-driver"); - priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] = 1; - } - } - - /* XenD is required to succeed if privileged. - * If it fails as non-root, then the proxy driver may take over - */ - DEBUG0("Trying XenD sub-driver"); - if (drivers[XEN_UNIFIED_XEND_OFFSET]->open(conn, auth, flags) == - VIR_DRV_OPEN_SUCCESS) { - DEBUG0("Activated XenD sub-driver"); - priv->opened[XEN_UNIFIED_XEND_OFFSET] = 1; - - /* XenD is active, so try the xm & xs drivers too, both requird to - * succeed if root, optional otherwise */ - if (priv->xendConfigVersion <= 2) { - DEBUG0("Trying XM sub-driver"); - if (drivers[XEN_UNIFIED_XM_OFFSET]->open(conn, auth, flags) == - VIR_DRV_OPEN_SUCCESS) { - DEBUG0("Activated XM sub-driver"); - priv->opened[XEN_UNIFIED_XM_OFFSET] = 1; - } - } - DEBUG0("Trying XS sub-driver"); - if (drivers[XEN_UNIFIED_XS_OFFSET]->open(conn, auth, flags) == - VIR_DRV_OPEN_SUCCESS) { - DEBUG0("Activated XS sub-driver"); - priv->opened[XEN_UNIFIED_XS_OFFSET] = 1; - } else { - if (xenHavePrivilege()) - goto fail; /* XS is mandatory when privileged */ - } - } else { - if (xenHavePrivilege()) { - goto fail; /* XenD is mandatory when privileged */ - } else { -#if WITH_PROXY - DEBUG0("Trying proxy sub-driver"); - if (drivers[XEN_UNIFIED_PROXY_OFFSET]->open(conn, auth, flags) == - VIR_DRV_OPEN_SUCCESS) { - DEBUG0("Activated proxy sub-driver"); - priv->opened[XEN_UNIFIED_PROXY_OFFSET] = 1; - } else { - goto fail; /* Proxy is mandatory if XenD failed */ - } -#else - DEBUG0("Handing off for remote driver"); - ret = VIR_DRV_OPEN_DECLINED; /* Let remote_driver try instead */ - goto clean; -#endif - } - } - - xenNumaInit(conn); - - if (!(priv->caps = xenHypervisorMakeCapabilities(conn))) { - DEBUG0("Failed to make capabilities"); - goto fail; - } - -#if WITH_XEN_INOTIFY - if (xenHavePrivilege()) { - DEBUG0("Trying Xen inotify sub-driver"); - if (drivers[XEN_UNIFIED_INOTIFY_OFFSET]->open(conn, auth, flags) == - VIR_DRV_OPEN_SUCCESS) { - DEBUG0("Activated Xen inotify sub-driver"); - priv->opened[XEN_UNIFIED_INOTIFY_OFFSET] = 1; - } - } -#endif - - return VIR_DRV_OPEN_SUCCESS; - -fail: - ret = VIR_DRV_OPEN_ERROR; -#ifndef WITH_PROXY -clean: -#endif - DEBUG0("Failed to activate a mandatory sub-driver"); - for (i = 0 ; i < XEN_UNIFIED_NR_DRIVERS ; i++) - if (priv->opened[i]) drivers[i]->close(conn); - virMutexDestroy(&priv->lock); - VIR_FREE(priv); - conn->privateData = NULL; - return ret; -} - -#define GET_PRIVATE(conn) \ - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) (conn)->privateData - -static int -xenUnifiedClose (virConnectPtr conn) -{ - GET_PRIVATE(conn); - int i; - - virCapabilitiesFree(priv->caps); - virDomainEventCallbackListFree(priv->domainEventCallbacks); - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->close) - (void) drivers[i]->close (conn); - - virMutexDestroy(&priv->lock); - VIR_FREE(conn->privateData); - - return 0; -} - - -#define HV_VERSION ((DOM0_INTERFACE_VERSION >> 24) * 1000000 + \ - ((DOM0_INTERFACE_VERSION >> 16) & 0xFF) * 1000 + \ - (DOM0_INTERFACE_VERSION & 0xFFFF)) - -unsigned long xenUnifiedVersion(void) -{ - return HV_VERSION; -} - - -static const char * -xenUnifiedType (virConnectPtr conn) -{ - GET_PRIVATE(conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i]) - return "Xen"; - - return NULL; -} - -/* Which features are supported by this driver? */ -static int -xenUnifiedSupportsFeature (virConnectPtr conn ATTRIBUTE_UNUSED, int feature) -{ - switch (feature) { - case VIR_DRV_FEATURE_MIGRATION_V1: return 1; - default: return 0; - } -} - -static int -xenUnifiedGetVersion (virConnectPtr conn, unsigned long *hvVer) -{ - GET_PRIVATE(conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->version && - drivers[i]->version (conn, hvVer) == 0) - return 0; - - return -1; -} - -/* NB: Even if connected to the proxy, we're still on the - * same machine. - */ -static char * -xenUnifiedGetHostname (virConnectPtr conn) -{ - char *result; - - result = virGetHostname(); - if (result == NULL) { - virReportSystemError(conn, errno, - "%s", _("cannot lookup hostname")); - return NULL; - } - /* Caller frees this string. */ - return result; -} - -static int -xenUnifiedGetMaxVcpus (virConnectPtr conn, const char *type) -{ - GET_PRIVATE(conn); - - if (type && STRCASENEQ (type, "Xen")) { - xenUnifiedError (conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return -1; - } - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) - return xenHypervisorGetMaxVcpus (conn, type); - else { - xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; - } -} - -static int -xenUnifiedNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info) -{ - GET_PRIVATE(conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->nodeGetInfo && - drivers[i]->nodeGetInfo (conn, info) == 0) - return 0; - - return -1; -} - -static char * -xenUnifiedGetCapabilities (virConnectPtr conn) -{ - xenUnifiedPrivatePtr priv = conn->privateData; - char *xml; - - if (!(xml = virCapabilitiesFormatXML(priv->caps))) { - virReportOOMError(conn); - return NULL; - } - - return xml; -} - -static int -xenUnifiedListDomains (virConnectPtr conn, int *ids, int maxids) -{ - GET_PRIVATE(conn); - int ret; - - /* Try xenstore. */ - if (priv->opened[XEN_UNIFIED_XS_OFFSET]) { - ret = xenStoreListDomains (conn, ids, maxids); - if (ret >= 0) return ret; - } - - /* Try HV. */ - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) { - ret = xenHypervisorListDomains (conn, ids, maxids); - if (ret >= 0) return ret; - } - - /* Try xend. */ - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { - ret = xenDaemonListDomains (conn, ids, maxids); - if (ret >= 0) return ret; - } - - /* Try proxy. */ - if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) { - ret = xenProxyListDomains (conn, ids, maxids); - if (ret >= 0) return ret; - } - return -1; -} - -static int -xenUnifiedNumOfDomains (virConnectPtr conn) -{ - GET_PRIVATE(conn); - int i, ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->numOfDomains) { - ret = drivers[i]->numOfDomains (conn); - if (ret >= 0) return ret; - } - - return -1; -} - -static virDomainPtr -xenUnifiedDomainCreateXML (virConnectPtr conn, - const char *xmlDesc, unsigned int flags) -{ - GET_PRIVATE(conn); - int i; - virDomainPtr ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainCreateXML) { - ret = drivers[i]->domainCreateXML (conn, xmlDesc, flags); - if (ret) return ret; - } - - return NULL; -} - -/* Assumption made in underlying drivers: - * If the domain is "not found" and there is no other error, then - * the Lookup* functions return a NULL but do not set virterror. - */ -static virDomainPtr -xenUnifiedDomainLookupByID (virConnectPtr conn, int id) -{ - GET_PRIVATE(conn); - virDomainPtr ret; - - /* Reset any connection-level errors in virterror first, in case - * there is one hanging around from a previous call. - */ - virConnResetLastError (conn); - - /* Try hypervisor/xenstore combo. */ - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) { - ret = xenHypervisorLookupDomainByID (conn, id); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Try proxy. */ - if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) { - ret = xenProxyLookupByID (conn, id); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Try xend. */ - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { - ret = xenDaemonLookupByID (conn, id); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Not found. */ - xenUnifiedError (conn, VIR_ERR_NO_DOMAIN, __FUNCTION__); - return NULL; -} - -static virDomainPtr -xenUnifiedDomainLookupByUUID (virConnectPtr conn, - const unsigned char *uuid) -{ - GET_PRIVATE(conn); - virDomainPtr ret; - - /* Reset any connection-level errors in virterror first, in case - * there is one hanging around from a previous call. - */ - virConnResetLastError (conn); - - /* Try hypervisor/xenstore combo. */ - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) { - ret = xenHypervisorLookupDomainByUUID (conn, uuid); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Try proxy. */ - if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) { - ret = xenProxyLookupByUUID (conn, uuid); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Try xend. */ - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { - ret = xenDaemonLookupByUUID (conn, uuid); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Try XM for inactive domains. */ - if (priv->opened[XEN_UNIFIED_XM_OFFSET]) { - ret = xenXMDomainLookupByUUID (conn, uuid); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Not found. */ - xenUnifiedError (conn, VIR_ERR_NO_DOMAIN, __FUNCTION__); - return NULL; -} - -static virDomainPtr -xenUnifiedDomainLookupByName (virConnectPtr conn, - const char *name) -{ - GET_PRIVATE(conn); - virDomainPtr ret; - - /* Reset any connection-level errors in virterror first, in case - * there is one hanging around from a previous call. - */ - virConnResetLastError (conn); - - /* Try proxy. */ - if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) { - ret = xenProxyLookupByName (conn, name); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Try xend. */ - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { - ret = xenDaemonLookupByName (conn, name); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Try xenstore for inactive domains. */ - if (priv->opened[XEN_UNIFIED_XS_OFFSET]) { - ret = xenStoreLookupByName (conn, name); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Try XM for inactive domains. */ - if (priv->opened[XEN_UNIFIED_XM_OFFSET]) { - ret = xenXMDomainLookupByName (conn, name); - if (ret || conn->err.code != VIR_ERR_OK) - return ret; - } - - /* Not found. */ - xenUnifiedError (conn, VIR_ERR_NO_DOMAIN, __FUNCTION__); - return NULL; -} - -static int -xenUnifiedDomainSuspend (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i; - - /* Try non-hypervisor methods first, then hypervisor direct method - * as a last resort. - */ - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (i != XEN_UNIFIED_HYPERVISOR_OFFSET && - priv->opened[i] && - drivers[i]->domainSuspend && - drivers[i]->domainSuspend (dom) == 0) - return 0; - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] && - drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainSuspend && - drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainSuspend (dom) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainResume (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i; - - /* Try non-hypervisor methods first, then hypervisor direct method - * as a last resort. - */ - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (i != XEN_UNIFIED_HYPERVISOR_OFFSET && - priv->opened[i] && - drivers[i]->domainResume && - drivers[i]->domainResume (dom) == 0) - return 0; - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] && - drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainResume && - drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainResume (dom) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainShutdown (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->domainShutdown && - drivers[i]->domainShutdown (dom) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainReboot (virDomainPtr dom, unsigned int flags) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->domainReboot && - drivers[i]->domainReboot (dom, flags) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainDestroy (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i; - - /* Try non-hypervisor methods first, then hypervisor direct method - * as a last resort. - */ - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (i != XEN_UNIFIED_HYPERVISOR_OFFSET && - priv->opened[i] && - drivers[i]->domainDestroy && - drivers[i]->domainDestroy (dom) == 0) - return 0; - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] && - drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainDestroy && - drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainDestroy (dom) == 0) - return 0; - - return -1; -} - -static char * -xenUnifiedDomainGetOSType (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i; - char *ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainGetOSType) { - ret = drivers[i]->domainGetOSType (dom); - if (ret) return ret; - } - - return NULL; -} - -static unsigned long -xenUnifiedDomainGetMaxMemory (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i; - unsigned long ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainGetMaxMemory) { - ret = drivers[i]->domainGetMaxMemory (dom); - if (ret != 0) return ret; - } - - return 0; -} - -static int -xenUnifiedDomainSetMaxMemory (virDomainPtr dom, unsigned long memory) -{ - GET_PRIVATE(dom->conn); - int i; - - /* Prefer xend for setting max memory */ - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { - if (xenDaemonDomainSetMaxMemory (dom, memory) == 0) - return 0; - } - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (i != XEN_UNIFIED_XEND_OFFSET && - priv->opened[i] && - drivers[i]->domainSetMaxMemory && - drivers[i]->domainSetMaxMemory (dom, memory) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainSetMemory (virDomainPtr dom, unsigned long memory) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->domainSetMemory && - drivers[i]->domainSetMemory (dom, memory) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainGetInfo (virDomainPtr dom, virDomainInfoPtr info) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->domainGetInfo && - drivers[i]->domainGetInfo (dom, info) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainSave (virDomainPtr dom, const char *to) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->domainSave && - drivers[i]->domainSave (dom, to) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainRestore (virConnectPtr conn, const char *from) -{ - GET_PRIVATE(conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->domainRestore && - drivers[i]->domainRestore (conn, from) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainCoreDump (virDomainPtr dom, const char *to, int flags) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->domainCoreDump && - drivers[i]->domainCoreDump (dom, to, flags) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainSetVcpus (virDomainPtr dom, unsigned int nvcpus) -{ - GET_PRIVATE(dom->conn); - int i; - - /* Try non-hypervisor methods first, then hypervisor direct method - * as a last resort. - */ - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (i != XEN_UNIFIED_HYPERVISOR_OFFSET && - priv->opened[i] && - drivers[i]->domainSetVcpus && - drivers[i]->domainSetVcpus (dom, nvcpus) == 0) - return 0; - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] && - drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainSetVcpus && - drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->domainSetVcpus (dom, nvcpus) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainPinVcpu (virDomainPtr dom, unsigned int vcpu, - unsigned char *cpumap, int maplen) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && - drivers[i]->domainPinVcpu && - drivers[i]->domainPinVcpu (dom, vcpu, cpumap, maplen) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainGetVcpus (virDomainPtr dom, - virVcpuInfoPtr info, int maxinfo, - unsigned char *cpumaps, int maplen) -{ - GET_PRIVATE(dom->conn); - int i, ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainGetVcpus) { - ret = drivers[i]->domainGetVcpus (dom, info, maxinfo, cpumaps, maplen); - if (ret > 0) - return ret; - } - return -1; -} - -static int -xenUnifiedDomainGetMaxVcpus (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i, ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainGetMaxVcpus) { - ret = drivers[i]->domainGetMaxVcpus (dom); - if (ret != 0) return ret; - } - - return -1; -} - -static char * -xenUnifiedDomainDumpXML (virDomainPtr dom, int flags) -{ - GET_PRIVATE(dom->conn); - - if (dom->id == -1 && priv->xendConfigVersion < 3 ) { - if (priv->opened[XEN_UNIFIED_XM_OFFSET]) - return xenXMDomainDumpXML(dom, flags); - } else { - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { - char *cpus, *res; - xenUnifiedLock(priv); - cpus = xenDomainUsedCpus(dom); - xenUnifiedUnlock(priv); - res = xenDaemonDomainDumpXML(dom, flags, cpus); - VIR_FREE(cpus); - return(res); - } - if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) - return xenProxyDomainDumpXML(dom, flags); - } - - xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return NULL; -} - - -static char * -xenUnifiedDomainXMLFromNative(virConnectPtr conn, - const char *format, - const char *config, - unsigned int flags ATTRIBUTE_UNUSED) -{ - virDomainDefPtr def = NULL; - char *ret = NULL; - virConfPtr conf = NULL; - GET_PRIVATE(conn); - - if (STRNEQ(format, XEN_CONFIG_FORMAT_XM) && - STRNEQ(format, XEN_CONFIG_FORMAT_SEXPR)) { - xenUnifiedError(conn, VIR_ERR_INVALID_ARG, - _("unsupported config type %s"), format); - return NULL; - } - - if (STREQ(format, XEN_CONFIG_FORMAT_XM)) { - conf = virConfReadMem(config, strlen(config), 0); - if (!conf) - goto cleanup; - - def = xenXMDomainConfigParse(conn, conf); - } else if (STREQ(format, XEN_CONFIG_FORMAT_SEXPR)) { - def = xenDaemonParseSxprString(conn, config, priv->xendConfigVersion); - } - if (!def) - goto cleanup; - - ret = virDomainDefFormat(conn, def, 0); - -cleanup: - virDomainDefFree(def); - return ret; -} - - -#define MAX_CONFIG_SIZE (1024 * 65) -static char * -xenUnifiedDomainXMLToNative(virConnectPtr conn, - const char *format, - const char *xmlData, - unsigned int flags ATTRIBUTE_UNUSED) -{ - virDomainDefPtr def = NULL; - char *ret = NULL; - virConfPtr conf = NULL; - GET_PRIVATE(conn); - - if (STRNEQ(format, XEN_CONFIG_FORMAT_XM) && - STRNEQ(format, XEN_CONFIG_FORMAT_SEXPR)) { - xenUnifiedError(conn, VIR_ERR_INVALID_ARG, - _("unsupported config type %s"), format); - goto cleanup; - } - - if (!(def = virDomainDefParseString(conn, - priv->caps, - xmlData, - 0))) - goto cleanup; - - if (STREQ(format, XEN_CONFIG_FORMAT_XM)) { - int len = MAX_CONFIG_SIZE; - conf = xenXMDomainConfigFormat(conn, def); - if (!conf) - goto cleanup; - - if (VIR_ALLOC_N(ret, len) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - if (virConfWriteMem(ret, &len, conf) < 0) { - VIR_FREE(ret); - goto cleanup; - } - } else if (STREQ(format, XEN_CONFIG_FORMAT_SEXPR)) { - ret = xenDaemonFormatSxpr(conn, def, priv->xendConfigVersion); - } - -cleanup: - virDomainDefFree(def); - if (conf) - virConfFree(conf); - return ret; -} - - -static int -xenUnifiedDomainMigratePrepare (virConnectPtr dconn, - char **cookie, - int *cookielen, - const char *uri_in, - char **uri_out, - unsigned long flags, - const char *dname, - unsigned long resource) -{ - GET_PRIVATE(dconn); - - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) - return xenDaemonDomainMigratePrepare (dconn, cookie, cookielen, - uri_in, uri_out, - flags, dname, resource); - - xenUnifiedError (dconn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - -static int -xenUnifiedDomainMigratePerform (virDomainPtr dom, - const char *cookie, - int cookielen, - const char *uri, - unsigned long flags, - const char *dname, - unsigned long resource) -{ - GET_PRIVATE(dom->conn); - - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) - return xenDaemonDomainMigratePerform (dom, cookie, cookielen, uri, - flags, dname, resource); - - xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - -static virDomainPtr -xenUnifiedDomainMigrateFinish (virConnectPtr dconn, - const char *dname, - const char *cookie ATTRIBUTE_UNUSED, - int cookielen ATTRIBUTE_UNUSED, - const char *uri ATTRIBUTE_UNUSED, - unsigned long flags ATTRIBUTE_UNUSED) -{ - return xenUnifiedDomainLookupByName (dconn, dname); -} - -static int -xenUnifiedListDefinedDomains (virConnectPtr conn, char **const names, - int maxnames) -{ - GET_PRIVATE(conn); - int i; - int ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->listDefinedDomains) { - ret = drivers[i]->listDefinedDomains (conn, names, maxnames); - if (ret >= 0) return ret; - } - - return -1; -} - -static int -xenUnifiedNumOfDefinedDomains (virConnectPtr conn) -{ - GET_PRIVATE(conn); - int i; - int ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->numOfDefinedDomains) { - ret = drivers[i]->numOfDefinedDomains (conn); - if (ret >= 0) return ret; - } - - return -1; -} - -static int -xenUnifiedDomainCreate (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainCreate && - drivers[i]->domainCreate (dom) == 0) - return 0; - - return -1; -} - -static virDomainPtr -xenUnifiedDomainDefineXML (virConnectPtr conn, const char *xml) -{ - GET_PRIVATE(conn); - int i; - virDomainPtr ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainDefineXML) { - ret = drivers[i]->domainDefineXML (conn, xml); - if (ret) return ret; - } - - return NULL; -} - -static int -xenUnifiedDomainUndefine (virDomainPtr dom) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainUndefine && - drivers[i]->domainUndefine (dom) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainAttachDevice (virDomainPtr dom, const char *xml) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainAttachDevice && - drivers[i]->domainAttachDevice (dom, xml) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainDetachDevice (virDomainPtr dom, const char *xml) -{ - GET_PRIVATE(dom->conn); - int i; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) - if (priv->opened[i] && drivers[i]->domainDetachDevice && - drivers[i]->domainDetachDevice (dom, xml) == 0) - return 0; - - return -1; -} - -static int -xenUnifiedDomainGetAutostart (virDomainPtr dom, int *autostart) -{ - GET_PRIVATE(dom->conn); - - if (priv->xendConfigVersion < 3) { - if (priv->opened[XEN_UNIFIED_XM_OFFSET]) - return xenXMDomainGetAutostart(dom, autostart); - } else { - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) - return xenDaemonDomainGetAutostart(dom, autostart); - } - - xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - -static int -xenUnifiedDomainSetAutostart (virDomainPtr dom, int autostart) -{ - GET_PRIVATE(dom->conn); - - if (priv->xendConfigVersion < 3) { - if (priv->opened[XEN_UNIFIED_XM_OFFSET]) - return xenXMDomainSetAutostart(dom, autostart); - } else { - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) - return xenDaemonDomainSetAutostart(dom, autostart); - } - - xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - -static char * -xenUnifiedDomainGetSchedulerType (virDomainPtr dom, int *nparams) -{ - GET_PRIVATE(dom->conn); - int i; - char *schedulertype; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; i++) { - if (priv->opened[i] && drivers[i]->domainGetSchedulerType) { - schedulertype = drivers[i]->domainGetSchedulerType (dom, nparams); - if (schedulertype != NULL) - return(schedulertype); - } - } - return(NULL); -} - -static int -xenUnifiedDomainGetSchedulerParameters (virDomainPtr dom, - virSchedParameterPtr params, int *nparams) -{ - GET_PRIVATE(dom->conn); - int i, ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) { - if (priv->opened[i] && drivers[i]->domainGetSchedulerParameters) { - ret = drivers[i]->domainGetSchedulerParameters(dom, params, nparams); - if (ret == 0) - return(0); - } - } - return(-1); -} - -static int -xenUnifiedDomainSetSchedulerParameters (virDomainPtr dom, - virSchedParameterPtr params, int nparams) -{ - GET_PRIVATE(dom->conn); - int i, ret; - - for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i) { - if (priv->opened[i] && drivers[i]->domainSetSchedulerParameters) { - ret = drivers[i]->domainSetSchedulerParameters(dom, params, nparams); - if (ret == 0) - return 0; - } - } - - return(-1); -} - -static int -xenUnifiedDomainBlockStats (virDomainPtr dom, const char *path, - struct _virDomainBlockStats *stats) -{ - GET_PRIVATE (dom->conn); - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) - return xenHypervisorDomainBlockStats (dom, path, stats); - - xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - -static int -xenUnifiedDomainInterfaceStats (virDomainPtr dom, const char *path, - struct _virDomainInterfaceStats *stats) -{ - GET_PRIVATE (dom->conn); - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) - return xenHypervisorDomainInterfaceStats (dom, path, stats); - - xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - -static int -xenUnifiedDomainBlockPeek (virDomainPtr dom, const char *path, - unsigned long long offset, size_t size, - void *buffer, unsigned int flags ATTRIBUTE_UNUSED) -{ - int r; - GET_PRIVATE (dom->conn); - - if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) { - r = xenDaemonDomainBlockPeek (dom, path, offset, size, buffer); - if (r != -2) return r; - /* r == -2 means declined, so fall through to XM driver ... */ - } - - if (priv->opened[XEN_UNIFIED_XM_OFFSET]) { - if (xenXMDomainBlockPeek (dom, path, offset, size, buffer) == 0) - return 0; - } - - xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - -static int -xenUnifiedNodeGetCellsFreeMemory (virConnectPtr conn, unsigned long long *freeMems, - int startCell, int maxCells) -{ - GET_PRIVATE (conn); - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) - return xenHypervisorNodeGetCellsFreeMemory (conn, freeMems, - startCell, maxCells); - - xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - -static unsigned long long -xenUnifiedNodeGetFreeMemory (virConnectPtr conn) -{ - unsigned long long freeMem = 0; - int ret; - GET_PRIVATE (conn); - - if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) { - ret = xenHypervisorNodeGetCellsFreeMemory (conn, &freeMem, - -1, 1); - if (ret != 1) - return (0); - return(freeMem); - } - - xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return(0); -} - -static int -xenUnifiedDomainEventRegister (virConnectPtr conn, - virConnectDomainEventCallback callback, - void *opaque, - void (*freefunc)(void *)) -{ - GET_PRIVATE (conn); - - int ret; - xenUnifiedLock(priv); - - if (priv->xsWatch == -1) { - xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - xenUnifiedUnlock(priv); - return -1; - } - - ret = virDomainEventCallbackListAdd(conn, priv->domainEventCallbacks, - callback, opaque, freefunc); - - if (ret == 0) - conn->refs++; - - xenUnifiedUnlock(priv); - return (ret); -} - -static int -xenUnifiedDomainEventDeregister (virConnectPtr conn, - virConnectDomainEventCallback callback) -{ - int ret; - GET_PRIVATE (conn); - xenUnifiedLock(priv); - - if (priv->xsWatch == -1) { - xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - xenUnifiedUnlock(priv); - return -1; - } - - if (priv->domainEventDispatching) - ret = virDomainEventCallbackListMarkDelete(conn, priv->domainEventCallbacks, - callback); - else - ret = virDomainEventCallbackListRemove(conn, priv->domainEventCallbacks, - callback); - - if (ret == 0) - virUnrefConnect(conn); - - xenUnifiedUnlock(priv); - return ret; -} - - -static int -xenUnifiedNodeDeviceGetPciInfo (virNodeDevicePtr dev, - unsigned *domain, - unsigned *bus, - unsigned *slot, - unsigned *function) -{ - virNodeDeviceDefPtr def = NULL; - virNodeDevCapsDefPtr cap; - char *xml = NULL; - int ret = -1; - - xml = virNodeDeviceGetXMLDesc(dev, 0); - if (!xml) - goto out; - - def = virNodeDeviceDefParseString(dev->conn, xml, EXISTING_DEVICE); - if (!def) - goto out; - - cap = def->caps; - while (cap) { - if (cap->type == VIR_NODE_DEV_CAP_PCI_DEV) { - *domain = cap->data.pci_dev.domain; - *bus = cap->data.pci_dev.bus; - *slot = cap->data.pci_dev.slot; - *function = cap->data.pci_dev.function; - break; - } - - cap = cap->next; - } - - if (!cap) { - xenUnifiedError(dev->conn, VIR_ERR_INVALID_ARG, - _("device %s is not a PCI device"), dev->name); - goto out; - } - - ret = 0; -out: - virNodeDeviceDefFree(def); - VIR_FREE(xml); - return ret; -} - -static int -xenUnifiedNodeDeviceDettach (virNodeDevicePtr dev) -{ - pciDevice *pci; - unsigned domain, bus, slot, function; - int ret = -1; - - if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) - return -1; - - pci = pciGetDevice(dev->conn, domain, bus, slot, function); - if (!pci) - return -1; - - if (pciDettachDevice(dev->conn, pci) < 0) - goto out; - - ret = 0; -out: - pciFreeDevice(dev->conn, pci); - return ret; -} - -static int -xenUnifiedNodeDeviceReAttach (virNodeDevicePtr dev) -{ - pciDevice *pci; - unsigned domain, bus, slot, function; - int ret = -1; - - if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) - return -1; - - pci = pciGetDevice(dev->conn, domain, bus, slot, function); - if (!pci) - return -1; - - if (pciReAttachDevice(dev->conn, pci) < 0) - goto out; - - ret = 0; -out: - pciFreeDevice(dev->conn, pci); - return ret; -} - -static int -xenUnifiedNodeDeviceReset (virNodeDevicePtr dev) -{ - pciDevice *pci; - unsigned domain, bus, slot, function; - int ret = -1; - - if (xenUnifiedNodeDeviceGetPciInfo(dev, &domain, &bus, &slot, &function) < 0) - return -1; - - pci = pciGetDevice(dev->conn, domain, bus, slot, function); - if (!pci) - return -1; - - if (pciResetDevice(dev->conn, pci, NULL) < 0) - goto out; - - ret = 0; -out: - pciFreeDevice(dev->conn, pci); - return ret; -} - - -/*----- Register with libvirt.c, and initialise Xen drivers. -----*/ - -/* The interface which we export upwards to libvirt.c. */ -static virDriver xenUnifiedDriver = { - VIR_DRV_XEN_UNIFIED, - "Xen", - xenUnifiedOpen, /* open */ - xenUnifiedClose, /* close */ - xenUnifiedSupportsFeature, /* supports_feature */ - xenUnifiedType, /* type */ - xenUnifiedGetVersion, /* version */ - xenUnifiedGetHostname, /* getHostname */ - xenUnifiedGetMaxVcpus, /* getMaxVcpus */ - xenUnifiedNodeGetInfo, /* nodeGetInfo */ - xenUnifiedGetCapabilities, /* getCapabilities */ - xenUnifiedListDomains, /* listDomains */ - xenUnifiedNumOfDomains, /* numOfDomains */ - xenUnifiedDomainCreateXML, /* domainCreateXML */ - xenUnifiedDomainLookupByID, /* domainLookupByID */ - xenUnifiedDomainLookupByUUID, /* domainLookupByUUID */ - xenUnifiedDomainLookupByName, /* domainLookupByName */ - xenUnifiedDomainSuspend, /* domainSuspend */ - xenUnifiedDomainResume, /* domainResume */ - xenUnifiedDomainShutdown, /* domainShutdown */ - xenUnifiedDomainReboot, /* domainReboot */ - xenUnifiedDomainDestroy, /* domainDestroy */ - xenUnifiedDomainGetOSType, /* domainGetOSType */ - xenUnifiedDomainGetMaxMemory, /* domainGetMaxMemory */ - xenUnifiedDomainSetMaxMemory, /* domainSetMaxMemory */ - xenUnifiedDomainSetMemory, /* domainSetMemory */ - xenUnifiedDomainGetInfo, /* domainGetInfo */ - xenUnifiedDomainSave, /* domainSave */ - xenUnifiedDomainRestore, /* domainRestore */ - xenUnifiedDomainCoreDump, /* domainCoreDump */ - xenUnifiedDomainSetVcpus, /* domainSetVcpus */ - xenUnifiedDomainPinVcpu, /* domainPinVcpu */ - xenUnifiedDomainGetVcpus, /* domainGetVcpus */ - xenUnifiedDomainGetMaxVcpus, /* domainGetMaxVcpus */ - NULL, /* domainGetSecurityLabel */ - NULL, /* nodeGetSecurityModel */ - xenUnifiedDomainDumpXML, /* domainDumpXML */ - xenUnifiedDomainXMLFromNative, /* domainXmlFromNative */ - xenUnifiedDomainXMLToNative, /* domainXmlToNative */ - xenUnifiedListDefinedDomains, /* listDefinedDomains */ - xenUnifiedNumOfDefinedDomains, /* numOfDefinedDomains */ - xenUnifiedDomainCreate, /* domainCreate */ - xenUnifiedDomainDefineXML, /* domainDefineXML */ - xenUnifiedDomainUndefine, /* domainUndefine */ - xenUnifiedDomainAttachDevice, /* domainAttachDevice */ - xenUnifiedDomainDetachDevice, /* domainDetachDevice */ - xenUnifiedDomainGetAutostart, /* domainGetAutostart */ - xenUnifiedDomainSetAutostart, /* domainSetAutostart */ - xenUnifiedDomainGetSchedulerType, /* domainGetSchedulerType */ - xenUnifiedDomainGetSchedulerParameters, /* domainGetSchedulerParameters */ - xenUnifiedDomainSetSchedulerParameters, /* domainSetSchedulerParameters */ - xenUnifiedDomainMigratePrepare, /* domainMigratePrepare */ - xenUnifiedDomainMigratePerform, /* domainMigratePerform */ - xenUnifiedDomainMigrateFinish, /* domainMigrateFinish */ - xenUnifiedDomainBlockStats, /* domainBlockStats */ - xenUnifiedDomainInterfaceStats, /* domainInterfaceStats */ - xenUnifiedDomainBlockPeek, /* domainBlockPeek */ - NULL, /* domainMemoryPeek */ - xenUnifiedNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ - xenUnifiedNodeGetFreeMemory, /* getFreeMemory */ - xenUnifiedDomainEventRegister, /* domainEventRegister */ - xenUnifiedDomainEventDeregister, /* domainEventDeregister */ - NULL, /* domainMigratePrepare2 */ - NULL, /* domainMigrateFinish2 */ - xenUnifiedNodeDeviceDettach, /* nodeDeviceDettach */ - xenUnifiedNodeDeviceReAttach, /* nodeDeviceReAttach */ - xenUnifiedNodeDeviceReset, /* nodeDeviceReset */ -}; - -/** - * xenRegister: - * - * Register xen related drivers - * - * Returns the driver priority or -1 in case of error. - */ -int -xenRegister (void) -{ - /* Ignore failures here. */ - (void) xenHypervisorInit (); - -#ifdef WITH_LIBVIRTD - if (virRegisterStateDriver (&state_driver) == -1) return -1; -#endif - - return virRegisterDriver (&xenUnifiedDriver); -} - -/** - * xenUnifiedDomainInfoListFree: - * - * Free the Domain Info List - */ -void -xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr list) -{ - int i; - - if (list == NULL) - return; - - for (i=0; icount; i++) { - VIR_FREE(list->doms[i]->name); - VIR_FREE(list->doms[i]); - } - VIR_FREE(list); -} - -/** - * xenUnifiedAddDomainInfo: - * - * Add name and uuid to the domain info list - * - * Returns: 0 on success, -1 on failure - */ -int -xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr list, - int id, char *name, - unsigned char *uuid) -{ - xenUnifiedDomainInfoPtr info; - int n; - - /* check if we already have this callback on our list */ - for (n=0; n < list->count; n++) { - if (STREQ(list->doms[n]->name, name) && - !memcmp(list->doms[n]->uuid, uuid, VIR_UUID_BUFLEN)) { - DEBUG0("WARNING: dom already tracked"); - return -1; - } - } - - if (VIR_ALLOC(info) < 0) - goto memory_error; - if (!(info->name = strdup(name))) - goto memory_error; - - memcpy(info->uuid, uuid, VIR_UUID_BUFLEN); - info->id = id; - - /* Make space on list */ - n = list->count; - if (VIR_REALLOC_N(list->doms, n + 1) < 0) { - goto memory_error; - } - - list->doms[n] = info; - list->count++; - return 0; -memory_error: - virReportOOMError (NULL); - if (info) - VIR_FREE(info->name); - VIR_FREE(info); - return -1; -} - -/** - * xenUnifiedRemoveDomainInfo: - * - * Removes name and uuid to the domain info list - * - * Returns: 0 on success, -1 on failure - */ -int -xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr list, - int id, char *name, - unsigned char *uuid) -{ - int i; - for (i = 0 ; i < list->count ; i++) { - if( list->doms[i]->id == id && - STREQ(list->doms[i]->name, name) && - !memcmp(list->doms[i]->uuid, uuid, VIR_UUID_BUFLEN)) { - - VIR_FREE(list->doms[i]->name); - VIR_FREE(list->doms[i]); - - if (i < (list->count - 1)) - memmove(list->doms + i, - list->doms + i + 1, - sizeof(*(list->doms)) * - (list->count - (i + 1))); - - if (VIR_REALLOC_N(list->doms, - list->count - 1) < 0) { - ; /* Failure to reduce memory allocation isn't fatal */ - } - list->count--; - - return 0; - } - } - return -1; -} - -static void -xenUnifiedDomainEventDispatchFunc(virConnectPtr conn, - virDomainEventPtr event, - virConnectDomainEventCallback cb, - void *cbopaque, - void *opaque) -{ - xenUnifiedPrivatePtr priv = opaque; - - /* - * Release the lock while the callback is running so that - * we're re-entrant safe for callback work - the callback - * may want to invoke other virt functions & we have already - * protected the one piece of state we have - the callback - * list - */ - xenUnifiedUnlock(priv); - virDomainEventDispatchDefaultFunc(conn, event, cb, cbopaque, NULL); - xenUnifiedLock(priv); -} - -/** - * xenUnifiedDomainEventDispatch: - * @priv: the connection to dispatch events on - * @event: the event to dispatch - * - * Dispatch domain events to registered callbacks - * - * The caller must hold the lock in 'priv' before invoking - * - */ -void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv, - virDomainEventPtr event) -{ - if (!priv) - return; - - priv->domainEventDispatching = 1; - - if (priv->domainEventCallbacks) { - virDomainEventDispatch(event, - priv->domainEventCallbacks, - xenUnifiedDomainEventDispatchFunc, - priv); - - /* Purge any deleted callbacks */ - virDomainEventCallbackListPurgeMarked(priv->domainEventCallbacks); - } - - virDomainEventFree(event); - - priv->domainEventDispatching = 0; -} - -void xenUnifiedLock(xenUnifiedPrivatePtr priv) -{ - virMutexLock(&priv->lock); -} - -void xenUnifiedUnlock(xenUnifiedPrivatePtr priv) -{ - virMutexUnlock(&priv->lock); -} diff --git a/src/xen_unified.h b/src/xen_unified.h deleted file mode 100644 index 9cc877b03a..0000000000 --- a/src/xen_unified.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * xen_unified.c: Unified Xen driver. - * - * Copyright (C) 2007 Red Hat, Inc. - * - * See COPYING.LIB for the License of this software - * - * Richard W.M. Jones - */ - -#ifndef __VIR_XEN_UNIFIED_H__ -#define __VIR_XEN_UNIFIED_H__ - -#include "internal.h" -#include "capabilities.h" -#include "driver.h" -#include "domain_conf.h" -#include "xs_internal.h" -#if WITH_XEN_INOTIFY -#include "xen_inotify.h" -#endif -#include "domain_event.h" -#include "hash.h" - -#ifndef HAVE_WINSOCK2_H -#include -#include -#else -#include -#endif - -extern int xenRegister (void); - -#define XEN_UNIFIED_HYPERVISOR_OFFSET 0 -#define XEN_UNIFIED_PROXY_OFFSET 1 -#define XEN_UNIFIED_XEND_OFFSET 2 -#define XEN_UNIFIED_XS_OFFSET 3 -#define XEN_UNIFIED_XM_OFFSET 4 - -#if WITH_XEN_INOTIFY -#define XEN_UNIFIED_INOTIFY_OFFSET 5 -#define XEN_UNIFIED_NR_DRIVERS 6 -#else -#define XEN_UNIFIED_NR_DRIVERS 5 -#endif - -#define MIN_XEN_GUEST_SIZE 64 /* 64 megabytes */ - -#define XEN_CONFIG_FORMAT_XM "xen-xm" -#define XEN_CONFIG_FORMAT_SEXPR "xen-sxpr" - -/* _xenUnifiedDriver: - * - * Entry points into the underlying Xen drivers. This structure - * will eventually go away and instead xen unified will make direct - * calls to the underlying Xen drivers. - * - * To reiterate - the goal is to remove elements from this structure - * until it is empty, replacing indirect calls through this - * structure with direct calls in xen_unified.c. - */ -struct xenUnifiedDriver { - virDrvOpen open; - virDrvClose close; - virDrvGetVersion version; - virDrvGetHostname getHostname; - virDrvNodeGetInfo nodeGetInfo; - virDrvGetCapabilities getCapabilities; - virDrvListDomains listDomains; - virDrvNumOfDomains numOfDomains; - virDrvDomainCreateXML domainCreateXML; - virDrvDomainSuspend domainSuspend; - virDrvDomainResume domainResume; - virDrvDomainShutdown domainShutdown; - virDrvDomainReboot domainReboot; - virDrvDomainDestroy domainDestroy; - virDrvDomainGetOSType domainGetOSType; - virDrvDomainGetMaxMemory domainGetMaxMemory; - virDrvDomainSetMaxMemory domainSetMaxMemory; - virDrvDomainSetMemory domainSetMemory; - virDrvDomainGetInfo domainGetInfo; - virDrvDomainSave domainSave; - virDrvDomainRestore domainRestore; - virDrvDomainCoreDump domainCoreDump; - virDrvDomainSetVcpus domainSetVcpus; - virDrvDomainPinVcpu domainPinVcpu; - virDrvDomainGetVcpus domainGetVcpus; - virDrvDomainGetMaxVcpus domainGetMaxVcpus; - virDrvListDefinedDomains listDefinedDomains; - virDrvNumOfDefinedDomains numOfDefinedDomains; - virDrvDomainCreate domainCreate; - virDrvDomainDefineXML domainDefineXML; - virDrvDomainUndefine domainUndefine; - virDrvDomainAttachDevice domainAttachDevice; - virDrvDomainDetachDevice domainDetachDevice; - virDrvDomainGetAutostart domainGetAutostart; - virDrvDomainSetAutostart domainSetAutostart; - virDrvDomainGetSchedulerType domainGetSchedulerType; - virDrvDomainGetSchedulerParameters domainGetSchedulerParameters; - virDrvDomainSetSchedulerParameters domainSetSchedulerParameters; -}; - -typedef struct xenXMConfCache *xenXMConfCachePtr; -typedef struct xenXMConfCache { - time_t refreshedAt; - char filename[PATH_MAX]; - virDomainDefPtr def; -} xenXMConfCache; - -/* xenUnifiedDomainInfoPtr: - * The minimal state we have about active domains - * This is the minmal info necessary to still get a - * virDomainPtr when the domain goes away - */ -struct _xenUnifiedDomainInfo { - int id; - char *name; - unsigned char uuid[VIR_UUID_BUFLEN]; -}; -typedef struct _xenUnifiedDomainInfo xenUnifiedDomainInfo; -typedef xenUnifiedDomainInfo *xenUnifiedDomainInfoPtr; - -struct _xenUnifiedDomainInfoList { - unsigned int count; - xenUnifiedDomainInfoPtr *doms; -}; -typedef struct _xenUnifiedDomainInfoList xenUnifiedDomainInfoList; -typedef xenUnifiedDomainInfoList *xenUnifiedDomainInfoListPtr; - -/* xenUnifiedPrivatePtr: - * - * Per-connection private data, stored in conn->privateData. All Xen - * low-level drivers access parts of this structure. - */ -struct _xenUnifiedPrivate { - virMutex lock; - - /* These initial vars are initialized in Open method - * and readonly thereafter, so can be used without - * holding the lock - */ - virCapsPtr caps; - int handle; /* Xen hypervisor handle */ - - int xendConfigVersion; /* XenD config version */ - - /* connection to xend */ - struct sockaddr_storage addr; - socklen_t addrlen; - int addrfamily; - int addrprotocol; - - /* Keep track of the drivers which opened. We keep a yes/no flag - * here for each driver, corresponding to the array drivers in - * xen_unified.c. - */ - int opened[XEN_UNIFIED_NR_DRIVERS]; - - - /* - * Everything from this point onwards must be protected - * by the lock when used - */ - - struct xs_handle *xshandle; /* handle to talk to the xenstore */ - - int proxy; /* fd of proxy. */ - - - /* A list of xenstore watches */ - xenStoreWatchListPtr xsWatchList; - int xsWatch; - /* A list of active domain name/uuids */ - xenUnifiedDomainInfoListPtr activeDomainList; - - /* NUMA topology info cache */ - int nbNodeCells; - int nbNodeCpus; - - /* An list of callbacks */ - virDomainEventCallbackListPtr domainEventCallbacks; - int domainEventDispatching; - - /* Location of config files, either /etc - * or /var/lib/xen */ - const char *configDir; - -#if WITH_XEN_INOTIFY - /* The inotify fd */ - int inotifyFD; - int inotifyWatch; - - int useXenConfigCache ; - xenUnifiedDomainInfoListPtr configInfoList; -#endif - - /* For the 'xm' driver */ - /* Primary config file name -> virDomainDef map */ - virHashTablePtr configCache; - /* Domain name to config file name */ - virHashTablePtr nameConfigMap; - /* So we don't refresh too often */ - time_t lastRefresh; -}; - -typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; - -char *xenDomainUsedCpus(virDomainPtr dom); - -void xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr info); -int xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr info, - int id, char *name, - unsigned char *uuid); -int xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr info, - int id, char *name, - unsigned char *uuid); -void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv, - virDomainEventPtr event); -unsigned long xenUnifiedVersion(void); - -#ifndef PROXY -void xenUnifiedLock(xenUnifiedPrivatePtr priv); -void xenUnifiedUnlock(xenUnifiedPrivatePtr priv); -#else -#define xenUnifiedLock(p) do {} while(0) -#define xenUnifiedUnlock(p) do {} while(0) -#endif - -#endif /* __VIR_XEN_UNIFIED_H__ */ diff --git a/src/xend_internal.c b/src/xend_internal.c deleted file mode 100644 index 7f55116c74..0000000000 --- a/src/xend_internal.c +++ /dev/null @@ -1,5914 +0,0 @@ -/* - * xend_internal.c: access to Xen though the Xen Daemon interface - * - * Copyright (C) 2005 - * - * Anthony Liguori - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License. See the file COPYING.LIB in the main directory of this - * archive for more details. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "virterror_internal.h" -#include "logging.h" -#include "datatypes.h" -#include "xend_internal.h" -#include "driver.h" -#include "util.h" -#include "sexpr.h" -#include "buf.h" -#include "uuid.h" -#include "xen_unified.h" -#include "xen_internal.h" -#include "xs_internal.h" /* To extract VNC port & Serial console TTY */ -#include "memory.h" - -/* required for cpumap_t */ -#include - -#define VIR_FROM_THIS VIR_FROM_XEND - -#ifndef PROXY - -/* - * The number of Xen scheduler parameters - */ -#define XEN_SCHED_SEDF_NPARAM 6 -#define XEN_SCHED_CRED_NPARAM 2 - -#endif /* PROXY */ - -#ifdef WITH_RHEL5_API -#define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 0 -#define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 2 -#else -#define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 3 -#define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 3 -#endif - - -#ifndef PROXY -static int -xenDaemonFormatSxprDisk(virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainDiskDefPtr def, - virBufferPtr buf, - int hvm, - int xendConfigVersion, - int isAttach); -static int -xenDaemonFormatSxprNet(virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainNetDefPtr def, - virBufferPtr buf, - int hvm, - int xendConfigVersion, - int isAttach); -static int -xenDaemonFormatSxprOnePCI(virConnectPtr conn, - virDomainHostdevDefPtr def, - virBufferPtr buf); - -static int -virDomainXMLDevID(virDomainPtr domain, - virDomainDeviceDefPtr dev, - char *class, - char *ref, - int ref_len); -#endif - -#define virXendError(conn, code, fmt...) \ - virReportErrorHelper(conn, VIR_FROM_XEND, code, __FILE__, \ - __FUNCTION__, __LINE__, fmt) - -#define virXendErrorInt(conn, code, ival) \ - virXendError(conn, code, "%d", ival) - -/** - * do_connect: - * @xend: pointer to the Xen Daemon structure - * - * Internal routine to (re)connect to the daemon - * - * Returns the socket file descriptor or -1 in case of error - */ -static int -do_connect(virConnectPtr xend) -{ - int s; - int serrno; - int no_slow_start = 1; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) xend->privateData; - - s = socket(priv->addrfamily, SOCK_STREAM, priv->addrprotocol); - if (s == -1) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to create a socket")); - return -1; - } - - /* - * try to desactivate slow-start - */ - setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start, - sizeof(no_slow_start)); - - - if (connect(s, (struct sockaddr *)&priv->addr, priv->addrlen) == -1) { - serrno = errno; - close(s); - errno = serrno; - s = -1; - - /* - * Connecting to XenD when privileged is mandatory, so log this - * error - */ - if (xenHavePrivilege()) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to connect to xend")); - } - } - - return s; -} - -/** - * wr_sync: - * @xend: the xend connection object - * @fd: the file descriptor - * @buffer: the I/O buffer - * @size: the size of the I/O - * @do_read: write operation if 0, read operation otherwise - * - * Do a synchronous read or write on the file descriptor - * - * Returns the number of bytes exchanged, or -1 in case of error - */ -static size_t -wr_sync(virConnectPtr xend, int fd, void *buffer, size_t size, int do_read) -{ - size_t offset = 0; - - while (offset < size) { - ssize_t len; - - if (do_read) { - len = read(fd, ((char *) buffer) + offset, size - offset); - } else { - len = write(fd, ((char *) buffer) + offset, size - offset); - } - - /* recoverable error, retry */ - if ((len == -1) && ((errno == EAGAIN) || (errno == EINTR))) { - continue; - } - - /* eof */ - if (len == 0) { - break; - } - - /* unrecoverable error */ - if (len == -1) { - if (do_read) - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to read from Xen Daemon")); - else - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to read from Xen Daemon")); - - return (-1); - } - - offset += len; - } - - return offset; -} - -/** - * sread: - * @xend: the xend connection object - * @fd: the file descriptor - * @buffer: the I/O buffer - * @size: the size of the I/O - * - * Internal routine to do a synchronous read - * - * Returns the number of bytes read, or -1 in case of error - */ -static ssize_t -sread(virConnectPtr xend, int fd, void *buffer, size_t size) -{ - return wr_sync(xend, fd, buffer, size, 1); -} - -/** - * swrite: - * @xend: the xend connection object - * @fd: the file descriptor - * @buffer: the I/O buffer - * @size: the size of the I/O - * - * Internal routine to do a synchronous write - * - * Returns the number of bytes written, or -1 in case of error - */ -static ssize_t -swrite(virConnectPtr xend, int fd, const void *buffer, size_t size) -{ - return wr_sync(xend, fd, (void *) buffer, size, 0); -} - -/** - * swrites: - * @xend: the xend connection object - * @fd: the file descriptor - * @string: the string to write - * - * Internal routine to do a synchronous write of a string - * - * Returns the number of bytes written, or -1 in case of error - */ -static ssize_t -swrites(virConnectPtr xend, int fd, const char *string) -{ - return swrite(xend, fd, string, strlen(string)); -} - -/** - * sreads: - * @xend: the xend connection object - * @fd: the file descriptor - * @buffer: the I/O buffer - * @n_buffer: the size of the I/O buffer - * - * Internal routine to do a synchronous read of a line - * - * Returns the number of bytes read, or -1 in case of error - */ -static ssize_t -sreads(virConnectPtr xend, int fd, char *buffer, size_t n_buffer) -{ - size_t offset; - - if (n_buffer < 1) - return (-1); - - for (offset = 0; offset < (n_buffer - 1); offset++) { - ssize_t ret; - - ret = sread(xend, fd, buffer + offset, 1); - if (ret == 0) - break; - else if (ret == -1) - return ret; - - if (buffer[offset] == '\n') { - offset++; - break; - } - } - buffer[offset] = 0; - - return offset; -} - -static int -istartswith(const char *haystack, const char *needle) -{ - return STRCASEEQLEN(haystack, needle, strlen(needle)); -} - - -/** - * xend_req: - * @xend: the xend connection object - * @fd: the file descriptor - * @content: the buffer to store the content - * @n_content: the size of the buffer - * - * Read the HTTP response from a Xen Daemon request. - * - * Returns the HTTP return code. - */ -static int -xend_req(virConnectPtr xend, int fd, char *content, size_t n_content) -{ - char buffer[4096]; - int content_length = -1; - int retcode = 0; - - while (sreads(xend, fd, buffer, sizeof(buffer)) > 0) { - if (STREQ(buffer, "\r\n")) - break; - - if (istartswith(buffer, "Content-Length: ")) - content_length = atoi(buffer + 16); - else if (istartswith(buffer, "HTTP/1.1 ")) - retcode = atoi(buffer + 9); - } - - if (content_length > -1) { - ssize_t ret; - - if ((unsigned int) content_length > (n_content + 1)) - content_length = n_content - 1; - - ret = sread(xend, fd, content, content_length); - if (ret < 0) - return -1; - - content[ret] = 0; - } else { - content[0] = 0; - } - - return retcode; -} - -/** - * xend_get: - * @xend: pointer to the Xen Daemon structure - * @path: the path used for the HTTP request - * @content: the buffer to store the content - * @n_content: the size of the buffer - * - * Do an HTTP GET RPC with the Xen Daemon - * - * Returns the HTTP return code or -1 in case or error. - */ -static int -xend_get(virConnectPtr xend, const char *path, - char *content, size_t n_content) -{ - int ret; - int s = do_connect(xend); - - if (s == -1) - return s; - - swrites(xend, s, "GET "); - swrites(xend, s, path); - swrites(xend, s, " HTTP/1.1\r\n"); - - swrites(xend, s, - "Host: localhost:8000\r\n" - "Accept-Encoding: identity\r\n" - "Content-Type: application/x-www-form-urlencoded\r\n" "\r\n"); - - ret = xend_req(xend, s, content, n_content); - close(s); - - if (((ret < 0) || (ret >= 300)) && - ((ret != 404) || (!STRPREFIX(path, "/xend/domain/")))) { - virXendError(xend, VIR_ERR_GET_FAILED, - _("%d status from xen daemon: %s:%s"), - ret, path, content); - } - - return ret; -} - -#ifndef PROXY -/** - * xend_post: - * @xend: pointer to the Xen Daemon structure - * @path: the path used for the HTTP request - * @ops: the information sent for the POST - * @content: the buffer to store the content - * @n_content: the size of the buffer - * - * Do an HTTP POST RPC with the Xen Daemon, this usually makes changes at the - * Xen level. - * - * Returns the HTTP return code or -1 in case or error. - */ -static int -xend_post(virConnectPtr xend, const char *path, const char *ops, - char *content, size_t n_content) -{ - char buffer[100]; - int ret; - int s = do_connect(xend); - - if (s == -1) - return s; - - swrites(xend, s, "POST "); - swrites(xend, s, path); - swrites(xend, s, " HTTP/1.1\r\n"); - - swrites(xend, s, - "Host: localhost:8000\r\n" - "Accept-Encoding: identity\r\n" - "Content-Type: application/x-www-form-urlencoded\r\n" - "Content-Length: "); - snprintf(buffer, sizeof(buffer), "%d", (int) strlen(ops)); - swrites(xend ,s, buffer); - swrites(xend, s, "\r\n\r\n"); - swrites(xend, s, ops); - - ret = xend_req(xend, s, content, n_content); - close(s); - - if ((ret < 0) || (ret >= 300)) { - virXendError(xend, VIR_ERR_POST_FAILED, - _("xend_post: error from xen daemon: %s"), content); - } else if ((ret == 202) && (strstr(content, "failed") != NULL)) { - virXendError(xend, VIR_ERR_POST_FAILED, - _("xend_post: error from xen daemon: %s"), content); - ret = -1; - } else if (((ret >= 200) && (ret <= 202)) && (strstr(content, "xend.err") != NULL)) { - /* This is to catch case of things like 'virsh dump Domain-0 foo' - * which returns a success code, but the word 'xend.err' - * in body to indicate error :-( - */ - virXendError(xend, VIR_ERR_POST_FAILED, - _("xend_post: error from xen daemon: %s"), content); - ret = -1; - } - - return ret; -} -#endif /* ! PROXY */ - - -/** - * http2unix: - * @xend: the xend connection object - * @ret: the http return code - * - * Convert the HTTP return code to 0/-1 and set errno if needed - * - * Return -1 in case of error code 0 otherwise - */ -static int -http2unix(virConnectPtr xend, int ret) -{ - switch (ret) { - case -1: - break; - case 200: - case 201: - case 202: - return 0; - case 404: - errno = ESRCH; - break; - case 500: - errno = EIO; - break; - default: - virXendErrorInt(xend, VIR_ERR_HTTP_ERROR, ret); - errno = EINVAL; - break; - } - return -1; -} - -#ifndef PROXY -/** - * xend_op_ext: - * @xend: pointer to the Xen Daemon structure - * @path: path for the object - * @error: buffer for the error output - * @n_error: size of @error - * @key: the key for the operation - * @ap: input values to pass to the operation - * - * internal routine to run a POST RPC operation to the Xen Daemon - * - * Returns 0 in case of success, -1 in case of failure. - */ -static int -xend_op_ext(virConnectPtr xend, const char *path, char *error, - size_t n_error, const char *key, va_list ap) -{ - const char *k = key, *v; - virBuffer buf = VIR_BUFFER_INITIALIZER; - int ret; - char *content; - - while (k) { - v = va_arg(ap, const char *); - - virBufferVSprintf(&buf, "%s", k); - virBufferVSprintf(&buf, "%s", "="); - virBufferVSprintf(&buf, "%s", v); - k = va_arg(ap, const char *); - - if (k) - virBufferVSprintf(&buf, "%s", "&"); - } - - if (virBufferError(&buf)) { - virReportOOMError(NULL); - return -1; - } - - content = virBufferContentAndReset(&buf); - ret = http2unix(xend, xend_post(xend, path, content, error, n_error)); - VIR_FREE(content); - - return ret; -} - - -/** - * xend_op: - * @xend: pointer to the Xen Daemon structure - * @name: the domain name target of this operation - * @error: buffer for the error output - * @n_error: size of @error - * @key: the key for the operation - * @ap: input values to pass to the operation - * @...: input values to pass to the operation - * - * internal routine to run a POST RPC operation to the Xen Daemon targetting - * a given domain. - * - * Returns 0 in case of success, -1 in case of failure. - */ -static int -xend_op(virConnectPtr xend, const char *name, const char *key, ...) -{ - char buffer[1024]; - char error[1024]; - va_list ap; - int ret; - - snprintf(buffer, sizeof(buffer), "/xend/domain/%s", name); - - va_start(ap, key); - ret = xend_op_ext(xend, buffer, error, sizeof(error), key, ap); - va_end(ap); - - return ret; -} - -#endif /* ! PROXY */ - -/** - * sexpr_get: - * @xend: pointer to the Xen Daemon structure - * @fmt: format string for the path of the operation - * @...: extra data to build the path of the operation - * - * Internal routine to run a simple GET RPC operation to the Xen Daemon - * - * Returns a parsed S-Expression in case of success, NULL in case of failure - */ -static struct sexpr *sexpr_get(virConnectPtr xend, const char *fmt, ...) - ATTRIBUTE_FMT_PRINTF(2,3); - -static struct sexpr * -sexpr_get(virConnectPtr xend, const char *fmt, ...) -{ - char buffer[4096]; - char path[1024]; - va_list ap; - int ret; - - va_start(ap, fmt); - vsnprintf(path, sizeof(path), fmt, ap); - va_end(ap); - - ret = xend_get(xend, path, buffer, sizeof(buffer)); - ret = http2unix(xend ,ret); - if (ret == -1) - return NULL; - - return string2sexpr(buffer); -} - -/** - * sexpr_int: - * @sexpr: an S-Expression - * @name: the name for the value - * - * convenience function to lookup an int value in the S-Expression - * - * Returns the value found or 0 if not found (but may not be an error). - * This function suffers from the flaw that zero is both a correct - * return value and an error indicator: careful! - */ -static int -sexpr_int(const struct sexpr *sexpr, const char *name) -{ - const char *value = sexpr_node(sexpr, name); - - if (value) { - return strtol(value, NULL, 0); - } - return 0; -} - - -/** - * sexpr_float: - * @sexpr: an S-Expression - * @name: the name for the value - * - * convenience function to lookup a float value in the S-Expression - * - * Returns the value found or 0 if not found (but may not be an error) - */ -static double -sexpr_float(const struct sexpr *sexpr, const char *name) -{ - const char *value = sexpr_node(sexpr, name); - - if (value) { - return strtod(value, NULL); - } - return 0; -} - -/** - * sexpr_u64: - * @sexpr: an S-Expression - * @name: the name for the value - * - * convenience function to lookup a 64bits unsigned int value in the - * S-Expression - * - * Returns the value found or 0 if not found (but may not be an error) - */ -static uint64_t -sexpr_u64(const struct sexpr *sexpr, const char *name) -{ - const char *value = sexpr_node(sexpr, name); - - if (value) { - return strtoll(value, NULL, 0); - } - return 0; -} - - -/** - * sexpr_uuid: - * @ptr: where to store the UUID, incremented - * @sexpr: an S-Expression - * @name: the name for the value - * - * convenience function to lookup an UUID value from the S-Expression - * - * Returns a -1 on error, 0 on success - */ -static int -sexpr_uuid(unsigned char *ptr, const struct sexpr *node, const char *path) -{ - const char *r = sexpr_node(node, path); - if (!r) - return -1; - return virUUIDParse(r, ptr); -} - - -#ifndef PROXY -/** - * urlencode: - * @string: the input URL - * - * Encode an URL see RFC 2396 and following - * - * Returns the new string or NULL in case of error. - */ -static char * -urlencode(const char *string) -{ - size_t len = strlen(string); - char *buffer; - char *ptr; - size_t i; - - if (VIR_ALLOC_N(buffer, len * 3 + 1) < 0) { - virReportOOMError(NULL); - return (NULL); - } - ptr = buffer; - for (i = 0; i < len; i++) { - switch (string[i]) { - case ' ': - case '\n': - snprintf(ptr, 4, "%%%02x", string[i]); - ptr += 3; - break; - default: - *ptr = string[i]; - ptr++; - } - } - - *ptr = 0; - - return buffer; -} -#endif /* ! PROXY */ - -/* PUBLIC FUNCTIONS */ - -/** - * xenDaemonOpen_unix: - * @conn: an existing virtual connection block - * @path: the path for the Xen Daemon socket - * - * Creates a localhost Xen Daemon connection - * Note: this doesn't try to check if the connection actually works - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenDaemonOpen_unix(virConnectPtr conn, const char *path) -{ - struct sockaddr_un *addr; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - if ((conn == NULL) || (path == NULL)) - return (-1); - - memset(&priv->addr, 0, sizeof(priv->addr)); - priv->addrfamily = AF_UNIX; - /* - * This must be zero on Solaris at least for AF_UNIX (which should - * really be PF_UNIX, but doesn't matter). - */ - priv->addrprotocol = 0; - priv->addrlen = sizeof(struct sockaddr_un); - - addr = (struct sockaddr_un *)&priv->addr; - addr->sun_family = AF_UNIX; - memset(addr->sun_path, 0, sizeof(addr->sun_path)); - strncpy(addr->sun_path, path, sizeof(addr->sun_path)); - - return (0); -} - -#ifndef PROXY -/** - * xenDaemonOpen_tcp: - * @conn: an existing virtual connection block - * @host: the host name for the Xen Daemon - * @port: the port - * - * Creates a possibly remote Xen Daemon connection - * Note: this doesn't try to check if the connection actually works - * - * Returns 0 in case of success, -1 in case of error. - */ -static int -xenDaemonOpen_tcp(virConnectPtr conn, const char *host, const char *port) -{ - xenUnifiedPrivatePtr priv; - struct addrinfo *res, *r; - struct addrinfo hints; - int saved_errno = EINVAL; - int ret; - - if ((conn == NULL) || (host == NULL) || (port == NULL)) - return (-1); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - priv->addrlen = 0; - memset(&priv->addr, 0, sizeof(priv->addr)); - - // http://people.redhat.com/drepper/userapi-ipv6.html - memset (&hints, 0, sizeof hints); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - - ret = getaddrinfo (host, port, &hints, &res); - if (ret != 0) { - virXendError(NULL, VIR_ERR_UNKNOWN_HOST, - _("unable to resolve hostname '%s': %s"), - host, gai_strerror (ret)); - return -1; - } - - /* Try to connect to each returned address in turn. */ - for (r = res; r; r = r->ai_next) { - int sock; - - sock = socket (r->ai_family, SOCK_STREAM, r->ai_protocol); - if (sock == -1) { - saved_errno = errno; - continue; - } - - if (connect (sock, r->ai_addr, r->ai_addrlen) == -1) { - saved_errno = errno; - close (sock); - continue; - } - - priv->addrlen = r->ai_addrlen; - priv->addrfamily = r->ai_family; - priv->addrprotocol = r->ai_protocol; - memcpy(&priv->addr, - r->ai_addr, - r->ai_addrlen); - close(sock); - break; - } - - freeaddrinfo (res); - - if (!priv->addrlen) { - /* Don't raise error when unprivileged, since proxy takes over */ - if (xenHavePrivilege()) - virReportSystemError(conn, saved_errno, - _("unable to connect to '%s:%s'"), - host, port); - return -1; - } - - return 0; -} - - -/** - * xend_wait_for_devices: - * @xend: pointer to the Xem Daemon block - * @name: name for the domain - * - * Block the domain until all the virtual devices are ready. This operation - * is needed when creating a domain before resuming it. - * - * Returns 0 in case of success, -1 (with errno) in case of error. - */ -int -xend_wait_for_devices(virConnectPtr xend, const char *name) -{ - return xend_op(xend, name, "op", "wait_for_devices", NULL); -} - - -#endif /* PROXY */ - - -/** - * xenDaemonListDomainsOld: - * @xend: pointer to the Xem Daemon block - * - * This method will return an array of names of currently running - * domains. The memory should be released will a call to free(). - * - * Returns a list of names or NULL in case of error. - */ -char ** -xenDaemonListDomainsOld(virConnectPtr xend) -{ - size_t extra = 0; - struct sexpr *root = NULL; - char **ret = NULL; - int count = 0; - int i; - char *ptr; - struct sexpr *_for_i, *node; - - root = sexpr_get(xend, "/xend/domain"); - if (root == NULL) - goto error; - - for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; - _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { - if (node->kind != SEXPR_VALUE) - continue; - extra += strlen(node->u.value) + 1; - count++; - } - - /* - * We can'tuse the normal allocation routines as we are mixing - * an array of char * at the beginning followed by an array of char - * ret points to the NULL terminated array of char * - * ptr points to the current string after that array but in the same - * allocated block - */ - if (virAlloc((void *)&ptr, - (count + 1) * sizeof(char *) + extra * sizeof(char)) < 0) - goto error; - - ret = (char **) ptr; - ptr += sizeof(char *) * (count + 1); - - i = 0; - for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; - _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { - if (node->kind != SEXPR_VALUE) - continue; - ret[i] = ptr; - strcpy(ptr, node->u.value); - ptr += strlen(node->u.value) + 1; - i++; - } - - ret[i] = NULL; - - error: - sexpr_free(root); - return ret; -} - -#ifndef PROXY -/** - * xenDaemonDomainCreateXML: - * @xend: A xend instance - * @sexpr: An S-Expr description of the domain. - * - * This method will create a domain based the passed in description. The - * domain will be paused after creation and must be unpaused with - * xenDaemonResumeDomain() to begin execution. - * This method may be deprecated once switching to XML-RPC based communcations - * with xend. - * - * Returns 0 for success, -1 (with errno) on error - */ - -int -xenDaemonDomainCreateXML(virConnectPtr xend, const char *sexpr) -{ - int ret, serrno; - char *ptr; - - ptr = urlencode(sexpr); - if (ptr == NULL) { - /* this should be caught at the interface but ... */ - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to urlencode the create S-Expr")); - return (-1); - } - - ret = xend_op(xend, "", "op", "create", "config", ptr, NULL); - - serrno = errno; - VIR_FREE(ptr); - errno = serrno; - - return ret; -} -#endif /* ! PROXY */ - -/** - * xenDaemonDomainLookupByName_ids: - * @xend: A xend instance - * @domname: The name of the domain - * @uuid: return value for the UUID if not NULL - * - * This method looks up the id of a domain - * - * Returns the id on success; -1 (with errno) on error - */ -int -xenDaemonDomainLookupByName_ids(virConnectPtr xend, const char *domname, - unsigned char *uuid) -{ - struct sexpr *root; - const char *value; - int ret = -1; - - if (uuid != NULL) - memset(uuid, 0, VIR_UUID_BUFLEN); - root = sexpr_get(xend, "/xend/domain/%s?detail=1", domname); - if (root == NULL) - goto error; - - value = sexpr_node(root, "domain/domid"); - if (value == NULL) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing domid")); - goto error; - } - ret = strtol(value, NULL, 0); - if ((ret == 0) && (value[0] != '0')) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incorrect domid not numeric")); - ret = -1; - } else if (uuid != NULL) { - if (sexpr_uuid(uuid, root, "domain/uuid") < 0) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing uuid")); - } - } - - error: - sexpr_free(root); - return (ret); -} - - -/** - * xenDaemonDomainLookupByID: - * @xend: A xend instance - * @id: The id of the domain - * @name: return value for the name if not NULL - * @uuid: return value for the UUID if not NULL - * - * This method looks up the name of a domain based on its id - * - * Returns the 0 on success; -1 (with errno) on error - */ -int -xenDaemonDomainLookupByID(virConnectPtr xend, - int id, - char **domname, - unsigned char *uuid) -{ - const char *name = NULL; - struct sexpr *root; - - memset(uuid, 0, VIR_UUID_BUFLEN); - - root = sexpr_get(xend, "/xend/domain/%d?detail=1", id); - if (root == NULL) - goto error; - - name = sexpr_node(root, "domain/name"); - if (name == NULL) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing name")); - goto error; - } - if (domname) - *domname = strdup(name); - - if (sexpr_uuid(uuid, root, "domain/uuid") < 0) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing uuid")); - goto error; - } - - sexpr_free(root); - return (0); - -error: - sexpr_free(root); - if (domname) - VIR_FREE(*domname); - return (-1); -} - - -#ifndef PROXY -static int -xend_detect_config_version(virConnectPtr conn) { - struct sexpr *root; - const char *value; - xenUnifiedPrivatePtr priv; - - if (!VIR_IS_CONNECT(conn)) { - virXendError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - root = sexpr_get(conn, "/xend/node/"); - if (root == NULL) - return (-1); - - value = sexpr_node(root, "node/xend_config_format"); - - if (value) { - priv->xendConfigVersion = strtol(value, NULL, 10); - } else { - /* Xen prior to 3.0.3 did not have the xend_config_format - field, and is implicitly version 1. */ - priv->xendConfigVersion = 1; - } - sexpr_free(root); - return (0); -} - -#endif /* PROXY */ - -/***************************************************************** - ****** - ****** Parsing of SEXPR into virDomainDef objects - ****** - *****************************************************************/ - -/** - * xenDaemonParseSxprOS - * @xend: the xend connection object - * @node: the root of the parsed S-Expression - * @def: the domain config - * @hvm: true or 1 if no contains HVM S-Expression - * @bootloader: true or 1 if a bootloader is defined - * - * Parse the xend sexp for description of os and append it to buf. - * - * Returns 0 in case of success and -1 in case of error - */ -static int -xenDaemonParseSxprOS(virConnectPtr xend, - const struct sexpr *node, - virDomainDefPtr def, - int hvm) -{ - if (hvm) { - if (sexpr_node_copy(node, "domain/image/hvm/loader", &def->os.loader) < 0) - goto no_memory; - if (def->os.loader == NULL) { - if (sexpr_node_copy(node, "domain/image/hvm/kernel", &def->os.loader) < 0) - goto no_memory; - - if (def->os.loader == NULL) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing HVM loader")); - return(-1); - } - } else { - if (sexpr_node_copy(node, "domain/image/hvm/kernel", &def->os.kernel) < 0) - goto no_memory; - if (sexpr_node_copy(node, "domain/image/hvm/ramdisk", &def->os.initrd) < 0) - goto no_memory; - if (sexpr_node_copy(node, "domain/image/hvm/args", &def->os.cmdline) < 0) - goto no_memory; - if (sexpr_node_copy(node, "domain/image/hvm/root", &def->os.root) < 0) - goto no_memory; - } - } else { - if (sexpr_node_copy(node, "domain/image/linux/kernel", &def->os.kernel) < 0) - goto no_memory; - if (sexpr_node_copy(node, "domain/image/linux/ramdisk", &def->os.initrd) < 0) - goto no_memory; - if (sexpr_node_copy(node, "domain/image/linux/args", &def->os.cmdline) < 0) - goto no_memory; - if (sexpr_node_copy(node, "domain/image/linux/root", &def->os.root) < 0) - goto no_memory; - } - - /* If HVM kenrel == loader, then old xend, so kill off kernel */ - if (hvm && - def->os.kernel && - STREQ(def->os.kernel, def->os.loader)) { - VIR_FREE(def->os.kernel); - } - - if (!def->os.kernel && - hvm) { - const char *boot = sexpr_node(node, "domain/image/hvm/boot"); - if ((boot != NULL) && (boot[0] != 0)) { - while (*boot && - def->os.nBootDevs < VIR_DOMAIN_BOOT_LAST) { - if (*boot == 'a') - def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_FLOPPY; - else if (*boot == 'c') - def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_DISK; - else if (*boot == 'd') - def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_CDROM; - else if (*boot == 'n') - def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_NET; - boot++; - } - } - } - - if (!hvm && - !def->os.kernel && - !def->os.bootloader) { - virXendError(xend, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing kernel & bootloader")); - return -1; - } - - return 0; - -no_memory: - virReportOOMError(xend); - return -1; -} - - -int -xend_parse_sexp_desc_char(virConnectPtr conn, - virBufferPtr buf, - const char *devtype, - int portNum, - const char *value, - const char *tty) -{ - const char *type; - int telnet = 0; - char *bindPort = NULL; - char *bindHost = NULL; - char *connectPort = NULL; - char *connectHost = NULL; - char *path = NULL; - int ret = -1; - - if (value[0] == '/') { - type = "dev"; - } else if (STRPREFIX(value, "null")) { - type = "null"; - value = NULL; - } else if (STRPREFIX(value, "vc")) { - type = "vc"; - value = NULL; - } else if (STRPREFIX(value, "pty")) { - type = "pty"; - value = NULL; - } else if (STRPREFIX(value, "stdio")) { - type = "stdio"; - value = NULL; - } else if (STRPREFIX(value, "file:")) { - type = "file"; - value += sizeof("file:")-1; - } else if (STRPREFIX(value, "pipe:")) { - type = "pipe"; - value += sizeof("pipe:")-1; - } else if (STRPREFIX(value, "tcp:")) { - type = "tcp"; - value += sizeof("tcp:")-1; - } else if (STRPREFIX(value, "telnet:")) { - type = "tcp"; - value += sizeof("telnet:")-1; - telnet = 1; - } else if (STRPREFIX(value, "udp:")) { - type = "udp"; - value += sizeof("udp:")-1; - } else if (STRPREFIX(value, "unix:")) { - type = "unix"; - value += sizeof("unix:")-1; - } else { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Unknown char device type")); - return -1; - } - - /* Compat with legacy syntax */ - if (STREQ(devtype, "console") && - STREQ(type, "pty") && - tty != NULL) { - virBufferVSprintf(buf, " <%s type='%s' tty='%s'>\n", - devtype, type, tty); - } else { - virBufferVSprintf(buf, " <%s type='%s'>\n", - devtype, type); - } - - if (STREQ(type, "null") || - STREQ(type, "vc") || - STREQ(type, "stdio")) { - /* no source needed */ - } else if (STREQ(type, "pty")) { - if (tty) - virBufferVSprintf(buf, " \n", - tty); - } else if (STREQ(type, "file") || - STREQ(type, "pipe")) { - virBufferVSprintf(buf, " \n", - value); - } else if (STREQ(type, "tcp")) { - const char *offset = strchr(value, ':'); - const char *offset2; - const char *mode, *protocol; - - if (offset == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("malformed char device string")); - goto error; - } - - if (offset != value && - (bindHost = strndup(value, offset - value)) == NULL) - goto no_memory; - - offset2 = strchr(offset, ','); - if (offset2 == NULL) - bindPort = strdup(offset+1); - else - bindPort = strndup(offset+1, offset2-(offset+1)); - if (bindPort == NULL) - goto no_memory; - - if (offset2 && strstr(offset2, ",listen")) - mode = "bind"; - else - mode = "connect"; - protocol = telnet ? "telnet":"raw"; - - if (bindHost) { - virBufferVSprintf(buf, - " \n", - mode, bindHost, bindPort); - } else { - virBufferVSprintf(buf, - " \n", - mode, bindPort); - } - virBufferVSprintf(buf, - " \n", - protocol); - } else if (STREQ(type, "udp")) { - const char *offset = strchr(value, ':'); - const char *offset2, *offset3; - - if (offset == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("malformed char device string")); - goto error; - } - - if (offset != value && - (connectHost = strndup(value, offset - value)) == NULL) - goto no_memory; - - offset2 = strchr(offset, '@'); - if (offset2 != NULL) { - if ((connectPort = strndup(offset + 1, offset2-(offset+1))) == NULL) - goto no_memory; - - offset3 = strchr(offset2, ':'); - if (offset3 == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("malformed char device string")); - goto error; - } - - if (offset3 > (offset2 + 1) && - (bindHost = strndup(offset2 + 1, offset3 - (offset2+1))) == NULL) - goto no_memory; - - if ((bindPort = strdup(offset3 + 1)) == NULL) - goto no_memory; - } else { - if ((connectPort = strdup(offset + 1)) == NULL) - goto no_memory; - } - - if (connectPort) { - if (connectHost) { - virBufferVSprintf(buf, - " \n", - connectHost, connectPort); - } else { - virBufferVSprintf(buf, - " \n", - connectPort); - } - } - if (bindPort) { - if (bindHost) { - virBufferVSprintf(buf, - " \n", - bindHost, bindPort); - } else { - virBufferVSprintf(buf, - " \n", - bindPort); - } - } - - } else if (STREQ(type, "unix")) { - const char *offset = strchr(value, ','); - int dolisten = 0; - if (offset) - path = strndup(value, (offset - value)); - else - path = strdup(value); - if (path == NULL) - goto no_memory; - - if (offset != NULL && - strstr(offset, ",listen") != NULL) - dolisten = 1; - - virBufferVSprintf(buf, " \n", - dolisten ? "bind" : "connect", path); - } - - virBufferVSprintf(buf, " \n", - portNum); - - virBufferVSprintf(buf, " \n", - devtype); - - ret = 0; - - if (ret == -1) { -no_memory: - virReportOOMError(conn); - } - -error: - - VIR_FREE(path); - VIR_FREE(bindHost); - VIR_FREE(bindPort); - VIR_FREE(connectHost); - VIR_FREE(connectPort); - - return ret; -} - -virDomainChrDefPtr -xenDaemonParseSxprChar(virConnectPtr conn, - const char *value, - const char *tty) -{ - char prefix[10]; - char *tmp; - virDomainChrDefPtr def; - - if (VIR_ALLOC(def) < 0) { - virReportOOMError(conn); - return NULL; - } - - strncpy(prefix, value, sizeof(prefix)-1); - NUL_TERMINATE(prefix); - - if (value[0] == '/') { - def->type = VIR_DOMAIN_CHR_TYPE_DEV; - } else { - if ((tmp = strchr(prefix, ':')) != NULL) { - *tmp = '\0'; - value += (tmp - prefix) + 1; - } - - if (STREQ(prefix, "telnet")) { - def->type = VIR_DOMAIN_CHR_TYPE_TCP; - def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; - } else { - if ((def->type = virDomainChrTypeFromString(prefix)) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown chr device type '%s'"), prefix); - goto error; - } - } - } - - /* Compat with legacy syntax */ - switch (def->type) { - case VIR_DOMAIN_CHR_TYPE_PTY: - if (tty != NULL && - !(def->data.file.path = strdup(tty))) - goto no_memory; - break; - - case VIR_DOMAIN_CHR_TYPE_FILE: - case VIR_DOMAIN_CHR_TYPE_PIPE: - if (!(def->data.file.path = strdup(value))) - goto no_memory; - break; - - case VIR_DOMAIN_CHR_TYPE_TCP: - { - const char *offset = strchr(value, ':'); - const char *offset2; - - if (offset == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("malformed char device string")); - goto error; - } - - if (offset != value && - (def->data.tcp.host = strndup(value, offset - value)) == NULL) - goto no_memory; - - offset2 = strchr(offset, ','); - if (offset2 == NULL) - def->data.tcp.service = strdup(offset+1); - else - def->data.tcp.service = strndup(offset+1, offset2-(offset+1)); - if (def->data.tcp.service == NULL) - goto no_memory; - - if (offset2 && strstr(offset2, ",listen")) - def->data.tcp.listen = 1; - } - break; - - case VIR_DOMAIN_CHR_TYPE_UDP: - { - const char *offset = strchr(value, ':'); - const char *offset2, *offset3; - - if (offset == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("malformed char device string")); - goto error; - } - - if (offset != value && - (def->data.udp.connectHost = strndup(value, offset - value)) == NULL) - goto no_memory; - - offset2 = strchr(offset, '@'); - if (offset2 != NULL) { - if ((def->data.udp.connectService = strndup(offset + 1, offset2-(offset+1))) == NULL) - goto no_memory; - - offset3 = strchr(offset2, ':'); - if (offset3 == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("malformed char device string")); - goto error; - } - - if (offset3 > (offset2 + 1) && - (def->data.udp.bindHost = strndup(offset2 + 1, offset3 - (offset2+1))) == NULL) - goto no_memory; - - if ((def->data.udp.bindService = strdup(offset3 + 1)) == NULL) - goto no_memory; - } else { - if ((def->data.udp.connectService = strdup(offset + 1)) == NULL) - goto no_memory; - } - } - break; - - case VIR_DOMAIN_CHR_TYPE_UNIX: - { - const char *offset = strchr(value, ','); - if (offset) - def->data.nix.path = strndup(value, (offset - value)); - else - def->data.nix.path = strdup(value); - if (def->data.nix.path == NULL) - goto no_memory; - - if (offset != NULL && - strstr(offset, ",listen") != NULL) - def->data.nix.listen = 1; - } - break; - } - - return def; - -no_memory: - virReportOOMError(conn); -error: - virDomainChrDefFree(def); - return NULL; -} - -/** - * xend_parse_sexp_desc_disks - * @conn: connection - * @root: root sexpr - * @xendConfigVersion: version of xend - * - * This parses out block devices from the domain sexpr - * - * Returns 0 if successful or -1 if failed. - */ -static int -xenDaemonParseSxprDisks(virConnectPtr conn, - virDomainDefPtr def, - const struct sexpr *root, - int hvm, - int xendConfigVersion) -{ - const struct sexpr *cur, *node; - virDomainDiskDefPtr disk = NULL; - - for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { - node = cur->u.s.car; - /* Normally disks are in a (device (vbd ...)) block - but blktap disks ended up in a differently named - (device (tap ....)) block.... */ - if (sexpr_lookup(node, "device/vbd") || - sexpr_lookup(node, "device/tap")) { - char *offset; - const char *src = NULL; - const char *dst = NULL; - const char *mode = NULL; - - /* Again dealing with (vbd...) vs (tap ...) differences */ - if (sexpr_lookup(node, "device/vbd")) { - src = sexpr_node(node, "device/vbd/uname"); - dst = sexpr_node(node, "device/vbd/dev"); - mode = sexpr_node(node, "device/vbd/mode"); - } else { - src = sexpr_node(node, "device/tap/uname"); - dst = sexpr_node(node, "device/tap/dev"); - mode = sexpr_node(node, "device/tap/mode"); - } - - if (VIR_ALLOC(disk) < 0) - goto no_memory; - - if (dst == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, vbd has no dev")); - goto error; - } - - if (src == NULL) { - /* There is a case without the uname to the CD-ROM device */ - offset = strchr(dst, ':'); - if (!offset || - !hvm || - STRNEQ(offset, ":cdrom")) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, vbd has no src")); - goto error; - } - } - - if (src != NULL) { - offset = strchr(src, ':'); - if (!offset) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot parse vbd filename, missing driver name")); - goto error; - } - - if (VIR_ALLOC_N(disk->driverName, (offset-src)+1) < 0) - goto no_memory; - strncpy(disk->driverName, src, (offset-src)); - disk->driverName[offset-src] = '\0'; - - src = offset + 1; - - if (STREQ (disk->driverName, "tap")) { - offset = strchr(src, ':'); - if (!offset) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot parse vbd filename, missing driver type")); - goto error; - } - - if (VIR_ALLOC_N(disk->driverType, (offset-src)+1)< 0) - goto no_memory; - strncpy(disk->driverType, src, (offset-src)); - disk->driverType[offset-src] = '\0'; - - src = offset + 1; - /* Its possible to use blktap driver for block devs - too, but kinda pointless because blkback is better, - so we assume common case here. If blktap becomes - omnipotent, we can revisit this, perhaps stat()'ing - the src file in question */ - disk->type = VIR_DOMAIN_DISK_TYPE_FILE; - } else if (STREQ(disk->driverName, "phy")) { - disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; - } else if (STREQ(disk->driverName, "file")) { - disk->type = VIR_DOMAIN_DISK_TYPE_FILE; - } - } else { - /* No CDROM media so can't really tell. We'll just - call if a FILE for now and update when media - is inserted later */ - disk->type = VIR_DOMAIN_DISK_TYPE_FILE; - } - - if (STREQLEN (dst, "ioemu:", 6)) - dst += 6; - - disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; - /* New style disk config from Xen >= 3.0.3 */ - if (xendConfigVersion > 1) { - offset = strrchr(dst, ':'); - if (offset) { - if (STREQ (offset, ":cdrom")) { - disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; - } else if (STREQ (offset, ":disk")) { - /* The default anyway */ - } else { - /* Unknown, lets pretend its a disk too */ - } - offset[0] = '\0'; - } - } - - if (!(disk->dst = strdup(dst))) - goto no_memory; - if (src && - !(disk->src = strdup(src))) - goto no_memory; - - if (STRPREFIX(disk->dst, "xvd")) - disk->bus = VIR_DOMAIN_DISK_BUS_XEN; - else if (STRPREFIX(disk->dst, "hd")) - disk->bus = VIR_DOMAIN_DISK_BUS_IDE; - else if (STRPREFIX(disk->dst, "sd")) - disk->bus = VIR_DOMAIN_DISK_BUS_SCSI; - else - disk->bus = VIR_DOMAIN_DISK_BUS_IDE; - - if (mode && - strchr(mode, 'r')) - disk->readonly = 1; - if (mode && - strchr(mode, '!')) - disk->shared = 1; - - if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) - goto no_memory; - - def->disks[def->ndisks++] = disk; - disk = NULL; - } - } - - return 0; - -no_memory: - virReportOOMError(conn); - -error: - virDomainDiskDefFree(disk); - return -1; -} - - -static int -xenDaemonParseSxprNets(virConnectPtr conn, - virDomainDefPtr def, - const struct sexpr *root) -{ - virDomainNetDefPtr net = NULL; - const struct sexpr *cur, *node; - const char *tmp; - int vif_index = 0; - - for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { - node = cur->u.s.car; - if (sexpr_lookup(node, "device/vif")) { - const char *tmp2, *model; - char buf[50]; - tmp2 = sexpr_node(node, "device/vif/script"); - tmp = sexpr_node(node, "device/vif/bridge"); - model = sexpr_node(node, "device/vif/model"); - - if (VIR_ALLOC(net) < 0) - goto no_memory; - - if (tmp != NULL || - (tmp2 != NULL && STREQ(tmp2, DEFAULT_VIF_SCRIPT))) { - net->type = VIR_DOMAIN_NET_TYPE_BRIDGE; - /* XXX virtual network reverse resolve */ - - if (tmp && - !(net->data.bridge.brname = strdup(tmp))) - goto no_memory; - if (tmp2 && - net->type == VIR_DOMAIN_NET_TYPE_BRIDGE && - !(net->data.bridge.script = strdup(tmp2))) - goto no_memory; - tmp = sexpr_node(node, "device/vif/ip"); - if (tmp && - !(net->data.bridge.ipaddr = strdup(tmp))) - goto no_memory; - } else { - net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; - if (tmp2 && - !(net->data.ethernet.script = strdup(tmp2))) - goto no_memory; - tmp = sexpr_node(node, "device/vif/ip"); - if (tmp && - !(net->data.ethernet.ipaddr = strdup(tmp))) - goto no_memory; - } - - tmp = sexpr_node(node, "device/vif/vifname"); - if (!tmp) { - snprintf(buf, sizeof(buf), "vif%d.%d", def->id, vif_index); - tmp = buf; - } - if (!(net->ifname = strdup(tmp))) - goto no_memory; - - tmp = sexpr_node(node, "device/vif/mac"); - if (tmp) { - unsigned int mac[6]; - if (sscanf(tmp, "%02x:%02x:%02x:%02x:%02x:%02x", - (unsigned int*)&mac[0], - (unsigned int*)&mac[1], - (unsigned int*)&mac[2], - (unsigned int*)&mac[3], - (unsigned int*)&mac[4], - (unsigned int*)&mac[5]) != 6) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("malformed mac address '%s'"), - tmp); - goto cleanup; - } - net->mac[0] = mac[0]; - net->mac[1] = mac[1]; - net->mac[2] = mac[2]; - net->mac[3] = mac[3]; - net->mac[4] = mac[4]; - net->mac[5] = mac[5]; - } - - if (model && - !(net->model = strdup(model))) - goto no_memory; - - if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0) - goto no_memory; - - def->nets[def->nnets++] = net; - vif_index++; - } - } - - return 0; - -no_memory: - virReportOOMError(conn); -cleanup: - virDomainNetDefFree(net); - return -1; -} - - -int -xenDaemonParseSxprSound(virConnectPtr conn, - virDomainDefPtr def, - const char *str) -{ - if (STREQ(str, "all")) { - int i; - - /* - * Special compatability code for Xen with a bogus - * sound=all in config. - * - * NB delibrately, don't include all possible - * sound models anymore, just the 2 that were - * historically present in Xen's QEMU. - * - * ie just es1370 + sb16. - * - * Hence use of MODEL_ES1370 + 1, instead of MODEL_LAST - */ - - if (VIR_ALLOC_N(def->sounds, - VIR_DOMAIN_SOUND_MODEL_ES1370 + 1) < 0) - goto no_memory; - - - for (i = 0 ; i < (VIR_DOMAIN_SOUND_MODEL_ES1370 + 1) ; i++) { - virDomainSoundDefPtr sound; - if (VIR_ALLOC(sound) < 0) - goto no_memory; - sound->model = i; - def->sounds[def->nsounds++] = sound; - } - } else { - char model[10]; - const char *offset = str, *offset2; - - do { - int len; - virDomainSoundDefPtr sound; - offset2 = strchr(offset, ','); - if (offset2) - len = (offset2 - offset); - else - len = strlen(offset); - if (len > (sizeof(model)-1)) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected sound model %s"), offset); - goto error; - } - strncpy(model, offset, len); - model[len] = '\0'; - - if (VIR_ALLOC(sound) < 0) - goto no_memory; - - if ((sound->model = virDomainSoundModelTypeFromString(model)) < 0) { - VIR_FREE(sound); - goto error; - } - - if (VIR_REALLOC_N(def->sounds, def->nsounds+1) < 0) { - virDomainSoundDefFree(sound); - goto no_memory; - } - - def->sounds[def->nsounds++] = sound; - offset = offset2 ? offset2 + 1 : NULL; - } while (offset); - } - - return 0; - -no_memory: - virReportOOMError(conn); -error: - return -1; -} - - -static int -xenDaemonParseSxprUSB(virConnectPtr conn, - virDomainDefPtr def, - const struct sexpr *root) -{ - struct sexpr *cur, *node; - const char *tmp; - - for (cur = sexpr_lookup(root, "domain/image/hvm"); cur && cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { - node = cur->u.s.car; - if (sexpr_lookup(node, "usbdevice")) { - tmp = sexpr_node(node, "usbdevice"); - if (tmp && *tmp) { - if (STREQ(tmp, "tablet") || - STREQ(tmp, "mouse")) { - virDomainInputDefPtr input; - if (VIR_ALLOC(input) < 0) - goto no_memory; - input->bus = VIR_DOMAIN_INPUT_BUS_USB; - if (STREQ(tmp, "tablet")) - input->type = VIR_DOMAIN_INPUT_TYPE_TABLET; - else - input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE; - - if (VIR_REALLOC_N(def->inputs, def->ninputs+1) < 0) { - VIR_FREE(input); - goto no_memory; - } - def->inputs[def->ninputs++] = input; - } else { - /* XXX Handle other non-input USB devices later */ - } - } - } - } - return 0; - -no_memory: - virReportOOMError(conn); - return -1; -} - -static int -xenDaemonParseSxprGraphicsOld(virConnectPtr conn, - virDomainDefPtr def, - const struct sexpr *root, - int hvm, - int xendConfigVersion) -{ -#ifndef PROXY - xenUnifiedPrivatePtr priv = conn->privateData; -#endif - const char *tmp; - virDomainGraphicsDefPtr graphics = NULL; - - if ((tmp = sexpr_fmt_node(root, "domain/image/%s/vnc", hvm ? "hvm" : "linux")) && - tmp[0] == '1') { - /* Graphics device (HVM, or old (pre-3.0.4) style PV VNC config) */ - int port; - const char *listenAddr = sexpr_fmt_node(root, "domain/image/%s/vnclisten", hvm ? "hvm" : "linux"); - const char *vncPasswd = sexpr_fmt_node(root, "domain/image/%s/vncpasswd", hvm ? "hvm" : "linux"); - const char *keymap = sexpr_fmt_node(root, "domain/image/%s/keymap", hvm ? "hvm" : "linux"); - const char *unused = sexpr_fmt_node(root, "domain/image/%s/vncunused", hvm ? "hvm" : "linux"); - - xenUnifiedLock(priv); - port = xenStoreDomainGetVNCPort(conn, def->id); - xenUnifiedUnlock(priv); - - if (VIR_ALLOC(graphics) < 0) - goto no_memory; - - graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; - /* For Xen >= 3.0.3, don't generate a fixed port mapping - * because it will almost certainly be wrong ! Just leave - * it as -1 which lets caller see that the VNC server isn't - * present yet. Subsquent dumps of the XML will eventually - * find the port in XenStore once VNC server has started - */ - if (port == -1 && xendConfigVersion < 2) - port = 5900 + def->id; - - if ((unused && STREQ(unused, "1")) || port == -1) - graphics->data.vnc.autoport = 1; - graphics->data.vnc.port = port; - - if (listenAddr && - !(graphics->data.vnc.listenAddr = strdup(listenAddr))) - goto no_memory; - - if (vncPasswd && - !(graphics->data.vnc.passwd = strdup(vncPasswd))) - goto no_memory; - - if (keymap && - !(graphics->data.vnc.keymap = strdup(keymap))) - goto no_memory; - - if (VIR_ALLOC_N(def->graphics, 1) < 0) - goto no_memory; - def->graphics[0] = graphics; - def->ngraphics = 1; - graphics = NULL; - } else if ((tmp = sexpr_fmt_node(root, "domain/image/%s/sdl", hvm ? "hvm" : "linux")) && - tmp[0] == '1') { - /* Graphics device (HVM, or old (pre-3.0.4) style PV sdl config) */ - const char *display = sexpr_fmt_node(root, "domain/image/%s/display", hvm ? "hvm" : "linux"); - const char *xauth = sexpr_fmt_node(root, "domain/image/%s/xauthority", hvm ? "hvm" : "linux"); - - if (VIR_ALLOC(graphics) < 0) - goto no_memory; - - graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; - if (display && - !(graphics->data.sdl.display = strdup(display))) - goto no_memory; - if (xauth && - !(graphics->data.sdl.xauth = strdup(xauth))) - goto no_memory; - - if (VIR_ALLOC_N(def->graphics, 1) < 0) - goto no_memory; - def->graphics[0] = graphics; - def->ngraphics = 1; - graphics = NULL; - } - - return 0; - -no_memory: - virReportOOMError(conn); - virDomainGraphicsDefFree(graphics); - return -1; -} - - -static int -xenDaemonParseSxprGraphicsNew(virConnectPtr conn, - virDomainDefPtr def, - const struct sexpr *root) -{ -#ifndef PROXY - xenUnifiedPrivatePtr priv = conn->privateData; -#endif - virDomainGraphicsDefPtr graphics = NULL; - const struct sexpr *cur, *node; - const char *tmp; - - /* append network devices and framebuffer */ - for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { - node = cur->u.s.car; - if (sexpr_lookup(node, "device/vfb")) { - /* New style graphics config for PV guests in >= 3.0.4, - * or for HVM guests in >= 3.0.5 */ - if (sexpr_node(node, "device/vfb/type")) { - tmp = sexpr_node(node, "device/vfb/type"); - } else if (sexpr_node(node, "device/vfb/vnc")) { - tmp = "vnc"; - } else if (sexpr_node(node, "device/vfb/sdl")) { - tmp = "sdl"; - } else { - tmp = "unknown"; - } - - if (VIR_ALLOC(graphics) < 0) - goto no_memory; - - if ((graphics->type = virDomainGraphicsTypeFromString(tmp)) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown graphics type '%s'"), tmp); - goto error; - } - - if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - const char *display = sexpr_node(node, "device/vfb/display"); - const char *xauth = sexpr_node(node, "device/vfb/xauthority"); - if (display && - !(graphics->data.sdl.display = strdup(display))) - goto no_memory; - if (xauth && - !(graphics->data.sdl.xauth = strdup(xauth))) - goto no_memory; - } else { - int port; - const char *listenAddr = sexpr_node(node, "device/vfb/vnclisten"); - const char *vncPasswd = sexpr_node(node, "device/vfb/vncpasswd"); - const char *keymap = sexpr_node(node, "device/vfb/keymap"); - const char *unused = sexpr_node(node, "device/vfb/vncunused"); - - xenUnifiedLock(priv); - port = xenStoreDomainGetVNCPort(conn, def->id); - xenUnifiedUnlock(priv); - - // Didn't find port entry in xenstore - if (port == -1) { - const char *str = sexpr_node(node, "device/vfb/vncdisplay"); - int val; - if (str != NULL && virStrToLong_i(str, NULL, 0, &val) == 0) - port = val; - } - - if ((unused && STREQ(unused, "1")) || port == -1) - graphics->data.vnc.autoport = 1; - - if (port >= 0 && port < 5900) - port += 5900; - graphics->data.vnc.port = port; - - if (listenAddr && - !(graphics->data.vnc.listenAddr = strdup(listenAddr))) - goto no_memory; - - if (vncPasswd && - !(graphics->data.vnc.passwd = strdup(vncPasswd))) - goto no_memory; - - if (keymap && - !(graphics->data.vnc.keymap = strdup(keymap))) - goto no_memory; - } - - if (VIR_ALLOC_N(def->graphics, 1) < 0) - goto no_memory; - def->graphics[0] = graphics; - def->ngraphics = 1; - graphics = NULL; - break; - } - } - - return 0; - -no_memory: - virReportOOMError(conn); -error: - virDomainGraphicsDefFree(graphics); - return -1; -} - -/** - * xenDaemonParseSxprPCI - * @conn: connection - * @root: root sexpr - * - * This parses out block devices from the domain sexpr - * - * Returns 0 if successful or -1 if failed. - */ -static int -xenDaemonParseSxprPCI(virConnectPtr conn, - virDomainDefPtr def, - const struct sexpr *root) -{ - const struct sexpr *cur, *tmp = NULL, *node; - virDomainHostdevDefPtr dev = NULL; - - /* - * With the (domain ...) block we have the following odd setup - * - * (device - * (pci - * (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0)) - * (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0)) - * ) - * ) - * - * Normally there is one (device ...) block per device, but in - * wierd world of Xen PCI, once (device ...) covers multiple - * devices. - */ - - for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { - node = cur->u.s.car; - if ((tmp = sexpr_lookup(node, "device/pci")) != NULL) - break; - } - - if (!tmp) - return 0; - - for (cur = tmp; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) { - const char *domain = NULL; - const char *bus = NULL; - const char *slot = NULL; - const char *func = NULL; - int domainID; - int busID; - int slotID; - int funcID; - - node = cur->u.s.car; - if (!sexpr_lookup(node, "dev")) - continue; - - if (!(domain = sexpr_node(node, "dev/domain"))) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("missing PCI domain")); - goto error; - } - if (!(bus = sexpr_node(node, "dev/bus"))) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("missing PCI bus")); - goto error; - } - if (!(slot = sexpr_node(node, "dev/slot"))) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("missing PCI slot")); - goto error; - } - if (!(func = sexpr_node(node, "dev/func"))) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("missing PCI func")); - goto error; - } - - if (virStrToLong_i(domain, NULL, 0, &domainID) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot parse PCI domain '%s'"), domain); - goto error; - } - if (virStrToLong_i(bus, NULL, 0, &busID) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot parse PCI bus '%s'"), bus); - goto error; - } - if (virStrToLong_i(slot, NULL, 0, &slotID) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot parse PCI slot '%s'"), slot); - goto error; - } - if (virStrToLong_i(func, NULL, 0, &funcID) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot parse PCI func '%s'"), func); - goto error; - } - - if (VIR_ALLOC(dev) < 0) - goto no_memory; - - dev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; - dev->managed = 0; - dev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; - dev->source.subsys.u.pci.domain = domainID; - dev->source.subsys.u.pci.bus = busID; - dev->source.subsys.u.pci.slot = slotID; - dev->source.subsys.u.pci.function = funcID; - - if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { - goto no_memory; - } - - def->hostdevs[def->nhostdevs++] = dev; - } - - return 0; - -no_memory: - virReportOOMError(conn); - -error: - virDomainHostdevDefFree(dev); - return -1; -} - - -/** - * xenDaemonParseSxpr: - * @conn: the connection associated with the XML - * @root: the root of the parsed S-Expression - * @xendConfigVersion: version of xend - * @cpus: set of cpus the domain may be pinned to - * - * Parse the xend sexp description and turn it into the XML format similar - * to the one unsed for creation. - * - * Returns the 0 terminated XML string or NULL in case of error. - * the caller must free() the returned value. - */ -static virDomainDefPtr -xenDaemonParseSxpr(virConnectPtr conn, - const struct sexpr *root, - int xendConfigVersion, - const char *cpus) -{ -#ifndef PROXY - xenUnifiedPrivatePtr priv = conn->privateData; -#endif - const char *tmp; - virDomainDefPtr def; - int hvm = 0; - char *tty = NULL; - - if (VIR_ALLOC(def) < 0) - goto no_memory; - - tmp = sexpr_node(root, "domain/domid"); - if (tmp == NULL && xendConfigVersion < 3) { /* Old XenD, domid was mandatory */ - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing id")); - goto error; - } - def->virtType = VIR_DOMAIN_VIRT_XEN; - if (tmp) - def->id = sexpr_int(root, "domain/domid"); - else - def->id = -1; - - if (sexpr_node_copy(root, "domain/name", &def->name) < 0) - goto no_memory; - if (def->name == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing name")); - goto error; - } - - tmp = sexpr_node(root, "domain/uuid"); - if (tmp == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing name")); - goto error; - } - virUUIDParse(tmp, def->uuid); - - hvm = sexpr_lookup(root, "domain/image/hvm") ? 1 : 0; - if (!hvm) { - if (sexpr_node_copy(root, "domain/bootloader", - &def->os.bootloader) < 0) - goto no_memory; - - if (!def->os.bootloader && - sexpr_has(root, "domain/bootloader") && - (def->os.bootloader = strdup("")) == NULL) - goto no_memory; - - if (def->os.bootloader && - sexpr_node_copy(root, "domain/bootloader_args", - &def->os.bootloaderArgs) < 0) - goto no_memory; - } - - if (!(def->os.type = strdup(hvm ? "hvm" : "linux"))) - goto no_memory; - - if (def->id != 0) { - if (sexpr_lookup(root, "domain/image")) { - if (xenDaemonParseSxprOS(conn, root, def, hvm) < 0) - goto error; - } - } - - def->maxmem = (unsigned long) (sexpr_u64(root, "domain/maxmem") << 10); - def->memory = (unsigned long) (sexpr_u64(root, "domain/memory") << 10); - if (def->memory > def->maxmem) - def->maxmem = def->memory; - - if (cpus != NULL) { - def->cpumasklen = VIR_DOMAIN_CPUMASK_LEN; - if (VIR_ALLOC_N(def->cpumask, def->cpumasklen) < 0) { - virReportOOMError(conn); - goto error; - } - - if (virDomainCpuSetParse(conn, &cpus, - 0, def->cpumask, - def->cpumasklen) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("invalid CPU mask %s"), cpus); - goto error; - } - } - - def->vcpus = sexpr_int(root, "domain/vcpus"); - - tmp = sexpr_node(root, "domain/on_poweroff"); - if (tmp != NULL) { - if ((def->onPoweroff = virDomainLifecycleTypeFromString(tmp)) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown lifecycle type %s"), tmp); - goto error; - } - } else - def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY; - - tmp = sexpr_node(root, "domain/on_reboot"); - if (tmp != NULL) { - if ((def->onReboot = virDomainLifecycleTypeFromString(tmp)) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown lifecycle type %s"), tmp); - goto error; - } - } else - def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART; - - tmp = sexpr_node(root, "domain/on_crash"); - if (tmp != NULL) { - if ((def->onCrash = virDomainLifecycleTypeFromString(tmp)) < 0) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unknown lifecycle type %s"), tmp); - goto error; - } - } else - def->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY; - - - if (hvm) { - if (sexpr_int(root, "domain/image/hvm/acpi")) - def->features |= (1 << VIR_DOMAIN_FEATURE_ACPI); - if (sexpr_int(root, "domain/image/hvm/apic")) - def->features |= (1 << VIR_DOMAIN_FEATURE_APIC); - if (sexpr_int(root, "domain/image/hvm/pae")) - def->features |= (1 << VIR_DOMAIN_FEATURE_PAE); - - /* Old XenD only allows localtime here for HVM */ - if (sexpr_int(root, "domain/image/hvm/localtime")) - def->localtime = 1; - } - - /* Current XenD allows localtime here, for PV and HVM */ - if (sexpr_int(root, "domain/localtime")) - def->localtime = 1; - - if (sexpr_node_copy(root, hvm ? - "domain/image/hvm/device_model" : - "domain/image/linux/device_model", - &def->emulator) < 0) - goto no_memory; - - /* append block devices */ - if (xenDaemonParseSxprDisks(conn, def, root, hvm, xendConfigVersion) < 0) - goto error; - - if (xenDaemonParseSxprNets(conn, def, root) < 0) - goto error; - - if (xenDaemonParseSxprPCI(conn, def, root) < 0) - goto error; - - /* New style graphics device config */ - if (xenDaemonParseSxprGraphicsNew(conn, def, root) < 0) - goto error; - - /* Graphics device (HVM <= 3.0.4, or PV <= 3.0.3) vnc config */ - if ((def->ngraphics == 0) && - xenDaemonParseSxprGraphicsOld(conn, def, root, hvm, xendConfigVersion) < 0) - goto error; - - - /* Old style cdrom config from Xen <= 3.0.2 */ - if (hvm && - xendConfigVersion == 1) { - tmp = sexpr_node(root, "domain/image/hvm/cdrom"); - if ((tmp != NULL) && (tmp[0] != 0)) { - virDomainDiskDefPtr disk; - if (VIR_ALLOC(disk) < 0) - goto no_memory; - if (!(disk->src = strdup(tmp))) { - virDomainDiskDefFree(disk); - goto no_memory; - } - disk->type = VIR_DOMAIN_DISK_TYPE_FILE; - disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; - if (!(disk->dst = strdup("hdc"))) { - virDomainDiskDefFree(disk); - goto no_memory; - } - if (!(disk->driverName = strdup("file"))) { - virDomainDiskDefFree(disk); - goto no_memory; - } - disk->bus = VIR_DOMAIN_DISK_BUS_IDE; - disk->readonly = 1; - - if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { - virDomainDiskDefFree(disk); - goto no_memory; - } - def->disks[def->ndisks++] = disk; - } - } - - - /* Floppy disk config */ - if (hvm) { - const char *const fds[] = { "fda", "fdb" }; - int i; - for (i = 0 ; i < ARRAY_CARDINALITY(fds) ; i++) { - tmp = sexpr_fmt_node(root, "domain/image/hvm/%s", fds[i]); - if ((tmp != NULL) && (tmp[0] != 0)) { - virDomainDiskDefPtr disk; - if (VIR_ALLOC(disk) < 0) - goto no_memory; - if (!(disk->src = strdup(tmp))) { - VIR_FREE(disk); - goto no_memory; - } - disk->type = VIR_DOMAIN_DISK_TYPE_FILE; - disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; - if (!(disk->dst = strdup(fds[i]))) { - virDomainDiskDefFree(disk); - goto no_memory; - } - if (!(disk->driverName = strdup("file"))) { - virDomainDiskDefFree(disk); - goto no_memory; - } - disk->bus = VIR_DOMAIN_DISK_BUS_FDC; - - if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { - virDomainDiskDefFree(disk); - goto no_memory; - } - def->disks[def->ndisks++] = disk; - } - } - } - - /* in case of HVM we have USB device emulation */ - if (hvm && - xenDaemonParseSxprUSB(conn, def, root) < 0) - goto error; - - /* Character device config */ - xenUnifiedLock(priv); - tty = xenStoreDomainGetConsolePath(conn, def->id); - xenUnifiedUnlock(priv); - if (hvm) { - tmp = sexpr_node(root, "domain/image/hvm/serial"); - if (tmp && STRNEQ(tmp, "none")) { - virDomainChrDefPtr chr; - if ((chr = xenDaemonParseSxprChar(conn, tmp, tty)) == NULL) - goto error; - if (VIR_REALLOC_N(def->serials, def->nserials+1) < 0) { - virDomainChrDefFree(chr); - goto no_memory; - } - def->serials[def->nserials++] = chr; - } - tmp = sexpr_node(root, "domain/image/hvm/parallel"); - if (tmp && STRNEQ(tmp, "none")) { - virDomainChrDefPtr chr; - /* XXX does XenD stuff parallel port tty info into xenstore somewhere ? */ - if ((chr = xenDaemonParseSxprChar(conn, tmp, NULL)) == NULL) - goto error; - if (VIR_REALLOC_N(def->parallels, def->nparallels+1) < 0) { - virDomainChrDefFree(chr); - goto no_memory; - } - def->parallels[def->nparallels++] = chr; - } - } else { - /* Fake a paravirt console, since that's not in the sexpr */ - if (!(def->console = xenDaemonParseSxprChar(conn, "pty", tty))) - goto error; - } - VIR_FREE(tty); - - - /* Sound device config */ - if (hvm && - (tmp = sexpr_node(root, "domain/image/hvm/soundhw")) != NULL && - *tmp) { - if (xenDaemonParseSxprSound(conn, def, tmp) < 0) - goto error; - } - - return def; - -no_memory: - virReportOOMError(conn); -error: - VIR_FREE(tty); - virDomainDefFree(def); - return NULL; -} - -virDomainDefPtr -xenDaemonParseSxprString(virConnectPtr conn, - const char *sexpr, - int xendConfigVersion) -{ - struct sexpr *root = string2sexpr(sexpr); - virDomainDefPtr def; - - if (!root) - return NULL; - - def = xenDaemonParseSxpr(conn, root, xendConfigVersion, NULL); - - sexpr_free(root); - - return def; -} - - -/** - * sexpr_to_xend_domain_info: - * @root: an S-Expression describing a domain - * @info: a info data structure to fill=up - * - * Internal routine filling up the info structure with the values from - * the domain root provided. - * - * Returns 0 in case of success, -1 in case of error - */ -static int -sexpr_to_xend_domain_info(virDomainPtr domain, const struct sexpr *root, - virDomainInfoPtr info) -{ - const char *flags; - - - if ((root == NULL) || (info == NULL)) - return (-1); - - info->memory = sexpr_u64(root, "domain/memory") << 10; - info->maxMem = sexpr_u64(root, "domain/maxmem") << 10; - flags = sexpr_node(root, "domain/state"); - - if (flags) { - if (strchr(flags, 'c')) - info->state = VIR_DOMAIN_CRASHED; - else if (strchr(flags, 's')) - info->state = VIR_DOMAIN_SHUTOFF; - else if (strchr(flags, 'd')) - info->state = VIR_DOMAIN_SHUTDOWN; - else if (strchr(flags, 'p')) - info->state = VIR_DOMAIN_PAUSED; - else if (strchr(flags, 'b')) - info->state = VIR_DOMAIN_BLOCKED; - else if (strchr(flags, 'r')) - info->state = VIR_DOMAIN_RUNNING; - } else { - /* Inactive domains don't have a state reported, so - mark them SHUTOFF, rather than NOSTATE */ - if (domain->id < 0) - info->state = VIR_DOMAIN_SHUTOFF; - else - info->state = VIR_DOMAIN_NOSTATE; - } - info->cpuTime = sexpr_float(root, "domain/cpu_time") * 1000000000; - info->nrVirtCpu = sexpr_int(root, "domain/vcpus"); - return (0); -} - -/** - * sexpr_to_xend_node_info: - * @root: an S-Expression describing a domain - * @info: a info data structure to fill up - * - * Internal routine filling up the info structure with the values from - * the node root provided. - * - * Returns 0 in case of success, -1 in case of error - */ -static int -sexpr_to_xend_node_info(const struct sexpr *root, virNodeInfoPtr info) -{ - const char *machine; - - - if ((root == NULL) || (info == NULL)) - return (-1); - - machine = sexpr_node(root, "node/machine"); - if (machine == NULL) { - info->model[0] = 0; - } else { - snprintf(&info->model[0], sizeof(info->model) - 1, "%s", machine); - info->model[sizeof(info->model) - 1] = 0; - } - info->memory = (unsigned long) sexpr_u64(root, "node/total_memory") << 10; - - info->cpus = sexpr_int(root, "node/nr_cpus"); - info->mhz = sexpr_int(root, "node/cpu_mhz"); - info->nodes = sexpr_int(root, "node/nr_nodes"); - info->sockets = sexpr_int(root, "node/sockets_per_node"); - info->cores = sexpr_int(root, "node/cores_per_socket"); - info->threads = sexpr_int(root, "node/threads_per_core"); - - /* Xen 3.2.0 replaces sockets_per_node with 'nr_cpus'. - * Old Xen calculated sockets_per_node using its internal - * nr_cpus / (nodes*cores*threads), so fake it ourselves - * in the same way - */ - if (info->sockets == 0) { - int nr_cpus = sexpr_int(root, "node/nr_cpus"); - int procs = info->nodes * info->cores * info->threads; - if (procs == 0) /* Sanity check in case of Xen bugs in futures..*/ - return (-1); - info->sockets = nr_cpus / procs; - /* Should already be fine, but for further sanity make - * sure we have at least one socket - */ - if (info->sockets == 0) - info->sockets = 1; - } - return (0); -} - - -/** - * sexpr_to_xend_topology - * @root: an S-Expression describing a node - * @caps: capability info - * - * Internal routine populating capability info with - * NUMA node mapping details - * - * Does nothing when the system doesn't support NUMA (not an error). - * - * Returns 0 in case of success, -1 in case of error - */ -static int -sexpr_to_xend_topology(virConnectPtr conn, - const struct sexpr *root, - virCapsPtr caps) -{ - const char *nodeToCpu; - const char *cur; - char *cpuset = NULL; - int *cpuNums = NULL; - int cell, cpu, nb_cpus; - int n = 0; - int numCpus; - - nodeToCpu = sexpr_node(root, "node/node_to_cpu"); - if (nodeToCpu == NULL) - return 0; /* no NUMA support */ - - numCpus = sexpr_int(root, "node/nr_cpus"); - - - if (VIR_ALLOC_N(cpuset, numCpus) < 0) - goto memory_error; - if (VIR_ALLOC_N(cpuNums, numCpus) < 0) - goto memory_error; - - cur = nodeToCpu; - while (*cur != 0) { - /* - * Find the next NUMA cell described in the xend output - */ - cur = strstr(cur, "node"); - if (cur == NULL) - break; - cur += 4; - cell = virParseNumber(&cur); - if (cell < 0) - goto parse_error; - virSkipSpaces(&cur); - if (*cur != ':') - goto parse_error; - cur++; - virSkipSpaces(&cur); - if (STRPREFIX(cur, "no cpus")) { - nb_cpus = 0; - for (cpu = 0; cpu < numCpus; cpu++) - cpuset[cpu] = 0; - } else { - nb_cpus = virDomainCpuSetParse(conn, &cur, 'n', cpuset, numCpus); - if (nb_cpus < 0) - goto error; - } - - for (n = 0, cpu = 0; cpu < numCpus; cpu++) - if (cpuset[cpu] == 1) - cpuNums[n++] = cpu; - - if (virCapabilitiesAddHostNUMACell(caps, - cell, - nb_cpus, - cpuNums) < 0) - goto memory_error; - } - VIR_FREE(cpuNums); - VIR_FREE(cpuset); - return (0); - - parse_error: - virXendError(conn, VIR_ERR_XEN_CALL, "%s", _("topology syntax error")); - error: - VIR_FREE(cpuNums); - VIR_FREE(cpuset); - - return (-1); - - memory_error: - VIR_FREE(cpuNums); - VIR_FREE(cpuset); - virReportOOMError(conn); - return (-1); -} - - -#ifndef PROXY -/** - * sexpr_to_domain: - * @conn: an existing virtual connection block - * @root: an S-Expression describing a domain - * - * Internal routine returning the associated virDomainPtr for this domain - * - * Returns the domain pointer or NULL in case of error. - */ -static virDomainPtr -sexpr_to_domain(virConnectPtr conn, const struct sexpr *root) -{ - virDomainPtr ret = NULL; - unsigned char uuid[VIR_UUID_BUFLEN]; - const char *name; - const char *tmp; - xenUnifiedPrivatePtr priv; - - if ((conn == NULL) || (root == NULL)) - return(NULL); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (sexpr_uuid(uuid, root, "domain/uuid") < 0) - goto error; - name = sexpr_node(root, "domain/name"); - if (name == NULL) - goto error; - - ret = virGetDomain(conn, name, uuid); - if (ret == NULL) return NULL; - - tmp = sexpr_node(root, "domain/domid"); - /* New 3.0.4 XenD will not report a domid for inactive domains, - * so only error out for old XenD - */ - if (!tmp && priv->xendConfigVersion < 3) - goto error; - - if (tmp) - ret->id = sexpr_int(root, "domain/domid"); - else - ret->id = -1; /* An inactive domain */ - - return (ret); - -error: - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to parse Xend domain information")); - if (ret != NULL) - virUnrefDomain(ret); - return(NULL); -} -#endif /* !PROXY */ - -/***************************************************************** - ****** - ****** - ****** - ****** - Refactored - ****** - ****** - ****** - ****** - *****************************************************************/ -#ifndef PROXY -/** - * xenDaemonOpen: - * @conn: an existing virtual connection block - * @name: optional argument to select a connection type - * @flags: combination of virDrvOpenFlag(s) - * - * Creates a localhost Xen Daemon connection - * - * Returns 0 in case of success, -1 in case of error. - */ -virDrvOpenStatus -xenDaemonOpen(virConnectPtr conn, - virConnectAuthPtr auth ATTRIBUTE_UNUSED, - int flags ATTRIBUTE_UNUSED) -{ - char *port = NULL; - int ret = VIR_DRV_OPEN_ERROR; - - /* Switch on the scheme, which we expect to be NULL (file), - * "http" or "xen". - */ - if (conn->uri->scheme == NULL) { - /* It should be a file access */ - if (conn->uri->path == NULL) { - virXendError(NULL, VIR_ERR_NO_CONNECT, __FUNCTION__); - goto failed; - } - if (xenDaemonOpen_unix(conn, conn->uri->path) < 0 || - xend_detect_config_version(conn) == -1) - goto failed; - } - else if (STRCASEEQ (conn->uri->scheme, "xen")) { - /* - * try first to open the unix socket - */ - if (xenDaemonOpen_unix(conn, "/var/lib/xend/xend-socket") == 0 && - xend_detect_config_version(conn) != -1) - goto done; - - /* - * try though http on port 8000 - */ - if (xenDaemonOpen_tcp(conn, "localhost", "8000") < 0 || - xend_detect_config_version(conn) == -1) - goto failed; - } else if (STRCASEEQ (conn->uri->scheme, "http")) { - if (conn->uri->port && - virAsprintf(&port, "%d", conn->uri->port) == -1) - goto failed; - - if (xenDaemonOpen_tcp(conn, - conn->uri->server ? conn->uri->server : "localhost", - port ? port : "8000") < 0 || - xend_detect_config_version(conn) == -1) - goto failed; - } else { - virXendError(NULL, VIR_ERR_NO_CONNECT, __FUNCTION__); - goto failed; - } - - done: - ret = VIR_DRV_OPEN_SUCCESS; - -failed: - VIR_FREE(port); - return ret; -} - - -/** - * xenDaemonClose: - * @conn: an existing virtual connection block - * - * This method should be called when a connection to xend instance - * initialized with xenDaemonOpen is no longer needed - * to free the associated resources. - * - * Returns 0 in case of success, -1 in case of error - */ -int -xenDaemonClose(virConnectPtr conn ATTRIBUTE_UNUSED) -{ - return 0; -} - -/** - * xenDaemonDomainSuspend: - * @domain: pointer to the Domain block - * - * Pause the domain, the domain is not scheduled anymore though its resources - * are preserved. Use xenDaemonDomainResume() to resume execution. - * - * Returns 0 in case of success, -1 (with errno) in case of error. - */ -int -xenDaemonDomainSuspend(virDomainPtr domain) -{ - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - if (domain->id < 0) { - virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, - _("Domain %s isn't running."), domain->name); - return(-1); - } - - return xend_op(domain->conn, domain->name, "op", "pause", NULL); -} - -/** - * xenDaemonDomainResume: - * @xend: pointer to the Xem Daemon block - * @name: name for the domain - * - * Resume the domain after xenDaemonDomainSuspend() has been called - * - * Returns 0 in case of success, -1 (with errno) in case of error. - */ -int -xenDaemonDomainResume(virDomainPtr domain) -{ - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - if (domain->id < 0) { - virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, - _("Domain %s isn't running."), domain->name); - return(-1); - } - - return xend_op(domain->conn, domain->name, "op", "unpause", NULL); -} - -/** - * xenDaemonDomainShutdown: - * @domain: pointer to the Domain block - * - * Shutdown the domain, the OS is requested to properly shutdown - * and the domain may ignore it. It will return immediately - * after queuing the request. - * - * Returns 0 in case of success, -1 (with errno) in case of error. - */ -int -xenDaemonDomainShutdown(virDomainPtr domain) -{ - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - if (domain->id < 0) { - virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, - _("Domain %s isn't running."), domain->name); - return(-1); - } - - return xend_op(domain->conn, domain->name, "op", "shutdown", "reason", "poweroff", NULL); -} - -/** - * xenDaemonDomainReboot: - * @domain: pointer to the Domain block - * @flags: extra flags for the reboot operation, not used yet - * - * Reboot the domain, the OS is requested to properly shutdown - * and restart but the domain may ignore it. It will return immediately - * after queuing the request. - * - * Returns 0 in case of success, -1 (with errno) in case of error. - */ -int -xenDaemonDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED) -{ - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - if (domain->id < 0) { - virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, - _("Domain %s isn't running."), domain->name); - return(-1); - } - - return xend_op(domain->conn, domain->name, "op", "shutdown", "reason", "reboot", NULL); -} - -/** - * xenDaemonDomainDestroy: - * @domain: pointer to the Domain block - * - * Abruptly halt the domain, the OS is not properly shutdown and the - * resources allocated for the domain are immediately freed, mounted - * filesystems will be marked as uncleanly shutdown. - * After calling this function, the domain's status will change to - * dying and will go away completely once all of the resources have been - * unmapped (usually from the backend devices). - * - * Returns 0 in case of success, -1 (with errno) in case of error. - */ -int -xenDaemonDomainDestroy(virDomainPtr domain) -{ - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - if (domain->id < 0) { - virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, - _("Domain %s isn't running."), domain->name); - return(-1); - } - - return xend_op(domain->conn, domain->name, "op", "destroy", NULL); -} - -/** - * xenDaemonDomainGetOSType: - * @domain: a domain object - * - * Get the type of domain operation system. - * - * Returns the new string or NULL in case of error, the string must be - * freed by the caller. - */ -static char * -xenDaemonDomainGetOSType(virDomainPtr domain) -{ - char *type; - struct sexpr *root; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(NULL); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id < 0 && priv->xendConfigVersion < 3) - return(NULL); - - /* can we ask for a subset ? worth it ? */ - root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); - if (root == NULL) - return(NULL); - - if (sexpr_lookup(root, "domain/image/hvm")) { - type = strdup("hvm"); - } else { - type = strdup("linux"); - } - - sexpr_free(root); - - return(type); -} - -/** - * xenDaemonDomainSave: - * @domain: pointer to the Domain block - * @filename: path for the output file - * - * This method will suspend a domain and save its memory contents to - * a file on disk. Use xenDaemonDomainRestore() to restore a domain after - * saving. - * Note that for remote Xen Daemon the file path will be interpreted in - * the remote host. - * - * Returns 0 in case of success, -1 (with errno) in case of error. - */ -int -xenDaemonDomainSave(virDomainPtr domain, const char *filename) -{ - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) || - (filename == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - if (domain->id < 0) { - virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, - _("Domain %s isn't running."), domain->name); - return(-1); - } - - /* We can't save the state of Domain-0, that would mean stopping it too */ - if (domain->id == 0) { - return(-1); - } - - return xend_op(domain->conn, domain->name, "op", "save", "file", filename, NULL); -} - -/** - * xenDaemonDomainCoreDump: - * @domain: pointer to the Domain block - * @filename: path for the output file - * @flags: extra flags, currently unused - * - * This method will dump the core of a domain on a given file for analysis. - * Note that for remote Xen Daemon the file path will be interpreted in - * the remote host. - * - * Returns 0 in case of success, -1 in case of error. - */ -static int -xenDaemonDomainCoreDump(virDomainPtr domain, const char *filename, - int flags ATTRIBUTE_UNUSED) -{ - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) || - (filename == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - if (domain->id < 0) { - virXendError(domain->conn, VIR_ERR_OPERATION_INVALID, - _("Domain %s isn't running."), domain->name); - return(-1); - } - - return xend_op(domain->conn, domain->name, "op", "dump", "file", filename, - "live", "0", "crash", "0", NULL); -} - -/** - * xenDaemonDomainRestore: - * @conn: pointer to the Xem Daemon block - * @filename: path for the output file - * - * This method will restore a domain saved to disk by xenDaemonDomainSave(). - * Note that for remote Xen Daemon the file path will be interpreted in - * the remote host. - * - * Returns 0 in case of success, -1 (with errno) in case of error. - */ -int -xenDaemonDomainRestore(virConnectPtr conn, const char *filename) -{ - if ((conn == NULL) || (filename == NULL)) { - /* this should be caught at the interface but ... */ - virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - return xend_op(conn, "", "op", "restore", "file", filename, NULL); -} -#endif /* !PROXY */ - -/** - * xenDaemonDomainGetMaxMemory: - * @domain: pointer to the domain block - * - * Ask the Xen Daemon for the maximum memory allowed for a domain - * - * Returns the memory size in kilobytes or 0 in case of error. - */ -unsigned long -xenDaemonDomainGetMaxMemory(virDomainPtr domain) -{ - unsigned long ret = 0; - struct sexpr *root; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id < 0 && priv->xendConfigVersion < 3) - return(-1); - - /* can we ask for a subset ? worth it ? */ - root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); - if (root == NULL) - return(0); - - ret = (unsigned long) sexpr_u64(root, "domain/memory") << 10; - sexpr_free(root); - - return(ret); -} - -#ifndef PROXY -/** - * xenDaemonDomainSetMaxMemory: - * @domain: pointer to the Domain block - * @memory: The maximum memory in kilobytes - * - * This method will set the maximum amount of memory that can be allocated to - * a domain. Please note that a domain is able to allocate up to this amount - * on its own. - * - * Returns 0 for success; -1 (with errno) on error - */ -int -xenDaemonDomainSetMaxMemory(virDomainPtr domain, unsigned long memory) -{ - char buf[1024]; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id < 0 && priv->xendConfigVersion < 3) - return(-1); - - snprintf(buf, sizeof(buf), "%lu", memory >> 10); - return xend_op(domain->conn, domain->name, "op", "maxmem_set", "memory", - buf, NULL); -} - -/** - * xenDaemonDomainSetMemory: - * @domain: pointer to the Domain block - * @memory: The target memory in kilobytes - * - * This method will set a target memory allocation for a given domain and - * request that the guest meet this target. The guest may or may not actually - * achieve this target. When this function returns, it does not signify that - * the domain has actually reached that target. - * - * Memory for a domain can only be allocated up to the maximum memory setting. - * There is no safe guard for allocations that are too small so be careful - * when using this function to reduce a domain's memory usage. - * - * Returns 0 for success; -1 (with errno) on error - */ -int -xenDaemonDomainSetMemory(virDomainPtr domain, unsigned long memory) -{ - char buf[1024]; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id < 0 && priv->xendConfigVersion < 3) - return(-1); - - snprintf(buf, sizeof(buf), "%lu", memory >> 10); - return xend_op(domain->conn, domain->name, "op", "mem_target_set", - "target", buf, NULL); -} - -#endif /* ! PROXY */ - -virDomainDefPtr -xenDaemonDomainFetch(virConnectPtr conn, - int domid, - const char *name, - const char *cpus) -{ - struct sexpr *root; - xenUnifiedPrivatePtr priv; - virDomainDefPtr def; - - if (name) - root = sexpr_get(conn, "/xend/domain/%s?detail=1", name); - else - root = sexpr_get(conn, "/xend/domain/%d?detail=1", domid); - if (root == NULL) { - virXendError (conn, VIR_ERR_XEN_CALL, - "%s", _("xenDaemonDomainFetch failed to" - " find this domain")); - return (NULL); - } - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (!(def = xenDaemonParseSxpr(conn, - root, - priv->xendConfigVersion, - cpus))) - goto cleanup; - -cleanup: - sexpr_free(root); - - return (def); -} - - -#ifndef PROXY -/** - * xenDaemonDomainDumpXML: - * @domain: a domain object - * @flags: potential dump flags - * @cpus: list of cpu the domain is pinned to. - * - * Provide an XML description of the domain. - * - * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error. - * the caller must free() the returned value. - */ -char * -xenDaemonDomainDumpXML(virDomainPtr domain, int flags, const char *cpus) -{ - xenUnifiedPrivatePtr priv; - virDomainDefPtr def; - char *xml; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(NULL); - } - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id < 0 && priv->xendConfigVersion < 3) { - // fall-through to the next driver to handle - return(NULL); - } - - if (!(def = xenDaemonDomainFetch(domain->conn, - domain->id, - domain->name, - cpus))) - return(NULL); - - xml = virDomainDefFormat(domain->conn, def, flags); - - virDomainDefFree(def); - - return xml; -} -#endif /* !PROXY */ - -/** - * xenDaemonDomainGetInfo: - * @domain: a domain object - * @info: pointer to a virDomainInfo structure allocated by the user - * - * This method looks up information about a domain and update the - * information block provided. - * - * Returns 0 in case of success, -1 in case of error - */ -int -xenDaemonDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) -{ - struct sexpr *root; - int ret; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) || - (info == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id < 0 && priv->xendConfigVersion < 3) - return(-1); - - root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); - if (root == NULL) - return (-1); - - ret = sexpr_to_xend_domain_info(domain, root, info); - sexpr_free(root); - return (ret); -} - -#ifndef PROXY -/** - * xenDaemonLookupByName: - * @conn: A xend instance - * @name: The name of the domain - * - * This method looks up information about a domain and returns - * it in the form of a struct xend_domain. This should be - * free()'d when no longer needed. - * - * Returns domain info on success; NULL (with errno) on error - */ -virDomainPtr -xenDaemonLookupByName(virConnectPtr conn, const char *domname) -{ - struct sexpr *root; - virDomainPtr ret = NULL; - - if ((conn == NULL) || (domname == NULL)) { - virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return(NULL); - } - - root = sexpr_get(conn, "/xend/domain/%s?detail=1", domname); - if (root == NULL) - goto error; - - ret = sexpr_to_domain(conn, root); - -error: - sexpr_free(root); - return(ret); -} -#endif /* ! PROXY */ - -/** - * xenDaemonNodeGetInfo: - * @conn: pointer to the Xen Daemon block - * @info: pointer to a virNodeInfo structure allocated by the user - * - * Extract hardware information about the node. - * - * Returns 0 in case of success and -1 in case of failure. - */ -int -xenDaemonNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info) { - int ret = -1; - struct sexpr *root; - - if (!VIR_IS_CONNECT(conn)) { - virXendError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - if (info == NULL) { - virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - root = sexpr_get(conn, "/xend/node/"); - if (root == NULL) - return (-1); - - ret = sexpr_to_xend_node_info(root, info); - sexpr_free(root); - return (ret); -} - -/** - * xenDaemonNodeGetTopology: - * @conn: pointer to the Xen Daemon block - * @caps: capabilities info - * - * This method retrieves a node's topology information. - * - * Returns -1 in case of error, 0 otherwise. - */ -int -xenDaemonNodeGetTopology(virConnectPtr conn, - virCapsPtr caps) { - int ret = -1; - struct sexpr *root; - - if (!VIR_IS_CONNECT(conn)) { - virXendError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - if (caps == NULL) { - virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - - root = sexpr_get(conn, "/xend/node/"); - if (root == NULL) { - return (-1); - } - - ret = sexpr_to_xend_topology(conn, root, caps); - sexpr_free(root); - return (ret); -} - -/** - * xenDaemonGetVersion: - * @conn: pointer to the Xen Daemon block - * @hvVer: return value for the version of the running hypervisor (OUT) - * - * Get the version level of the Hypervisor running. - * - * Returns -1 in case of error, 0 otherwise. if the version can't be - * extracted by lack of capacities returns 0 and @hvVer is 0, otherwise - * @hvVer value is major * 1,000,000 + minor * 1,000 + release - */ -int -xenDaemonGetVersion(virConnectPtr conn, unsigned long *hvVer) -{ - struct sexpr *root; - int major, minor; - unsigned long version; - - if (!VIR_IS_CONNECT(conn)) { - virXendError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - if (hvVer == NULL) { - virXendError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (-1); - } - root = sexpr_get(conn, "/xend/node/"); - if (root == NULL) - return(-1); - - major = sexpr_int(root, "node/xen_major"); - minor = sexpr_int(root, "node/xen_minor"); - sexpr_free(root); - version = major * 1000000 + minor * 1000; - *hvVer = version; - return(0); -} - -#ifndef PROXY -/** - * xenDaemonListDomains: - * @conn: pointer to the hypervisor connection - * @ids: array to collect the list of IDs of active domains - * @maxids: size of @ids - * - * Collect the list of active domains, and store their ID in @maxids - * TODO: this is quite expensive at the moment since there isn't one - * xend RPC providing both name and id for all domains. - * - * Returns the number of domain found or -1 in case of error - */ -int -xenDaemonListDomains(virConnectPtr conn, int *ids, int maxids) -{ - struct sexpr *root = NULL; - int ret = -1; - struct sexpr *_for_i, *node; - long id; - - if (maxids == 0) - return(0); - - if ((ids == NULL) || (maxids < 0)) - goto error; - root = sexpr_get(conn, "/xend/domain"); - if (root == NULL) - goto error; - - ret = 0; - - for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; - _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { - if (node->kind != SEXPR_VALUE) - continue; - id = xenDaemonDomainLookupByName_ids(conn, node->u.value, NULL); - if (id >= 0) - ids[ret++] = (int) id; - if (ret >= maxids) - break; - } - -error: - sexpr_free(root); - return(ret); -} - -/** - * xenDaemonNumOfDomains: - * @conn: pointer to the hypervisor connection - * - * Provides the number of active domains. - * - * Returns the number of domain found or -1 in case of error - */ -static int -xenDaemonNumOfDomains(virConnectPtr conn) -{ - struct sexpr *root = NULL; - int ret = -1; - struct sexpr *_for_i, *node; - - root = sexpr_get(conn, "/xend/domain"); - if (root == NULL) - goto error; - - ret = 0; - - for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; - _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { - if (node->kind != SEXPR_VALUE) - continue; - ret++; - } - -error: - sexpr_free(root); - return(ret); -} -#endif /* ! PROXY */ - -#ifndef PROXY -/** - * xenDaemonLookupByID: - * @conn: pointer to the hypervisor connection - * @id: the domain ID number - * - * Try to find a domain based on the hypervisor ID number - * - * Returns a new domain object or NULL in case of failure - */ -virDomainPtr -xenDaemonLookupByID(virConnectPtr conn, int id) { - char *name = NULL; - unsigned char uuid[VIR_UUID_BUFLEN]; - virDomainPtr ret; - - if (xenDaemonDomainLookupByID(conn, id, &name, uuid) < 0) { - goto error; - } - - ret = virGetDomain(conn, name, uuid); - if (ret == NULL) goto error; - - ret->id = id; - VIR_FREE(name); - return (ret); - - error: - VIR_FREE(name); - return (NULL); -} - -/** - * xenDaemonDomainSetVcpus: - * @domain: pointer to domain object - * @nvcpus: the new number of virtual CPUs for this domain - * - * Dynamically change the number of virtual CPUs used by the domain. - * - * Returns 0 for success; -1 (with errno) on error - */ -int -xenDaemonDomainSetVcpus(virDomainPtr domain, unsigned int vcpus) -{ - char buf[VIR_UUID_BUFLEN]; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) - || (vcpus < 1)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id < 0 && priv->xendConfigVersion < 3) - return(-1); - - snprintf(buf, sizeof(buf), "%d", vcpus); - return(xend_op(domain->conn, domain->name, "op", "set_vcpus", "vcpus", - buf, NULL)); -} - -/** - * xenDaemonDomainPinCpu: - * @domain: pointer to domain object - * @vcpu: virtual CPU number - * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) - * @maplen: length of cpumap in bytes - * - * Dynamically change the real CPUs which can be allocated to a virtual CPU. - * NOTE: The XenD cpu affinity map format changed from "[0,1,2]" to - * "0,1,2" - * the XenD cpu affinity works only after cset 19579. - * there is no fine grained xend version detection possible, so we - * use the old format for anything before version 3 - * - * Returns 0 for success; -1 (with errno) on error - */ -int -xenDaemonDomainPinVcpu(virDomainPtr domain, unsigned int vcpu, - unsigned char *cpumap, int maplen) -{ - char buf[VIR_UUID_BUFLEN], mapstr[sizeof(cpumap_t) * 64]; - int i, j; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) - || (cpumap == NULL) || (maplen < 1) || (maplen > (int)sizeof(cpumap_t))) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xendConfigVersion < 3) { - mapstr[0] = '['; - mapstr[1] = 0; - } else { - mapstr[0] = 0; - } - - /* from bit map, build character string of mapped CPU numbers */ - for (i = 0; i < maplen; i++) for (j = 0; j < 8; j++) - if (cpumap[i] & (1 << j)) { - snprintf(buf, sizeof(buf), "%d,", (8 * i) + j); - strcat(mapstr, buf); - } - if (priv->xendConfigVersion < 3) - mapstr[strlen(mapstr) - 1] = ']'; - else - mapstr[strlen(mapstr) - 1] = 0; - - snprintf(buf, sizeof(buf), "%d", vcpu); - return(xend_op(domain->conn, domain->name, "op", "pincpu", "vcpu", buf, - "cpumap", mapstr, NULL)); -} - -/** - * virDomainGetVcpus: - * @domain: pointer to domain object, or NULL for Domain0 - * @info: pointer to an array of virVcpuInfo structures (OUT) - * @maxinfo: number of structures in info array - * @cpumaps: pointer to an bit map of real CPUs for all vcpus of this domain (in 8-bit bytes) (OUT) - * If cpumaps is NULL, then no cpumap information is returned by the API. - * It's assumed there is cpumap in cpumaps array. - * The memory allocated to cpumaps must be (maxinfo * maplen) bytes - * (ie: calloc(maxinfo, maplen)). - * One cpumap inside cpumaps has the format described in virDomainPinVcpu() API. - * @maplen: number of bytes in one cpumap, from 1 up to size of CPU map in - * underlying virtualization system (Xen...). - * - * Extract information about virtual CPUs of domain, store it in info array - * and also in cpumaps if this pointer isn't NULL. - * - * Returns the number of info filled in case of success, -1 in case of failure. - */ -int -xenDaemonDomainGetVcpus(virDomainPtr domain, virVcpuInfoPtr info, int maxinfo, - unsigned char *cpumaps, int maplen) -{ - struct sexpr *root, *s, *t; - virVcpuInfoPtr ipt = info; - int nbinfo = 0, oln; - unsigned char *cpumap; - int vcpu, cpu; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) - || (info == NULL) || (maxinfo < 1)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - if (cpumaps != NULL && maplen < 1) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - - root = sexpr_get(domain->conn, "/xend/domain/%s?op=vcpuinfo", domain->name); - if (root == NULL) - return (-1); - - if (cpumaps != NULL) - memset(cpumaps, 0, maxinfo * maplen); - - /* scan the sexprs from "(vcpu (number x)...)" and get parameter values */ - for (s = root; s->kind == SEXPR_CONS; s = s->u.s.cdr) { - if ((s->u.s.car->kind == SEXPR_CONS) && - (s->u.s.car->u.s.car->kind == SEXPR_VALUE) && - STREQ(s->u.s.car->u.s.car->u.value, "vcpu")) { - t = s->u.s.car; - vcpu = ipt->number = sexpr_int(t, "vcpu/number"); - if ((oln = sexpr_int(t, "vcpu/online")) != 0) { - if (sexpr_int(t, "vcpu/running")) ipt->state = VIR_VCPU_RUNNING; - if (sexpr_int(t, "vcpu/blocked")) ipt->state = VIR_VCPU_BLOCKED; - } - else - ipt->state = VIR_VCPU_OFFLINE; - ipt->cpuTime = sexpr_float(t, "vcpu/cpu_time") * 1000000000; - ipt->cpu = oln ? sexpr_int(t, "vcpu/cpu") : -1; - - if (cpumaps != NULL && vcpu >= 0 && vcpu < maxinfo) { - cpumap = (unsigned char *) VIR_GET_CPUMAP(cpumaps, maplen, vcpu); - /* - * get sexpr from "(cpumap (x y z...))" and convert values - * to bitmap - */ - for (t = t->u.s.cdr; t->kind == SEXPR_CONS; t = t->u.s.cdr) - if ((t->u.s.car->kind == SEXPR_CONS) && - (t->u.s.car->u.s.car->kind == SEXPR_VALUE) && - STREQ(t->u.s.car->u.s.car->u.value, "cpumap") && - (t->u.s.car->u.s.cdr->kind == SEXPR_CONS)) { - for (t = t->u.s.car->u.s.cdr->u.s.car; t->kind == SEXPR_CONS; t = t->u.s.cdr) - if (t->u.s.car->kind == SEXPR_VALUE - && virStrToLong_i(t->u.s.car->u.value, NULL, 10, &cpu) == 0 - && cpu >= 0 - && (VIR_CPU_MAPLEN(cpu+1) <= maplen)) { - VIR_USE_CPU(cpumap, cpu); - } - break; - } - } - - if (++nbinfo == maxinfo) break; - ipt++; - } - } - sexpr_free(root); - return(nbinfo); -} - -/** - * xenDaemonLookupByUUID: - * @conn: pointer to the hypervisor connection - * @uuid: the raw UUID for the domain - * - * Try to lookup a domain on xend based on its UUID. - * - * Returns a new domain object or NULL in case of failure - */ -virDomainPtr -xenDaemonLookupByUUID(virConnectPtr conn, const unsigned char *uuid) -{ - virDomainPtr ret; - char *name = NULL; - int id = -1; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - /* Old approach for xen <= 3.0.3 */ - if (priv->xendConfigVersion < 3) { - char **names, **tmp; - unsigned char ident[VIR_UUID_BUFLEN]; - names = xenDaemonListDomainsOld(conn); - tmp = names; - - if (names == NULL) { - return (NULL); - } - while (*tmp != NULL) { - id = xenDaemonDomainLookupByName_ids(conn, *tmp, &ident[0]); - if (id >= 0) { - if (!memcmp(uuid, ident, VIR_UUID_BUFLEN)) { - name = strdup(*tmp); - break; - } - } - tmp++; - } - VIR_FREE(names); - } else { /* New approach for xen >= 3.0.4 */ - char *domname = NULL; - char uuidstr[VIR_UUID_STRING_BUFLEN]; - struct sexpr *root = NULL; - - virUUIDFormat(uuid, uuidstr); - root = sexpr_get(conn, "/xend/domain/%s?detail=1", uuidstr); - if (root == NULL) - return (NULL); - domname = (char*)sexpr_node(root, "domain/name"); - if (sexpr_node(root, "domain/domid")) /* only active domains have domid */ - id = sexpr_int(root, "domain/domid"); - else - id = -1; - name = domname ? strdup(domname) : NULL; - sexpr_free(root); - } - - if (name == NULL) - return (NULL); - - ret = virGetDomain(conn, name, uuid); - if (ret == NULL) goto cleanup; - - ret->id = id; - - cleanup: - VIR_FREE(name); - return (ret); -} - -/** - * xenDaemonCreateXML: - * @conn: pointer to the hypervisor connection - * @xmlDesc: an XML description of the domain - * @flags: an optional set of virDomainFlags - * - * Launch a new Linux guest domain, based on an XML description similar - * to the one returned by virDomainGetXMLDesc() - * This function may requires privileged access to the hypervisor. - * - * Returns a new domain object or NULL in case of failure - */ -static virDomainPtr -xenDaemonCreateXML(virConnectPtr conn, const char *xmlDesc, - unsigned int flags ATTRIBUTE_UNUSED) -{ - int ret; - char *sexpr; - virDomainPtr dom = NULL; - xenUnifiedPrivatePtr priv; - virDomainDefPtr def; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (!(def = virDomainDefParseString(conn, - priv->caps, - xmlDesc, - VIR_DOMAIN_XML_INACTIVE))) - return (NULL); - - if (!(sexpr = xenDaemonFormatSxpr(conn, def, priv->xendConfigVersion))) { - virDomainDefFree(def); - return (NULL); - } - - ret = xenDaemonDomainCreateXML(conn, sexpr); - VIR_FREE(sexpr); - if (ret != 0) { - goto error; - } - - /* This comes before wait_for_devices, to ensure that latter - cleanup will destroy the domain upon failure */ - if (!(dom = virDomainLookupByName(conn, def->name))) - goto error; - - if (xend_wait_for_devices(conn, def->name) < 0) - goto error; - - if (xenDaemonDomainResume(dom) < 0) - goto error; - - virDomainDefFree(def); - return (dom); - - error: - /* Make sure we don't leave a still-born domain around */ - if (dom != NULL) { - xenDaemonDomainDestroy(dom); - virUnrefDomain(dom); - } - virDomainDefFree(def); - return (NULL); -} - -/** - * xenDaemonAttachDevice: - * @domain: pointer to domain object - * @xml: pointer to XML description of device - * - * Create a virtual device attachment to backend. - * XML description is translated into S-expression. - * - * Returns 0 in case of success, -1 in case of failure. - */ -static int -xenDaemonAttachDevice(virDomainPtr domain, const char *xml) -{ - xenUnifiedPrivatePtr priv; - char *sexpr = NULL; - int ret = -1; - virDomainDeviceDefPtr dev = NULL; - virDomainDefPtr def = NULL; - virBuffer buf = VIR_BUFFER_INITIALIZER; - char class[8], ref[80]; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return -1; - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - /* - * on older Xen without the inactive guests management - * avoid doing this on inactive guests - */ - if ((domain->id < 0) && (priv->xendConfigVersion < 3)) - return -1; - - if (!(def = xenDaemonDomainFetch(domain->conn, - domain->id, - domain->name, - NULL))) - goto cleanup; - - if (!(dev = virDomainDeviceDefParse(domain->conn, - priv->caps, - def, xml, VIR_DOMAIN_XML_INACTIVE))) - goto cleanup; - - - switch (dev->type) { - case VIR_DOMAIN_DEVICE_DISK: - if (xenDaemonFormatSxprDisk(domain->conn, - dev->data.disk, - &buf, - STREQ(def->os.type, "hvm") ? 1 : 0, - priv->xendConfigVersion, 1) < 0) - goto cleanup; - break; - - case VIR_DOMAIN_DEVICE_NET: - if (xenDaemonFormatSxprNet(domain->conn, - dev->data.net, - &buf, - STREQ(def->os.type, "hvm") ? 1 : 0, - priv->xendConfigVersion, 1) < 0) - goto cleanup; - break; - - case VIR_DOMAIN_DEVICE_HOSTDEV: - if (dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { - if (xenDaemonFormatSxprOnePCI(domain->conn, - dev->data.hostdev, - &buf) < 0) - goto cleanup; - } else { - virXendError(domain->conn, VIR_ERR_NO_SUPPORT, "%s", - _("unsupported device type")); - goto cleanup; - } - break; - - default: - virXendError(domain->conn, VIR_ERR_NO_SUPPORT, "%s", - _("unsupported device type")); - goto cleanup; - } - - sexpr = virBufferContentAndReset(&buf); - - if (virDomainXMLDevID(domain, dev, class, ref, sizeof(ref))) { - /* device doesn't exist, define it */ - ret = xend_op(domain->conn, domain->name, "op", "device_create", - "config", sexpr, NULL); - } - else { - /* device exists, attempt to modify it */ - ret = xend_op(domain->conn, domain->name, "op", "device_configure", - "config", sexpr, "dev", ref, NULL); - } - -cleanup: - VIR_FREE(sexpr); - virDomainDefFree(def); - virDomainDeviceDefFree(dev); - return ret; -} - -/** - * xenDaemonDetachDevice: - * @domain: pointer to domain object - * @xml: pointer to XML description of device - * - * Destroy a virtual device attachment to backend. - * - * Returns 0 in case of success, -1 in case of failure. - */ -static int -xenDaemonDetachDevice(virDomainPtr domain, const char *xml) -{ - xenUnifiedPrivatePtr priv; - char class[8], ref[80]; - virDomainDeviceDefPtr dev = NULL; - virDomainDefPtr def = NULL; - int ret = -1; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - /* - * on older Xen without the inactive guests management - * avoid doing this on inactive guests - */ - if ((domain->id < 0) && (priv->xendConfigVersion < 3)) - return -1; - - if (!(def = xenDaemonDomainFetch(domain->conn, - domain->id, - domain->name, - NULL))) - goto cleanup; - - if (!(dev = virDomainDeviceDefParse(domain->conn, - priv->caps, - def, xml, VIR_DOMAIN_XML_INACTIVE))) - goto cleanup; - - if (virDomainXMLDevID(domain, dev, class, ref, sizeof(ref))) - goto cleanup; - - ret = xend_op(domain->conn, domain->name, "op", "device_destroy", - "type", class, "dev", ref, "force", "0", "rm_cfg", "1", NULL); - -cleanup: - virDomainDefFree(def); - virDomainDeviceDefFree(dev); - - return ret; -} - -int -xenDaemonDomainGetAutostart(virDomainPtr domain, - int *autostart) -{ - struct sexpr *root; - const char *tmp; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - - /* xm_internal.c (the support for defined domains from /etc/xen - * config files used by old Xen) will handle this. - */ - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xendConfigVersion < 3) - return(-1); - - root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); - if (root == NULL) { - virXendError (domain->conn, VIR_ERR_XEN_CALL, - "%s", _("xenDaemonGetAutostart failed to find this domain")); - return (-1); - } - - *autostart = 0; - - tmp = sexpr_node(root, "domain/on_xend_start"); - if (tmp && STREQ(tmp, "start")) { - *autostart = 1; - } - - sexpr_free(root); - return 0; -} - -int -xenDaemonDomainSetAutostart(virDomainPtr domain, - int autostart) -{ - struct sexpr *root, *autonode; - const char *autostr; - char buf[4096]; - int ret = -1; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INTERNAL_ERROR, - __FUNCTION__); - return (-1); - } - - /* xm_internal.c (the support for defined domains from /etc/xen - * config files used by old Xen) will handle this. - */ - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xendConfigVersion < 3) - return(-1); - - root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); - if (root == NULL) { - virXendError (domain->conn, VIR_ERR_XEN_CALL, - "%s", _("xenDaemonSetAutostart failed to find this domain")); - return (-1); - } - - autostr = sexpr_node(root, "domain/on_xend_start"); - if (autostr) { - if (!STREQ(autostr, "ignore") && !STREQ(autostr, "start")) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("unexpected value from on_xend_start")); - goto error; - } - - // Change the autostart value in place, then define the new sexpr - autonode = sexpr_lookup(root, "domain/on_xend_start"); - VIR_FREE(autonode->u.s.car->u.value); - autonode->u.s.car->u.value = (autostart ? strdup("start") - : strdup("ignore")); - if (!(autonode->u.s.car->u.value)) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("no memory")); - goto error; - } - - if (sexpr2string(root, buf, sizeof(buf)) == 0) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("sexpr2string failed")); - goto error; - } - if (xend_op(domain->conn, "", "op", "new", "config", buf, NULL) != 0) { - virXendError(domain->conn, VIR_ERR_XEN_CALL, - "%s", _("Failed to redefine sexpr")); - goto error; - } - } else { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("on_xend_start not present in sexpr")); - goto error; - } - - ret = 0; - error: - sexpr_free(root); - return ret; -} - -int -xenDaemonDomainMigratePrepare (virConnectPtr dconn, - char **cookie ATTRIBUTE_UNUSED, - int *cookielen ATTRIBUTE_UNUSED, - const char *uri_in, - char **uri_out, - unsigned long flags ATTRIBUTE_UNUSED, - const char *dname ATTRIBUTE_UNUSED, - unsigned long resource ATTRIBUTE_UNUSED) -{ - int r; - char hostname [HOST_NAME_MAX+1]; - - /* If uri_in is NULL, get the current hostname as a best guess - * of how the source host should connect to us. Note that caller - * deallocates this string. - */ - if (uri_in == NULL) { - r = gethostname (hostname, HOST_NAME_MAX+1); - if (r == -1) { - virReportSystemError(dconn, errno, - _("unable to resolve name %s"), hostname); - return -1; - } - *uri_out = strdup (hostname); - if (*uri_out == NULL) { - virReportOOMError(dconn); - return -1; - } - } - - return 0; -} - -int -xenDaemonDomainMigratePerform (virDomainPtr domain, - const char *cookie ATTRIBUTE_UNUSED, - int cookielen ATTRIBUTE_UNUSED, - const char *uri, - unsigned long flags, - const char *dname, - unsigned long bandwidth) -{ - /* Upper layers have already checked domain. */ - virConnectPtr conn = domain->conn; - /* NB: Passing port=0 to xend means it ignores - * the port. However this is somewhat specific to - * the internals of the xend Python code. (XXX). - */ - char port[16] = "0"; - char live[2] = "0"; - int ret; - char *p, *hostname = NULL; - - /* Xen doesn't support renaming domains during migration. */ - if (dname) { - virXendError (conn, VIR_ERR_NO_SUPPORT, - "%s", _("xenDaemonDomainMigrate: Xen does not support" - " renaming domains during migration")); - return -1; - } - - /* Xen (at least up to 3.1.0) takes a resource parameter but - * ignores it. - */ - if (bandwidth) { - virXendError (conn, VIR_ERR_NO_SUPPORT, - "%s", _("xenDaemonDomainMigrate: Xen does not support" - " bandwidth limits during migration")); - return -1; - } - - /* Check the flags. */ - if ((flags & VIR_MIGRATE_LIVE)) { - strcpy (live, "1"); - flags &= ~VIR_MIGRATE_LIVE; - } - if (flags != 0) { - virXendError (conn, VIR_ERR_NO_SUPPORT, - "%s", _("xenDaemonDomainMigrate: unsupported flag")); - return -1; - } - - /* Set hostname and port. - * - * URI is non-NULL (guaranteed by caller). We expect either - * "hostname", "hostname:port" or "xenmigr://hostname[:port]/". - */ - if (strstr (uri, "//")) { /* Full URI. */ - xmlURIPtr uriptr = xmlParseURI (uri); - if (!uriptr) { - virXendError (conn, VIR_ERR_INVALID_ARG, - "%s", _("xenDaemonDomainMigrate: invalid URI")); - return -1; - } - if (uriptr->scheme && STRCASENEQ (uriptr->scheme, "xenmigr")) { - virXendError (conn, VIR_ERR_INVALID_ARG, - "%s", _("xenDaemonDomainMigrate: only xenmigr://" - " migrations are supported by Xen")); - xmlFreeURI (uriptr); - return -1; - } - if (!uriptr->server) { - virXendError (conn, VIR_ERR_INVALID_ARG, - "%s", _("xenDaemonDomainMigrate: a hostname must be" - " specified in the URI")); - xmlFreeURI (uriptr); - return -1; - } - hostname = strdup (uriptr->server); - if (!hostname) { - virReportOOMError (conn); - xmlFreeURI (uriptr); - return -1; - } - if (uriptr->port) - snprintf (port, sizeof port, "%d", uriptr->port); - xmlFreeURI (uriptr); - } - else if ((p = strrchr (uri, ':')) != NULL) { /* "hostname:port" */ - int port_nr, n; - - if (sscanf (p+1, "%d", &port_nr) != 1) { - virXendError (conn, VIR_ERR_INVALID_ARG, - "%s", _("xenDaemonDomainMigrate: invalid port number")); - return -1; - } - snprintf (port, sizeof port, "%d", port_nr); - - /* Get the hostname. */ - n = p - uri; /* n = Length of hostname in bytes. */ - hostname = strdup (uri); - if (!hostname) { - virReportOOMError (conn); - return -1; - } - hostname[n] = '\0'; - } - else { /* "hostname" (or IP address) */ - hostname = strdup (uri); - if (!hostname) { - virReportOOMError (conn); - return -1; - } - } - - DEBUG("hostname = %s, port = %s", hostname, port); - - /* Make the call. */ - ret = xend_op (domain->conn, domain->name, - "op", "migrate", - "destination", hostname, - "live", live, - "port", port, - "node", "-1", - "ssl", "0", - "resource", "0", /* required, xend ignores it */ - NULL); - VIR_FREE (hostname); - - DEBUG0("migration done"); - - return ret; -} - -virDomainPtr xenDaemonDomainDefineXML(virConnectPtr conn, const char *xmlDesc) { - int ret; - char *sexpr; - virDomainPtr dom; - xenUnifiedPrivatePtr priv; - virDomainDefPtr def; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (priv->xendConfigVersion < 3) - return(NULL); - - if (!(def = virDomainDefParseString(conn, priv->caps, xmlDesc, - VIR_DOMAIN_XML_INACTIVE))) { - virXendError(conn, VIR_ERR_XML_ERROR, - "%s", _("failed to parse domain description")); - return (NULL); - } - - if (!(sexpr = xenDaemonFormatSxpr(conn, def, priv->xendConfigVersion))) { - virXendError(conn, VIR_ERR_XML_ERROR, - "%s", _("failed to build sexpr")); - goto error; - } - - DEBUG("Defining w/ sexpr: \n%s", sexpr); - - ret = xend_op(conn, "", "op", "new", "config", sexpr, NULL); - VIR_FREE(sexpr); - if (ret != 0) { - virXendError(conn, VIR_ERR_XEN_CALL, - _("Failed to create inactive domain %s\n"), def->name); - goto error; - } - - dom = virDomainLookupByName(conn, def->name); - if (dom == NULL) { - goto error; - } - virDomainDefFree(def); - return (dom); - - error: - virDomainDefFree(def); - return (NULL); -} -int xenDaemonDomainCreate(virDomainPtr domain) -{ - xenUnifiedPrivatePtr priv; - int ret; - virDomainPtr tmp; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (priv->xendConfigVersion < 3) - return(-1); - - ret = xend_op(domain->conn, domain->name, "op", "start", NULL); - - if (ret != -1) { - /* Need to force a refresh of this object's ID */ - tmp = virDomainLookupByName(domain->conn, domain->name); - if (tmp) { - domain->id = tmp->id; - virDomainFree(tmp); - } - } - return ret; -} - -int xenDaemonDomainUndefine(virDomainPtr domain) -{ - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (priv->xendConfigVersion < 3) - return(-1); - - return xend_op(domain->conn, domain->name, "op", "delete", NULL); -} - -/** - * xenDaemonNumOfDomains: - * @conn: pointer to the hypervisor connection - * - * Provides the number of active domains. - * - * Returns the number of domain found or -1 in case of error - */ -static int -xenDaemonNumOfDefinedDomains(virConnectPtr conn) -{ - struct sexpr *root = NULL; - int ret = -1; - struct sexpr *_for_i, *node; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - /* xm_internal.c (the support for defined domains from /etc/xen - * config files used by old Xen) will handle this. - */ - if (priv->xendConfigVersion < 3) - return(-1); - - root = sexpr_get(conn, "/xend/domain?state=halted"); - if (root == NULL) - goto error; - - ret = 0; - - for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; - _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { - if (node->kind != SEXPR_VALUE) - continue; - ret++; - } - -error: - sexpr_free(root); - return(ret); -} - -static int -xenDaemonListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) { - struct sexpr *root = NULL; - int ret = -1; - struct sexpr *_for_i, *node; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (priv->xendConfigVersion < 3) - return(-1); - - if ((names == NULL) || (maxnames < 0)) - goto error; - if (maxnames == 0) - return(0); - - root = sexpr_get(conn, "/xend/domain?state=halted"); - if (root == NULL) - goto error; - - ret = 0; - - for (_for_i = root, node = root->u.s.car; _for_i->kind == SEXPR_CONS; - _for_i = _for_i->u.s.cdr, node = _for_i->u.s.car) { - if (node->kind != SEXPR_VALUE) - continue; - - names[ret++] = strdup(node->u.value); - if (ret >= maxnames) - break; - } - -error: - sexpr_free(root); - return(ret); -} - -/** - * xenDaemonGetSchedulerType: - * @domain: pointer to the Domain block - * @nparams: give a number of scheduler parameters - * - * Get the scheduler type of Xen - * - * Returns a scheduler name (credit or sedf) which must be freed by the - * caller or NULL in case of failure - */ -static char * -xenDaemonGetSchedulerType(virDomainPtr domain, int *nparams) -{ - xenUnifiedPrivatePtr priv; - struct sexpr *root; - const char *ret = NULL; - char *schedulertype = NULL; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) - || (nparams == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return NULL; - } - - /* Support only xendConfigVersion >=4 */ - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xendConfigVersion < 4) { - virXendError (domain->conn, VIR_ERR_NO_SUPPORT, - "%s", _("unsupported in xendConfigVersion < 4")); - return NULL; - } - - root = sexpr_get(domain->conn, "/xend/node/"); - if (root == NULL) - return NULL; - - /* get xen_scheduler from xend/node */ - ret = sexpr_node(root, "node/xen_scheduler"); - if (ret == NULL){ - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("node information incomplete, missing scheduler name")); - goto error; - } - if (STREQ (ret, "credit")) { - schedulertype = strdup("credit"); - if (schedulertype == NULL){ - virXendError(domain->conn, VIR_ERR_SYSTEM_ERROR, "%s", _("strdup failed")); - goto error; - } - *nparams = XEN_SCHED_CRED_NPARAM; - } else if (STREQ (ret, "sedf")) { - schedulertype = strdup("sedf"); - if (schedulertype == NULL){ - virXendError(domain->conn, VIR_ERR_SYSTEM_ERROR, "%s", _("strdup failed")); - goto error; - } - *nparams = XEN_SCHED_SEDF_NPARAM; - } else { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("Unknown scheduler")); - goto error; - } - -error: - sexpr_free(root); - return schedulertype; - -} - -static const char *str_weight = "weight"; -static const char *str_cap = "cap"; - -/** - * xenDaemonGetSchedulerParameters: - * @domain: pointer to the Domain block - * @params: pointer to scheduler parameters - * This memory area must be allocated by the caller - * @nparams: a number of scheduler parameters which should be same as a - * given number from xenDaemonGetSchedulerType() - * - * Get the scheduler parameters - * - * Returns 0 or -1 in case of failure - */ -static int -xenDaemonGetSchedulerParameters(virDomainPtr domain, - virSchedParameterPtr params, int *nparams) -{ - xenUnifiedPrivatePtr priv; - struct sexpr *root; - char *sched_type = NULL; - int sched_nparam = 0; - int ret = -1; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) - || (params == NULL) || (nparams == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - - /* Support only xendConfigVersion >=4 */ - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xendConfigVersion < 4) { - virXendError (domain->conn, VIR_ERR_NO_SUPPORT, - "%s", _("unsupported in xendConfigVersion < 4")); - return (-1); - } - - /* look up the information by domain name */ - root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); - if (root == NULL) - return (-1); - - /* get the scheduler type */ - sched_type = xenDaemonGetSchedulerType(domain, &sched_nparam); - if (sched_type == NULL) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to get a scheduler name")); - goto error; - } - - switch (sched_nparam){ - case XEN_SCHED_SEDF_NPARAM: - /* TODO: Implement for Xen/SEDF */ - TODO - goto error; - case XEN_SCHED_CRED_NPARAM: - /* get cpu_weight/cpu_cap from xend/domain */ - if (sexpr_node(root, "domain/cpu_weight") == NULL) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing cpu_weight")); - goto error; - } - if (sexpr_node(root, "domain/cpu_cap") == NULL) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing cpu_cap")); - goto error; - } - - strncpy (params[0].field, str_weight, VIR_DOMAIN_SCHED_FIELD_LENGTH); - params[0].field[VIR_DOMAIN_SCHED_FIELD_LENGTH-1] = '\0'; - params[0].type = VIR_DOMAIN_SCHED_FIELD_UINT; - params[0].value.ui = sexpr_int(root, "domain/cpu_weight"); - - strncpy (params[1].field, str_cap, VIR_DOMAIN_SCHED_FIELD_LENGTH); - params[1].field[VIR_DOMAIN_SCHED_FIELD_LENGTH-1] = '\0'; - params[1].type = VIR_DOMAIN_SCHED_FIELD_UINT; - params[1].value.ui = sexpr_int(root, "domain/cpu_cap"); - *nparams = XEN_SCHED_CRED_NPARAM; - ret = 0; - break; - default: - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("Unknown scheduler")); - goto error; - } - -error: - sexpr_free(root); - VIR_FREE(sched_type); - return (ret); -} - -/** - * xenDaemonSetSchedulerParameters: - * @domain: pointer to the Domain block - * @params: pointer to scheduler parameters - * @nparams: a number of scheduler setting parameters - * - * Set the scheduler parameters - * - * Returns 0 or -1 in case of failure - */ -static int -xenDaemonSetSchedulerParameters(virDomainPtr domain, - virSchedParameterPtr params, int nparams) -{ - xenUnifiedPrivatePtr priv; - struct sexpr *root; - char *sched_type = NULL; - int i; - int sched_nparam = 0; - int ret = -1; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) - || (params == NULL)) { - virXendError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - - /* Support only xendConfigVersion >=4 and active domains */ - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xendConfigVersion < 4) { - virXendError (domain->conn, VIR_ERR_NO_SUPPORT, - "%s", _("unsupported in xendConfigVersion < 4")); - return (-1); - } - - /* look up the information by domain name */ - root = sexpr_get(domain->conn, "/xend/domain/%s?detail=1", domain->name); - if (root == NULL) - return (-1); - - /* get the scheduler type */ - sched_type = xenDaemonGetSchedulerType(domain, &sched_nparam); - if (sched_type == NULL) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to get a scheduler name")); - goto error; - } - - switch (sched_nparam){ - case XEN_SCHED_SEDF_NPARAM: - /* TODO: Implement for Xen/SEDF */ - TODO - goto error; - case XEN_SCHED_CRED_NPARAM: { - char buf_weight[VIR_UUID_BUFLEN]; - char buf_cap[VIR_UUID_BUFLEN]; - const char *weight = NULL; - const char *cap = NULL; - - /* get the scheduler parameters */ - memset(&buf_weight, 0, VIR_UUID_BUFLEN); - memset(&buf_cap, 0, VIR_UUID_BUFLEN); - for (i = 0; i < nparams; i++) { - if (STREQ (params[i].field, str_weight) && - params[i].type == VIR_DOMAIN_SCHED_FIELD_UINT) { - snprintf(buf_weight, sizeof(buf_weight), "%u", params[i].value.ui); - } else if (STREQ (params[i].field, str_cap) && - params[i].type == VIR_DOMAIN_SCHED_FIELD_UINT) { - snprintf(buf_cap, sizeof(buf_cap), "%u", params[i].value.ui); - } else { - virXendError(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - goto error; - } - } - - /* if not get the scheduler parameter, set the current setting */ - if (strlen(buf_weight) == 0) { - weight = sexpr_node(root, "domain/cpu_weight"); - if (weight == NULL) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing cpu_weight")); - goto error; - } - snprintf(buf_weight, sizeof(buf_weight), "%s", weight); - } - if (strlen(buf_cap) == 0) { - cap = sexpr_node(root, "domain/cpu_cap"); - if (cap == NULL) { - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("domain information incomplete, missing cpu_cap")); - goto error; - } - snprintf(buf_cap, sizeof(buf_cap), "%s", cap); - } - - ret = xend_op(domain->conn, domain->name, "op", - "domain_sched_credit_set", "weight", buf_weight, - "cap", buf_cap, NULL); - break; - } - default: - virXendError(domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("Unknown scheduler")); - goto error; - } - -error: - sexpr_free(root); - VIR_FREE(sched_type); - return (ret); -} - -/** - * xenDaemonDomainBlockPeek: - * @dom: domain object - * @path: path to the file or device - * @offset: offset - * @size: size - * @buffer: return buffer - * - * Returns 0 if successful, -1 if error, -2 if declined. - */ -int -xenDaemonDomainBlockPeek (virDomainPtr domain, const char *path, - unsigned long long offset, size_t size, - void *buffer) -{ - xenUnifiedPrivatePtr priv; - struct sexpr *root = NULL; - int fd = -1, ret = -1; - int found = 0, i; - virDomainDefPtr def; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id < 0 && priv->xendConfigVersion < 3) - return -2; /* Decline, allow XM to handle it. */ - - /* Security check: The path must correspond to a block device. */ - if (domain->id > 0) - root = sexpr_get (domain->conn, "/xend/domain/%d?detail=1", - domain->id); - else if (domain->id < 0) - root = sexpr_get (domain->conn, "/xend/domain/%s?detail=1", - domain->name); - else { - /* This call always fails for dom0. */ - virXendError (domain->conn, VIR_ERR_NO_SUPPORT, - "%s", _("domainBlockPeek is not supported for dom0")); - return -1; - } - - if (!root) { - virXendError (domain->conn, VIR_ERR_XEN_CALL, __FUNCTION__); - return -1; - } - - if (!(def = xenDaemonParseSxpr(domain->conn, root, priv->xendConfigVersion, NULL))) - goto cleanup; - - for (i = 0 ; i < def->ndisks ; i++) { - if (def->disks[i]->src && - STREQ(def->disks[i]->src, path)) { - found = 1; - break; - } - } - if (!found) { - virXendError (domain->conn, VIR_ERR_INVALID_ARG, - _("%s: invalid path"), path); - goto cleanup; - } - - /* The path is correct, now try to open it and get its size. */ - fd = open (path, O_RDONLY); - if (fd == -1) { - virReportSystemError(domain->conn, errno, - _("failed to open for reading: %s"), - path); - goto cleanup; - } - - /* Seek and read. */ - /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should - * be 64 bits on all platforms. - */ - if (lseek (fd, offset, SEEK_SET) == (off_t) -1 || - saferead (fd, buffer, size) == (ssize_t) -1) { - virReportSystemError(domain->conn, errno, - _("failed to lseek or read from file: %s"), - path); - goto cleanup; - } - - ret = 0; - cleanup: - if (fd >= 0) close (fd); - sexpr_free(root); - virDomainDefFree(def); - return ret; -} - -struct xenUnifiedDriver xenDaemonDriver = { - xenDaemonOpen, /* open */ - xenDaemonClose, /* close */ - xenDaemonGetVersion, /* version */ - NULL, /* hostname */ - xenDaemonNodeGetInfo, /* nodeGetInfo */ - NULL, /* getCapabilities */ - xenDaemonListDomains, /* listDomains */ - xenDaemonNumOfDomains, /* numOfDomains */ - xenDaemonCreateXML, /* domainCreateXML */ - xenDaemonDomainSuspend, /* domainSuspend */ - xenDaemonDomainResume, /* domainResume */ - xenDaemonDomainShutdown, /* domainShutdown */ - xenDaemonDomainReboot, /* domainReboot */ - xenDaemonDomainDestroy, /* domainDestroy */ - xenDaemonDomainGetOSType, /* domainGetOSType */ - xenDaemonDomainGetMaxMemory, /* domainGetMaxMemory */ - xenDaemonDomainSetMaxMemory, /* domainSetMaxMemory */ - xenDaemonDomainSetMemory, /* domainMaxMemory */ - xenDaemonDomainGetInfo, /* domainGetInfo */ - xenDaemonDomainSave, /* domainSave */ - xenDaemonDomainRestore, /* domainRestore */ - xenDaemonDomainCoreDump, /* domainCoreDump */ - xenDaemonDomainSetVcpus, /* domainSetVcpus */ - xenDaemonDomainPinVcpu, /* domainPinVcpu */ - xenDaemonDomainGetVcpus, /* domainGetVcpus */ - NULL, /* domainGetMaxVcpus */ - xenDaemonListDefinedDomains, /* listDefinedDomains */ - xenDaemonNumOfDefinedDomains,/* numOfDefinedDomains */ - xenDaemonDomainCreate, /* domainCreate */ - xenDaemonDomainDefineXML, /* domainDefineXML */ - xenDaemonDomainUndefine, /* domainUndefine */ - xenDaemonAttachDevice, /* domainAttachDevice */ - xenDaemonDetachDevice, /* domainDetachDevice */ - xenDaemonDomainGetAutostart, /* domainGetAutostart */ - xenDaemonDomainSetAutostart, /* domainSetAutostart */ - xenDaemonGetSchedulerType, /* domainGetSchedulerType */ - xenDaemonGetSchedulerParameters, /* domainGetSchedulerParameters */ - xenDaemonSetSchedulerParameters, /* domainSetSchedulerParameters */ -}; - -/************************************************************************ - * * - * Converter functions to go from the XML tree to an S-Expr for Xen * - * * - ************************************************************************/ - - -/** - * virtDomainParseXMLGraphicsDescVFB: - * @conn: pointer to the hypervisor connection - * @node: node containing graphics description - * @buf: a buffer for the result S-Expr - * - * Parse the graphics part of the XML description and add it to the S-Expr - * in buf. This is a temporary interface as the S-Expr interface will be - * replaced by XML-RPC in the future. However the XML format should stay - * valid over time. - * - * Returns 0 in case of success, -1 in case of error - */ -static int -xenDaemonFormatSxprGraphicsNew(virConnectPtr conn, - virDomainGraphicsDefPtr def, - virBufferPtr buf) -{ - if (def->type != VIR_DOMAIN_GRAPHICS_TYPE_SDL && - def->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected graphics type %d"), - def->type); - return -1; - } - - virBufferAddLit(buf, "(device (vkbd))"); - virBufferAddLit(buf, "(device (vfb "); - - if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - virBufferAddLit(buf, "(type sdl)"); - if (def->data.sdl.display) - virBufferVSprintf(buf, "(display '%s')", def->data.sdl.display); - if (def->data.sdl.xauth) - virBufferVSprintf(buf, "(xauthority '%s')", def->data.sdl.xauth); - } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - virBufferAddLit(buf, "(type vnc)"); - if (def->data.vnc.autoport) { - virBufferAddLit(buf, "(vncunused 1)"); - } else { - virBufferAddLit(buf, "(vncunused 0)"); - virBufferVSprintf(buf, "(vncdisplay %d)", def->data.vnc.port-5900); - } - - if (def->data.vnc.listenAddr) - virBufferVSprintf(buf, "(vnclisten '%s')", def->data.vnc.listenAddr); - if (def->data.vnc.passwd) - virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.passwd); - if (def->data.vnc.keymap) - virBufferVSprintf(buf, "(keymap '%s')", def->data.vnc.keymap); - } - - virBufferAddLit(buf, "))"); - - return 0; -} - - -static int -xenDaemonFormatSxprGraphicsOld(virConnectPtr conn, - virDomainGraphicsDefPtr def, - virBufferPtr buf, - int xendConfigVersion) -{ - if (def->type != VIR_DOMAIN_GRAPHICS_TYPE_SDL && - def->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected graphics type %d"), - def->type); - return -1; - } - - if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - virBufferAddLit(buf, "(sdl 1)"); - if (def->data.sdl.display) - virBufferVSprintf(buf, "(display '%s')", def->data.sdl.display); - if (def->data.sdl.xauth) - virBufferVSprintf(buf, "(xauthority '%s')", def->data.sdl.xauth); - } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - virBufferAddLit(buf, "(vnc 1)"); - if (xendConfigVersion >= 2) { - if (def->data.vnc.autoport) { - virBufferAddLit(buf, "(vncunused 1)"); - } else { - virBufferAddLit(buf, "(vncunused 0)"); - virBufferVSprintf(buf, "(vncdisplay %d)", def->data.vnc.port-5900); - } - - if (def->data.vnc.listenAddr) - virBufferVSprintf(buf, "(vnclisten '%s')", def->data.vnc.listenAddr); - if (def->data.vnc.passwd) - virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.passwd); - if (def->data.vnc.keymap) - virBufferVSprintf(buf, "(keymap '%s')", def->data.vnc.keymap); - - } - } - - return 0; -} - -int -xenDaemonFormatSxprChr(virConnectPtr conn, - virDomainChrDefPtr def, - virBufferPtr buf) -{ - const char *type = virDomainChrTypeToString(def->type); - - if (!type) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("unexpected chr device type")); - return -1; - } - - switch (def->type) { - case VIR_DOMAIN_CHR_TYPE_NULL: - case VIR_DOMAIN_CHR_TYPE_STDIO: - case VIR_DOMAIN_CHR_TYPE_VC: - case VIR_DOMAIN_CHR_TYPE_PTY: - virBufferVSprintf(buf, "%s", type); - break; - - case VIR_DOMAIN_CHR_TYPE_FILE: - case VIR_DOMAIN_CHR_TYPE_PIPE: - virBufferVSprintf(buf, "%s:%s", type, def->data.file.path); - break; - - case VIR_DOMAIN_CHR_TYPE_DEV: - virBufferVSprintf(buf, "%s", def->data.file.path); - break; - - case VIR_DOMAIN_CHR_TYPE_TCP: - virBufferVSprintf(buf, "%s:%s:%s%s", - (def->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW ? - "tcp" : "telnet"), - (def->data.tcp.host ? def->data.tcp.host : ""), - (def->data.tcp.service ? def->data.tcp.service : ""), - (def->data.tcp.listen ? ",listen" : "")); - break; - - case VIR_DOMAIN_CHR_TYPE_UDP: - virBufferVSprintf(buf, "%s:%s:%s@%s:%s", type, - (def->data.udp.connectHost ? def->data.udp.connectHost : ""), - (def->data.udp.connectService ? def->data.udp.connectService : ""), - (def->data.udp.bindHost ? def->data.udp.bindHost : ""), - (def->data.udp.bindService ? def->data.udp.bindService : "")); - break; - - case VIR_DOMAIN_CHR_TYPE_UNIX: - virBufferVSprintf(buf, "%s:%s%s", type, - def->data.nix.path, - def->data.nix.listen ? ",listen" : ""); - break; - } - - if (virBufferError(buf)) - return -1; - - return 0; -} - - -/** - * virDomainParseXMLDiskDesc: - * @node: node containing disk description - * @conn: pointer to the hypervisor connection - * @buf: a buffer for the result S-Expr - * @xendConfigVersion: xend configuration file format - * - * Parse the one disk in the XML description and add it to the S-Expr in buf - * This is a temporary interface as the S-Expr interface - * will be replaced by XML-RPC in the future. However the XML format should - * stay valid over time. - * - * Returns 0 in case of success, -1 in case of error. - */ -static int -xenDaemonFormatSxprDisk(virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainDiskDefPtr def, - virBufferPtr buf, - int hvm, - int xendConfigVersion, - int isAttach) -{ - /* Xend (all versions) put the floppy device config - * under the hvm (image (os)) block - */ - if (hvm && - def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) { - if (isAttach) { - virXendError(conn, VIR_ERR_INVALID_ARG, - _("Cannot directly attach floppy %s"), def->src); - return -1; - } - return 0; - } - - /* Xend <= 3.0.2 doesn't include cdrom config here */ - if (hvm && - def->device == VIR_DOMAIN_DISK_DEVICE_CDROM && - xendConfigVersion == 1) { - if (isAttach) { - virXendError(conn, VIR_ERR_INVALID_ARG, - _("Cannot directly attach CDROM %s"), def->src); - return -1; - } - return 0; - } - - if (!isAttach) - virBufferAddLit(buf, "(device "); - - /* Normally disks are in a (device (vbd ...)) block - * but blktap disks ended up in a differently named - * (device (tap ....)) block.... */ - if (def->driverName && - STREQ(def->driverName, "tap")) { - virBufferAddLit(buf, "(tap "); - } else { - virBufferAddLit(buf, "(vbd "); - } - - if (hvm) { - /* Xend <= 3.0.2 wants a ioemu: prefix on devices for HVM */ - if (xendConfigVersion == 1) - virBufferVSprintf(buf, "(dev 'ioemu:%s')", def->dst); - else /* But newer does not */ - virBufferVSprintf(buf, "(dev '%s:%s')", def->dst, - def->device == VIR_DOMAIN_DISK_DEVICE_CDROM ? - "cdrom" : "disk"); - } else if (def->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { - virBufferVSprintf(buf, "(dev '%s:cdrom')", def->dst); - } else { - virBufferVSprintf(buf, "(dev '%s')", def->dst); - } - - if (def->src) { - if (def->driverName) { - if (STREQ(def->driverName, "tap")) { - virBufferVSprintf(buf, "(uname '%s:%s:%s')", - def->driverName, - def->driverType ? def->driverType : "aio", - def->src); - } else { - virBufferVSprintf(buf, "(uname '%s:%s')", - def->driverName, - def->src); - } - } else { - if (def->type == VIR_DOMAIN_DISK_TYPE_FILE) { - virBufferVSprintf(buf, "(uname 'file:%s')", def->src); - } else { - if (def->src[0] == '/') - virBufferVSprintf(buf, "(uname 'phy:%s')", def->src); - else - virBufferVSprintf(buf, "(uname 'phy:/dev/%s')", def->src); - } - } - } - - if (def->readonly) - virBufferAddLit(buf, "(mode 'r')"); - else if (def->shared) - virBufferAddLit(buf, "(mode 'w!')"); - else - virBufferAddLit(buf, "(mode 'w')"); - - if (!isAttach) - virBufferAddLit(buf, ")"); - - virBufferAddLit(buf, ")"); - - return 0; -} - -/** - * xenDaemonFormatSxprNet - * @conn: pointer to the hypervisor connection - * @node: node containing the interface description - * @buf: a buffer for the result S-Expr - * @xendConfigVersion: xend configuration file format - * - * Parse the one interface the XML description and add it to the S-Expr in buf - * This is a temporary interface as the S-Expr interface - * will be replaced by XML-RPC in the future. However the XML format should - * stay valid over time. - * - * Returns 0 in case of success, -1 in case of error. - */ -static int -xenDaemonFormatSxprNet(virConnectPtr conn, - virDomainNetDefPtr def, - virBufferPtr buf, - int hvm, - int xendConfigVersion, - int isAttach) -{ - const char *script = DEFAULT_VIF_SCRIPT; - - if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE && - def->type != VIR_DOMAIN_NET_TYPE_NETWORK && - def->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unsupported network type %d"), def->type); - return -1; - } - - if (!isAttach) - virBufferAddLit(buf, "(device "); - - virBufferAddLit(buf, "(vif "); - - virBufferVSprintf(buf, - "(mac '%02x:%02x:%02x:%02x:%02x:%02x')", - def->mac[0], def->mac[1], def->mac[2], - def->mac[3], def->mac[4], def->mac[5]); - - switch (def->type) { - case VIR_DOMAIN_NET_TYPE_BRIDGE: - virBufferVSprintf(buf, "(bridge '%s')", def->data.bridge.brname); - if (def->data.bridge.script) - script = def->data.bridge.script; - - virBufferVSprintf(buf, "(script '%s')", script); - if (def->data.bridge.ipaddr != NULL) - virBufferVSprintf(buf, "(ip '%s')", def->data.bridge.ipaddr); - break; - - case VIR_DOMAIN_NET_TYPE_NETWORK: - { - virNetworkPtr network = - virNetworkLookupByName(conn, def->data.network.name); - char *bridge; - - if (!network) { - virXendError(conn, VIR_ERR_NO_NETWORK, "%s", - def->data.network.name); - return -1; - } - - bridge = virNetworkGetBridgeName(network); - virNetworkFree(network); - if (!bridge) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("network %s is not active"), - def->data.network.name); - return -1; - } - virBufferVSprintf(buf, "(bridge '%s')", bridge); - virBufferVSprintf(buf, "(script '%s')", script); - VIR_FREE(bridge); - } - break; - - case VIR_DOMAIN_NET_TYPE_ETHERNET: - if (def->data.ethernet.script) - virBufferVSprintf(buf, "(script '%s')", def->data.ethernet.script); - if (def->data.ethernet.ipaddr != NULL) - virBufferVSprintf(buf, "(ip '%s')", def->data.ethernet.ipaddr); - break; - } - - if (def->ifname != NULL && - !STRPREFIX(def->ifname, "vif")) - virBufferVSprintf(buf, "(vifname '%s')", def->ifname); - - if (def->model != NULL) - virBufferVSprintf(buf, "(model '%s')", def->model); - - /* - * apparently (type ioemu) breaks paravirt drivers on HVM so skip this - * from Xen 3.1.0 - */ - if (hvm && xendConfigVersion <= XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU) - virBufferAddLit(buf, "(type ioemu)"); - - if (!isAttach) - virBufferAddLit(buf, ")"); - - virBufferAddLit(buf, ")"); - - return 0; -} - - -static void -xenDaemonFormatSxprPCI(virDomainHostdevDefPtr def, - virBufferPtr buf) -{ - virBufferVSprintf(buf, "(dev (domain 0x%04x)(bus 0x%02x)(slot 0x%02x)(func 0x%x))", - def->source.subsys.u.pci.domain, - def->source.subsys.u.pci.bus, - def->source.subsys.u.pci.slot, - def->source.subsys.u.pci.function); -} - -static int -xenDaemonFormatSxprOnePCI(virConnectPtr conn, - virDomainHostdevDefPtr def, - virBufferPtr buf) -{ - if (def->managed) { - virXendError(conn, VIR_ERR_NO_SUPPORT, "%s", - _("managed PCI devices not supported with XenD")); - return -1; - } - - virBufferAddLit(buf, "(pci "); - xenDaemonFormatSxprPCI(def, buf); - virBufferAddLit(buf, ")"); - - return 0; -} - -static int -xenDaemonFormatSxprAllPCI(virConnectPtr conn, - virDomainDefPtr def, - virBufferPtr buf) -{ - int hasPCI = 0; - int i; - - for (i = 0 ; i < def->nhostdevs ; i++) - if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) - hasPCI = 1; - - if (!hasPCI) - return 0; - - /* - * With the (domain ...) block we have the following odd setup - * - * (device - * (pci - * (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0)) - * (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0)) - * ) - * ) - * - * Normally there is one (device ...) block per device, but in - * wierd world of Xen PCI, once (device ...) covers multiple - * devices. - */ - - virBufferAddLit(buf, "(device (pci "); - for (i = 0 ; i < def->nhostdevs ; i++) { - if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { - if (def->hostdevs[i]->managed) { - virXendError(conn, VIR_ERR_NO_SUPPORT, "%s", - _("managed PCI devices not supported with XenD")); - return -1; - } - - xenDaemonFormatSxprPCI(def->hostdevs[i], buf); - } - } - virBufferAddLit(buf, "))"); - - return 0; -} - -int -xenDaemonFormatSxprSound(virConnectPtr conn, - virDomainDefPtr def, - virBufferPtr buf) -{ - const char *str; - int i; - - for (i = 0 ; i < def->nsounds ; i++) { - if (!(str = virDomainSoundModelTypeToString(def->sounds[i]->model))) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected sound model %d"), - def->sounds[i]->model); - return -1; - } - virBufferVSprintf(buf, "%s%s", i ? "," : "", str); - } - - if (virBufferError(buf)) - return -1; - - return 0; -} - - -static int -xenDaemonFormatSxprInput(virConnectPtr conn, - virDomainInputDefPtr input, - virBufferPtr buf) -{ - if (input->bus != VIR_DOMAIN_INPUT_BUS_USB) - return 0; - - if (input->type != VIR_DOMAIN_INPUT_TYPE_MOUSE && - input->type != VIR_DOMAIN_INPUT_TYPE_TABLET) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected input type %d"), input->type); - return -1; - } - - virBufferVSprintf(buf, "(usbdevice %s)", - input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? - "mouse" : "tablet"); - - return 0; -} - - -/** - * xenDaemonFormatSxpr: - * @conn: pointer to the hypervisor connection - * @def: domain config definition - * @xendConfigVersion: xend configuration file format - * - * Generate an SEXPR representing the domain configuration. - * - * Returns the 0 terminatedi S-Expr string or NULL in case of error. - * the caller must free() the returned value. - */ -char * -xenDaemonFormatSxpr(virConnectPtr conn, - virDomainDefPtr def, - int xendConfigVersion) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - char uuidstr[VIR_UUID_STRING_BUFLEN]; - const char *tmp; - int hvm = 0, i; - - virBufferAddLit(&buf, "(vm "); - virBufferVSprintf(&buf, "(name '%s')", def->name); - virBufferVSprintf(&buf, "(memory %lu)(maxmem %lu)", - def->memory/1024, def->maxmem/1024); - virBufferVSprintf(&buf, "(vcpus %lu)", def->vcpus); - - if (def->cpumask) { - char *ranges = virDomainCpuSetFormat(conn, def->cpumask, def->cpumasklen); - if (ranges == NULL) - goto error; - virBufferVSprintf(&buf, "(cpus '%s')", ranges); - VIR_FREE(ranges); - } - - virUUIDFormat(def->uuid, uuidstr); - virBufferVSprintf(&buf, "(uuid '%s')", uuidstr); - - if (def->os.bootloader) { - if (def->os.bootloader[0]) - virBufferVSprintf(&buf, "(bootloader '%s')", def->os.bootloader); - else - virBufferAddLit(&buf, "(bootloader)"); - - if (def->os.bootloaderArgs) - virBufferVSprintf(&buf, "(bootloader_args '%s')", def->os.bootloaderArgs); - } - - if (!(tmp = virDomainLifecycleTypeToString(def->onPoweroff))) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected lifecycle value %d"), def->onPoweroff); - goto error; - } - virBufferVSprintf(&buf, "(on_poweroff '%s')", tmp); - - if (!(tmp = virDomainLifecycleTypeToString(def->onReboot))) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected lifecycle value %d"), def->onReboot); - goto error; - } - virBufferVSprintf(&buf, "(on_reboot '%s')", tmp); - - if (!(tmp = virDomainLifecycleTypeToString(def->onCrash))) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected lifecycle value %d"), def->onCrash); - goto error; - } - virBufferVSprintf(&buf, "(on_crash '%s')", tmp); - - /* Set localtime here for current XenD (both PV & HVM) */ - if (def->localtime) - virBufferAddLit(&buf, "(localtime 1)"); - - if (!def->os.bootloader) { - if (STREQ(def->os.type, "hvm")) - hvm = 1; - - if (hvm) - virBufferAddLit(&buf, "(image (hvm "); - else - virBufferAddLit(&buf, "(image (linux "); - - if (hvm && - def->os.loader == NULL) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - "%s",_("no HVM domain loader")); - goto error; - } - - if (def->os.kernel) - virBufferVSprintf(&buf, "(kernel '%s')", def->os.kernel); - if (def->os.initrd) - virBufferVSprintf(&buf, "(ramdisk '%s')", def->os.initrd); - if (def->os.root) - virBufferVSprintf(&buf, "(root '%s')", def->os.root); - if (def->os.cmdline) - virBufferVSprintf(&buf, "(args '%s')", def->os.cmdline); - - if (hvm) { - char bootorder[VIR_DOMAIN_BOOT_LAST+1]; - if (def->os.kernel) - virBufferVSprintf(&buf, "(loader '%s')", def->os.loader); - else - virBufferVSprintf(&buf, "(kernel '%s')", def->os.loader); - - virBufferVSprintf(&buf, "(vcpus %lu)", def->vcpus); - - for (i = 0 ; i < def->os.nBootDevs ; i++) { - switch (def->os.bootDevs[i]) { - case VIR_DOMAIN_BOOT_FLOPPY: - bootorder[i] = 'a'; - break; - default: - case VIR_DOMAIN_BOOT_DISK: - bootorder[i] = 'c'; - break; - case VIR_DOMAIN_BOOT_CDROM: - bootorder[i] = 'd'; - break; - case VIR_DOMAIN_BOOT_NET: - bootorder[i] = 'n'; - break; - } - } - if (def->os.nBootDevs == 0) { - bootorder[0] = 'c'; - bootorder[1] = '\0'; - } else { - bootorder[def->os.nBootDevs] = '\0'; - } - virBufferVSprintf(&buf, "(boot %s)", bootorder); - - /* some disk devices are defined here */ - for (i = 0 ; i < def->ndisks ; i++) { - switch (def->disks[i]->device) { - case VIR_DOMAIN_DISK_DEVICE_CDROM: - /* Only xend <= 3.0.2 wants cdrom config here */ - if (xendConfigVersion != 1) - break; - if (!STREQ(def->disks[i]->dst, "hdc") || - def->disks[i]->src == NULL) - break; - - virBufferVSprintf(&buf, "(cdrom '%s')", - def->disks[i]->src); - break; - - case VIR_DOMAIN_DISK_DEVICE_FLOPPY: - /* all xend versions define floppies here */ - virBufferVSprintf(&buf, "(%s '%s')", def->disks[i]->dst, - def->disks[i]->src); - break; - - default: - break; - } - } - - if (def->features & (1 << VIR_DOMAIN_FEATURE_ACPI)) - virBufferAddLit(&buf, "(acpi 1)"); - if (def->features & (1 << VIR_DOMAIN_FEATURE_APIC)) - virBufferAddLit(&buf, "(apic 1)"); - if (def->features & (1 << VIR_DOMAIN_FEATURE_PAE)) - virBufferAddLit(&buf, "(pae 1)"); - - virBufferAddLit(&buf, "(usb 1)"); - - for (i = 0 ; i < def->ninputs ; i++) - if (xenDaemonFormatSxprInput(conn, def->inputs[i], &buf) < 0) - goto error; - - if (def->parallels) { - virBufferAddLit(&buf, "(parallel "); - if (xenDaemonFormatSxprChr(conn, def->parallels[0], &buf) < 0) - goto error; - virBufferAddLit(&buf, ")"); - } else { - virBufferAddLit(&buf, "(parallel none)"); - } - if (def->serials) { - virBufferAddLit(&buf, "(serial "); - if (xenDaemonFormatSxprChr(conn, def->serials[0], &buf) < 0) - goto error; - virBufferAddLit(&buf, ")"); - } else { - virBufferAddLit(&buf, "(serial none)"); - } - - /* Set localtime here to keep old XenD happy for HVM */ - if (def->localtime) - virBufferAddLit(&buf, "(localtime 1)"); - - if (def->sounds) { - virBufferAddLit(&buf, "(soundhw '"); - if (xenDaemonFormatSxprSound(conn, def, &buf) < 0) - goto error; - virBufferAddLit(&buf, "')"); - } - } - - /* get the device emulation model */ - if (def->emulator && (hvm || xendConfigVersion >= 3)) - virBufferVSprintf(&buf, "(device_model '%s')", def->emulator); - - - /* PV graphics for xen <= 3.0.4, or HVM graphics for xen <= 3.1.0 */ - if ((!hvm && xendConfigVersion < XEND_CONFIG_MIN_VERS_PVFB_NEWCONF) || - (hvm && xendConfigVersion < 4)) { - if ((def->ngraphics == 1) && - xenDaemonFormatSxprGraphicsOld(conn, def->graphics[0], - &buf, xendConfigVersion) < 0) - goto error; - } - - virBufferAddLit(&buf, "))"); - } - - for (i = 0 ; i < def->ndisks ; i++) - if (xenDaemonFormatSxprDisk(conn, def->disks[i], - &buf, hvm, xendConfigVersion, 0) < 0) - goto error; - - for (i = 0 ; i < def->nnets ; i++) - if (xenDaemonFormatSxprNet(conn, def->nets[i], - &buf, hvm, xendConfigVersion, 0) < 0) - goto error; - - if (xenDaemonFormatSxprAllPCI(conn, def, &buf) < 0) - goto error; - - /* New style PV graphics config xen >= 3.0.4, - * or HVM graphics config xen >= 3.0.5 */ - if ((xendConfigVersion >= XEND_CONFIG_MIN_VERS_PVFB_NEWCONF && !hvm) || - (xendConfigVersion >= 4 && hvm)) { - if ((def->ngraphics == 1) && - xenDaemonFormatSxprGraphicsNew(conn, def->graphics[0], &buf) < 0) - goto error; - } - - virBufferAddLit(&buf, ")"); /* closes (vm */ - - if (virBufferError(&buf)) { - virReportOOMError(conn); - return NULL; - } - - return virBufferContentAndReset(&buf); - -error: - tmp = virBufferContentAndReset(&buf); - VIR_FREE(tmp); - return NULL; -} - - -/** - * virDomainXMLDevID: - * @domain: pointer to domain object - * @dev: pointer to device config object - * @class: Xen device class "vbd" or "vif" (OUT) - * @ref: Xen device reference (OUT) - * - * Set class according to XML root, and: - * - if disk, copy in ref the target name from description - * - if network, get MAC address from description, scan XenStore and - * copy in ref the corresponding vif number. - * - * Returns 0 in case of success, -1 in case of failure. - */ -static int -virDomainXMLDevID(virDomainPtr domain, - virDomainDeviceDefPtr dev, - char *class, - char *ref, - int ref_len) -{ - xenUnifiedPrivatePtr priv = domain->conn->privateData; - char *xref; - - if (dev->type == VIR_DOMAIN_DEVICE_DISK) { - if (dev->data.disk->driverName && - STREQ(dev->data.disk->driverName, "tap")) - strcpy(class, "tap"); - else - strcpy(class, "vbd"); - - if (dev->data.disk->dst == NULL) - return -1; - xenUnifiedLock(priv); - xref = xenStoreDomainGetDiskID(domain->conn, domain->id, - dev->data.disk->dst); - xenUnifiedUnlock(priv); - if (xref == NULL) - return -1; - - strncpy(ref, xref, ref_len); - free(xref); - ref[ref_len - 1] = '\0'; - } else if (dev->type == VIR_DOMAIN_DEVICE_NET) { - char mac[30]; - virDomainNetDefPtr def = dev->data.net; - snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", - def->mac[0], def->mac[1], def->mac[2], - def->mac[3], def->mac[4], def->mac[5]); - - strcpy(class, "vif"); - - xenUnifiedLock(priv); - xref = xenStoreDomainGetNetworkID(domain->conn, domain->id, - mac); - xenUnifiedUnlock(priv); - if (xref == NULL) - return -1; - - strncpy(ref, xref, ref_len); - free(xref); - ref[ref_len - 1] = '\0'; - } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV && - dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { - } else { - virXendError(NULL, VIR_ERR_NO_SUPPORT, - "%s", _("hotplug of device type not supported")); - return -1; - } - - return 0; -} - -#endif /* ! PROXY */ diff --git a/src/xend_internal.h b/src/xend_internal.h deleted file mode 100644 index 9d2571bd16..0000000000 --- a/src/xend_internal.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * xend_internal.h - * - * Copyright (C) 2005,2006 - * - * Anthony Liguori - * Daniel Veillard - * - * Copyright 2006-2008 Red Hat - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License. See the file COPYING in the main directory of this archive - * for more details. - */ - -#ifndef __XEND_INTERNAL_H_ -#define __XEND_INTERNAL_H_ - -#include -#include -#include -#include - -#include "internal.h" -#include "capabilities.h" -#include "domain_conf.h" -#include "driver.h" -#include "buf.h" - -#ifdef __sun -#define DEFAULT_VIF_SCRIPT "vif-vnic" -#else -#define DEFAULT_VIF_SCRIPT "vif-bridge" -#endif - -int -xenDaemonOpen_unix(virConnectPtr conn, const char *path); - -/** - * \brief Blocks until a domain's devices are initialized - * \param xend A xend instance - * \param name The domain's name - * \return 0 for success; -1 (with errno) on error - * - * xen_create() returns after a domain has been allocated including - * its memory. This does not guarentee, though, that the devices - * have come up properly. For instance, if you create a VBD with an - * invalid filename, the error won't occur until after this function - * returns. - */ - int xend_wait_for_devices(virConnectPtr xend, const char *name); - - -/** - * \brief Create a new domain - * \param xend A xend instance - * \param sexpr An S-Expr defining the domain - * \return 0 for success; -1 (with errno) on error - * - * This method will create a domain based the passed in description. The - * domain will be paused after creation and must be unpaused with - * xenDaemonResumeDomain() to begin execution. - */ -int xenDaemonDomainCreateXML(virConnectPtr xend, const char *sexpr); - -/** - * \brief Lookup the id of a domain - * \param xend A xend instance - * \param name The name of the domain - * \param uuid pointer to store a copy of the uuid - * \return the id number on success; -1 (with errno) on error - * - * This method looks up the ids of a domain - */ -int xenDaemonDomainLookupByName_ids(virConnectPtr xend, - const char *name, unsigned char *uuid); - - -/** - * \brief Lookup the name of a domain - * \param xend A xend instance - * \param id The id of the domain - * \param name pointer to store a copy of the name - * \param uuid pointer to store a copy of the uuid - * - * This method looks up the name/uuid of a domain - */ -int xenDaemonDomainLookupByID(virConnectPtr xend, - int id, - char **name, unsigned char *uuid); - - -virDomainDefPtr -xenDaemonDomainFetch(virConnectPtr xend, - int domid, - const char *name, - const char *cpus); - - int xend_parse_sexp_desc_char(virConnectPtr conn, - virBufferPtr buf, - const char *devtype, - int portNum, - const char *value, - const char *tty); - -virDomainDefPtr -xenDaemonParseSxprString(virConnectPtr conn, - const char *sexpr, - int xendConfigVersion); - -int -xenDaemonParseSxprSound(virConnectPtr conn, - virDomainDefPtr def, - const char *str); -virDomainChrDefPtr -xenDaemonParseSxprChar(virConnectPtr conn, - const char *value, - const char *tty); - -int -xenDaemonFormatSxprChr(virConnectPtr conn, - virDomainChrDefPtr def, - virBufferPtr buf); -int -xenDaemonFormatSxprSound(virConnectPtr conn, - virDomainDefPtr def, - virBufferPtr buf); - -char * -xenDaemonFormatSxpr(virConnectPtr conn, - virDomainDefPtr def, - int xendConfigVersion); - - int is_sound_model_valid(const char *model); - int is_sound_model_conflict(const char *model, const char *soundstr); - - -/* refactored ones */ -virDrvOpenStatus xenDaemonOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags); -int xenDaemonClose(virConnectPtr conn); -int xenDaemonGetVersion(virConnectPtr conn, unsigned long *hvVer); -int xenDaemonNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info); -int xenDaemonNodeGetTopology(virConnectPtr conn, virCapsPtr caps); -int xenDaemonDomainSuspend(virDomainPtr domain); -int xenDaemonDomainResume(virDomainPtr domain); -int xenDaemonDomainShutdown(virDomainPtr domain); -int xenDaemonDomainReboot(virDomainPtr domain, unsigned int flags); -int xenDaemonDomainDestroy(virDomainPtr domain); -int xenDaemonDomainSave(virDomainPtr domain, const char *filename); -int xenDaemonDomainRestore(virConnectPtr conn, const char *filename); -int xenDaemonDomainSetMemory(virDomainPtr domain, unsigned long memory); -int xenDaemonDomainSetMaxMemory(virDomainPtr domain, unsigned long memory); -int xenDaemonDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info); -char *xenDaemonDomainDumpXML(virDomainPtr domain, int flags, const char *cpus); -unsigned long xenDaemonDomainGetMaxMemory(virDomainPtr domain); -char **xenDaemonListDomainsOld(virConnectPtr xend); - -virDomainPtr xenDaemonDomainDefineXML(virConnectPtr xend, const char *sexpr); -int xenDaemonDomainCreate(virDomainPtr domain); -int xenDaemonDomainUndefine(virDomainPtr domain); - -int xenDaemonDomainSetVcpus (virDomainPtr domain, - unsigned int vcpus); -int xenDaemonDomainPinVcpu (virDomainPtr domain, - unsigned int vcpu, - unsigned char *cpumap, - int maplen); -int xenDaemonDomainGetVcpus (virDomainPtr domain, - virVcpuInfoPtr info, - int maxinfo, - unsigned char *cpumaps, - int maplen); -int xenDaemonDomainGetAutostart (virDomainPtr dom, - int *autostart); -int xenDaemonDomainSetAutostart (virDomainPtr domain, - int autostart); - -/* xen_unified calls through here. */ -extern struct xenUnifiedDriver xenDaemonDriver; -int xenDaemonInit (void); - -virDomainPtr xenDaemonLookupByID(virConnectPtr conn, int id); -virDomainPtr xenDaemonLookupByUUID(virConnectPtr conn, const unsigned char *uuid); -virDomainPtr xenDaemonLookupByName(virConnectPtr conn, const char *domname); -int xenDaemonDomainMigratePrepare (virConnectPtr dconn, char **cookie, int *cookielen, const char *uri_in, char **uri_out, unsigned long flags, const char *dname, unsigned long resource); -int xenDaemonDomainMigratePerform (virDomainPtr domain, const char *cookie, int cookielen, const char *uri, unsigned long flags, const char *dname, unsigned long resource); - -int xenDaemonDomainBlockPeek (virDomainPtr domain, const char *path, unsigned long long offset, size_t size, void *buffer); -int xenDaemonListDomains(virConnectPtr conn, int *ids, int maxids); - -#endif /* __XEND_INTERNAL_H_ */ diff --git a/src/xm_internal.c b/src/xm_internal.c deleted file mode 100644 index de3aca9e9d..0000000000 --- a/src/xm_internal.c +++ /dev/null @@ -1,3066 +0,0 @@ -/* - * xm_internal.h: helper routines for dealing with inactive domains - * - * Copyright (C) 2006-2007, 2009 Red Hat - * Copyright (C) 2006 Daniel P. Berrange - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Daniel P. Berrange - * - */ - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "virterror_internal.h" -#include "datatypes.h" -#include "xm_internal.h" -#include "xen_unified.h" -#include "xend_internal.h" -#include "hash.h" -#include "buf.h" -#include "uuid.h" -#include "util.h" -#include "memory.h" -#include "logging.h" - -#define VIR_FROM_THIS VIR_FROM_XENXM - -#ifdef WITH_RHEL5_API -#define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 0 -#define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 2 -#else -#define XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU 3 -#define XEND_CONFIG_MIN_VERS_PVFB_NEWCONF 3 -#endif - -/* The true Xen limit varies but so far is always way - less than 1024, which is the Linux kernel limit according - to sched.h, so we'll match that for now */ -#define XEN_MAX_PHYSICAL_CPU 1024 - -static int xenXMConfigSetString(virConfPtr conf, const char *setting, - const char *str); -char * xenXMAutoAssignMac(void); -static int xenXMDomainAttachDevice(virDomainPtr domain, const char *xml); -static int xenXMDomainDetachDevice(virDomainPtr domain, const char *xml); - -#define XM_REFRESH_INTERVAL 10 - -#define XM_CONFIG_DIR "/etc/xen" -#define XM_EXAMPLE_PREFIX "xmexample" -#define XEND_CONFIG_FILE "xend-config.sxp" -#define XEND_PCI_CONFIG_PREFIX "xend-pci-" -#define QEMU_IF_SCRIPT "qemu-ifup" -#define XM_XML_ERROR "Invalid xml" - -struct xenUnifiedDriver xenXMDriver = { - xenXMOpen, /* open */ - xenXMClose, /* close */ - NULL, /* version */ - NULL, /* hostname */ - NULL, /* nodeGetInfo */ - NULL, /* getCapabilities */ - NULL, /* listDomains */ - NULL, /* numOfDomains */ - NULL, /* domainCreateXML */ - NULL, /* domainSuspend */ - NULL, /* domainResume */ - NULL, /* domainShutdown */ - NULL, /* domainReboot */ - NULL, /* domainDestroy */ - NULL, /* domainGetOSType */ - xenXMDomainGetMaxMemory, /* domainGetMaxMemory */ - xenXMDomainSetMaxMemory, /* domainSetMaxMemory */ - xenXMDomainSetMemory, /* domainMaxMemory */ - xenXMDomainGetInfo, /* domainGetInfo */ - NULL, /* domainSave */ - NULL, /* domainRestore */ - NULL, /* domainCoreDump */ - xenXMDomainSetVcpus, /* domainSetVcpus */ - xenXMDomainPinVcpu, /* domainPinVcpu */ - NULL, /* domainGetVcpus */ - NULL, /* domainGetMaxVcpus */ - xenXMListDefinedDomains, /* listDefinedDomains */ - xenXMNumOfDefinedDomains, /* numOfDefinedDomains */ - xenXMDomainCreate, /* domainCreate */ - xenXMDomainDefineXML, /* domainDefineXML */ - xenXMDomainUndefine, /* domainUndefine */ - xenXMDomainAttachDevice, /* domainAttachDevice */ - xenXMDomainDetachDevice, /* domainDetachDevice */ - NULL, /* domainGetAutostart */ - NULL, /* domainSetAutostart */ - NULL, /* domainGetSchedulerType */ - NULL, /* domainGetSchedulerParameters */ - NULL, /* domainSetSchedulerParameters */ -}; - -#define xenXMError(conn, code, fmt...) \ - virReportErrorHelper(conn, VIR_FROM_XENXM, code, __FILE__, \ - __FUNCTION__, __LINE__, fmt) - -#ifndef WITH_XEN_INOTIFY -static int xenInotifyActive(virConnectPtr conn ATTRIBUTE_UNUSED) -{ - return 0; -} -#else -static int xenInotifyActive(virConnectPtr conn) -{ - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - return priv->inotifyWatch > 0; -} -#endif - -/* Convenience method to grab a int from the config file object */ -static int xenXMConfigGetBool(virConnectPtr conn, - virConfPtr conf, - const char *name, - int *value, - int def) { - virConfValuePtr val; - - *value = 0; - if (!(val = virConfGetValue(conf, name))) { - *value = def; - return 0; - } - - if (val->type == VIR_CONF_LONG) { - *value = val->l ? 1 : 0; - } else if (val->type == VIR_CONF_STRING) { - if (!val->str) { - *value = def; - } - *value = STREQ(val->str, "1") ? 1 : 0; - } else { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("config value %s was malformed"), name); - return -1; - } - return 0; -} - - -/* Convenience method to grab a int from the config file object */ -static int xenXMConfigGetULong(virConnectPtr conn, - virConfPtr conf, - const char *name, - unsigned long *value, - int def) { - virConfValuePtr val; - - *value = 0; - if (!(val = virConfGetValue(conf, name))) { - *value = def; - return 0; - } - - if (val->type == VIR_CONF_LONG) { - *value = val->l; - } else if (val->type == VIR_CONF_STRING) { - char *ret; - if (!val->str) { - *value = def; - } - *value = strtol(val->str, &ret, 10); - if (ret == val->str) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("config value %s was malformed"), name); - return -1; - } - } else { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("config value %s was malformed"), name); - return -1; - } - return 0; -} - - -/* Convenience method to grab a string from the config file object */ -static int xenXMConfigGetString(virConnectPtr conn, - virConfPtr conf, - const char *name, - const char **value, - const char *def) { - virConfValuePtr val; - - *value = NULL; - if (!(val = virConfGetValue(conf, name))) { - *value = def; - return 0; - } - - if (val->type != VIR_CONF_STRING) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("config value %s was malformed"), name); - return -1; - } - if (!val->str) - *value = def; - else - *value = val->str; - return 0; -} - -static int xenXMConfigCopyStringInternal(virConnectPtr conn, - virConfPtr conf, - const char *name, - char **value, - int allowMissing) { - virConfValuePtr val; - - *value = NULL; - if (!(val = virConfGetValue(conf, name))) { - if (allowMissing) - return 0; - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("config value %s was missing"), name); - return -1; - } - - if (val->type != VIR_CONF_STRING) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("config value %s was not a string"), name); - return -1; - } - if (!val->str) { - if (allowMissing) - return 0; - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("config value %s was missing"), name); - return -1; - } - - if (!(*value = strdup(val->str))) { - virReportOOMError(conn); - return -1; - } - - return 0; -} - - -static int xenXMConfigCopyString(virConnectPtr conn, - virConfPtr conf, - const char *name, - char **value) { - return xenXMConfigCopyStringInternal(conn, conf, name, value, 0); -} - -static int xenXMConfigCopyStringOpt(virConnectPtr conn, - virConfPtr conf, - const char *name, - char **value) { - return xenXMConfigCopyStringInternal(conn, conf, name, value, 1); -} - - -/* Convenience method to grab a string UUID from the config file object */ -static int xenXMConfigGetUUID(virConfPtr conf, const char *name, unsigned char *uuid) { - virConfValuePtr val; - if (!uuid || !name || !conf) - return (-1); - if (!(val = virConfGetValue(conf, name))) { - return (-1); - } - - if (val->type != VIR_CONF_STRING) - return (-1); - if (!val->str) - return (-1); - - if (virUUIDParse(val->str, uuid) < 0) - return (-1); - - return (0); -} - - -/* Release memory associated with a cached config object */ -static void xenXMConfigFree(void *payload, const char *key ATTRIBUTE_UNUSED) { - xenXMConfCachePtr entry = (xenXMConfCachePtr)payload; - virDomainDefFree(entry->def); - VIR_FREE(entry); -} - -struct xenXMConfigReaperData { - xenUnifiedPrivatePtr priv; - time_t now; -}; - -/* Remove any configs which were not refreshed recently */ -static int xenXMConfigReaper(const void *payload, const char *key ATTRIBUTE_UNUSED, const void *data) { - const struct xenXMConfigReaperData *args = data; - xenXMConfCachePtr entry = (xenXMConfCachePtr)payload; - - /* We're going to purge this config file, so check if it - is currently mapped as owner of a named domain. */ - if (entry->refreshedAt != args->now) { - const char *olddomname = entry->def->name; - char *nameowner = (char *)virHashLookup(args->priv->nameConfigMap, olddomname); - if (nameowner && STREQ(nameowner, key)) { - virHashRemoveEntry(args->priv->nameConfigMap, olddomname, NULL); - } - return (1); - } - return (0); -} - - -static virDomainDefPtr -xenXMConfigReadFile(virConnectPtr conn, const char *filename) { - virConfPtr conf; - virDomainDefPtr def; - - if (!(conf = virConfReadFile(filename, 0))) - return NULL; - - def = xenXMDomainConfigParse(conn, conf); - virConfFree(conf); - - return def; -} - -static int -xenXMConfigSaveFile(virConnectPtr conn, const char *filename, virDomainDefPtr def) { - virConfPtr conf; - int ret; - - if (!(conf = xenXMDomainConfigFormat(conn, def))) - return -1; - - ret = virConfWriteFile(filename, conf); - virConfFree(conf); - return ret; -} - - -/* - * Caller must hold the lock on 'conn->privateData' before - * calling this funtion - */ -int -xenXMConfigCacheRemoveFile(virConnectPtr conn, - const char *filename) -{ - xenUnifiedPrivatePtr priv = conn->privateData; - xenXMConfCachePtr entry; - - entry = virHashLookup(priv->configCache, filename); - if (!entry) { - DEBUG("No config entry for %s", filename); - return 0; - } - - virHashRemoveEntry(priv->nameConfigMap, entry->def->name, NULL); - virHashRemoveEntry(priv->configCache, filename, xenXMConfigFree); - DEBUG("Removed %s %s", entry->def->name, filename); - return 0; -} - - -/* - * Caller must hold the lock on 'conn->privateData' before - * calling this funtion - */ -int -xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename) -{ - xenUnifiedPrivatePtr priv = conn->privateData; - xenXMConfCachePtr entry; - struct stat st; - int newborn = 0; - time_t now = time(NULL); - - DEBUG("Adding file %s", filename); - - /* Get modified time */ - if ((stat(filename, &st) < 0)) { - virReportSystemError(conn, errno, - _("cannot stat: %s"), - filename); - return -1; - } - - /* Ignore zero length files, because inotify fires before - any content has actually been created */ - if (st.st_size == 0) { - DEBUG("Ignoring zero length file %s", filename); - return -1; - } - - /* If we already have a matching entry and it is not - modified, then carry on to next one*/ - if ((entry = virHashLookup(priv->configCache, filename))) { - char *nameowner; - - if (entry->refreshedAt >= st.st_mtime) { - entry->refreshedAt = now; - /* return success if up-to-date */ - return 0; - } - - /* If we currently own the name, then release it and - re-acquire it later - just in case it was renamed */ - nameowner = (char *)virHashLookup(priv->nameConfigMap, entry->def->name); - if (nameowner && STREQ(nameowner, filename)) { - virHashRemoveEntry(priv->nameConfigMap, entry->def->name, NULL); - } - - /* Clear existing config entry which needs refresh */ - virDomainDefFree(entry->def); - entry->def = NULL; - } else { /* Completely new entry */ - newborn = 1; - if (VIR_ALLOC(entry) < 0) { - virReportOOMError(conn); - return -1; - } - memcpy(entry->filename, filename, PATH_MAX); - } - entry->refreshedAt = now; - - if (!(entry->def = xenXMConfigReadFile(conn, entry->filename))) { - DEBUG("Failed to read %s", entry->filename); - if (!newborn) - virHashRemoveEntry(priv->configCache, filename, NULL); - VIR_FREE(entry); - return -1; - } - - /* If its a completely new entry, it must be stuck into - the cache (refresh'd entries are already registered) */ - if (newborn) { - if (virHashAddEntry(priv->configCache, entry->filename, entry) < 0) { - virDomainDefFree(entry->def); - VIR_FREE(entry); - xenXMError (conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("xenXMConfigCacheRefresh: virHashAddEntry")); - return -1; - } - } - - /* See if we need to map this config file in as the primary owner - * of the domain in question - */ - if (!virHashLookup(priv->nameConfigMap, entry->def->name)) { - if (virHashAddEntry(priv->nameConfigMap, entry->def->name, entry->filename) < 0) { - virHashRemoveEntry(priv->configCache, filename, NULL); - virDomainDefFree(entry->def); - VIR_FREE(entry); - } - } - DEBUG("Added config %s %s", entry->def->name, filename); - - return 0; -} - -/* This method is called by various methods to scan /etc/xen - * (or whatever directory was set by LIBVIRT_XM_CONFIG_DIR - * environment variable) and process any domain configs. It - * has rate-limited so never rescans more frequently than - * once every X seconds - * - * Caller must hold the lock on 'conn->privateData' before - * calling this funtion - */ -int xenXMConfigCacheRefresh (virConnectPtr conn) { - xenUnifiedPrivatePtr priv = conn->privateData; - DIR *dh; - struct dirent *ent; - time_t now = time(NULL); - int ret = -1; - struct xenXMConfigReaperData args; - - if (now == ((time_t)-1)) { - virReportSystemError(conn, errno, - "%s", _("cannot get time of day")); - return (-1); - } - - /* Rate limit re-scans */ - if ((now - priv->lastRefresh) < XM_REFRESH_INTERVAL) - return (0); - - priv->lastRefresh = now; - - /* Process the files in the config dir */ - if (!(dh = opendir(priv->configDir))) { - virReportSystemError(conn, errno, - _("cannot read directory %s"), - priv->configDir); - return (-1); - } - - while ((ent = readdir(dh))) { - struct stat st; - char path[PATH_MAX]; - - /* - * Skip a bunch of crufty files that clearly aren't config files - */ - - /* Like 'dot' files... */ - if (STRPREFIX(ent->d_name, ".")) - continue; - /* ...and the XenD server config file */ - if (STRPREFIX(ent->d_name, XEND_CONFIG_FILE)) - continue; - /* ...and random PCI config cruft */ - if (STRPREFIX(ent->d_name, XEND_PCI_CONFIG_PREFIX)) - continue; - /* ...and the example domain configs */ - if (STRPREFIX(ent->d_name, XM_EXAMPLE_PREFIX)) - continue; - /* ...and the QEMU networking script */ - if (STRPREFIX(ent->d_name, QEMU_IF_SCRIPT)) - continue; - - /* ...and editor backups */ - if (ent->d_name[0] == '#') - continue; - if (ent->d_name[strlen(ent->d_name)-1] == '~') - continue; - - /* Build the full file path */ - if ((strlen(priv->configDir) + 1 + strlen(ent->d_name) + 1) > PATH_MAX) - continue; - strcpy(path, priv->configDir); - strcat(path, "/"); - strcat(path, ent->d_name); - - /* Skip anything which isn't a file (takes care of scripts/ subdir */ - if ((stat(path, &st) < 0) || - (!S_ISREG(st.st_mode))) { - continue; - } - - /* If we already have a matching entry and it is not - modified, then carry on to next one*/ - if (xenXMConfigCacheAddFile(conn, path) < 0) { - /* Ignoring errors, since alot of stuff goes wrong in /etc/xen */ - } - } - - /* Reap all entries which were not changed, by comparing - their refresh timestamp - the timestamp should match - 'now' if they were refreshed. If timestamp doesn't match - then the config is no longer on disk */ - args.now = now; - args.priv = priv; - virHashRemoveSet(priv->configCache, xenXMConfigReaper, xenXMConfigFree, &args); - ret = 0; - - if (dh) - closedir(dh); - - return (ret); -} - - -/* - * The XM driver keeps a cache of config files as virDomainDefPtr - * objects in the xenUnifiedPrivatePtr. Optionally inotify lets - * us watch for changes (see separate driver), otherwise we poll - * every few seconds - */ -virDrvOpenStatus -xenXMOpen (virConnectPtr conn, - virConnectAuthPtr auth ATTRIBUTE_UNUSED, - int flags ATTRIBUTE_UNUSED) -{ - xenUnifiedPrivatePtr priv = conn->privateData; - - priv->configDir = XM_CONFIG_DIR; - - priv->configCache = virHashCreate(50); - if (!priv->configCache) - return (-1); - priv->nameConfigMap = virHashCreate(50); - if (!priv->nameConfigMap) { - virHashFree(priv->configCache, NULL); - priv->configCache = NULL; - return (-1); - } - /* Force the cache to be reloaded next time that - * xenXMConfigCacheRefresh is called. - */ - priv->lastRefresh = 0; - - return (0); -} - -/* - * Free the cached config files associated with this - * connection - */ -int xenXMClose(virConnectPtr conn) { - xenUnifiedPrivatePtr priv = conn->privateData; - - virHashFree(priv->nameConfigMap, NULL); - virHashFree(priv->configCache, xenXMConfigFree); - - return (0); -} - -/* - * Since these are all offline domains, we only return info about - * VCPUs and memory. - */ -int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info) { - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - if (domain->id != -1) - return (-1); - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto error; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto error; - - memset(info, 0, sizeof(virDomainInfo)); - info->maxMem = entry->def->maxmem; - info->memory = entry->def->memory; - info->nrVirtCpu = entry->def->vcpus; - info->state = VIR_DOMAIN_SHUTOFF; - info->cpuTime = 0; - - xenUnifiedUnlock(priv); - return (0); - -error: - xenUnifiedUnlock(priv); - return -1; -} - -#define MAX_VFB 1024 -/* - * Turn a config record into a lump of XML describing the - * domain, suitable for later feeding for virDomainCreateXML - */ -virDomainDefPtr -xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { - const char *str; - int hvm = 0; - int val; - virConfValuePtr list; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - virDomainDefPtr def = NULL; - virDomainDiskDefPtr disk = NULL; - virDomainNetDefPtr net = NULL; - virDomainGraphicsDefPtr graphics = NULL; - virDomainHostdevDefPtr hostdev = NULL; - int i; - const char *defaultArch, *defaultMachine; - - if (VIR_ALLOC(def) < 0) - return NULL; - - def->virtType = VIR_DOMAIN_VIRT_XEN; - def->id = -1; - - if (xenXMConfigCopyString(conn, conf, "name", &def->name) < 0) - goto cleanup; - if (xenXMConfigGetUUID(conf, "uuid", def->uuid) < 0) - goto cleanup; - - - if ((xenXMConfigGetString(conn, conf, "builder", &str, "linux") == 0) && - STREQ(str, "hvm")) - hvm = 1; - - if (!(def->os.type = strdup(hvm ? "hvm" : "xen"))) - goto no_memory; - - defaultArch = virCapabilitiesDefaultGuestArch(priv->caps, def->os.type, virDomainVirtTypeToString(def->virtType)); - if (defaultArch == NULL) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("no supported architecture for os type '%s'"), - def->os.type); - goto cleanup; - } - if (!(def->os.arch = strdup(defaultArch))) - goto no_memory; - - defaultMachine = virCapabilitiesDefaultGuestMachine(priv->caps, - def->os.type, - def->os.arch); - if (defaultMachine != NULL) { - if (!(def->os.machine = strdup(defaultMachine))) - goto no_memory; - } - - if (hvm) { - const char *boot; - if (xenXMConfigCopyString(conn, conf, "kernel", &def->os.loader) < 0) - goto cleanup; - - if (xenXMConfigGetString(conn, conf, "boot", &boot, "c") < 0) - goto cleanup; - - for (i = 0 ; i < VIR_DOMAIN_BOOT_LAST && boot[i] ; i++) { - switch (*boot) { - case 'a': - def->os.bootDevs[i] = VIR_DOMAIN_BOOT_FLOPPY; - break; - case 'd': - def->os.bootDevs[i] = VIR_DOMAIN_BOOT_CDROM; - break; - case 'n': - def->os.bootDevs[i] = VIR_DOMAIN_BOOT_NET; - break; - case 'c': - default: - def->os.bootDevs[i] = VIR_DOMAIN_BOOT_DISK; - break; - } - def->os.nBootDevs++; - } - } else { - if (xenXMConfigCopyStringOpt(conn, conf, "bootloader", &def->os.bootloader) < 0) - goto cleanup; - if (xenXMConfigCopyStringOpt(conn, conf, "bootargs", &def->os.bootloaderArgs) < 0) - goto cleanup; - - if (xenXMConfigCopyStringOpt(conn, conf, "kernel", &def->os.kernel) < 0) - goto cleanup; - if (xenXMConfigCopyStringOpt(conn, conf, "ramdisk", &def->os.initrd) < 0) - goto cleanup; - if (xenXMConfigCopyStringOpt(conn, conf, "extra", &def->os.cmdline) < 0) - goto cleanup; - } - - if (xenXMConfigGetULong(conn, conf, "memory", &def->memory, MIN_XEN_GUEST_SIZE * 2) < 0) - goto cleanup; - - if (xenXMConfigGetULong(conn, conf, "maxmem", &def->maxmem, def->memory) < 0) - goto cleanup; - - def->memory *= 1024; - def->maxmem *= 1024; - - - if (xenXMConfigGetULong(conn, conf, "vcpus", &def->vcpus, 1) < 0) - goto cleanup; - - if (xenXMConfigGetString(conn, conf, "cpus", &str, NULL) < 0) - goto cleanup; - if (str) { - def->cpumasklen = 4096; - if (VIR_ALLOC_N(def->cpumask, def->cpumasklen) < 0) - goto no_memory; - - if (virDomainCpuSetParse(conn, &str, 0, - def->cpumask, def->cpumasklen) < 0) - goto cleanup; - } - - - if (xenXMConfigGetString(conn, conf, "on_poweroff", &str, "destroy") < 0) - goto cleanup; - if ((def->onPoweroff = virDomainLifecycleTypeFromString(str)) < 0) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected value %s for on_poweroff"), str); - goto cleanup; - } - - if (xenXMConfigGetString(conn, conf, "on_reboot", &str, "restart") < 0) - goto cleanup; - if ((def->onReboot = virDomainLifecycleTypeFromString(str)) < 0) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected value %s for on_reboot"), str); - goto cleanup; - } - - if (xenXMConfigGetString(conn, conf, "on_crash", &str, "restart") < 0) - goto cleanup; - if ((def->onCrash = virDomainLifecycleTypeFromString(str)) < 0) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected value %s for on_crash"), str); - goto cleanup; - } - - - - if (hvm) { - if (xenXMConfigGetBool(conn, conf, "pae", &val, 0) < 0) - goto cleanup; - else if (val) - def->features |= (1 << VIR_DOMAIN_FEATURE_PAE); - if (xenXMConfigGetBool(conn, conf, "acpi", &val, 0) < 0) - goto cleanup; - else if (val) - def->features |= (1 << VIR_DOMAIN_FEATURE_ACPI); - if (xenXMConfigGetBool(conn, conf, "apic", &val, 0) < 0) - goto cleanup; - else if (val) - def->features |= (1 << VIR_DOMAIN_FEATURE_APIC); - } - if (xenXMConfigGetBool(conn, conf, "localtime", &def->localtime, 0) < 0) - goto cleanup; - - if (xenXMConfigCopyStringOpt(conn, conf, "device_model", &def->emulator) < 0) - goto cleanup; - - list = virConfGetValue(conf, "disk"); - if (list && list->type == VIR_CONF_LIST) { - list = list->list; - while (list) { - char *head; - char *offset; - char *tmp; - - if ((list->type != VIR_CONF_STRING) || (list->str == NULL)) - goto skipdisk; - head = list->str; - - if (VIR_ALLOC(disk) < 0) - goto no_memory; - - /* - * Disks have 3 components, SOURCE,DEST-DEVICE,MODE - * eg, phy:/dev/HostVG/XenGuest1,xvda,w - * The SOURCE is usually prefixed with a driver type, - * and optionally driver sub-type - * The DEST-DEVICE is optionally post-fixed with disk type - */ - - /* Extract the source file path*/ - if (!(offset = strchr(head, ','))) - goto skipdisk; - if ((offset - head) >= (PATH_MAX-1)) - goto skipdisk; - - if (offset == head) { - disk->src = NULL; /* No source file given, eg CDROM with no media */ - } else { - if (VIR_ALLOC_N(disk->src, (offset - head) + 1) < 0) - goto no_memory; - strncpy(disk->src, head, (offset - head)); - disk->src[(offset-head)] = '\0'; - } - head = offset + 1; - - /* Remove legacy ioemu: junk */ - if (STRPREFIX(head, "ioemu:")) - head = head + 6; - - /* Extract the dest device name */ - if (!(offset = strchr(head, ','))) - goto skipdisk; - if (VIR_ALLOC_N(disk->dst, (offset - head) + 1) < 0) - goto no_memory; - strncpy(disk->dst, head, (offset - head)); - disk->dst[(offset-head)] = '\0'; - head = offset + 1; - - - /* Extract source driver type */ - if (disk->src) { - /* The main type phy:, file:, tap: ... */ - if ((tmp = strchr(disk->src, ':')) != NULL) { - if (VIR_ALLOC_N(disk->driverName, (tmp - disk->src) + 1) < 0) - goto no_memory; - strncpy(disk->driverName, disk->src, (tmp - disk->src)); - disk->driverName[tmp - disk->src] = '\0'; - - /* Strip the prefix we found off the source file name */ - memmove(disk->src, disk->src+(tmp-disk->src)+1, - strlen(disk->src)-(tmp-disk->src)); - } - - /* And the sub-type for tap:XXX: type */ - if (disk->driverName && - STREQ(disk->driverName, "tap")) { - if (!(tmp = strchr(disk->src, ':'))) - goto skipdisk; - if (VIR_ALLOC_N(disk->driverType, (tmp - disk->src) + 1) < 0) - goto no_memory; - strncpy(disk->driverType, disk->src, (tmp - disk->src)); - disk->driverType[tmp - disk->src] = '\0'; - - /* Strip the prefix we found off the source file name */ - memmove(disk->src, disk->src+(tmp-disk->src)+1, - strlen(disk->src)-(tmp-disk->src)); - } - } - - /* No source, or driver name, so fix to phy: */ - if (!disk->driverName && - !(disk->driverName = strdup("phy"))) - goto no_memory; - - - /* phy: type indicates a block device */ - disk->type = STREQ(disk->driverName, "phy") ? - VIR_DOMAIN_DISK_TYPE_BLOCK : VIR_DOMAIN_DISK_TYPE_FILE; - - /* Check for a :cdrom/:disk postfix */ - disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; - if ((tmp = strchr(disk->dst, ':')) != NULL) { - if (STREQ(tmp, ":cdrom")) - disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; - tmp[0] = '\0'; - } - - if (STRPREFIX(disk->dst, "xvd") || !hvm) { - disk->bus = VIR_DOMAIN_DISK_BUS_XEN; - } else if (STRPREFIX(disk->dst, "sd")) { - disk->bus = VIR_DOMAIN_DISK_BUS_SCSI; - } else { - disk->bus = VIR_DOMAIN_DISK_BUS_IDE; - } - - if (STREQ(head, "r") || - STREQ(head, "ro")) - disk->readonly = 1; - else if ((STREQ(head, "w!")) || - (STREQ(head, "!"))) - disk->shared = 1; - - /* Maintain list in sorted order according to target device name */ - if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) - goto no_memory; - def->disks[def->ndisks++] = disk; - disk = NULL; - - skipdisk: - list = list->next; - virDomainDiskDefFree(disk); - } - } - - if (hvm && priv->xendConfigVersion == 1) { - if (xenXMConfigGetString(conn, conf, "cdrom", &str, NULL) < 0) - goto cleanup; - if (str) { - if (VIR_ALLOC(disk) < 0) - goto no_memory; - - disk->type = VIR_DOMAIN_DISK_TYPE_FILE; - disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; - if (!(disk->driverName = strdup("file"))) - goto no_memory; - if (!(disk->src = strdup(str))) - goto no_memory; - if (!(disk->dst = strdup("hdc"))) - goto no_memory; - disk->bus = VIR_DOMAIN_DISK_BUS_IDE; - disk->readonly = 1; - - if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) - goto no_memory; - def->disks[def->ndisks++] = disk; - disk = NULL; - } - } - - list = virConfGetValue(conf, "vif"); - if (list && list->type == VIR_CONF_LIST) { - list = list->list; - while (list) { - char script[PATH_MAX]; - char model[10]; - char ip[16]; - char mac[18]; - char bridge[50]; - char vifname[50]; - char *key; - - bridge[0] = '\0'; - mac[0] = '\0'; - script[0] = '\0'; - ip[0] = '\0'; - model[0] = '\0'; - vifname[0] = '\0'; - - if ((list->type != VIR_CONF_STRING) || (list->str == NULL)) - goto skipnic; - - key = list->str; - while (key) { - char *data; - char *nextkey = strchr(key, ','); - - if (!(data = strchr(key, '='))) - goto skipnic; - data++; - - if (STRPREFIX(key, "mac=")) { - int len = nextkey ? (nextkey - data) : 17; - if (len > 17) - len = 17; - strncpy(mac, data, len); - mac[len] = '\0'; - } else if (STRPREFIX(key, "bridge=")) { - int len = nextkey ? (nextkey - data) : sizeof(bridge)-1; - if (len > (sizeof(bridge)-1)) - len = sizeof(bridge)-1; - strncpy(bridge, data, len); - bridge[len] = '\0'; - } else if (STRPREFIX(key, "script=")) { - int len = nextkey ? (nextkey - data) : PATH_MAX-1; - if (len > (PATH_MAX-1)) - len = PATH_MAX-1; - strncpy(script, data, len); - script[len] = '\0'; - } else if (STRPREFIX(key, "model=")) { - int len = nextkey ? (nextkey - data) : sizeof(model)-1; - if (len > (sizeof(model)-1)) - len = sizeof(model)-1; - strncpy(model, data, len); - model[len] = '\0'; - } else if (STRPREFIX(key, "vifname=")) { - int len = nextkey ? (nextkey - data) : sizeof(vifname)-1; - if (len > (sizeof(vifname)-1)) - len = sizeof(vifname)-1; - strncpy(vifname, data, len); - vifname[len] = '\0'; - } else if (STRPREFIX(key, "ip=")) { - int len = nextkey ? (nextkey - data) : 15; - if (len > 15) - len = 15; - strncpy(ip, data, len); - ip[len] = '\0'; - } - - while (nextkey && (nextkey[0] == ',' || - nextkey[0] == ' ' || - nextkey[0] == '\t')) - nextkey++; - key = nextkey; - } - - if (VIR_ALLOC(net) < 0) - goto cleanup; - - if (mac[0]) { - unsigned int rawmac[6]; - sscanf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", - (unsigned int*)&rawmac[0], - (unsigned int*)&rawmac[1], - (unsigned int*)&rawmac[2], - (unsigned int*)&rawmac[3], - (unsigned int*)&rawmac[4], - (unsigned int*)&rawmac[5]); - net->mac[0] = rawmac[0]; - net->mac[1] = rawmac[1]; - net->mac[2] = rawmac[2]; - net->mac[3] = rawmac[3]; - net->mac[4] = rawmac[4]; - net->mac[5] = rawmac[5]; - } - - if (bridge[0] || STREQ(script, "vif-bridge") || - STREQ(script, "vif-vnic")) { - net->type = VIR_DOMAIN_NET_TYPE_BRIDGE; - } else { - net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; - } - - if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { - if (bridge[0] && - !(net->data.bridge.brname = strdup(bridge))) - goto no_memory; - if (script[0] && - !(net->data.bridge.script = strdup(script))) - goto no_memory; - if (ip[0] && - !(net->data.bridge.ipaddr = strdup(ip))) - goto no_memory; - } else { - if (script[0] && - !(net->data.ethernet.script = strdup(script))) - goto no_memory; - if (ip[0] && - !(net->data.ethernet.ipaddr = strdup(ip))) - goto no_memory; - } - if (model[0] && - !(net->model = strdup(model))) - goto no_memory; - - if (vifname[0] && - !(net->ifname = strdup(vifname))) - goto no_memory; - - if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) - goto no_memory; - def->nets[def->nnets++] = net; - net = NULL; - - skipnic: - list = list->next; - virDomainNetDefFree(net); - } - } - - list = virConfGetValue(conf, "pci"); - if (list && list->type == VIR_CONF_LIST) { - list = list->list; - while (list) { - char domain[5]; - char bus[3]; - char slot[3]; - char func[2]; - char *key, *nextkey; - int domainID; - int busID; - int slotID; - int funcID; - - domain[0] = bus[0] = slot[0] = func[0] = '\0'; - - if ((list->type != VIR_CONF_STRING) || (list->str == NULL)) - goto skippci; - - /* pci=['0000:00:1b.0','0000:00:13.0'] */ - if (!(key = list->str)) - goto skippci; - if (!(nextkey = strchr(key, ':'))) - goto skippci; - - if ((nextkey - key) > (sizeof(domain)-1)) - goto skippci; - - strncpy(domain, key, sizeof(domain)); - domain[sizeof(domain)-1] = '\0'; - - key = nextkey + 1; - if (!(nextkey = strchr(key, ':'))) - goto skippci; - - strncpy(bus, key, sizeof(bus)); - bus[sizeof(bus)-1] = '\0'; - - key = nextkey + 1; - if (!(nextkey = strchr(key, '.'))) - goto skippci; - - strncpy(slot, key, sizeof(slot)); - slot[sizeof(slot)-1] = '\0'; - - key = nextkey + 1; - if (strlen(key) != 1) - goto skippci; - - strncpy(func, key, sizeof(func)); - func[sizeof(func)-1] = '\0'; - - if (virStrToLong_i(domain, NULL, 16, &domainID) < 0) - goto skippci; - if (virStrToLong_i(bus, NULL, 16, &busID) < 0) - goto skippci; - if (virStrToLong_i(slot, NULL, 16, &slotID) < 0) - goto skippci; - if (virStrToLong_i(func, NULL, 16, &funcID) < 0) - goto skippci; - - if (VIR_ALLOC(hostdev) < 0) - goto cleanup; - - hostdev->managed = 0; - hostdev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; - hostdev->source.subsys.u.pci.domain = domainID; - hostdev->source.subsys.u.pci.bus = busID; - hostdev->source.subsys.u.pci.slot = slotID; - hostdev->source.subsys.u.pci.function = funcID; - - if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) - goto no_memory; - def->hostdevs[def->nhostdevs++] = hostdev; - hostdev = NULL; - - skippci: - list = list->next; - } - } - - if (hvm) { - if (xenXMConfigGetString(conn, conf, "usbdevice", &str, NULL) < 0) - goto cleanup; - if (str && - (STREQ(str, "tablet") || - STREQ(str, "mouse"))) { - virDomainInputDefPtr input; - if (VIR_ALLOC(input) < 0) - goto no_memory; - input->bus = VIR_DOMAIN_INPUT_BUS_USB; - input->type = STREQ(str, "tablet") ? - VIR_DOMAIN_INPUT_TYPE_TABLET : - VIR_DOMAIN_INPUT_TYPE_MOUSE; - if (VIR_ALLOC_N(def->inputs, 1) < 0) { - virDomainInputDefFree(input); - goto no_memory; - } - def->inputs[0] = input; - def->ninputs = 1; - } - } - - /* HVM guests, or old PV guests use this config format */ - if (hvm || priv->xendConfigVersion < 3) { - if (xenXMConfigGetBool(conn, conf, "vnc", &val, 0) < 0) - goto cleanup; - - if (val) { - if (VIR_ALLOC(graphics) < 0) - goto no_memory; - graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; - if (xenXMConfigGetBool(conn, conf, "vncunused", &val, 1) < 0) - goto cleanup; - graphics->data.vnc.autoport = val ? 1 : 0; - - if (!graphics->data.vnc.autoport) { - unsigned long vncdisplay; - if (xenXMConfigGetULong(conn, conf, "vncdisplay", &vncdisplay, 0) < 0) - goto cleanup; - graphics->data.vnc.port = (int)vncdisplay + 5900; - } - if (xenXMConfigCopyStringOpt(conn, conf, "vnclisten", &graphics->data.vnc.listenAddr) < 0) - goto cleanup; - if (xenXMConfigCopyStringOpt(conn, conf, "vncpasswd", &graphics->data.vnc.passwd) < 0) - goto cleanup; - if (xenXMConfigCopyStringOpt(conn, conf, "keymap", &graphics->data.vnc.keymap) < 0) - goto cleanup; - - if (VIR_ALLOC_N(def->graphics, 1) < 0) - goto no_memory; - def->graphics[0] = graphics; - def->ngraphics = 1; - graphics = NULL; - } else { - if (xenXMConfigGetBool(conn, conf, "sdl", &val, 0) < 0) - goto cleanup; - if (val) { - if (VIR_ALLOC(graphics) < 0) - goto no_memory; - graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; - if (xenXMConfigCopyStringOpt(conn, conf, "display", &graphics->data.sdl.display) < 0) - goto cleanup; - if (xenXMConfigCopyStringOpt(conn, conf, "xauthority", &graphics->data.sdl.xauth) < 0) - goto cleanup; - if (VIR_ALLOC_N(def->graphics, 1) < 0) - goto no_memory; - def->graphics[0] = graphics; - def->ngraphics = 1; - graphics = NULL; - } - } - } - - if (!hvm && def->graphics == NULL) { /* New PV guests use this format */ - list = virConfGetValue(conf, "vfb"); - if (list && list->type == VIR_CONF_LIST && - list->list && list->list->type == VIR_CONF_STRING && - list->list->str) { - char vfb[MAX_VFB]; - char *key = vfb; - strncpy(vfb, list->list->str, MAX_VFB-1); - vfb[MAX_VFB-1] = '\0'; - - if (VIR_ALLOC(graphics) < 0) - goto no_memory; - - if (strstr(key, "type=sdl")) - graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; - else - graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; - - while (key) { - char *data; - char *nextkey = strchr(key, ','); - char *end = nextkey; - if (nextkey) { - *end = '\0'; - nextkey++; - } - - if (!(data = strchr(key, '='))) - break; - - if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - if (STRPREFIX(key, "vncunused=")) { - if (STREQ(key + 10, "1")) - graphics->data.vnc.autoport = 1; - } else if (STRPREFIX(key, "vnclisten=")) { - if (!(graphics->data.vnc.listenAddr = strdup(key + 10))) - goto no_memory; - } else if (STRPREFIX(key, "vncpasswd=")) { - if (!(graphics->data.vnc.passwd = strdup(key + 10))) - goto no_memory; - } else if (STRPREFIX(key, "keymap=")) { - if (!(graphics->data.vnc.keymap = strdup(key + 7))) - goto no_memory; - } else if (STRPREFIX(key, "vncdisplay=")) { - graphics->data.vnc.port = strtol(key+11, NULL, 10) + 5900; - } - } else { - if (STRPREFIX(key, "display=")) { - if (!(graphics->data.sdl.display = strdup(key + 8))) - goto no_memory; - } else if (STRPREFIX(key, "xauthority=")) { - if (!(graphics->data.sdl.xauth = strdup(key + 11))) - goto no_memory; - } - } - - while (nextkey && (nextkey[0] == ',' || - nextkey[0] == ' ' || - nextkey[0] == '\t')) - nextkey++; - key = nextkey; - } - if (VIR_ALLOC_N(def->graphics, 1) < 0) - goto no_memory; - def->graphics[0] = graphics; - def->ngraphics = 1; - graphics = NULL; - } - } - - if (hvm) { - virDomainChrDefPtr chr = NULL; - - if (xenXMConfigGetString(conn, conf, "parallel", &str, NULL) < 0) - goto cleanup; - if (str && STRNEQ(str, "none") && - !(chr = xenDaemonParseSxprChar(conn, str, NULL))) - goto cleanup; - - if (chr) { - if (VIR_ALLOC_N(def->parallels, 1) < 0) { - virDomainChrDefFree(chr); - goto no_memory; - } - def->parallels[0] = chr; - def->nparallels++; - chr = NULL; - } - - if (xenXMConfigGetString(conn, conf, "serial", &str, NULL) < 0) - goto cleanup; - if (str && STRNEQ(str, "none") && - !(chr = xenDaemonParseSxprChar(conn, str, NULL))) - goto cleanup; - - if (chr) { - if (VIR_ALLOC_N(def->serials, 1) < 0) { - virDomainChrDefFree(chr); - goto no_memory; - } - def->serials[0] = chr; - def->nserials++; - } - } else { - if (!(def->console = xenDaemonParseSxprChar(conn, "pty", NULL))) - goto cleanup; - } - - if (hvm) { - if (xenXMConfigGetString(conn, conf, "soundhw", &str, NULL) < 0) - goto cleanup; - - if (str && - xenDaemonParseSxprSound(conn, def, str) < 0) - goto cleanup; - } - - return def; - -no_memory: - virReportOOMError(conn); - /* fallthrough */ -cleanup: - virDomainGraphicsDefFree(graphics); - virDomainNetDefFree(net); - virDomainDiskDefFree(disk); - virDomainDefFree(def); - return NULL; -} - - -/* - * Turn a config record into a lump of XML describing the - * domain, suitable for later feeding for virDomainCreateXML - */ -char *xenXMDomainDumpXML(virDomainPtr domain, int flags) { - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - char *ret = NULL; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(NULL); - } - if (domain->id != -1) - return (NULL); - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto cleanup; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - - ret = virDomainDefFormat(domain->conn, entry->def, flags); - -cleanup: - xenUnifiedUnlock(priv); - return ret; -} - - -/* - * Update amount of memory in the config file - */ -int xenXMDomainSetMemory(virDomainPtr domain, unsigned long memory) { - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - int ret = -1; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) - return (-1); - if (domain->id != -1) - return (-1); - if (memory < 1024 * MIN_XEN_GUEST_SIZE) - return (-1); - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto cleanup; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - - entry->def->memory = memory; - if (entry->def->memory > entry->def->maxmem) - entry->def->memory = entry->def->maxmem; - - /* If this fails, should we try to undo our changes to the - * in-memory representation of the config file. I say not! - */ - if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - goto cleanup; - ret = 0; - -cleanup: - xenUnifiedUnlock(priv); - return ret; -} - -/* - * Update maximum memory limit in config - */ -int xenXMDomainSetMaxMemory(virDomainPtr domain, unsigned long memory) { - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - int ret = -1; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) - return (-1); - if (domain->id != -1) - return (-1); - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto cleanup; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - - entry->def->maxmem = memory; - if (entry->def->memory > entry->def->maxmem) - entry->def->memory = entry->def->maxmem; - - /* If this fails, should we try to undo our changes to the - * in-memory representation of the config file. I say not! - */ - if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - goto cleanup; - ret = 0; - -cleanup: - xenUnifiedUnlock(priv); - return ret; -} - -/* - * Get max memory limit from config - */ -unsigned long xenXMDomainGetMaxMemory(virDomainPtr domain) { - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - unsigned long ret = 0; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (0); - } - if (domain->id != -1) - return (0); - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto cleanup; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - - ret = entry->def->maxmem; - -cleanup: - xenUnifiedUnlock(priv); - return ret; -} - -/* - * Set the VCPU count in config - */ -int xenXMDomainSetVcpus(virDomainPtr domain, unsigned int vcpus) { - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - int ret = -1; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - if (domain->conn->flags & VIR_CONNECT_RO) - return (-1); - if (domain->id != -1) - return (-1); - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto cleanup; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - - entry->def->vcpus = vcpus; - - /* If this fails, should we try to undo our changes to the - * in-memory representation of the config file. I say not! - */ - if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - goto cleanup; - ret = 0; - -cleanup: - xenUnifiedUnlock(priv); - return ret; -} - -/** - * xenXMDomainPinVcpu: - * @domain: pointer to domain object - * @vcpu: virtual CPU number (reserved) - * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) - * @maplen: length of cpumap in bytes - * - * Set the vcpu affinity in config - * - * Returns 0 for success; -1 (with errno) on error - */ -int xenXMDomainPinVcpu(virDomainPtr domain, - unsigned int vcpu ATTRIBUTE_UNUSED, - unsigned char *cpumap, int maplen) -{ - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - virBuffer mapbuf = VIR_BUFFER_INITIALIZER; - char *mapstr = NULL, *mapsave = NULL; - int i, j, n, comma = 0; - int ret = -1; - char *cpuset = NULL; - int maxcpu = XEN_MAX_PHYSICAL_CPU; - - if (domain == NULL || domain->conn == NULL || domain->name == NULL - || cpumap == NULL || maplen < 1 || maplen > (int)sizeof(cpumap_t)) { - xenXMError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG, - __FUNCTION__); - return -1; - } - if (domain->conn->flags & VIR_CONNECT_RO) { - xenXMError (domain->conn, VIR_ERR_INVALID_ARG, - "%s", _("read only connection")); - return -1; - } - if (domain->id != -1) { - xenXMError (domain->conn, VIR_ERR_INVALID_ARG, - "%s", _("not inactive domain")); - return -1; - } - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) { - xenXMError (domain->conn, VIR_ERR_INTERNAL_ERROR, "%s", _("virHashLookup")); - goto cleanup; - } - if (!(entry = virHashLookup(priv->configCache, filename))) { - xenXMError (domain->conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("can't retrieve config file for domain")); - goto cleanup; - } - - /* from bit map, build character string of mapped CPU numbers */ - for (i = 0; i < maplen; i++) - for (j = 0; j < 8; j++) - if ((cpumap[i] & (1 << j))) { - n = i*8 + j; - - if (comma) - virBufferAddLit (&mapbuf, ","); - comma = 1; - - virBufferVSprintf (&mapbuf, "%d", n); - } - - if (virBufferError(&mapbuf)) { - virReportOOMError(domain->conn); - goto cleanup; - } - - mapstr = virBufferContentAndReset(&mapbuf); - mapsave = mapstr; - - if (VIR_ALLOC_N(cpuset, maxcpu) < 0) { - virReportOOMError(domain->conn); - goto cleanup; - } - if (virDomainCpuSetParse(domain->conn, - (const char **)&mapstr, 0, - cpuset, maxcpu) < 0) - goto cleanup; - - VIR_FREE(entry->def->cpumask); - entry->def->cpumask = cpuset; - entry->def->cpumasklen = maxcpu; - cpuset = NULL; - - if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - goto cleanup; - - ret = 0; - - cleanup: - VIR_FREE(mapsave); - VIR_FREE(cpuset); - xenUnifiedUnlock(priv); - return (ret); -} - -/* - * Find an inactive domain based on its name - */ -virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname) { - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - virDomainPtr ret = NULL; - - if (!VIR_IS_CONNECT(conn)) { - xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (domname == NULL) { - xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - priv = conn->privateData; - xenUnifiedLock(priv); - - if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) - goto cleanup; - - if (!(filename = virHashLookup(priv->nameConfigMap, domname))) - goto cleanup; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - - if (!(ret = virGetDomain(conn, domname, entry->def->uuid))) - goto cleanup; - - /* Ensure its marked inactive, because may be cached - handle to a previously active domain */ - ret->id = -1; - -cleanup: - xenUnifiedUnlock(priv); - return (ret); -} - - -/* - * Hash table iterator to search for a domain based on UUID - */ -static int xenXMDomainSearchForUUID(const void *payload, const char *name ATTRIBUTE_UNUSED, const void *data) { - const unsigned char *wantuuid = (const unsigned char *)data; - const xenXMConfCachePtr entry = (const xenXMConfCachePtr)payload; - - if (!memcmp(entry->def->uuid, wantuuid, VIR_UUID_BUFLEN)) - return (1); - - return (0); -} - -/* - * Find an inactive domain based on its UUID - */ -virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn, - const unsigned char *uuid) { - xenUnifiedPrivatePtr priv; - xenXMConfCachePtr entry; - virDomainPtr ret = NULL; - - if (!VIR_IS_CONNECT(conn)) { - xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (uuid == NULL) { - xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - - priv = conn->privateData; - xenUnifiedLock(priv); - - if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) - goto cleanup; - - if (!(entry = virHashSearch(priv->configCache, xenXMDomainSearchForUUID, (const void *)uuid))) - goto cleanup; - - if (!(ret = virGetDomain(conn, entry->def->name, uuid))) - goto cleanup; - - /* Ensure its marked inactive, because may be cached - handle to a previously active domain */ - ret->id = -1; - -cleanup: - xenUnifiedUnlock(priv); - return (ret); -} - - -/* - * Start a domain from an existing defined config file - */ -int xenXMDomainCreate(virDomainPtr domain) { - char *sexpr; - int ret = -1; - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - - if (domain->id != -1) - return (-1); - - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto error; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto error; - - if (!(sexpr = xenDaemonFormatSxpr(domain->conn, entry->def, priv->xendConfigVersion))) - goto error; - - ret = xenDaemonDomainCreateXML(domain->conn, sexpr); - VIR_FREE(sexpr); - if (ret != 0) - goto error; - - if ((ret = xenDaemonDomainLookupByName_ids(domain->conn, domain->name, - entry->def->uuid)) < 0) - goto error; - domain->id = ret; - - if (xend_wait_for_devices(domain->conn, domain->name) < 0) - goto error; - - if (xenDaemonDomainResume(domain) < 0) - goto error; - - xenUnifiedUnlock(priv); - return (0); - - error: - if (domain->id != -1) { - xenDaemonDomainDestroy(domain); - domain->id = -1; - } - xenUnifiedUnlock(priv); - return (-1); -} - - -static -int xenXMConfigSetInt(virConfPtr conf, const char *setting, long l) { - virConfValuePtr value = NULL; - - if (VIR_ALLOC(value) < 0) - return -1; - - value->type = VIR_CONF_LONG; - value->next = NULL; - value->l = l; - - return virConfSetValue(conf, setting, value); -} - - -static -int xenXMConfigSetString(virConfPtr conf, const char *setting, const char *str) { - virConfValuePtr value = NULL; - - if (VIR_ALLOC(value) < 0) - return -1; - - value->type = VIR_CONF_STRING; - value->next = NULL; - if (!(value->str = strdup(str))) { - VIR_FREE(value); - return -1; - } - - return virConfSetValue(conf, setting, value); -} - - -static int xenXMDomainConfigFormatDisk(virConnectPtr conn, - virConfValuePtr list, - virDomainDiskDefPtr disk, - int hvm, - int xendConfigVersion) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - virConfValuePtr val, tmp; - char *str; - - if(disk->src) { - if (disk->driverName) { - virBufferVSprintf(&buf, "%s:", disk->driverName); - if (STREQ(disk->driverName, "tap")) - virBufferVSprintf(&buf, "%s:", disk->driverType ? disk->driverType : "aio"); - } else { - virBufferVSprintf(&buf, "%s:", - disk->type == VIR_DOMAIN_DISK_TYPE_FILE ? - "file" : "phy"); - } - virBufferVSprintf(&buf, "%s", disk->src); - } - virBufferAddLit(&buf, ","); - if (hvm && xendConfigVersion == 1) - virBufferAddLit(&buf, "ioemu:"); - - virBufferVSprintf(&buf, "%s", disk->dst); - if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) - virBufferAddLit(&buf, ":cdrom"); - - if (disk->readonly) - virBufferAddLit(&buf, ",r"); - else if (disk->shared) - virBufferAddLit(&buf, ",!"); - else - virBufferAddLit(&buf, ",w"); - - if (virBufferError(&buf)) { - virReportOOMError(conn); - return -1; - } - - if (VIR_ALLOC(val) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - val->type = VIR_CONF_STRING; - val->str = virBufferContentAndReset(&buf); - tmp = list->list; - while (tmp && tmp->next) - tmp = tmp->next; - if (tmp) - tmp->next = val; - else - list->list = val; - - return 0; - -cleanup: - str = virBufferContentAndReset(&buf); - VIR_FREE(str); - return -1; -} - -static int xenXMDomainConfigFormatNet(virConnectPtr conn, - virConfValuePtr list, - virDomainNetDefPtr net, - int hvm) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - virConfValuePtr val, tmp; - char *str; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - virBufferVSprintf(&buf, "mac=%02x:%02x:%02x:%02x:%02x:%02x", - net->mac[0], net->mac[1], - net->mac[2], net->mac[3], - net->mac[4], net->mac[5]); - - switch (net->type) { - case VIR_DOMAIN_NET_TYPE_BRIDGE: - virBufferVSprintf(&buf, ",bridge=%s", net->data.bridge.brname); - if (net->data.bridge.ipaddr) - virBufferVSprintf(&buf, ",ip=%s", net->data.bridge.ipaddr); - virBufferVSprintf(&buf, ",script=%s", DEFAULT_VIF_SCRIPT); - break; - - case VIR_DOMAIN_NET_TYPE_ETHERNET: - if (net->data.ethernet.script) - virBufferVSprintf(&buf, ",script=%s", net->data.ethernet.script); - if (net->data.ethernet.ipaddr) - virBufferVSprintf(&buf, ",ip=%s", net->data.ethernet.ipaddr); - break; - - case VIR_DOMAIN_NET_TYPE_NETWORK: - { - virNetworkPtr network = virNetworkLookupByName(conn, net->data.network.name); - char *bridge; - if (!network) { - xenXMError(conn, VIR_ERR_NO_NETWORK, "%s", - net->data.network.name); - return -1; - } - bridge = virNetworkGetBridgeName(network); - virNetworkFree(network); - if (!bridge) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("network %s is not active"), - net->data.network.name); - return -1; - } - - virBufferVSprintf(&buf, ",bridge=%s", bridge); - virBufferVSprintf(&buf, ",script=%s", DEFAULT_VIF_SCRIPT); - } - break; - - default: - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("unsupported network type %d"), - net->type); - goto cleanup; - } - - if (hvm && priv->xendConfigVersion <= XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU) - virBufferAddLit(&buf, ",type=ioemu"); - - if (net->model) - virBufferVSprintf(&buf, ",model=%s", - net->model); - - if (net->ifname) - virBufferVSprintf(&buf, ",vifname=%s", - net->ifname); - - if (virBufferError(&buf)) - goto cleanup; - - if (VIR_ALLOC(val) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - val->type = VIR_CONF_STRING; - val->str = virBufferContentAndReset(&buf); - tmp = list->list; - while (tmp && tmp->next) - tmp = tmp->next; - if (tmp) - tmp->next = val; - else - list->list = val; - - return 0; - -cleanup: - str = virBufferContentAndReset(&buf); - VIR_FREE(str); - return -1; -} - - - -static int -xenXMDomainConfigFormatPCI(virConnectPtr conn, - virConfPtr conf, - virDomainDefPtr def) -{ - - virConfValuePtr pciVal = NULL; - int hasPCI = 0; - int i; - - for (i = 0 ; i < def->nhostdevs ; i++) - if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) - hasPCI = 1; - - if (!hasPCI) - return 0; - - if (VIR_ALLOC(pciVal) < 0) - return -1; - - pciVal->type = VIR_CONF_LIST; - pciVal->list = NULL; - - for (i = 0 ; i < def->nhostdevs ; i++) { - if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { - virConfValuePtr val, tmp; - char *buf; - - if (virAsprintf(&buf, "%04x:%02x:%02x.%x", - def->hostdevs[i]->source.subsys.u.pci.domain, - def->hostdevs[i]->source.subsys.u.pci.bus, - def->hostdevs[i]->source.subsys.u.pci.slot, - def->hostdevs[i]->source.subsys.u.pci.function) < 0) - goto error; - - if (VIR_ALLOC(val) < 0) { - VIR_FREE(buf); - virReportOOMError(conn); - goto error; - } - val->type = VIR_CONF_STRING; - val->str = buf; - tmp = pciVal->list; - while (tmp && tmp->next) - tmp = tmp->next; - if (tmp) - tmp->next = val; - else - pciVal->list = val; - } - } - - if (pciVal->list != NULL) { - int ret = virConfSetValue(conf, "pci", pciVal); - pciVal = NULL; - if (ret < 0) - return -1; - } - VIR_FREE(pciVal); - - return 0; - -error: - virConfFreeValue(pciVal); - return -1; -} - - -virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, - virDomainDefPtr def) { - virConfPtr conf = NULL; - int hvm = 0, i; - xenUnifiedPrivatePtr priv; - char *cpus = NULL; - const char *lifecycle; - char uuid[VIR_UUID_STRING_BUFLEN]; - virConfValuePtr diskVal = NULL; - virConfValuePtr netVal = NULL; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (!(conf = virConfNew())) - goto cleanup; - - - if (xenXMConfigSetString(conf, "name", def->name) < 0) - goto no_memory; - - virUUIDFormat(def->uuid, uuid); - if (xenXMConfigSetString(conf, "uuid", uuid) < 0) - goto no_memory; - - if (xenXMConfigSetInt(conf, "maxmem", def->maxmem / 1024) < 0) - goto no_memory; - - if (xenXMConfigSetInt(conf, "memory", def->memory / 1024) < 0) - goto no_memory; - - if (xenXMConfigSetInt(conf, "vcpus", def->vcpus) < 0) - goto no_memory; - - if (def->cpumask && - !(cpus = virDomainCpuSetFormat(conn, def->cpumask, def->cpumasklen)) < 0) - goto cleanup; - - if (cpus && - xenXMConfigSetString(conf, "cpus", cpus) < 0) - goto no_memory; - VIR_FREE(cpus); - - hvm = STREQ(def->os.type, "hvm") ? 1 : 0; - - if (hvm) { - char boot[VIR_DOMAIN_BOOT_LAST+1]; - if (xenXMConfigSetString(conf, "builder", "hvm") < 0) - goto no_memory; - - if (def->os.loader && - xenXMConfigSetString(conf, "kernel", def->os.loader) < 0) - goto no_memory; - - for (i = 0 ; i < def->os.nBootDevs ; i++) { - switch (def->os.bootDevs[i]) { - case VIR_DOMAIN_BOOT_FLOPPY: - boot[i] = 'a'; - break; - case VIR_DOMAIN_BOOT_CDROM: - boot[i] = 'd'; - break; - case VIR_DOMAIN_BOOT_NET: - boot[i] = 'n'; - break; - case VIR_DOMAIN_BOOT_DISK: - default: - boot[i] = 'c'; - break; - } - } - if (!def->os.nBootDevs) { - boot[0] = 'c'; - boot[1] = '\0'; - } else { - boot[def->os.nBootDevs] = '\0'; - } - - if (xenXMConfigSetString(conf, "boot", boot) < 0) - goto no_memory; - - if (xenXMConfigSetInt(conf, "pae", - (def->features & - (1 << VIR_DOMAIN_FEATURE_PAE)) ? 1 : 0) < 0) - goto no_memory; - - if (xenXMConfigSetInt(conf, "acpi", - (def->features & - (1 << VIR_DOMAIN_FEATURE_ACPI)) ? 1 : 0) < 0) - goto no_memory; - - if (xenXMConfigSetInt(conf, "apic", - (def->features & - (1 << VIR_DOMAIN_FEATURE_APIC)) ? 1 : 0) < 0) - goto no_memory; - - - if (xenXMConfigSetInt(conf, "localtime", def->localtime ? 1 : 0) < 0) - goto no_memory; - - if (priv->xendConfigVersion == 1) { - for (i = 0 ; i < def->ndisks ; i++) { - if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_CDROM && - def->disks[i]->dst && - STREQ(def->disks[i]->dst, "hdc") && - def->disks[i]->src) { - if (xenXMConfigSetString(conf, "cdrom", - def->disks[i]->src) < 0) - goto no_memory; - break; - } - } - } - - /* XXX floppy disks */ - } else { - if (def->os.bootloader && - xenXMConfigSetString(conf, "bootloader", def->os.bootloader) < 0) - goto no_memory; - if (def->os.bootloaderArgs && - xenXMConfigSetString(conf, "bootloader_args", def->os.bootloaderArgs) < 0) - goto no_memory; - if (def->os.kernel && - xenXMConfigSetString(conf, "kernel", def->os.kernel) < 0) - goto no_memory; - if (def->os.initrd && - xenXMConfigSetString(conf, "ramdisk", def->os.initrd) < 0) - goto no_memory; - if (def->os.cmdline && - xenXMConfigSetString(conf, "extra", def->os.cmdline) < 0) - goto no_memory; - - } - - if (!(lifecycle = virDomainLifecycleTypeToString(def->onPoweroff))) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected lifecycle action %d"), def->onPoweroff); - goto cleanup; - } - if (xenXMConfigSetString(conf, "on_poweroff", lifecycle) < 0) - goto no_memory; - - - if (!(lifecycle = virDomainLifecycleTypeToString(def->onReboot))) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected lifecycle action %d"), def->onReboot); - goto cleanup; - } - if (xenXMConfigSetString(conf, "on_reboot", lifecycle) < 0) - goto no_memory; - - - if (!(lifecycle = virDomainLifecycleTypeToString(def->onCrash))) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected lifecycle action %d"), def->onCrash); - goto cleanup; - } - if (xenXMConfigSetString(conf, "on_crash", lifecycle) < 0) - goto no_memory; - - - - if (hvm) { - if (def->emulator && - xenXMConfigSetString(conf, "device_model", def->emulator) < 0) - goto no_memory; - - for (i = 0 ; i < def->ninputs ; i++) { - if (def->inputs[i]->bus == VIR_DOMAIN_INPUT_BUS_USB) { - if (xenXMConfigSetInt(conf, "usb", 1) < 0) - goto no_memory; - if (xenXMConfigSetString(conf, "usbdevice", - def->inputs[i]->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? - "mouse" : "tablet") < 0) - goto no_memory; - break; - } - } - } - - if (def->ngraphics == 1) { - if (priv->xendConfigVersion < (hvm ? 4 : XEND_CONFIG_MIN_VERS_PVFB_NEWCONF)) { - if (def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - if (xenXMConfigSetInt(conf, "sdl", 1) < 0) - goto no_memory; - if (xenXMConfigSetInt(conf, "vnc", 0) < 0) - goto no_memory; - if (def->graphics[0]->data.sdl.display && - xenXMConfigSetString(conf, "display", - def->graphics[0]->data.sdl.display) < 0) - goto no_memory; - if (def->graphics[0]->data.sdl.xauth && - xenXMConfigSetString(conf, "xauthority", - def->graphics[0]->data.sdl.xauth) < 0) - goto no_memory; - } else { - if (xenXMConfigSetInt(conf, "sdl", 0) < 0) - goto no_memory; - if (xenXMConfigSetInt(conf, "vnc", 1) < 0) - goto no_memory; - if (xenXMConfigSetInt(conf, "vncunused", - def->graphics[0]->data.vnc.autoport ? 1 : 0) < 0) - goto no_memory; - if (!def->graphics[0]->data.vnc.autoport && - xenXMConfigSetInt(conf, "vncdisplay", - def->graphics[0]->data.vnc.port - 5900) < 0) - goto no_memory; - if (def->graphics[0]->data.vnc.listenAddr && - xenXMConfigSetString(conf, "vnclisten", - def->graphics[0]->data.vnc.listenAddr) < 0) - goto no_memory; - if (def->graphics[0]->data.vnc.passwd && - xenXMConfigSetString(conf, "vncpasswd", - def->graphics[0]->data.vnc.passwd) < 0) - goto no_memory; - if (def->graphics[0]->data.vnc.keymap && - xenXMConfigSetString(conf, "keymap", - def->graphics[0]->data.vnc.keymap) < 0) - goto no_memory; - } - } else { - virConfValuePtr vfb, disp; - char *vfbstr = NULL; - virBuffer buf = VIR_BUFFER_INITIALIZER; - if (def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - virBufferAddLit(&buf, "type=sdl"); - if (def->graphics[0]->data.sdl.display) - virBufferVSprintf(&buf, ",display=%s", - def->graphics[0]->data.sdl.display); - if (def->graphics[0]->data.sdl.xauth) - virBufferVSprintf(&buf, ",xauthority=%s", - def->graphics[0]->data.sdl.xauth); - } else { - virBufferAddLit(&buf, "type=vnc"); - virBufferVSprintf(&buf, ",vncunused=%d", - def->graphics[0]->data.vnc.autoport ? 1 : 0); - if (!def->graphics[0]->data.vnc.autoport) - virBufferVSprintf(&buf, ",vncdisplay=%d", - def->graphics[0]->data.vnc.port - 5900); - if (def->graphics[0]->data.vnc.listenAddr) - virBufferVSprintf(&buf, ",vnclisten=%s", - def->graphics[0]->data.vnc.listenAddr); - if (def->graphics[0]->data.vnc.passwd) - virBufferVSprintf(&buf, ",vncpasswd=%s", - def->graphics[0]->data.vnc.passwd); - if (def->graphics[0]->data.vnc.keymap) - virBufferVSprintf(&buf, ",keymap=%s", - def->graphics[0]->data.vnc.keymap); - } - if (virBufferError(&buf)) - goto no_memory; - - vfbstr = virBufferContentAndReset(&buf); - - if (VIR_ALLOC(vfb) < 0) { - VIR_FREE(vfbstr); - goto no_memory; - } - - if (VIR_ALLOC(disp) < 0) { - VIR_FREE(vfb); - VIR_FREE(vfbstr); - goto no_memory; - } - - vfb->type = VIR_CONF_LIST; - vfb->list = disp; - disp->type = VIR_CONF_STRING; - disp->str = vfbstr; - - if (virConfSetValue(conf, "vfb", vfb) < 0) - goto no_memory; - } - } - - /* analyze of the devices */ - if (VIR_ALLOC(diskVal) < 0) - goto no_memory; - diskVal->type = VIR_CONF_LIST; - diskVal->list = NULL; - - for (i = 0 ; i < def->ndisks ; i++) { - if (priv->xendConfigVersion == 1 && - def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_CDROM && - def->disks[i]->dst && - STREQ(def->disks[i]->dst, "hdc")) { - continue; - } - if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) - continue; - - if (xenXMDomainConfigFormatDisk(conn, diskVal, def->disks[i], - hvm, priv->xendConfigVersion) < 0) - goto cleanup; - } - if (diskVal->list != NULL) { - int ret = virConfSetValue(conf, "disk", diskVal); - diskVal = NULL; - if (ret < 0) - goto no_memory; - } - VIR_FREE(diskVal); - - if (VIR_ALLOC(netVal) < 0) - goto no_memory; - netVal->type = VIR_CONF_LIST; - netVal->list = NULL; - - for (i = 0 ; i < def->nnets ; i++) { - if (xenXMDomainConfigFormatNet(conn, netVal, - def->nets[i], - hvm) < 0) - goto cleanup; - } - if (netVal->list != NULL) { - int ret = virConfSetValue(conf, "vif", netVal); - netVal = NULL; - if (ret < 0) - goto no_memory; - } - VIR_FREE(netVal); - - if (xenXMDomainConfigFormatPCI(conn, conf, def) < 0) - goto cleanup; - - if (hvm) { - if (def->nparallels) { - virBuffer buf = VIR_BUFFER_INITIALIZER; - char *str; - int ret; - - ret = xenDaemonFormatSxprChr(conn, def->parallels[0], &buf); - str = virBufferContentAndReset(&buf); - if (ret == 0) - ret = xenXMConfigSetString(conf, "parallel", str); - VIR_FREE(str); - if (ret < 0) - goto no_memory; - } else { - if (xenXMConfigSetString(conf, "parallel", "none") < 0) - goto no_memory; - } - - if (def->nserials) { - virBuffer buf = VIR_BUFFER_INITIALIZER; - char *str; - int ret; - - ret = xenDaemonFormatSxprChr(conn, def->serials[0], &buf); - str = virBufferContentAndReset(&buf); - if (ret == 0) - ret = xenXMConfigSetString(conf, "serial", str); - VIR_FREE(str); - if (ret < 0) - goto no_memory; - } else { - if (xenXMConfigSetString(conf, "serial", "none") < 0) - goto no_memory; - } - - - if (def->sounds) { - virBuffer buf = VIR_BUFFER_INITIALIZER; - char *str = NULL; - int ret = xenDaemonFormatSxprSound(conn, - def, - &buf); - str = virBufferContentAndReset(&buf); - if (ret == 0) - ret = xenXMConfigSetString(conf, "soundhw", str); - - VIR_FREE(str); - if (ret < 0) - goto no_memory; - } - } - - return conf; - -no_memory: - virReportOOMError(conn); - -cleanup: - virConfFreeValue(diskVal); - virConfFreeValue(netVal); - VIR_FREE(cpus); - if (conf) - virConfFree(conf); - return (NULL); -} - -/* - * Create a config file for a domain, based on an XML - * document describing its config - */ -virDomainPtr xenXMDomainDefineXML(virConnectPtr conn, const char *xml) { - virDomainPtr ret; - virDomainPtr olddomain; - char filename[PATH_MAX]; - const char * oldfilename; - virDomainDefPtr def = NULL; - xenXMConfCachePtr entry = NULL; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (!VIR_IS_CONNECT(conn)) { - xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (NULL); - } - if (xml == NULL) { - xenXMError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return (NULL); - } - if (conn->flags & VIR_CONNECT_RO) - return (NULL); - - xenUnifiedLock(priv); - - if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) { - xenUnifiedUnlock(priv); - return (NULL); - } - - if (!(def = virDomainDefParseString(conn, priv->caps, xml, - VIR_DOMAIN_XML_INACTIVE))) { - xenUnifiedUnlock(priv); - return (NULL); - } - - if (virHashLookup(priv->nameConfigMap, def->name)) { - /* domain exists, we will overwrite it */ - - if (!(oldfilename = (char *)virHashLookup(priv->nameConfigMap, def->name))) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("can't retrieve config filename for domain to overwrite")); - goto error; - } - - if (!(entry = virHashLookup(priv->configCache, oldfilename))) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("can't retrieve config entry for domain to overwrite")); - goto error; - } - - /* XXX wtf.com is this line for - it appears to be amemory leak */ - if (!(olddomain = virGetDomain(conn, def->name, entry->def->uuid))) - goto error; - - /* Remove the name -> filename mapping */ - if (virHashRemoveEntry(priv->nameConfigMap, def->name, NULL) < 0) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to remove old domain from config map")); - goto error; - } - - /* Remove the config record itself */ - if (virHashRemoveEntry(priv->configCache, oldfilename, xenXMConfigFree) < 0) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to remove old domain from config map")); - goto error; - } - - entry = NULL; - } - - if ((strlen(priv->configDir) + 1 + strlen(def->name) + 1) > PATH_MAX) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("config file name is too long")); - goto error; - } - - strcpy(filename, priv->configDir); - strcat(filename, "/"); - strcat(filename, def->name); - - if (xenXMConfigSaveFile(conn, filename, def) < 0) - goto error; - - if (VIR_ALLOC(entry) < 0) { - virReportOOMError(conn); - goto error; - } - - if ((entry->refreshedAt = time(NULL)) == ((time_t)-1)) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("unable to get current time")); - goto error; - } - - memmove(entry->filename, filename, PATH_MAX); - entry->def = def; - - if (virHashAddEntry(priv->configCache, filename, entry) < 0) { - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("unable to store config file handle")); - goto error; - } - - if (virHashAddEntry(priv->nameConfigMap, def->name, entry->filename) < 0) { - virHashRemoveEntry(priv->configCache, filename, NULL); - xenXMError(conn, VIR_ERR_INTERNAL_ERROR, - "%s", _("unable to store config file handle")); - goto error; - } - - ret = virGetDomain(conn, def->name, def->uuid); - xenUnifiedUnlock(priv); - return (ret); - - error: - VIR_FREE(entry); - virDomainDefFree(def); - xenUnifiedUnlock(priv); - return (NULL); -} - -/* - * Delete a domain from disk - */ -int xenXMDomainUndefine(virDomainPtr domain) { - xenUnifiedPrivatePtr priv; - const char *filename; - xenXMConfCachePtr entry; - int ret = -1; - - if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return (-1); - } - - if (domain->id != -1) - return (-1); - if (domain->conn->flags & VIR_CONNECT_RO) - return (-1); - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto cleanup; - - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - - if (unlink(entry->filename) < 0) - goto cleanup; - - /* Remove the name -> filename mapping */ - if (virHashRemoveEntry(priv->nameConfigMap, domain->name, NULL) < 0) - goto cleanup; - - /* Remove the config record itself */ - if (virHashRemoveEntry(priv->configCache, entry->filename, xenXMConfigFree) < 0) - goto cleanup; - - ret = 0; - -cleanup: - xenUnifiedUnlock(priv); - return ret; -} - -struct xenXMListIteratorContext { - virConnectPtr conn; - int max; - int count; - char ** names; -}; - -static void xenXMListIterator(const void *payload ATTRIBUTE_UNUSED, const char *name, const void *data) { - struct xenXMListIteratorContext *ctx = (struct xenXMListIteratorContext *)data; - virDomainPtr dom = NULL; - - if (ctx->count == ctx->max) - return; - - dom = xenDaemonLookupByName(ctx->conn, name); - if (!dom) { - ctx->names[ctx->count] = strdup(name); - ctx->count++; - } else { - virDomainFree(dom); - } -} - - -/* - * List all defined domains, filtered to remove any which - * are currently running - */ -int xenXMListDefinedDomains(virConnectPtr conn, char **const names, int maxnames) { - xenUnifiedPrivatePtr priv; - struct xenXMListIteratorContext ctx; - int ret = -1; - - if (!VIR_IS_CONNECT(conn)) { - xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - priv = conn->privateData; - xenUnifiedLock(priv); - - if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) - goto cleanup; - - if (maxnames > virHashSize(priv->configCache)) - maxnames = virHashSize(priv->configCache); - - ctx.conn = conn; - ctx.count = 0; - ctx.max = maxnames; - ctx.names = names; - - virHashForEach(priv->nameConfigMap, xenXMListIterator, &ctx); - ret = ctx.count; - -cleanup: - xenUnifiedUnlock(priv); - return ret; -} - -/* - * Return the maximum number of defined domains - not filtered - * based on number running - */ -int xenXMNumOfDefinedDomains(virConnectPtr conn) { - xenUnifiedPrivatePtr priv; - int ret = -1; - - if (!VIR_IS_CONNECT(conn)) { - xenXMError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); - return (-1); - } - - priv = conn->privateData; - xenUnifiedLock(priv); - - if (!xenInotifyActive(conn) && xenXMConfigCacheRefresh (conn) < 0) - goto cleanup; - - ret = virHashSize(priv->nameConfigMap); - -cleanup: - xenUnifiedUnlock(priv); - return ret; -} - - -/** - * xenXMDomainAttachDevice: - * @domain: pointer to domain object - * @xml: pointer to XML description of device - * - * Create a virtual device attachment to backend. - * XML description is translated into config file. - * - * Returns 0 in case of success, -1 in case of failure. - */ -static int -xenXMDomainAttachDevice(virDomainPtr domain, const char *xml) { - const char *filename = NULL; - xenXMConfCachePtr entry = NULL; - int ret = -1; - virDomainDeviceDefPtr dev = NULL; - virDomainDefPtr def; - xenUnifiedPrivatePtr priv; - - if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return -1; - } - - if (domain->conn->flags & VIR_CONNECT_RO) - return -1; - if (domain->id != -1) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto cleanup; - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - def = entry->def; - - if (!(dev = virDomainDeviceDefParse(domain->conn, - priv->caps, - entry->def, - xml, VIR_DOMAIN_XML_INACTIVE))) - goto cleanup; - - switch (dev->type) { - case VIR_DOMAIN_DEVICE_DISK: - { - if (virDomainDiskInsert(def, dev->data.disk) < 0) { - virReportOOMError(domain->conn); - goto cleanup; - } - dev->data.disk = NULL; - } - break; - - case VIR_DOMAIN_DEVICE_NET: - { - if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) { - virReportOOMError(domain->conn); - goto cleanup; - } - def->nets[def->nnets++] = dev->data.net; - dev->data.net = NULL; - break; - } - - default: - xenXMError(domain->conn, VIR_ERR_XML_ERROR, - "%s", _("unknown device")); - goto cleanup; - } - - /* If this fails, should we try to undo our changes to the - * in-memory representation of the config file. I say not! - */ - if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - goto cleanup; - - ret = 0; - - cleanup: - virDomainDeviceDefFree(dev); - xenUnifiedUnlock(priv); - return ret; -} - - -/** - * xenXMDomainDetachDevice: - * @domain: pointer to domain object - * @xml: pointer to XML description of device - * - * Destroy a virtual device attachment to backend. - * - * Returns 0 in case of success, -1 in case of failure. - */ -static int -xenXMDomainDetachDevice(virDomainPtr domain, const char *xml) { - const char *filename = NULL; - xenXMConfCachePtr entry = NULL; - virDomainDeviceDefPtr dev = NULL; - virDomainDefPtr def; - int ret = -1; - int i; - xenUnifiedPrivatePtr priv; - - if ((!domain) || (!domain->conn) || (!domain->name) || (!xml)) { - xenXMError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return -1; - } - - - if (domain->conn->flags & VIR_CONNECT_RO) - return -1; - if (domain->id != -1) - return -1; - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - xenUnifiedLock(priv); - - if (!(filename = virHashLookup(priv->nameConfigMap, domain->name))) - goto cleanup; - if (!(entry = virHashLookup(priv->configCache, filename))) - goto cleanup; - def = entry->def; - - if (!(dev = virDomainDeviceDefParse(domain->conn, - priv->caps, - entry->def, - xml, VIR_DOMAIN_XML_INACTIVE))) - goto cleanup; - - switch (dev->type) { - case VIR_DOMAIN_DEVICE_DISK: - { - for (i = 0 ; i < def->ndisks ; i++) { - if (def->disks[i]->dst && - dev->data.disk->dst && - STREQ(def->disks[i]->dst, dev->data.disk->dst)) { - virDomainDiskDefFree(def->disks[i]); - if (i < (def->ndisks - 1)) - memmove(def->disks + i, - def->disks + i + 1, - sizeof(*def->disks) * - (def->ndisks - (i + 1))); - break; - } - } - break; - } - - case VIR_DOMAIN_DEVICE_NET: - { - for (i = 0 ; i < def->nnets ; i++) { - if (!memcmp(def->nets[i]->mac, - dev->data.net->mac, - sizeof(def->nets[i]->mac))) { - virDomainNetDefFree(def->nets[i]); - if (i < (def->nnets - 1)) - memmove(def->nets + i, - def->nets + i + 1, - sizeof(*def->nets) * - (def->nnets - (i + 1))); - break; - } - } - break; - } - default: - xenXMError(domain->conn, VIR_ERR_XML_ERROR, - "%s", _("unknown device")); - goto cleanup; - } - - /* If this fails, should we try to undo our changes to the - * in-memory representation of the config file. I say not! - */ - if (xenXMConfigSaveFile(domain->conn, entry->filename, entry->def) < 0) - goto cleanup; - - ret = 0; - - cleanup: - virDomainDeviceDefFree(dev); - xenUnifiedUnlock(priv); - return (ret); -} - -int -xenXMDomainBlockPeek (virDomainPtr dom, - const char *path ATTRIBUTE_UNUSED, - unsigned long long offset ATTRIBUTE_UNUSED, - size_t size ATTRIBUTE_UNUSED, - void *buffer ATTRIBUTE_UNUSED) -{ - xenXMError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); - return -1; -} - - -static char *xenXMAutostartLinkName(virDomainPtr dom) -{ - char *ret; - virAsprintf(&ret, "/etc/xen/auto/%s", dom->name); - return ret; -} - -static char *xenXMDomainConfigName(virDomainPtr dom) -{ - char *ret; - virAsprintf(&ret, "/etc/xen/%s", dom->name); - return ret; -} - -int xenXMDomainGetAutostart(virDomainPtr dom, int *autostart) -{ - char *linkname = xenXMAutostartLinkName(dom); - char *config = xenXMDomainConfigName(dom); - int ret = -1; - - if (!linkname || !config) { - virReportOOMError(dom->conn); - goto cleanup; - } - - *autostart = virFileLinkPointsTo(linkname, config); - if (*autostart < 0) { - virReportSystemError(dom->conn, errno, - _("cannot check link %s points to config %s"), - linkname, config); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FREE(linkname); - VIR_FREE(config); - return ret; -} - - -int xenXMDomainSetAutostart(virDomainPtr dom, int autostart) -{ - char *linkname = xenXMAutostartLinkName(dom); - char *config = xenXMDomainConfigName(dom); - int ret = -1; - - if (!linkname || !config) { - virReportOOMError(dom->conn); - goto cleanup; - } - - if (autostart) { - if (symlink(config, linkname) < 0 && - errno != EEXIST) { - virReportSystemError(dom->conn, errno, - _("failed to create link %s to %s"), - config, linkname); - goto cleanup; - } - } else { - if (unlink(linkname) < 0 && - errno != ENOENT) { - virReportSystemError(dom->conn, errno, - _("failed to remove link %s"), - linkname); - goto cleanup; - } - } - ret = 0; - -cleanup: - VIR_FREE(linkname); - VIR_FREE(config); - - return ret; -} diff --git a/src/xm_internal.h b/src/xm_internal.h deleted file mode 100644 index 7d52ac8c95..0000000000 --- a/src/xm_internal.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * xm_internal.h: helper routines for dealing with inactive domains - * - * Copyright (C) 2006-2007 Red Hat - * Copyright (C) 2006 Daniel P. Berrange - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Daniel P. Berrange - * - */ - -#ifndef _LIBVIRT_XM_INTERNAL_H_ -#define _LIBVIRT_XM_INTERNAL_H_ - -#include "internal.h" -#include "driver.h" -#include "conf.h" -#include "domain_conf.h" - -extern struct xenUnifiedDriver xenXMDriver; - -int xenXMConfigCacheRefresh (virConnectPtr conn); -int xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename); -int xenXMConfigCacheRemoveFile(virConnectPtr conn, const char *filename); - -virDrvOpenStatus xenXMOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags); -int xenXMClose(virConnectPtr conn); -const char *xenXMGetType(virConnectPtr conn); -int xenXMDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info); -char *xenXMDomainDumpXML(virDomainPtr domain, int flags); -int xenXMDomainSetMemory(virDomainPtr domain, unsigned long memory); -int xenXMDomainSetMaxMemory(virDomainPtr domain, unsigned long memory); -unsigned long xenXMDomainGetMaxMemory(virDomainPtr domain); -int xenXMDomainSetVcpus(virDomainPtr domain, unsigned int vcpus); -int xenXMDomainPinVcpu(virDomainPtr domain, unsigned int vcpu, - unsigned char *cpumap, int maplen); -virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname); -virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn, - const unsigned char *uuid); - -int xenXMListDefinedDomains(virConnectPtr conn, char ** const names, int maxnames); -int xenXMNumOfDefinedDomains(virConnectPtr conn); - -int xenXMDomainCreate(virDomainPtr domain); -virDomainPtr xenXMDomainDefineXML(virConnectPtr con, const char *xml); -int xenXMDomainUndefine(virDomainPtr domain); - -virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, virDomainDefPtr def); -virDomainDefPtr xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf); - -int xenXMDomainBlockPeek (virDomainPtr dom, const char *path, unsigned long long offset, size_t size, void *buffer); - -int xenXMDomainGetAutostart(virDomainPtr dom, int *autostart); -int xenXMDomainSetAutostart(virDomainPtr dom, int autostart); - -#endif diff --git a/src/xs_internal.c b/src/xs_internal.c deleted file mode 100644 index 1f54b1f0e7..0000000000 --- a/src/xs_internal.c +++ /dev/null @@ -1,1407 +0,0 @@ -/* - * xs_internal.c: access to Xen Store - * - * Copyright (C) 2006, 2009 Red Hat, Inc. - * - * See COPYING.LIB for the License of this software - * - * Daniel Veillard - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include "virterror_internal.h" -#include "datatypes.h" -#include "driver.h" -#include "memory.h" -#include "event.h" -#include "logging.h" -#include "uuid.h" -#include "xen_unified.h" -#include "xs_internal.h" -#include "xen_internal.h" - -#define VIR_FROM_THIS VIR_FROM_XEN - -#ifndef PROXY -static char *xenStoreDomainGetOSType(virDomainPtr domain); -static void xenStoreWatchEvent(int watch, int fd, int events, void *data); -static void xenStoreWatchListFree(xenStoreWatchListPtr list); - -struct xenUnifiedDriver xenStoreDriver = { - xenStoreOpen, /* open */ - xenStoreClose, /* close */ - NULL, /* version */ - NULL, /* hostname */ - NULL, /* nodeGetInfo */ - NULL, /* getCapabilities */ - xenStoreListDomains, /* listDomains */ - NULL, /* numOfDomains */ - NULL, /* domainCreateXML */ - NULL, /* domainSuspend */ - NULL, /* domainResume */ - xenStoreDomainShutdown, /* domainShutdown */ - xenStoreDomainReboot, /* domainReboot */ - NULL, /* domainDestroy */ - xenStoreDomainGetOSType, /* domainGetOSType */ - xenStoreDomainGetMaxMemory, /* domainGetMaxMemory */ - NULL, /* domainSetMaxMemory */ - xenStoreDomainSetMemory, /* domainSetMemory */ - xenStoreGetDomainInfo, /* domainGetInfo */ - NULL, /* domainSave */ - NULL, /* domainRestore */ - NULL, /* domainCoreDump */ - NULL, /* domainSetVcpus */ - NULL, /* domainPinVcpu */ - NULL, /* domainGetVcpus */ - NULL, /* domainGetMaxVcpus */ - NULL, /* listDefinedDomains */ - NULL, /* numOfDefinedDomains */ - NULL, /* domainCreate */ - NULL, /* domainDefineXML */ - NULL, /* domainUndefine */ - NULL, /* domainAttachDevice */ - NULL, /* domainDetachDevice */ - NULL, /* domainGetAutostart */ - NULL, /* domainSetAutostart */ - NULL, /* domainGetSchedulerType */ - NULL, /* domainGetSchedulerParameters */ - NULL, /* domainSetSchedulerParameters */ -}; - -#endif /* ! PROXY */ - -#define virXenStoreError(conn, code, fmt...) \ - virReportErrorHelper(NULL, VIR_FROM_XENSTORE, code, __FILE__, \ - __FUNCTION__, __LINE__, fmt) - -/************************************************************************ - * * - * Helper internal APIs * - * * - ************************************************************************/ -#ifndef PROXY -/** - * virConnectDoStoreList: - * @conn: pointer to the hypervisor connection - * @path: the absolute path of the directory in the store to list - * @nb: OUT pointer to the number of items found - * - * Internal API querying the Xenstore for a list - * - * Returns a string which must be freed by the caller or NULL in case of error - */ -static char ** -virConnectDoStoreList(virConnectPtr conn, const char *path, - unsigned int *nb) -{ - xenUnifiedPrivatePtr priv; - - if (conn == NULL) - return NULL; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL || path == NULL || nb == NULL) - return (NULL); - - return xs_directory (priv->xshandle, 0, path, nb); -} -#endif /* ! PROXY */ - -/** - * virDomainDoStoreQuery: - * @conn: pointer to the hypervisor connection - * @domid: id of the domain - * @path: the relative path of the data in the store to retrieve - * - * Internal API querying the Xenstore for a string value. - * - * Returns a string which must be freed by the caller or NULL in case of error - */ -static char * -virDomainDoStoreQuery(virConnectPtr conn, int domid, const char *path) -{ - char s[256]; - unsigned int len = 0; - xenUnifiedPrivatePtr priv; - - if (!conn) - return NULL; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL) - return (NULL); - - snprintf(s, 255, "/local/domain/%d/%s", domid, path); - s[255] = 0; - - return xs_read(priv->xshandle, 0, &s[0], &len); -} - -#ifndef PROXY -/** - * virDomainDoStoreWrite: - * @domain: a domain object - * @path: the relative path of the data in the store to retrieve - * - * Internal API setting up a string value in the Xenstore - * Requires write access to the XenStore - * - * Returns 0 in case of success, -1 in case of failure - */ -static int -virDomainDoStoreWrite(virDomainPtr domain, const char *path, - const char *value) -{ - char s[256]; - xenUnifiedPrivatePtr priv; - int ret = -1; - - if (!VIR_IS_CONNECTED_DOMAIN(domain)) - return (-1); - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xshandle == NULL) - return (-1); - if (domain->conn->flags & VIR_CONNECT_RO) - return (-1); - - snprintf(s, 255, "/local/domain/%d/%s", domain->id, path); - s[255] = 0; - - if (xs_write(priv->xshandle, 0, &s[0], value, strlen(value))) - ret = 0; - - return (ret); -} - -/** - * virDomainGetVM: - * @domain: a domain object - * - * Internal API extracting a xenstore vm path. - * - * Returns the new string or NULL in case of error - */ -static char * -virDomainGetVM(virDomainPtr domain) -{ - char *vm; - char query[200]; - unsigned int len; - xenUnifiedPrivatePtr priv; - - if (!VIR_IS_CONNECTED_DOMAIN(domain)) - return (NULL); - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xshandle == NULL) - return (NULL); - - snprintf(query, 199, "/local/domain/%d/vm", virDomainGetID(domain)); - query[199] = 0; - - vm = xs_read(priv->xshandle, 0, &query[0], &len); - - return (vm); -} - -/** - * virDomainGetVMInfo: - * @domain: a domain object - * @vm: the xenstore vm path - * @name: the value's path - * - * Internal API extracting one information the device used - * by the domain from xensttore - * - * Returns the new string or NULL in case of error - */ -static char * -virDomainGetVMInfo(virDomainPtr domain, const char *vm, const char *name) -{ - char s[256]; - char *ret = NULL; - unsigned int len = 0; - xenUnifiedPrivatePtr priv; - - if (!VIR_IS_CONNECTED_DOMAIN(domain)) - return (NULL); - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xshandle == NULL) - return (NULL); - - snprintf(s, 255, "%s/%s", vm, name); - s[255] = 0; - - ret = xs_read(priv->xshandle, 0, &s[0], &len); - - return (ret); -} - -#endif /* ! PROXY */ - -/************************************************************************ - * * - * Canonical internal APIs * - * * - ************************************************************************/ -/** - * xenStoreOpen: - * @conn: pointer to the connection block - * @name: URL for the target, NULL for local - * @flags: combination of virDrvOpenFlag(s) - * - * Connects to the Xen hypervisor. - * - * Returns 0 or -1 in case of error. - */ -virDrvOpenStatus -xenStoreOpen(virConnectPtr conn, - virConnectAuthPtr auth ATTRIBUTE_UNUSED, - int flags ATTRIBUTE_UNUSED) -{ - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - -#ifdef PROXY - priv->xshandle = xs_daemon_open_readonly(); -#else - if (flags & VIR_CONNECT_RO) - priv->xshandle = xs_daemon_open_readonly(); - else - priv->xshandle = xs_daemon_open(); -#endif /* ! PROXY */ - - if (priv->xshandle == NULL) { - /* - * not being able to connect via the socket as an unprivileged - * user is rather normal, this should fallback to the proxy (or - * remote) mechanism. - */ - if (xenHavePrivilege()) { - virXenStoreError(NULL, VIR_ERR_NO_XEN, - "%s", _("failed to connect to Xen Store")); - } - return (-1); - } - -#ifndef PROXY - /* Init activeDomainList */ - if (VIR_ALLOC(priv->activeDomainList) < 0) { - virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to allocate activeDomainList")); - return -1; - } - - /* Init watch list before filling in domInfoList, - so we can know if it is the first time through - when the callback fires */ - if ( VIR_ALLOC(priv->xsWatchList) < 0 ) { - virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("failed to allocate xsWatchList")); - return -1; - } - - /* This will get called once at start */ - if ( xenStoreAddWatch(conn, "@releaseDomain", - "releaseDomain", xenStoreDomainReleased, priv) < 0 ) - { - virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("adding watch @releaseDomain")); - return -1; - } - - /* The initial call of this will fill domInfoList */ - if( xenStoreAddWatch(conn, "@introduceDomain", - "introduceDomain", xenStoreDomainIntroduced, priv) < 0 ) - { - virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("adding watch @introduceDomain")); - return -1; - } - - /* Add an event handle */ - if ((priv->xsWatch = virEventAddHandle(xs_fileno(priv->xshandle), - VIR_EVENT_HANDLE_READABLE, - xenStoreWatchEvent, - conn, - NULL)) < 0) - DEBUG0("Failed to add event handle, disabling events\n"); - -#endif //PROXY - return 0; -} - -/** - * xenStoreClose: - * @conn: pointer to the connection block - * - * Close the connection to the Xen hypervisor. - * - * Returns 0 in case of success or -1 in case of error. - */ -int -xenStoreClose(virConnectPtr conn) -{ - xenUnifiedPrivatePtr priv; - - if (conn == NULL) { - virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) conn->privateData; - -#ifndef PROXY - if (xenStoreRemoveWatch(conn, "@introduceDomain", "introduceDomain") < 0) { - DEBUG0("Warning, could not remove @introduceDomain watch"); - /* not fatal */ - } - - if (xenStoreRemoveWatch(conn, "@releaseDomain", "releaseDomain") < 0) { - DEBUG0("Warning, could not remove @releaseDomain watch"); - /* not fatal */ - } - - xenStoreWatchListFree(priv->xsWatchList); - priv->xsWatchList = NULL; - xenUnifiedDomainInfoListFree(priv->activeDomainList); - priv->activeDomainList = NULL; -#endif - if (priv->xshandle == NULL) - return(-1); - - if (priv->xsWatch != -1) - virEventRemoveHandle(priv->xsWatch); - xs_daemon_close(priv->xshandle); - priv->xshandle = NULL; - - return (0); -} - -#ifndef PROXY -/** - * xenStoreGetDomainInfo: - * @domain: pointer to the domain block - * @info: the place where information should be stored - * - * Do an hypervisor call to get the related set of domain information. - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenStoreGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info) -{ - char *tmp, **tmp2; - unsigned int nb_vcpus; - char request[200]; - xenUnifiedPrivatePtr priv; - - if (!VIR_IS_CONNECTED_DOMAIN(domain)) - return (-1); - - if ((domain == NULL) || (domain->conn == NULL) || (info == NULL)) { - virXenStoreError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) domain->conn->privateData; - if (priv->xshandle == NULL) - return(-1); - - if (domain->id == -1) - return(-1); - - tmp = virDomainDoStoreQuery(domain->conn, domain->id, "running"); - if (tmp != NULL) { - if (tmp[0] == '1') - info->state = VIR_DOMAIN_RUNNING; - free(tmp); - } else { - info->state = VIR_DOMAIN_NOSTATE; - } - tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target"); - if (tmp != NULL) { - info->memory = atol(tmp); - info->maxMem = atol(tmp); - free(tmp); - } else { - info->memory = 0; - info->maxMem = 0; - } -#if 0 - /* doesn't seems to work */ - tmp = virDomainDoStoreQuery(domain->conn, domain->id, "cpu_time"); - if (tmp != NULL) { - info->cpuTime = atol(tmp); - free(tmp); - } else { - info->cpuTime = 0; - } -#endif - snprintf(request, 199, "/local/domain/%d/cpu", domain->id); - request[199] = 0; - tmp2 = virConnectDoStoreList(domain->conn, request, &nb_vcpus); - if (tmp2 != NULL) { - info->nrVirtCpu = nb_vcpus; - free(tmp2); - } - return (0); -} - -/** - * xenStoreDomainSetMemory: - * @domain: pointer to the domain block - * @memory: the max memory size in kilobytes. - * - * Change the maximum amount of memory allowed in the xen store - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenStoreDomainSetMemory(virDomainPtr domain, unsigned long memory) -{ - int ret; - char value[20]; - - if ((domain == NULL) || (domain->conn == NULL) || - (memory < 1024 * MIN_XEN_GUEST_SIZE)) { - virXenStoreError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - if (domain->id == -1) - return(-1); - if ((domain->id == 0) && (memory < (2 * MIN_XEN_GUEST_SIZE * 1024))) - return(-1); - snprintf(value, 19, "%lu", memory); - value[19] = 0; - ret = virDomainDoStoreWrite(domain, "memory/target", &value[0]); - if (ret < 0) - return (-1); - return (0); -} - -/** - * xenStoreDomainGetMaxMemory: - * @domain: pointer to the domain block - * - * Ask the xenstore for the maximum memory allowed for a domain - * - * Returns the memory size in kilobytes or 0 in case of error. - */ -unsigned long -xenStoreDomainGetMaxMemory(virDomainPtr domain) -{ - char *tmp; - unsigned long ret = 0; - xenUnifiedPrivatePtr priv; - - if (!VIR_IS_CONNECTED_DOMAIN(domain)) - return (ret); - if (domain->id == -1) - return(-1); - - priv = domain->conn->privateData; - xenUnifiedLock(priv); - tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target"); - if (tmp != NULL) { - ret = (unsigned long) atol(tmp); - VIR_FREE(tmp); - } - xenUnifiedUnlock(priv); - return(ret); -} - -/** - * xenStoreNumOfDomains: - * @conn: pointer to the hypervisor connection - * - * Provides the number of active domains. - * - * Returns the number of domain found or -1 in case of error - */ -int -xenStoreNumOfDomains(virConnectPtr conn) -{ - unsigned int num; - char **idlist; - int ret = -1; - xenUnifiedPrivatePtr priv; - - if (conn == NULL) { - virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return -1; - } - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL) { - virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return(-1); - } - idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num); - if (idlist) { - free(idlist); - ret = num; - } - return(ret); -} - -/** - * xenStoreDoListDomains: - * @conn: pointer to the hypervisor connection - * @ids: array to collect the list of IDs of active domains - * @maxids: size of @ids - * - * Internal API: collect the list of active domains, and store - * their ID in @maxids. The driver lock must be held. - * - * Returns the number of domain found or -1 in case of error - */ -static int -xenStoreDoListDomains(xenUnifiedPrivatePtr priv, int *ids, int maxids) -{ - char **idlist = NULL, *endptr; - unsigned int num, i; - int ret = -1; - long id; - - if (priv->xshandle == NULL) - goto out; - - idlist = xs_directory (priv->xshandle, 0, "/local/domain", &num); - if (idlist == NULL) - goto out; - - for (ret = 0, i = 0; (i < num) && (ret < maxids); i++) { - id = strtol(idlist[i], &endptr, 10); - if ((endptr == idlist[i]) || (*endptr != 0)) - goto out; - ids[ret++] = (int) id; - } - -out: - VIR_FREE (idlist); - return ret; -} - -/** - * xenStoreListDomains: - * @conn: pointer to the hypervisor connection - * @ids: array to collect the list of IDs of active domains - * @maxids: size of @ids - * - * Collect the list of active domains, and store their ID in @maxids - * - * Returns the number of domain found or -1 in case of error - */ -int -xenStoreListDomains(virConnectPtr conn, int *ids, int maxids) -{ - xenUnifiedPrivatePtr priv; - int ret; - - if ((conn == NULL) || (ids == NULL)) { - virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return(-1); - } - - priv = (xenUnifiedPrivatePtr) conn->privateData; - - xenUnifiedLock(priv); - ret = xenStoreDoListDomains(priv, ids, maxids); - xenUnifiedUnlock(priv); - - return(ret); -} - -/** - * xenStoreLookupByName: - * @conn: A xend instance - * @name: The name of the domain - * - * Try to lookup a domain on the Xen Store based on its name. - * - * Returns a new domain object or NULL in case of failure - */ -virDomainPtr -xenStoreLookupByName(virConnectPtr conn, const char *name) -{ - virDomainPtr ret = NULL; - unsigned int num, i, len; - long id = -1; - char **idlist = NULL, *endptr; - char prop[200], *tmp; - int found = 0; - struct xend_domain *xenddomain = NULL; - xenUnifiedPrivatePtr priv; - - if ((conn == NULL) || (name == NULL)) { - virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); - return(NULL); - } - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL) - return(NULL); - - idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num); - if (idlist == NULL) - goto done; - - for (i = 0; i < num; i++) { - id = strtol(idlist[i], &endptr, 10); - if ((endptr == idlist[i]) || (*endptr != 0)) { - goto done; - } -#if 0 - if (virConnectCheckStoreID(conn, (int) id) < 0) - continue; -#endif - snprintf(prop, 199, "/local/domain/%s/name", idlist[i]); - prop[199] = 0; - tmp = xs_read(priv->xshandle, 0, prop, &len); - if (tmp != NULL) { - found = STREQ (name, tmp); - free(tmp); - if (found) - break; - } - } - if (!found) - goto done; - - ret = virGetDomain(conn, name, NULL); - if (ret == NULL) - goto done; - - ret->id = id; - -done: - free(xenddomain); - free(idlist); - - return(ret); -} - -/** - * xenStoreDomainShutdown: - * @domain: pointer to the Domain block - * - * Shutdown the domain, the OS is requested to properly shutdown - * and the domain may ignore it. It will return immediately - * after queuing the request. - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenStoreDomainShutdown(virDomainPtr domain) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL)) { - virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - if (domain->id == -1 || domain->id == 0) - return(-1); - /* - * this is very hackish, the domU kernel probes for a special - * node in the xenstore and launch the shutdown command if found. - */ - priv = domain->conn->privateData; - xenUnifiedLock(priv); - ret = virDomainDoStoreWrite(domain, "control/shutdown", "poweroff"); - xenUnifiedUnlock(priv); - return ret; -} - -/** - * xenStoreDomainReboot: - * @domain: pointer to the Domain block - * @flags: extra flags for the reboot operation, not used yet - * - * Reboot the domain, the OS is requested to properly shutdown - * and reboot but the domain may ignore it. It will return immediately - * after queuing the request. - * - * Returns 0 in case of success, -1 in case of error. - */ -int -xenStoreDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED) -{ - int ret; - xenUnifiedPrivatePtr priv; - - if ((domain == NULL) || (domain->conn == NULL)) { - virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(-1); - } - if (domain->id == -1 || domain->id == 0) - return(-1); - /* - * this is very hackish, the domU kernel probes for a special - * node in the xenstore and launch the shutdown command if found. - */ - priv = domain->conn->privateData; - xenUnifiedLock(priv); - ret = virDomainDoStoreWrite(domain, "control/shutdown", "reboot"); - xenUnifiedUnlock(priv); - return ret; -} - -/* - * xenStoreDomainGetOSType: - * @domain: a domain object - * - * Get the type of domain operation system. - * - * Returns the new string or NULL in case of error, the string must be - * freed by the caller. - */ -static char * -xenStoreDomainGetOSType(virDomainPtr domain) { - char *vm, *str = NULL; - - if ((domain == NULL) || (domain->conn == NULL)) { - virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG, - __FUNCTION__); - return(NULL); - } - - vm = virDomainGetVM(domain); - if (vm) { - xenUnifiedPrivatePtr priv = domain->conn->privateData; - xenUnifiedLock(priv); - str = virDomainGetVMInfo(domain, vm, "image/ostype"); - xenUnifiedUnlock(priv); - VIR_FREE(vm); - } - - return (str); -} -#endif /* ! PROXY */ - -/** - * xenStoreDomainGetVNCPort: - * @conn: the hypervisor connection - * @domid: id of the domain - * - * Return the port number on which the domain is listening for VNC - * connections. - * - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - * - * Returns the port number, -1 in case of error - */ -int xenStoreDomainGetVNCPort(virConnectPtr conn, int domid) { - char *tmp; - int ret = -1; - - tmp = virDomainDoStoreQuery(conn, domid, "console/vnc-port"); - if (tmp != NULL) { - char *end; - ret = strtol(tmp, &end, 10); - if (ret == 0 && end == tmp) - ret = -1; - free(tmp); - } - return(ret); -} - -/** - * xenStoreDomainGetConsolePath: - * @conn: the hypervisor connection - * @domid: id of the domain - * - * Return the path to the psuedo TTY on which the guest domain's - * serial console is attached. - * - * Returns the path to the serial console. It is the callers - * responsibilty to free() the return string. Returns NULL - * on error - * - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - */ -char * xenStoreDomainGetConsolePath(virConnectPtr conn, int domid) { - return virDomainDoStoreQuery(conn, domid, "console/tty"); -} - -#ifdef PROXY -/* - * xenStoreDomainGetOSTypeID: - * @conn: pointer to the connection. - * @id: the domain id - * - * Get the type of domain operation system. - * - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - * - * Returns the new string or NULL in case of error, the string must be - * freed by the caller. - */ -char * -xenStoreDomainGetOSTypeID(virConnectPtr conn, int id) { - char *vm, *str = NULL; - char query[200]; - unsigned int len; - xenUnifiedPrivatePtr priv; - - if (id < 0) - return(NULL); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL) - return (NULL); - - snprintf(query, 199, "/local/domain/%d/vm", id); - query[199] = 0; - - vm = xs_read(priv->xshandle, 0, &query[0], &len); - - if (vm) { - snprintf(query, 199, "%s/image/ostype", vm); - str = xs_read(priv->xshandle, 0, &query[0], &len); - free(vm); - } - if (str == NULL) - str = strdup("linux"); - - - return (str); -} -#endif /* PROXY */ - -/* - * xenStoreDomainGetNetworkID: - * @conn: pointer to the connection. - * @id: the domain id - * @mac: the mac address - * - * Get the reference (i.e. the string number) for the device on that domain - * which uses the given mac address - * - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - * - * Returns the new string or NULL in case of error, the string must be - * freed by the caller. - */ -char * -xenStoreDomainGetNetworkID(virConnectPtr conn, int id, const char *mac) { - char dir[80], path[128], **list = NULL, *val = NULL; - unsigned int len, i, num; - char *ret = NULL; - xenUnifiedPrivatePtr priv; - - if (id < 0) - return(NULL); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL) - return (NULL); - if (mac == NULL) - return (NULL); - - snprintf(dir, sizeof(dir), "/local/domain/0/backend/vif/%d", id); - list = xs_directory(priv->xshandle, 0, dir, &num); - if (list == NULL) - return(NULL); - for (i = 0; i < num; i++) { - snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "mac"); - if ((val = xs_read(priv->xshandle, 0, path, &len)) == NULL) - break; - - bool match = (virMacAddrCompare(val, mac) == 0); - - VIR_FREE(val); - - if (match) { - ret = strdup(list[i]); - break; - } - } - - VIR_FREE(list); - return(ret); -} - -/* - * xenStoreDomainGetDiskID: - * @conn: pointer to the connection. - * @id: the domain id - * @dev: the virtual block device name - * - * Get the reference (i.e. the string number) for the device on that domain - * which uses the given virtual block device name - * - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - * - * Returns the new string or NULL in case of error, the string must be - * freed by the caller. - */ -char * -xenStoreDomainGetDiskID(virConnectPtr conn, int id, const char *dev) { - char dir[80], path[128], **list = NULL, *val = NULL; - unsigned int devlen, len, i, num; - char *ret = NULL; - xenUnifiedPrivatePtr priv; - - if (id < 0) - return(NULL); - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL) - return (NULL); - if (dev == NULL) - return (NULL); - devlen = strlen(dev); - if (devlen <= 0) - return (NULL); - - snprintf(dir, sizeof(dir), "/local/domain/0/backend/vbd/%d", id); - list = xs_directory(priv->xshandle, 0, dir, &num); - if (list != NULL) { - for (i = 0; i < num; i++) { - snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "dev"); - val = xs_read(priv->xshandle, 0, path, &len); - if (val == NULL) - break; - if ((devlen != len) || memcmp(val, dev, len)) { - free (val); - } else { - ret = strdup(list[i]); - free (val); - free (list); - return (ret); - } - } - free (list); - } - snprintf(dir, sizeof(dir), "/local/domain/0/backend/tap/%d", id); - list = xs_directory(priv->xshandle, 0, dir, &num); - if (list != NULL) { - for (i = 0; i < num; i++) { - snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "dev"); - val = xs_read(priv->xshandle, 0, path, &len); - if (val == NULL) - break; - if ((devlen != len) || memcmp(val, dev, len)) { - free (val); - } else { - ret = strdup(list[i]); - free (val); - free (list); - return (ret); - } - } - free (list); - } - return (NULL); -} - -/* - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - */ -char *xenStoreDomainGetName(virConnectPtr conn, - int id) { - char prop[200]; - xenUnifiedPrivatePtr priv; - unsigned int len; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL) - return(NULL); - - snprintf(prop, 199, "/local/domain/%d/name", id); - prop[199] = 0; - return xs_read(priv->xshandle, 0, prop, &len); -} - -#ifndef PROXY -/* - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - */ -int xenStoreDomainGetUUID(virConnectPtr conn, - int id, - unsigned char *uuid) { - char prop[200]; - xenUnifiedPrivatePtr priv; - unsigned int len; - char *uuidstr; - int ret = 0; - - priv = (xenUnifiedPrivatePtr) conn->privateData; - if (priv->xshandle == NULL) - return -1; - - snprintf(prop, 199, "/local/domain/%d/vm", id); - prop[199] = 0; - // This will return something like - // /vm/00000000-0000-0000-0000-000000000000 - uuidstr = xs_read(priv->xshandle, 0, prop, &len); - - // remove "/vm/" - ret = virUUIDParse(uuidstr + 4, uuid); - - VIR_FREE(uuidstr); - - return ret; -} - -static void -xenStoreWatchListFree(xenStoreWatchListPtr list) -{ - int i; - for (i=0; icount; i++) { - VIR_FREE(list->watches[i]->path); - VIR_FREE(list->watches[i]->token); - VIR_FREE(list->watches[i]); - } - VIR_FREE(list); -} - -/* - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - */ -int xenStoreAddWatch(virConnectPtr conn, - const char *path, - const char *token, - xenStoreWatchCallback cb, - void *opaque) -{ - xenStoreWatchPtr watch; - int n; - xenStoreWatchListPtr list; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (priv->xshandle == NULL) - return -1; - - list = priv->xsWatchList; - if(!list) - return -1; - - /* check if we already have this callback on our list */ - for (n=0; n < list->count; n++) { - if( STREQ(list->watches[n]->path, path) && - STREQ(list->watches[n]->token, token)) { - virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("watch already tracked")); - return -1; - } - } - - if (VIR_ALLOC(watch) < 0) - return -1; - watch->path = strdup(path); - watch->token = strdup(token); - watch->cb = cb; - watch->opaque = opaque; - - /* Make space on list */ - n = list->count; - if (VIR_REALLOC_N(list->watches, n + 1) < 0) { - virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, - "%s", _("reallocating list")); - VIR_FREE(watch); - return -1; - } - - list->watches[n] = watch; - list->count++; - - conn->refs++; - - return xs_watch(priv->xshandle, watch->path, watch->token); -} - -/* - * The caller must hold the lock on the privateData - * associated with the 'conn' parameter. - */ -int xenStoreRemoveWatch(virConnectPtr conn, - const char *path, - const char *token) -{ - int i; - xenStoreWatchListPtr list; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - if (priv->xshandle == NULL) - return -1; - - list = priv->xsWatchList; - if(!list) - return -1; - - for (i = 0 ; i < list->count ; i++) { - if( STREQ(list->watches[i]->path, path) && - STREQ(list->watches[i]->token, token)) { - - if (!xs_unwatch(priv->xshandle, - list->watches[i]->path, - list->watches[i]->token)) - { - DEBUG0("WARNING: Could not remove watch"); - /* Not fatal, continue */ - } - - VIR_FREE(list->watches[i]->path); - VIR_FREE(list->watches[i]->token); - VIR_FREE(list->watches[i]); - - if (i < (list->count - 1)) - memmove(list->watches + i, - list->watches + i + 1, - sizeof(*(list->watches)) * - (list->count - (i + 1))); - - if (VIR_REALLOC_N(list->watches, - list->count - 1) < 0) { - ; /* Failure to reduce memory allocation isn't fatal */ - } - list->count--; - virUnrefConnect(conn); - return 0; - } - } - return -1; -} - -static xenStoreWatchPtr -xenStoreFindWatch(xenStoreWatchListPtr list, - const char *path, - const char *token) -{ - int i; - for (i = 0 ; i < list->count ; i++) - if( STREQ(path, list->watches[i]->path) && - STREQ(token, list->watches[i]->token) ) - return list->watches[i]; - - return NULL; -} - -static void -xenStoreWatchEvent(int watch ATTRIBUTE_UNUSED, - int fd ATTRIBUTE_UNUSED, - int events, - void *data) -{ - char **event; - char *path; - char *token; - unsigned int stringCount; - xenStoreWatchPtr sw; - - virConnectPtr conn = data; - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; - - if(!priv) return; - - /* only set a watch on read and write events */ - if (events & (VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP)) return; - - xenUnifiedLock(priv); - - if(!priv->xshandle) - goto cleanup; - - event = xs_read_watch(priv->xshandle, &stringCount); - if (!event) - goto cleanup; - - path = event[XS_WATCH_PATH]; - token = event[XS_WATCH_TOKEN]; - - sw = xenStoreFindWatch(priv->xsWatchList, path, token); - if( sw ) - sw->cb(conn, path, token, sw->opaque); - VIR_FREE(event); - -cleanup: - xenUnifiedUnlock(priv); -} - - -/* - * The domain callback for the @introduceDomain watch - * - * The lock on 'priv' is held when calling this - */ -int xenStoreDomainIntroduced(virConnectPtr conn, - const char *path ATTRIBUTE_UNUSED, - const char *token ATTRIBUTE_UNUSED, - void *opaque) -{ - int i, j, found, missing = 0, retries = 20; - int new_domain_cnt; - int *new_domids; - int nread; - - xenUnifiedPrivatePtr priv = opaque; - -retry: - new_domain_cnt = xenStoreNumOfDomains(conn); - if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) { - virReportOOMError(NULL); - return -1; - } - nread = xenStoreDoListDomains(priv, new_domids, new_domain_cnt); - if (nread != new_domain_cnt) { - // mismatch. retry this read - VIR_FREE(new_domids); - goto retry; - } - - missing = 0; - for (i=0 ; i < new_domain_cnt ; i++) { - found = 0; - for (j = 0 ; j < priv->activeDomainList->count ; j++) { - if (priv->activeDomainList->doms[j]->id == new_domids[i]) { - found = 1; - break; - } - } - - if (!found) { - virDomainEventPtr event; - char *name; - unsigned char uuid[VIR_UUID_BUFLEN]; - - if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { - missing = 1; - continue; - } - if (xenStoreDomainGetUUID(conn, new_domids[i], uuid) < 0) { - missing = 1; - VIR_FREE(name); - continue; - } - - event = virDomainEventNew(new_domids[i], name, uuid, - VIR_DOMAIN_EVENT_STARTED, - VIR_DOMAIN_EVENT_STARTED_BOOTED); - if (event) - xenUnifiedDomainEventDispatch(priv, event); - - /* Add to the list */ - xenUnifiedAddDomainInfo(priv->activeDomainList, - new_domids[i], name, uuid); - - VIR_FREE(name); - } - } - VIR_FREE(new_domids); - - if (missing && retries--) { - DEBUG0("Some domains were missing, trying again"); - usleep(100 * 1000); - goto retry; - } - return 0; -} - -/* - * The domain callback for the @destroyDomain watch - * - * The lock on 'priv' is held when calling this - */ -int xenStoreDomainReleased(virConnectPtr conn, - const char *path ATTRIBUTE_UNUSED, - const char *token ATTRIBUTE_UNUSED, - void *opaque) -{ - int i, j, found, removed, retries = 20; - int new_domain_cnt; - int *new_domids; - int nread; - - xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) opaque; - - if(!priv->activeDomainList->count) return 0; - -retry: - new_domain_cnt = xenStoreNumOfDomains(conn); - - if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) { - virReportOOMError(NULL); - return -1; - } - nread = xenStoreDoListDomains(priv, new_domids, new_domain_cnt); - if (nread != new_domain_cnt) { - // mismatch. retry this read - VIR_FREE(new_domids); - goto retry; - } - - removed = 0; - for (j=0 ; j < priv->activeDomainList->count ; j++) { - found = 0; - for (i=0 ; i < new_domain_cnt ; i++) { - if (priv->activeDomainList->doms[j]->id == new_domids[i]) { - found = 1; - break; - } - } - - if (!found) { - virDomainEventPtr event = - virDomainEventNew(-1, - priv->activeDomainList->doms[j]->name, - priv->activeDomainList->doms[j]->uuid, - VIR_DOMAIN_EVENT_STOPPED, - VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN); - if (event) - xenUnifiedDomainEventDispatch(priv, event); - - /* Remove from the list */ - xenUnifiedRemoveDomainInfo(priv->activeDomainList, - priv->activeDomainList->doms[j]->id, - priv->activeDomainList->doms[j]->name, - priv->activeDomainList->doms[j]->uuid); - - removed = 1; - } - } - - VIR_FREE(new_domids); - - if (!removed && retries--) { - DEBUG0("No domains removed, retrying"); - usleep(100 * 1000); - goto retry; - } - return 0; -} - -#endif //PROXY diff --git a/src/xs_internal.h b/src/xs_internal.h deleted file mode 100644 index 29e680fb07..0000000000 --- a/src/xs_internal.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * xs_internal.h: internal API for access to XenStore - * - * Copyright (C) 2006 Red Hat, Inc. - * - * See COPYING.LIB for the License of this software - * - * Daniel Veillard - */ - -#ifndef __VIR_XS_INTERNAL_H__ -#define __VIR_XS_INTERNAL_H__ - -#include "internal.h" -#include "driver.h" - -extern struct xenUnifiedDriver xenStoreDriver; -int xenStoreInit (void); - -virDrvOpenStatus xenStoreOpen (virConnectPtr conn, - virConnectAuthPtr auth, - int flags); -int xenStoreClose (virConnectPtr conn); -int xenStoreGetDomainInfo (virDomainPtr domain, - virDomainInfoPtr info); -int xenStoreNumOfDomains (virConnectPtr conn); -int xenStoreListDomains (virConnectPtr conn, - int *ids, - int maxids); -virDomainPtr xenStoreLookupByName(virConnectPtr conn, - const char *name); -unsigned long xenStoreGetMaxMemory (virDomainPtr domain); -int xenStoreDomainSetMemory (virDomainPtr domain, - unsigned long memory); -unsigned long xenStoreDomainGetMaxMemory(virDomainPtr domain); -int xenStoreDomainShutdown (virDomainPtr domain); -int xenStoreDomainReboot (virDomainPtr domain, - unsigned int flags); - -/* those are entry point for the proxy */ -int xenStoreDomainGetVNCPort(virConnectPtr conn, - int domid); -char * xenStoreDomainGetConsolePath(virConnectPtr conn, - int domid); -char * xenStoreDomainGetOSTypeID(virConnectPtr conn, - int id); -char * xenStoreDomainGetNetworkID(virConnectPtr conn, - int id, - const char *mac); -char * xenStoreDomainGetDiskID(virConnectPtr conn, - int id, - const char *dev); -char * xenStoreDomainGetName(virConnectPtr conn, - int id); -int xenStoreDomainGetUUID(virConnectPtr conn, - int id, - unsigned char *uuid); - -typedef int (*xenStoreWatchCallback)(virConnectPtr conn, - const char *path, - const char *token, - void *opaque); - -struct _xenStoreWatch { - char *path; - char *token; - xenStoreWatchCallback cb; - void *opaque; -}; -typedef struct _xenStoreWatch xenStoreWatch; -typedef xenStoreWatch *xenStoreWatchPtr; - -struct _xenStoreWatchList { - unsigned int count; - xenStoreWatchPtr *watches; -}; -typedef struct _xenStoreWatchList xenStoreWatchList; -typedef xenStoreWatchList *xenStoreWatchListPtr; - - -int xenStoreAddWatch(virConnectPtr conn, - const char *path, - const char *token, - xenStoreWatchCallback cb, - void *opaque); -int xenStoreRemoveWatch(virConnectPtr conn, - const char *path, - const char *token); - -/* domain events */ -int xenStoreDomainIntroduced(virConnectPtr conn, - const char *path, - const char *token, - void *opaque); -int xenStoreDomainReleased(virConnectPtr conn, - const char *path, - const char *token, - void *opaque); - -int xenStoreDomainEventEmitted(virDomainEventType evt); -#endif /* __VIR_XS_INTERNAL_H__ */ diff --git a/tests/sexpr2xmltest.c b/tests/sexpr2xmltest.c index 7b834a9200..1196e23468 100644 --- a/tests/sexpr2xmltest.c +++ b/tests/sexpr2xmltest.c @@ -7,8 +7,8 @@ #include "internal.h" #include "xml.h" #include "datatypes.h" -#include "xen_unified.h" -#include "xend_internal.h" +#include "xen/xen_driver.h" +#include "xen/xend_internal.h" #include "testutils.h" #include "testutilsxen.h" diff --git a/tests/statstest.c b/tests/statstest.c index 5b7d35a58c..82c0daa395 100644 --- a/tests/statstest.c +++ b/tests/statstest.c @@ -6,7 +6,7 @@ #include "stats_linux.h" #include "internal.h" - +#include "xen/block_stats.h" #include "testutils.h" #if WITH_XEN diff --git a/tests/xencapstest.c b/tests/xencapstest.c index c01efc0544..8f3fb3eb65 100644 --- a/tests/xencapstest.c +++ b/tests/xencapstest.c @@ -8,7 +8,7 @@ #include "internal.h" #include "xml.h" #include "testutils.h" -#include "xen_internal.h" +#include "xen/xen_hypervisor.h" static char *progname; static char *abs_srcdir; diff --git a/tests/xmconfigtest.c b/tests/xmconfigtest.c index 2255984248..f6aea5a433 100644 --- a/tests/xmconfigtest.c +++ b/tests/xmconfigtest.c @@ -29,8 +29,8 @@ #include "internal.h" #include "datatypes.h" -#include "xen_unified.h" -#include "xm_internal.h" +#include "xen/xen_driver.h" +#include "xen/xm_internal.h" #include "testutils.h" #include "testutilsxen.h" #include "memory.h" diff --git a/tests/xml2sexprtest.c b/tests/xml2sexprtest.c index b4ee4e0b23..d83b37cc81 100644 --- a/tests/xml2sexprtest.c +++ b/tests/xml2sexprtest.c @@ -9,7 +9,7 @@ #include #include "internal.h" -#include "xend_internal.h" +#include "xen/xend_internal.h" #include "testutils.h" #include "testutilsxen.h"