summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2008-11-25 10:44:52 +0000
committerDaniel P. Berrange <berrange@redhat.com>2008-11-25 10:44:52 +0000
commit1eeceaa649c6ff92baf13a4f31edb1eb0fdb5990 (patch)
treec873023ede3eb530645c71d6fcdbc62d29e0ccdd /src
parentAvoid symbol clash with win32 headers and node device APIs (diff)
downloadlibvirt-1eeceaa649c6ff92baf13a4f31edb1eb0fdb5990.tar.gz
libvirt-1eeceaa649c6ff92baf13a4f31edb1eb0fdb5990.tar.bz2
libvirt-1eeceaa649c6ff92baf13a4f31edb1eb0fdb5990.zip
Support domain lifecycle events for Xen (Ben Guthro & Daniel Berrange)
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am5
-rw-r--r--src/util.c4
-rw-r--r--src/virterror.c3
-rw-r--r--src/xen_inotify.c458
-rw-r--r--src/xen_inotify.h31
-rw-r--r--src/xen_unified.c197
-rw-r--r--src/xen_unified.h63
-rw-r--r--src/xm_internal.c198
-rw-r--r--src/xm_internal.h6
-rw-r--r--src/xs_internal.c413
-rw-r--r--src/xs_internal.h52
11 files changed, 1362 insertions, 68 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d0f881fc7..4cf1a3426 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -101,6 +101,9 @@ XEN_DRIVER_SOURCES = \
xend_internal.c xend_internal.h \
xm_internal.c xm_internal.h \
xs_internal.c xs_internal.h
+if WITH_XEN_INOTIFY
+XEN_DRIVER_SOURCES += xen_inotify.c xen_inotify.h
+endif
LXC_DRIVER_SOURCES = \
lxc_conf.c lxc_conf.h \
@@ -189,6 +192,8 @@ libvirt_driver_la_SOURCES = \
libvirt_driver_la_CFLAGS = $(XEN_CFLAGS)
libvirt_driver_la_LDFLAGS = $(XEN_LIBS)
+libvirt_driver_la_CFLAGS = $(XEN_CFLAGS)
+
if WITH_TEST
if WITH_DRIVER_MODULES
mod_LTLIBRARIES += libvirt_driver_test.la
diff --git a/src/util.c b/src/util.c
index 25eec54af..da260096a 100644
--- a/src/util.c
+++ b/src/util.c
@@ -613,6 +613,10 @@ virRun(virConnectPtr conn,
VIR_FREE(outbuf);
VIR_FREE(errbuf);
VIR_FREE(argv_str);
+ if (outfd != -1)
+ close(outfd);
+ if (errfd != -1)
+ close(errfd);
return ret;
}
diff --git a/src/virterror.c b/src/virterror.c
index 112a19e8a..5a0b82703 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -262,6 +262,9 @@ virDefaultErrorFunc(virErrorPtr err)
case VIR_FROM_XENSTORE:
dom = "Xen Store ";
break;
+ case VIR_FROM_XEN_INOTIFY:
+ dom = "Xen Inotify ";
+ break;
case VIR_FROM_DOM:
dom = "Domain ";
break;
diff --git a/src/xen_inotify.c b/src/xen_inotify.c
new file mode 100644
index 000000000..110662e8f
--- /dev/null
+++ b/src/xen_inotify.c
@@ -0,0 +1,458 @@
+/*
+ * 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 <config.h>
+#include <dirent.h>
+#include <sys/inotify.h>
+
+#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 virXenInotifyError(conn, code, fmt...) \
+ virReportErrorHelper(NULL, VIR_FROM_XEN_INOTIFY, code, __FILE__, \
+ __FUNCTION__, __LINE__, fmt)
+
+#define LIBVIRTD_DOMAINS_DIR "/var/lib/xend/domains"
+static const char *configDir = NULL;
+static int useXenConfigCache = 0;
+static xenUnifiedDomainInfoListPtr configInfoList = NULL;
+
+struct xenUnifiedDriver xenInotifyDriver = {
+ xenInotifyOpen, /* open */
+ xenInotifyClose, /* close */
+ NULL, /* version */
+ NULL, /* hostname */
+ NULL, /* URI */
+ 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 virDomainPtr
+xenInotifyXenCacheLookup(virConnectPtr conn, const char *filename) {
+ xenXMConfCachePtr entry;
+ virDomainPtr dom;
+
+ if (!(entry = virHashLookup(xenXMGetConfigCache(), filename))) {
+ DEBUG("No config found for %s", filename);
+ return NULL;
+ }
+
+ if(!(dom = virGetDomain(conn, entry->def->name,
+ (unsigned char*)entry->def->uuid))) {
+ DEBUG0("Error getting dom from def");
+ return NULL;
+ }
+ return dom;
+}
+
+static virDomainPtr
+xenInotifyXendDomainsDirLookup(virConnectPtr conn, const char *filename) {
+ int i;
+ virDomainPtr dom;
+ const char *uuid_str;
+ unsigned char uuid[VIR_UUID_BUFLEN];
+
+ /* xend is managing domains. we will get
+ * a filename in the manner:
+ * /var/lib/xend/domains/<uuid>/
+ */
+ uuid_str = filename + strlen(LIBVIRTD_DOMAINS_DIR) + 1;
+
+ if (virUUIDParse(uuid_str, uuid) < 0) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "parsing uuid %s", uuid_str);
+ return (NULL);
+ }
+ /* 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);
+ if(!(dom = xenDaemonLookupByUUID(conn, uuid))) {
+ /* If we are here, the domain has gone away.
+ search for, and create a domain from the stored
+ list info */
+ for (i=0; i<configInfoList->count; i++) {
+ if (!memcmp(uuid, configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) {
+ if(!(dom = virGetDomain(conn, configInfoList->doms[i]->name,
+ configInfoList->doms[i]->uuid))) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "finding dom for %s", uuid_str);
+ return NULL;
+ }
+ DEBUG0("Found dom on list");
+ return dom;
+ }
+ }
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("finding dom on config list"));
+ return NULL;
+ }
+
+ /* succeeded too find domain by uuid */
+ return dom;
+}
+
+static virDomainPtr
+xenInotifyDomainLookup(virConnectPtr conn, const char *filename) {
+ virDomainPtr dom;
+ virDomainInfo info;
+
+ dom = useXenConfigCache ? xenInotifyXenCacheLookup(conn, filename) :
+ xenInotifyXendDomainsDirLookup(conn, filename);
+
+ if(dom) {
+ if ( (useXenConfigCache ? xenXMDomainGetInfo(dom, &info) :
+ xenDaemonDomainGetInfo(dom, &info)) < 0)
+ dom->id = -1;
+ else
+ dom->id = (info.state == VIR_DOMAIN_SHUTOFF) ? -1 : dom->id;
+ return dom;
+ }
+ return NULL;
+}
+
+static int
+xenInotifyXendDomainsDirRemoveEntry(virConnectPtr conn ATTRIBUTE_UNUSED,
+ const char *fname) {
+ 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<configInfoList->count; i++) {
+ if (!memcmp(uuid, configInfoList->doms[i]->uuid, VIR_UUID_BUFLEN)) {
+ VIR_FREE(configInfoList->doms[i]->name);
+ VIR_FREE(configInfoList->doms[i]);
+
+ if (i < (configInfoList->count - 1))
+ memmove(configInfoList->doms + i,
+ configInfoList->doms + i + 1,
+ sizeof(*(configInfoList->doms)) *
+ (configInfoList->count - (i + 1)));
+
+ if (VIR_REALLOC_N(configInfoList->doms,
+ configInfoList->count - 1) < 0) {
+ ; /* Failure to reduce memory allocation isn't fatal */
+ }
+ configInfoList->count--;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int
+xenInotifyXendDomainsDirAddEntry(virConnectPtr conn,
+ const char *fname) {
+ virDomainPtr dom = xenInotifyDomainLookup(conn, fname);
+ if(!dom) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Error looking up domain"));
+ return -1;
+ }
+
+ if( xenUnifiedAddDomainInfo(configInfoList,
+ dom->id, dom->name, dom->uuid) < 0) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Error adding file to config cache"));
+ virUnrefDomain(dom);
+ return -1;
+ }
+ virUnrefDomain(dom);
+ return 0;
+}
+
+static int
+xenInotifyRemoveDomainConfigInfo(virConnectPtr conn,
+ const char *fname) {
+ return useXenConfigCache ? xenXMConfigCacheRemoveFile(conn, fname) :
+ xenInotifyXendDomainsDirRemoveEntry(conn, fname);
+}
+
+static int
+xenInotifyAddDomainConfigInfo(virConnectPtr conn,
+ const char *fname) {
+ return 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 = (virConnectPtr) data;
+ xenUnifiedPrivatePtr priv = NULL;
+ virDomainPtr dom = 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;
+ }
+
+reread:
+ got = read(fd, buf, sizeof(buf));
+ if (got == -1) {
+ if (errno == EINTR)
+ goto reread;
+ return;
+ }
+
+ tmp = buf;
+ while (got) {
+ if (got < sizeof(struct inotify_event))
+ return; /* bad */
+
+ e = (struct inotify_event *)tmp;
+ tmp += sizeof(struct inotify_event);
+ got -= sizeof(struct inotify_event);
+
+ if (got < e->len)
+ return;
+
+ tmp += e->len;
+ got -= e->len;
+
+ name = (char *)&(e->name);
+
+ snprintf(fname, 1024, "%s/%s", configDir, name);
+
+ if (e->mask & (IN_DELETE | IN_MOVED_FROM)) {
+ if (!(dom = xenInotifyDomainLookup(conn, fname))) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("looking up dom"));
+ continue;
+ }
+
+ xenUnifiedDomainEventDispatch(conn->privateData, dom,
+ VIR_DOMAIN_EVENT_UNDEFINED,
+ VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
+
+
+ if (xenInotifyRemoveDomainConfigInfo(conn, fname) < 0 ) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Error adding file to config cache"));
+ return;
+ }
+ } else if (e->mask & ( IN_CREATE | IN_CLOSE_WRITE | IN_MOVED_TO) ) {
+ if (xenInotifyAddDomainConfigInfo(conn, fname) < 0 ) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Error adding file to config cache"));
+ return;
+ }
+
+ if (!(dom = xenInotifyDomainLookup(conn, fname))) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("looking up dom"));
+ continue;
+ }
+
+ xenUnifiedDomainEventDispatch(conn->privateData, dom,
+ VIR_DOMAIN_EVENT_DEFINED,
+ VIR_DOMAIN_EVENT_DEFINED_ADDED);
+ }
+
+ }
+}
+
+/**
+ * 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.
+ */
+int
+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->xendConfigVersion <= 2) {
+ /* /etc/xen */
+ configDir = xenXMGetConfigDir();
+ useXenConfigCache = 1;
+ } else {
+ /* /var/lib/xend/domains/<uuid>/config.sxp */
+ configDir = LIBVIRTD_DOMAINS_DIR;
+ useXenConfigCache = 0;
+
+ if ( VIR_ALLOC(configInfoList ) < 0) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("failed to allocate configInfoList"));
+ return -1;
+ }
+
+ /* populate initial list */
+ if (!(dh = opendir(configDir))) {
+ virXenInotifyError (NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", strerror(errno));
+ return -1;
+ }
+ while ((ent = readdir(dh))) {
+ if (STRPREFIX(ent->d_name, "."))
+ continue;
+
+ /* Build the full file path */
+ if ((strlen(configDir) + 1 + strlen(ent->d_name) + 1) > PATH_MAX)
+ continue;
+ strcpy(path, 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"));
+ return -1;
+ }
+ }
+
+ }
+
+ if ((priv->inotifyFD = inotify_init()) < 0) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("initializing inotify"));
+ return -1;
+ }
+
+ DEBUG("Adding a watch on %s", configDir);
+ if (inotify_add_watch(priv->inotifyFD,
+ configDir,
+ IN_CREATE |
+ IN_CLOSE_WRITE | IN_DELETE |
+ IN_MOVED_TO | IN_MOVED_FROM) < 0) {
+ virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "adding watch on %s", _(configDir));
+ return -1;
+ }
+
+ DEBUG0("Building initial config cache");
+ if (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");
+ }
+
+ conn->refs++;
+ 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 = (xenUnifiedPrivatePtr) conn->privateData;
+
+ if(configInfoList)
+ xenUnifiedDomainInfoListFree(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
new file mode 100644
index 000000000..a8ff6929f
--- /dev/null
+++ b/src/xen_inotify.h
@@ -0,0 +1,31 @@
+/*
+ * 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"
+extern struct xenUnifiedDriver xenInotifyDriver;
+
+int xenInotifyOpen (virConnectPtr conn,
+ virConnectAuthPtr auth,
+ int flags);
+int xenInotifyClose (virConnectPtr conn);
+#endif
diff --git a/src/xen_unified.c b/src/xen_unified.c
index 36d244fbd..89cfc25a0 100644
--- a/src/xen_unified.c
+++ b/src/xen_unified.c
@@ -37,6 +37,9 @@
#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"
@@ -57,6 +60,9 @@ static struct xenUnifiedDriver *drivers[XEN_UNIFIED_NR_DRIVERS] = {
[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
};
#define xenUnifiedError(conn, code, fmt...) \
@@ -223,6 +229,7 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags)
{
int i, ret = VIR_DRV_OPEN_DECLINED;
xenUnifiedPrivatePtr priv;
+ virDomainEventCallbackListPtr cbList;
if (conn->uri == NULL) {
if (!xenUnifiedProbe())
@@ -260,6 +267,13 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags)
}
conn->privateData = priv;
+ /* Allocate callback list */
+ if (VIR_ALLOC(cbList) < 0) {
+ xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating callback list");
+ return VIR_DRV_OPEN_ERROR;
+ }
+ priv->domainEventCallbacks = cbList;
+
priv->handle = -1;
priv->xendConfigVersion = -1;
priv->type = -1;
@@ -333,6 +347,15 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags)
goto fail;
}
+#if WITH_XEN_INOTIFY
+ 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:
@@ -357,6 +380,8 @@ xenUnifiedClose (virConnectPtr 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);
@@ -1292,6 +1317,40 @@ xenUnifiedNodeGetFreeMemory (virConnectPtr conn)
return(0);
}
+static int
+xenUnifiedDomainEventRegister (virConnectPtr conn,
+ void *callback,
+ void *opaque,
+ void (*freefunc)(void *))
+{
+ GET_PRIVATE (conn);
+ if (priv->xsWatch == -1) {
+ xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+ return -1;
+ }
+
+ conn->refs++;
+ return virDomainEventCallbackListAdd(conn, priv->domainEventCallbacks,
+ callback, opaque, freefunc);
+}
+
+static int
+xenUnifiedDomainEventDeregister (virConnectPtr conn,
+ void *callback)
+{
+ int ret;
+ GET_PRIVATE (conn);
+ if (priv->xsWatch == -1) {
+ xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+ return -1;
+ }
+
+ ret = virDomainEventCallbackListRemove(conn, priv->domainEventCallbacks,
+ callback);
+ virUnrefConnect(conn);
+ return ret;
+}
+
/*----- Register with libvirt.c, and initialise Xen drivers. -----*/
#define HV_VERSION ((DOM0_INTERFACE_VERSION >> 24) * 1000000 + \
@@ -1356,6 +1415,8 @@ static virDriver xenUnifiedDriver = {
.domainBlockPeek = xenUnifiedDomainBlockPeek,
.nodeGetCellsFreeMemory = xenUnifiedNodeGetCellsFreeMemory,
.getFreeMemory = xenUnifiedNodeGetFreeMemory,
+ .domainEventRegister = xenUnifiedDomainEventRegister,
+ .domainEventDeregister = xenUnifiedDomainEventDeregister,
};
/**
@@ -1375,3 +1436,139 @@ xenRegister (void)
return virRegisterDriver (&xenUnifiedDriver);
}
+/**
+ * xenUnifiedDomainInfoListFree:
+ *
+ * Free the Domain Info List
+ */
+void
+xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr list)
+{
+ int i;
+ for (i=0; i<list->count; 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:
+ xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating domain info");
+ 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;
+}
+
+/**
+ * xenUnifiedDomainEventDispatch:
+ *
+ * Dispatch domain events to registered callbacks
+ *
+ */
+void xenUnifiedDomainEventDispatch (xenUnifiedPrivatePtr priv,
+ virDomainPtr dom,
+ int event,
+ int detail)
+{
+ int i;
+ virDomainEventCallbackListPtr cbList;
+
+ if(!priv) return;
+
+ cbList = priv->domainEventCallbacks;
+ if(!cbList) return;
+
+ for(i=0 ; i < cbList->count ; i++) {
+ if(cbList->callbacks[i] && cbList->callbacks[i]->cb) {
+ if (dom) {
+ DEBUG("Dispatching callback %p %p event %d",
+ cbList->callbacks[i],
+ cbList->callbacks[i]->cb, event);
+ cbList->callbacks[i]->cb(cbList->callbacks[i]->conn,
+ dom, event, detail,
+ cbList->callbacks[i]->opaque);
+ }
+ }
+ }
+}
diff --git a/src/xen_unified.h b/src/xen_unified.h
index 445086e79..2f9346f62 100644
--- a/src/xen_unified.h
+++ b/src/xen_unified.h
@@ -14,6 +14,12 @@
#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"
#ifndef HAVE_WINSOCK2_H
#include <sys/un.h>
@@ -29,7 +35,13 @@ extern int xenRegister (void);
#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 */
@@ -85,6 +97,33 @@ struct xenUnifiedDriver {
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
@@ -113,6 +152,19 @@ struct _xenUnifiedPrivate {
* xen_unified.c.
*/
int opened[XEN_UNIFIED_NR_DRIVERS];
+
+ /* A list of xenstore watches */
+ xenStoreWatchListPtr xsWatchList;
+ int xsWatch;
+
+ /* An list of callbacks */
+ virDomainEventCallbackListPtr domainEventCallbacks;
+
+#if WITH_XEN_INOTIFY
+ /* The inotify fd */
+ int inotifyFD;
+ int inotifyWatch;
+#endif
};
typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr;
@@ -122,4 +174,15 @@ int xenNbCells(virConnectPtr conn);
int xenNbCpus(virConnectPtr conn);
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,
+ virDomainPtr dom,
+ int event,
+ int detail);
#endif /* __VIR_XEN_UNIFIED_H__ */
diff --git a/src/xm_internal.c b/src/xm_internal.c
index b89945485..18e88a120 100644
--- a/src/xm_internal.c
+++ b/src/xm_internal.c
@@ -45,6 +45,8 @@
#include "uuid.h"
#include "util.h"
#include "memory.h"
+#include "logging.h"
+
/* The true Xen limit varies but so far is always way
less than 1024, which is the Linux kernel limit according
@@ -54,13 +56,6 @@
static int xenXMConfigSetString(virConfPtr conf, const char *setting,
const char *str);
-typedef struct xenXMConfCache *xenXMConfCachePtr;
-typedef struct xenXMConfCache {
- time_t refreshedAt;
- char filename[PATH_MAX];
- virDomainDefPtr def;
-} xenXMConfCache;
-
static char configDir[PATH_MAX];
/* Config file name to config object */
static virHashTablePtr configCache = NULL;
@@ -124,6 +119,14 @@ struct xenUnifiedDriver xenXMDriver = {
NULL, /* domainSetSchedulerParameters */
};
+virHashTablePtr xenXMGetConfigCache (void) {
+ return configCache;
+}
+
+char *xenXMGetConfigDir (void) {
+ return configDir;
+}
+
#define xenXMError(conn, code, fmt...) \
virReportErrorHelper(conn, VIR_FROM_XENXM, code, __FILE__, \
__FUNCTION__, __LINE__, fmt)
@@ -371,13 +374,121 @@ xenXMConfigSaveFile(virConnectPtr conn, const char *filename, virDomainDefPtr de
return ret;
}
+int
+xenXMConfigCacheRemoveFile(virConnectPtr conn ATTRIBUTE_UNUSED,
+ const char *filename)
+{
+ xenXMConfCachePtr entry;
+
+ entry = virHashLookup(configCache, filename);
+ if (!entry) {
+ DEBUG("No config entry for %s", filename);
+ return 0;
+ }
+
+ virHashRemoveEntry(nameConfigMap, entry->def->name, NULL);
+ virHashRemoveEntry(configCache, filename, xenXMConfigFree);
+ DEBUG("Removed %s %s", entry->def->name, filename);
+ return 0;
+}
+
+
+int
+xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename)
+{
+ 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)) {
+ xenXMError (conn, VIR_ERR_INTERNAL_ERROR,
+ "cannot stat %s: %s", filename, strerror(errno));
+ 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(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(nameConfigMap, entry->def->name);
+ if (nameowner && STREQ(nameowner, filename)) {
+ virHashRemoveEntry(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) {
+ xenXMError (conn, VIR_ERR_NO_MEMORY, "%s", strerror(errno));
+ 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(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(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(nameConfigMap, entry->def->name)) {
+ if (virHashAddEntry(nameConfigMap, entry->def->name, entry->filename) < 0) {
+ virHashRemoveEntry(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 */
-static int xenXMConfigCacheRefresh (virConnectPtr conn) {
+int xenXMConfigCacheRefresh (virConnectPtr conn) {
DIR *dh;
struct dirent *ent;
time_t now = time(NULL);
@@ -401,9 +512,7 @@ static int xenXMConfigCacheRefresh (virConnectPtr conn) {
}
while ((ent = readdir(dh))) {
- xenXMConfCachePtr entry;
struct stat st;
- int newborn = 0;
char path[PATH_MAX];
/*
@@ -447,62 +556,8 @@ static int xenXMConfigCacheRefresh (virConnectPtr conn) {
/* If we already have a matching entry and it is not
modified, then carry on to next one*/
- if ((entry = virHashLookup(configCache, path))) {
- char *nameowner;
-
- if (entry->refreshedAt >= st.st_mtime) {
- entry->refreshedAt = now;
- continue;
- }
-
- /* If we currently own the name, then release it and
- re-acquire it later - just in case it was renamed */
- nameowner = (char *)virHashLookup(nameConfigMap, entry->def->name);
- if (nameowner && STREQ(nameowner, path)) {
- virHashRemoveEntry(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) {
- xenXMError (conn, VIR_ERR_NO_MEMORY, "%s", strerror(errno));
- goto cleanup;
- }
- memcpy(entry->filename, path, PATH_MAX);
- }
- entry->refreshedAt = now;
-
- if (!(entry->def = xenXMConfigReadFile(conn, entry->filename))) {
- if (!newborn)
- virHashRemoveEntry(configCache, path, NULL);
- VIR_FREE(entry);
- continue;
- }
-
- /* If its a completely new entry, it must be stuck into
- the cache (refresh'd entries are already registered) */
- if (newborn) {
- if (virHashAddEntry(configCache, entry->filename, entry) < 0) {
- virDomainDefFree(entry->def);
- VIR_FREE(entry);
- xenXMError (conn, VIR_ERR_INTERNAL_ERROR,
- "%s", _("xenXMConfigCacheRefresh: virHashAddEntry"));
- goto cleanup;
- }
- }
-
- /* See if we need to map this config file in as the primary owner
- * of the domain in question
- */
- if (!virHashLookup(nameConfigMap, entry->def->name)) {
- if (virHashAddEntry(nameConfigMap, entry->def->name, entry->filename) < 0) {
- virHashRemoveEntry(configCache, ent->d_name, NULL);
- virDomainDefFree(entry->def);
- VIR_FREE(entry);
- }
+ if (xenXMConfigCacheAddFile(conn, path) < 0) {
+ /* Ignoring errors, since alot of stuff goes wrong in /etc/xen */
}
}
@@ -513,7 +568,6 @@ static int xenXMConfigCacheRefresh (virConnectPtr conn) {
virHashRemoveSet(configCache, xenXMConfigReaper, xenXMConfigFree, (const void*) &now);
ret = 0;
- cleanup:
if (dh)
closedir(dh);
@@ -1503,8 +1557,10 @@ virDomainPtr xenXMDomainLookupByName(virConnectPtr conn, const char *domname) {
return (NULL);
}
+#ifndef WITH_XEN_INOTIFY
if (xenXMConfigCacheRefresh (conn) < 0)
return (NULL);
+#endif
if (!(filename = virHashLookup(nameConfigMap, domname)))
return (NULL);
@@ -1555,8 +1611,10 @@ virDomainPtr xenXMDomainLookupByUUID(virConnectPtr conn,
return (NULL);
}
+#ifndef WITH_XEN_INOTIFY
if (xenXMConfigCacheRefresh (conn) < 0)
return (NULL);
+#endif
if (!(entry = virHashSearch(configCache, xenXMDomainSearchForUUID, (const void *)uuid))) {
return (NULL);
@@ -2208,8 +2266,10 @@ virDomainPtr xenXMDomainDefineXML(virConnectPtr conn, const char *xml) {
if (conn->flags & VIR_CONNECT_RO)
return (NULL);
+#ifndef WITH_XEN_INOTIFY
if (xenXMConfigCacheRefresh (conn) < 0)
return (NULL);
+#endif
if (!(def = virDomainDefParseString(conn, priv->caps, xml)))
return (NULL);
@@ -2376,8 +2436,10 @@ int xenXMListDefinedDomains(virConnectPtr conn, char **const names, int maxnames
return (-1);
}
+#ifndef WITH_XEN_INOTIFY
if (xenXMConfigCacheRefresh (conn) < 0)
return (-1);
+#endif
if (maxnames > virHashSize(configCache))
maxnames = virHashSize(configCache);
@@ -2401,8 +2463,10 @@ int xenXMNumOfDefinedDomains(virConnectPtr conn) {
return (-1);
}
+#ifndef WITH_XEN_INOTIFY
if (xenXMConfigCacheRefresh (conn) < 0)
return (-1);
+#endif
return virHashSize(nameConfigMap);
}
diff --git a/src/xm_internal.h b/src/xm_internal.h
index e3cea0052..1d14b79c1 100644
--- a/src/xm_internal.h
+++ b/src/xm_internal.h
@@ -32,6 +32,12 @@
extern struct xenUnifiedDriver xenXMDriver;
int xenXMInit (void);
+virHashTablePtr xenXMGetConfigCache(void);
+char *xenXMGetConfigDir(void);
+int xenXMConfigCacheRefresh (virConnectPtr conn);
+int xenXMConfigCacheAddFile(virConnectPtr conn, const char *filename);
+int xenXMConfigCacheRemoveFile(virConnectPtr conn, const char *filename);
+
int xenXMOpen(virConnectPtr conn, virConnectAuthPtr auth, int flags);
int xenXMClose(virConnectPtr conn);
const char *xenXMGetType(virConnectPtr conn);
diff --git a/src/xs_internal.c b/src/xs_internal.c
index f43a217e6..ae773805d 100644
--- a/src/xs_internal.c
+++ b/src/xs_internal.c
@@ -29,6 +29,10 @@
#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" /* for xenHypervisorCheckID */
@@ -42,6 +46,9 @@
#endif
#ifndef PROXY
+/* A list of active domain name/uuids */
+static xenUnifiedDomainInfoListPtr activeDomainList = NULL;
+
static char *xenStoreDomainGetOSType(virDomainPtr domain);
struct xenUnifiedDriver xenStoreDriver = {
@@ -302,7 +309,52 @@ xenStoreOpen(virConnectPtr conn,
}
return (-1);
}
- return (0);
+
+#ifndef PROXY
+ /* Init activeDomainList */
+ if ( VIR_ALLOC(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;
}
/**
@@ -324,10 +376,29 @@ xenStoreClose(virConnectPtr conn)
}
priv = (xenUnifiedPrivatePtr) conn->privateData;
+
+ 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);
+#ifndef PROXY
+ xenUnifiedDomainInfoListFree(activeDomainList);
+#endif
if (priv->xshandle == NULL)
return(-1);
+ if (priv->xsWatch != -1)
+ virEventRemoveHandle(priv->xsWatch);
xs_daemon_close(priv->xshandle);
+ priv->xshandle = NULL;
+
return (0);
}
@@ -920,3 +991,343 @@ char *xenStoreDomainGetName(virConnectPtr conn,
return xs_read(priv->xshandle, 0, prop, &len);
}
+#ifndef PROXY
+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;
+}
+#endif //PROXY
+
+void xenStoreWatchListFree(xenStoreWatchListPtr list)
+{
+ int i;
+ for (i=0; i<list->count; i++) {
+ VIR_FREE(list->watches[i]->path);
+ VIR_FREE(list->watches[i]->token);
+ VIR_FREE(list->watches[i]);
+ }
+ VIR_FREE(list);
+}
+
+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);
+}
+
+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]->path))
+ {
+ 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--;
+#ifndef PROXY
+ virUnrefConnect(conn);
+#endif
+ return 0;
+ }
+ }
+ return -1;
+}
+
+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;
+}
+
+void xenStoreWatchEvent(int watch ATTRIBUTE_UNUSED,
+ int fd ATTRIBUTE_UNUSED,
+ int events ATTRIBUTE_UNUSED,
+ void *data)
+{
+ char **event;
+ char *path;
+ char *token;
+ unsigned int stringCount;
+ xenStoreWatchPtr sw;
+
+ virConnectPtr conn = data;
+ xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
+ if(!priv) return;
+ if(!priv->xshandle) return;
+
+ event = xs_read_watch(priv->xshandle, &stringCount);
+ if (!event)
+ return;
+
+ 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);
+}
+
+#ifndef PROXY
+/* The domain callback for the @introduceDomain watch */
+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 = (xenUnifiedPrivatePtr) opaque;
+
+retry:
+ new_domain_cnt = xenStoreNumOfDomains(conn);
+ if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) {
+ virXenStoreError(NULL, VIR_ERR_NO_MEMORY,
+ "%s", _("failed to allocate domids"));
+ return -1;
+ }
+ nread = xenStoreListDomains(conn, 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 < activeDomainList->count ; j++) {
+ if (activeDomainList->doms[j]->id == new_domids[i]) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ virDomainPtr dom;
+ 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;
+ }
+
+ dom = virGetDomain(conn, name, uuid);
+ if (dom) {
+ dom->id = new_domids[i];
+
+ /* This domain was not in the old list. Emit an event */
+ xenUnifiedDomainEventDispatch(priv, dom,
+ VIR_DOMAIN_EVENT_STARTED,
+ VIR_DOMAIN_EVENT_STARTED_BOOTED);
+
+ /* Add to the list */
+ xenUnifiedAddDomainInfo(activeDomainList,
+ new_domids[i], name, uuid);
+
+ virUnrefDomain(dom);
+ }
+
+ 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 */
+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(!activeDomainList->count) return 0;
+
+retry:
+ new_domain_cnt = xenStoreNumOfDomains(conn);
+
+ if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) {
+ virXenStoreError(NULL, VIR_ERR_NO_MEMORY,
+ "%s", _("failed to allocate domids"));
+ return -1;
+ }
+ nread = xenStoreListDomains(conn, 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 < activeDomainList->count ; j++) {
+ found = 0;
+ for (i=0 ; i < new_domain_cnt ; i++) {
+ if (activeDomainList->doms[j]->id == new_domids[i]) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ virDomainPtr dom = virGetDomain(conn,
+ activeDomainList->doms[j]->name,
+ activeDomainList->doms[j]->uuid);
+ if(dom) {
+ dom->id = -1;
+ /* This domain was not in the new list. Emit an event */
+ xenUnifiedDomainEventDispatch(priv, dom,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
+ /* Remove from the list */
+ xenUnifiedRemoveDomainInfo(activeDomainList,
+ activeDomainList->doms[j]->id,
+ activeDomainList->doms[j]->name,
+ activeDomainList->doms[j]->uuid);
+
+ virUnrefDomain(dom);
+ 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
index c3c402fe3..cd593b4e1 100644
--- a/src/xs_internal.h
+++ b/src/xs_internal.h
@@ -51,5 +51,57 @@ char * xenStoreDomainGetDiskID(virConnectPtr conn,
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;
+
+
+void xenStoreWatchListFree(xenStoreWatchListPtr head);
+
+int xenStoreAddWatch(virConnectPtr conn,
+ const char *path,
+ const char *token,
+ xenStoreWatchCallback cb,
+ void *opaque);
+int xenStoreRemoveWatch(virConnectPtr conn,
+ const char *path,
+ const char *token);
+xenStoreWatchPtr xenStoreFindWatch(xenStoreWatchListPtr list,
+ const char *path,
+ const char *token);
+
+void xenStoreWatchEvent(int watch, int fd, int events, void *data);
+
+/* 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__ */