Daniel B pointed out that my attached file only contained a diffstat. New patch attached (Sorry if you get this multiple times Daniel - I realized you had not CC'd the list after I sent you the patch) Ben Guthro wrote on 11/21/2008 02:11 PM: > I have integrated your changes, and moved this bit referenced below, so it works with my setup. Could you please test with yours, and make sure I didn't break anything? > > configure.in | 13 > examples/domain-events/events-c/event-test.c | 2 > include/libvirt/virterror.h | 1 > po/POTFILES.in | 1 > python/libvir.c | 3 > src/Makefile.am | 5 > src/util.c | 4 > src/virterror.c | 3 > src/xen_inotify.c | 453 +++++++++++++++++++++++++++ > src/xen_inotify.h | 31 + > src/xen_unified.c | 200 +++++++++++ > src/xen_unified.h | 61 +++ > src/xm_internal.c | 198 +++++++---- > src/xm_internal.h | 6 > src/xs_internal.c | 431 +++++++++++++++++++++++++ > src/xs_internal.h | 51 +++ > 16 files changed, 1393 insertions(+), 70 deletions(-) > > > Daniel P. Berrange wrote on 11/21/2008 02:07 PM: >> On Fri, Nov 21, 2008 at 01:56:16PM -0500, Ben Guthro wrote: >>> I think I found the culprit of this: >>> >>> Daniel P. Berrange wrote on 11/21/2008 11:13 AM: >>>> - Allocate capabilities info before initializing inotify driver >>>> because loading XM config files /etc/xen requires this >>>> >>> ... >>>> - if (!(priv->caps = xenHypervisorMakeCapabilities(conn))) { >>>> - DEBUG0("Failed to make capabilities"); >>>> - goto fail; >>>> - } >>> If I undo this part of your patch, everything else works as designed. >>> >>> While I have no evidence yet to support this claim, I think this is >>> failing trying to open the xend socket, perhaps before the xend driver >>> is open... >> Doh. Merge error. Originally I had moved it to be immediately before >> the call to initialize xen inotify driver. For inexplicable reasons, >> in the new patch I have it before the XenD driver. >> >> Daniel >> >> ------------------------------------------------------------------------ >> >> -- >> Libvir-list mailing list >> Libvir-list@xxxxxxxxxx >> https://www.redhat.com/mailman/listinfo/libvir-list
diff --git a/configure.in b/configure.in index 76e6d77..a6518ee 100644 --- a/configure.in +++ b/configure.in @@ -147,6 +147,8 @@ fi dnl Allow to build without Xen, QEMU/KVM, test or remote driver AC_ARG_WITH([xen], [ --with-xen add XEN support (on)],[],[with_xen=yes]) +AC_ARG_WITH([xen-inotify], +[ --with-xen-inotify add XEN inotify support (on)],[],[with_xen_inotify=yes]) AC_ARG_WITH([qemu], [ --with-qemu add QEMU/KVM support (on)],[],[with_qemu=yes]) AC_ARG_WITH([uml], @@ -335,6 +337,17 @@ AC_SUBST([XEN_CFLAGS]) AC_SUBST([XEN_LIBS]) dnl +dnl check for kernel headers required by xen_inotify +dnl +if test "$with_xen_inotify" != "no"; then + AC_CHECK_HEADER([linux/inotify.h],[],[with_xen_inotify=no]) +fi +if test "$with_xen_inotify" = "yes"; then + AC_DEFINE_UNQUOTED([WITH_XEN_INOTIFY], 1,[whether Xen inotify sub-driver is enabled]) +fi +AM_CONDITIONAL([WITH_XEN_INOTIFY], [test "$with_xen_inotify" = "yes"]) + +dnl dnl check for kernel headers required by src/bridge.c dnl if test "$with_qemu" = "yes" -o "$with_lxc" = "yes" ; then diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index f569808..e265693 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -308,7 +308,7 @@ int main(int argc, char **argv) myEventRemoveTimeoutFunc); virConnectPtr dconn = NULL; - dconn = virConnectOpen (argv[1] ? argv[1] : "qemu:///system"); + dconn = virConnectOpen (argv[1] ? argv[1] : NULL); if (!dconn) { printf("error opening\n"); return -1; diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 870ebbc..0f1f08c 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -60,6 +60,7 @@ typedef enum { VIR_FROM_DOMAIN, /* Error from domain config */ VIR_FROM_UML, /* Error at the UML driver */ VIR_FROM_NODEDEV, /* Error from node device monitor */ + VIR_FROM_XEN_INOTIFY, /* Error from xen inotify layer */ } virErrorDomain; diff --git a/po/POTFILES.in b/po/POTFILES.in index 99c0b0f..d33d24b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -35,6 +35,7 @@ src/util.c src/uuid.c src/virsh.c src/virterror.c +src/xen_inotify.c src/xen_internal.c src/xend_internal.c src/xm_internal.c diff --git a/python/libvir.c b/python/libvir.c index 7d58442..61ae975 100644 --- a/python/libvir.c +++ b/python/libvir.c @@ -1564,7 +1564,8 @@ getLibvirtModuleObject (void) { return libvirt_module; // PyImport_ImportModule returns a new reference - libvirt_module = PyImport_ImportModule("libvirt"); + /* Bogus (char *) cast for RHEL-5 python API brokenness */ + libvirt_module = PyImport_ImportModule((char *)"libvirt"); if(!libvirt_module) { #if DEBUG_ERROR printf("%s Error importing libvirt module\n", __FUNCTION__); diff --git a/src/Makefile.am b/src/Makefile.am index a8f440e..d22e64d 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 25eec54..da26009 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 0723db8..9fadca4 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 0000000..a66bbe2 --- /dev/null +++ b/src/xen_inotify.c @@ -0,0 +1,453 @@ +/* + * 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 (STREQ(uuid_str, configInfoList->doms[i]->uuid)) { + if(!(dom = virGetDomain(conn, configInfoList->doms[i]->name, + (unsigned char *)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; + int i; + + /* match and remove on uuid */ + for (i=0; i<configInfoList->count; i++) { + if (STREQ(uuidstr, configInfoList->doms[i]->uuid)) { + VIR_FREE(configInfoList->doms[i]->name); + VIR_FREE(configInfoList->doms[i]->uuid); + 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) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virDomainPtr dom = xenInotifyDomainLookup(conn, fname); + if(!dom) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Error looking up domain")); + return -1; + } + + virUUIDFormat(dom->uuid, uuidstr); + if( xenUnifiedAddDomainInfo(configInfoList, + dom->id, dom->name, uuidstr) < 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) { + 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) ) { + 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) < 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 0000000..a8ff692 --- /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 36d244f..92168b0 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,142 @@ 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]->uuid); + 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, 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) && + STREQ(list->doms[n]->uuid, uuid)) { + DEBUG0("WARNING: dom already tracked"); + return -1; + } + } + + if (VIR_ALLOC(info) < 0) + goto memory_error; + if (!(info->name = strdup(name))) + goto memory_error; + if (!(info->uuid = strdup(uuid))) + goto memory_error; + 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 && info->name) + VIR_FREE(info->name); + if (info && info->uuid) + VIR_FREE(info->uuid); + if (info) + 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, char *uuid) +{ + int i; + for (i = 0 ; i < list->count ; i++) { + if( list->doms[i]->id == id && + STREQ(list->doms[i]->name, name) && + STREQ(list->doms[i]->uuid, uuid)) { + + VIR_FREE(list->doms[i]->name); + VIR_FREE(list->doms[i]->uuid); + 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 445086e..fc38675 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; + char *uuid; +}; +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,13 @@ int xenNbCells(virConnectPtr conn); int xenNbCpus(virConnectPtr conn); char *xenDomainUsedCpus(virDomainPtr dom); +void xenUnifiedDomainInfoListFree(xenUnifiedDomainInfoListPtr info); +int xenUnifiedAddDomainInfo(xenUnifiedDomainInfoListPtr info, + int id, char *name, char *uuid); +int xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr info, + int id, char *name, 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 b899454..18e88a1 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 e3cea00..1d14b79 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 f43a217..5d4372c 100644 --- a/src/xs_internal.c +++ b/src/xs_internal.c @@ -29,6 +29,9 @@ #include "virterror_internal.h" #include "datatypes.h" #include "driver.h" +#include "memory.h" +#include "event.h" +#include "logging.h" #include "xen_unified.h" #include "xs_internal.h" #include "xen_internal.h" /* for xenHypervisorCheckID */ @@ -42,6 +45,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 +308,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 +375,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 +990,362 @@ char *xenStoreDomainGetName(virConnectPtr conn, return xs_read(priv->xshandle, 0, prop, &len); } +#ifndef PROXY +char *xenStoreDomainGetUUIDString(virConnectPtr conn, + int id) { + char prop[200]; + xenUnifiedPrivatePtr priv; + unsigned int len; + char *vm_string; + char *ret; + + priv = (xenUnifiedPrivatePtr) conn->privateData; + if (priv->xshandle == NULL) + return(NULL); + + snprintf(prop, 199, "/local/domain/%d/vm", id); + prop[199] = 0; + // This will return something like + // /vm/00000000-0000-0000-0000-000000000000 + vm_string = xs_read(priv->xshandle, 0, prop, &len); + + // remove "/vm/" + ret = strdup(vm_string + 4); + + VIR_FREE(vm_string); + + 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; + int new_domain_cnt; + int *new_domids; + char *name; + char *uuid; + 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; + } + + // Initial callback, fill list (This should at least contain dom0) + if(!activeDomainList->count) { + for (i=0 ; i < new_domain_cnt ; i++) { + if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { + continue; + } + if (!(uuid = xenStoreDomainGetUUIDString(conn, new_domids[i]))) { + VIR_FREE(name); + continue; + } + xenUnifiedAddDomainInfo(activeDomainList, new_domids[i], + name, uuid); + VIR_FREE(name); + VIR_FREE(uuid); + } + VIR_FREE(new_domids); + return 0; + } + + for (i=0 ; i < new_domain_cnt ; i++) { + if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { + continue; + } + if (!(uuid = xenStoreDomainGetUUIDString(conn, new_domids[i]))) { + VIR_FREE(name); + continue; + } + /* We must iterate over both lists to avoid a race where + 2 domains were created by the time we get here */ + for (j=0 ; j < activeDomainList->count ; j++) { + if( !STREQ(name, activeDomainList->doms[j]->name) || + !STREQ(uuid, activeDomainList->doms[j]->uuid) ) { + virDomainPtr dom = virGetDomain(conn, name, (unsigned char*)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(uuid); + } + VIR_FREE(new_domids); + 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; + int new_domain_cnt; + int *new_domids; + char *name; + char *uuid; + 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 (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { + continue; + } + + if (!(uuid = xenStoreDomainGetUUIDString(conn, new_domids[i]))) { + VIR_FREE(name); + continue; + } + + if( STREQ(name, activeDomainList->doms[j]->name) || + STREQ(uuid, activeDomainList->doms[j]->uuid) ) { + found = 1; + VIR_FREE(name); + VIR_FREE(uuid); + break; + } + + VIR_FREE(name); + VIR_FREE(uuid); + } + + if (!found) { + virDomainPtr dom = virGetDomain(conn, + activeDomainList->doms[j]->name, + (unsigned char*)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) { + // xenstore sometimes calls us back before + // domids are fully torn down + usleep(1000); + goto retry; + } + return 0; +} +#endif //PROXY diff --git a/src/xs_internal.h b/src/xs_internal.h index c3c402f..f712118 100644 --- a/src/xs_internal.h +++ b/src/xs_internal.h @@ -51,5 +51,56 @@ char * xenStoreDomainGetDiskID(virConnectPtr conn, const char *dev); char * xenStoreDomainGetName(virConnectPtr conn, int id); +char * xenStoreDomainGetUUIDString(virConnectPtr conn, + int id); +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__ */
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list