This version of the Xen-Events patch goes a step further than the last version, and now emits the following domain events: STARTED STOPPED ADDED REMOVED This is accomplished by monitoring /etc/xen, and taking advantage of what seemed to be dormant code in the XM driver. By re-enabling the "config cache", we can properly track domains when files come, and go into this directory. configure.in | 13 + include/libvirt/virterror.h | 1 po/POTFILES.in | 1 src/Makefile.am | 3 src/virterror.c | 3 src/xen_inotify.c | 270 +++++++++++++++++++++++++++ src/xen_inotify.h | 32 +++ src/xen_unified.c | 190 +++++++++++++++++++ src/xen_unified.h | 61 ++++++ src/xm_internal.c | 24 +- src/xs_internal.c | 430 +++++++++++++++++++++++++++++++++++++++++++- src/xs_internal.h | 51 +++++ 12 files changed, 1069 insertions(+), 10 deletions(-)
diff --git a/configure.in b/configure.in index ef69730..6ac4c4e 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([openvz], @@ -328,6 +330,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/include/libvirt/virterror.h b/include/libvirt/virterror.h index 8e24708..ff07542 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -58,6 +58,7 @@ typedef enum { VIR_FROM_STORAGE, /* Error from storage driver */ VIR_FROM_NETWORK, /* Error from network config */ VIR_FROM_DOMAIN, /* Error from domain config */ + VIR_FROM_XEN_INOTIFY, /* Error from xen inotify layer */ } virErrorDomain; diff --git a/po/POTFILES.in b/po/POTFILES.in index 3f8fdd2..5804be6 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -32,6 +32,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/src/Makefile.am b/src/Makefile.am index 9b9520e..880733b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -93,6 +93,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 \ diff --git a/src/virterror.c b/src/virterror.c index 15eb0a1..1ca821d 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..b388ab1 --- /dev/null +++ b/src/xen_inotify.c @@ -0,0 +1,270 @@ +/* + * 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 <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 "logging.h" + +#include "xm_internal.h" /* for xenXMDomainConfigParse */ + +#define virXenInotifyError(conn, code, fmt...) \ + virReportErrorHelper(NULL, VIR_FROM_XEN_INOTIFY, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + +/* declared in xm_internal.c */ +virHashTablePtr xenXMGetConfigCache(void); +char *xenXMGetConfigDir(void); +int xenXMConfigCacheRefresh (virConnectPtr conn); + +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 +xenInotifyConfigLookupCache(virConnectPtr conn, const char *filename) { + xenXMConfCachePtr entry; + virDomainPtr dom; + virDomainInfo info; + + if (!(entry = virHashLookup(xenXMGetConfigCache(), filename))) { + DEBUG("No config found for %s", filename); + return NULL; + } + + DEBUG0("Getting Domain Def"); + if(!(dom = virGetDomain(conn, entry->def->name, + (unsigned char*)entry->def->uuid))) { + DEBUG0("Error getting dom from def"); + return NULL; + } + + if (virDomainGetInfo(dom, &info) < 0) + dom->id = -1; + else + dom->id = (info.state == VIR_DOMAIN_SHUTOFF) ? -1 : dom->id; + return dom; +} +static void +xenInotifyEvent(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", xenXMGetConfigDir(), name); + + if (e->mask & IN_DELETE) { + if (!(dom = xenInotifyConfigLookupCache(conn, fname))) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("looking up dom")); + continue; + } + + xenUnifiedDomainEventDispatch(conn->privateData, + dom, VIR_DOMAIN_EVENT_REMOVED); + + if (xenXMConfigCacheRefresh (conn) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Error refreshing config cache")); + return; + } + } else if (e->mask & IN_MODIFY) { + /* if we track IN_CREATED we get 2 added events */ + if (xenXMConfigCacheRefresh (conn) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Error refreshing config cache")); + return; + } + + if (!(dom = xenInotifyConfigLookupCache(conn, fname))) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("looking up dom")); + continue; + } + + xenUnifiedDomainEventDispatch(conn->privateData, + dom, VIR_DOMAIN_EVENT_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, + xmlURIPtr uri ATTRIBUTE_UNUSED, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData; + + if ((priv->inotifyFD = inotify_init()) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("initializing inotify")); + return -1; + } + + DEBUG("Adding a watch on %s", xenXMGetConfigDir()); + if (inotify_add_watch(priv->inotifyFD, + xenXMGetConfigDir(), + IN_CREATE | IN_MODIFY | IN_DELETE) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "adding watch on %s", _(xenXMGetConfigDir())); + return -1; + } + + DEBUG0("Refreshing config cache"); + if (xenXMConfigCacheRefresh (conn) < 0) + return -1; + + /* Add the handle for monitoring */ + if (virEventAddHandle(priv->inotifyFD, VIR_EVENT_HANDLE_READABLE, + xenInotifyEvent, conn) < 0) { + virXenInotifyError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to add inotify event handle")); + return -1; + } + + 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; + + virEventRemoveHandle(priv->inotifyFD); + 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..4001384 --- /dev/null +++ b/src/xen_inotify.h @@ -0,0 +1,32 @@ +/* + * 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, + xmlURIPtr uri, + virConnectAuthPtr auth, + int flags); +int xenInotifyClose (virConnectPtr conn); +#endif diff --git a/src/xen_unified.c b/src/xen_unified.c index dd38875..11c5474 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, xmlURIPtr uri, virConnectAuthPtr auth, int f { int i, ret = VIR_DRV_OPEN_DECLINED; xenUnifiedPrivatePtr priv; + virDomainEventCallbackListPtr cbList; /* Refuse any scheme which isn't "xen://" or "http://". */ if (uri->scheme && @@ -249,6 +256,13 @@ xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int f } 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; @@ -279,7 +293,7 @@ xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int f /* XenD is active, so try the xm & xs drivers too, both requird to * succeed if root, optional otherwise */ - if (priv->xendConfigVersion <= 2) { + if (priv->xendConfigVersion <= 2 || WITH_XEN_INOTIFY) { DEBUG0("Trying XM sub-driver"); if (drivers[XEN_UNIFIED_XM_OFFSET]->open(conn, uri, auth, flags) == VIR_DRV_OPEN_SUCCESS) { @@ -317,6 +331,15 @@ xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int f } } +#if WITH_XEN_INOTIFY + DEBUG0("Trying Xen inotify sub-driver"); + if (drivers[XEN_UNIFIED_INOTIFY_OFFSET]->open(conn, uri, auth, flags) == + VIR_DRV_OPEN_SUCCESS) { + DEBUG0("Activated Xen inotify sub-driver"); + priv->opened[XEN_UNIFIED_INOTIFY_OFFSET] = 1; + } +#endif + if (!(priv->caps = xenHypervisorMakeCapabilities(conn))) { DEBUG0("Failed to make capabilities"); goto fail; @@ -346,6 +369,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); @@ -1281,6 +1306,29 @@ xenUnifiedNodeGetFreeMemory (virConnectPtr conn) return(0); } +static int +xenUnifiedDomainEventRegister (virConnectPtr conn, + void *callback, + void *opaque) +{ + GET_PRIVATE (conn); + conn->refs++; + return virDomainEventCallbackListAdd(conn, priv->domainEventCallbacks, + callback, opaque); +} + +static int +xenUnifiedDomainEventDeregister (virConnectPtr conn, + void *callback) +{ + int ret; + GET_PRIVATE (conn); + 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 + \ @@ -1346,6 +1394,8 @@ static virDriver xenUnifiedDriver = { .domainBlockPeek = xenUnifiedDomainBlockPeek, .nodeGetCellsFreeMemory = xenUnifiedNodeGetCellsFreeMemory, .getFreeMemory = xenUnifiedNodeGetFreeMemory, + .domainEventRegister = xenUnifiedDomainEventRegister, + .domainEventDeregister = xenUnifiedDomainEventDeregister, }; /** @@ -1365,3 +1415,141 @@ xenUnifiedRegister (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, + virDomainEventType evt) +{ + 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, evt); + cbList->callbacks[i]->cb(cbList->callbacks[i]->conn, + dom, evt, + cbList->callbacks[i]->opaque); + } + } + } +} diff --git a/src/xen_unified.h b/src/xen_unified.h index 48415a8..d88c281 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 xenUnifiedRegister (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,20 @@ struct _xenUnifiedPrivate { * xen_unified.c. */ int opened[XEN_UNIFIED_NR_DRIVERS]; + + /* A list of xenstore watches */ + xenStoreWatchListPtr xsWatchList; + + /* A list of active domain name/uuids */ + xenUnifiedDomainInfoListPtr activeDomainList; + + /* An list of callbacks */ + virDomainEventCallbackListPtr domainEventCallbacks; + +#if WITH_XEN_INOTIFY + /* The inotify fd */ + int inotifyFD; +#endif }; typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; @@ -122,4 +175,12 @@ 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, + virDomainEventType evt); #endif /* __VIR_XEN_UNIFIED_H__ */ diff --git a/src/xm_internal.c b/src/xm_internal.c index c298888..eff892b 100644 --- a/src/xm_internal.c +++ b/src/xm_internal.c @@ -45,6 +45,7 @@ #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 +55,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; @@ -72,6 +66,9 @@ static time_t lastRefresh = 0; char * xenXMAutoAssignMac(void); static int xenXMDomainAttachDevice(virDomainPtr domain, const char *xml); static int xenXMDomainDetachDevice(virDomainPtr domain, const char *xml); +virHashTablePtr xenXMGetConfigCache (void); +char *xenXMGetConfigDir (void); +int xenXMConfigCacheRefresh (virConnectPtr conn); #define XM_REFRESH_INTERVAL 10 @@ -124,6 +121,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) @@ -377,7 +382,7 @@ xenXMConfigSaveFile(virConnectPtr conn, const char *filename, virDomainDefPtr de 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); @@ -445,6 +450,8 @@ static int xenXMConfigCacheRefresh (virConnectPtr conn) { continue; } + DEBUG("Checking for %s", path); + /* If we already have a matching entry and it is not modified, then carry on to next one*/ if ((entry = virHashLookup(configCache, path))) { @@ -466,6 +473,7 @@ static int xenXMConfigCacheRefresh (virConnectPtr conn) { virDomainDefFree(entry->def); entry->def = NULL; } else { /* Completely new entry */ + DEBUG("Adding new entry: %s", path); newborn = 1; if (VIR_ALLOC(entry) < 0) { xenXMError (conn, VIR_ERR_NO_MEMORY, "%s", strerror(errno)); diff --git a/src/xs_internal.c b/src/xs_internal.c index b096acd..962205b 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 */ @@ -303,7 +306,55 @@ xenStoreOpen(virConnectPtr conn, } return (-1); } - return (0); + +#ifndef PROXY + /* Init activeDomainList */ + if ( VIR_ALLOC(priv->activeDomainList ) < 0) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to allocate virXenInotifyError")); + 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 (virEventAddHandle(xs_fileno(priv->xshandle), + VIR_EVENT_HANDLE_READABLE, + xenStoreWatchEvent, + conn) < 0) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to handle")); + + return -1; + } + +#endif //PROXY + return 0; } /** @@ -317,6 +368,7 @@ xenStoreOpen(virConnectPtr conn, int xenStoreClose(virConnectPtr conn) { + int fd; xenUnifiedPrivatePtr priv; if (conn == NULL) { @@ -325,10 +377,30 @@ 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(priv->activeDomainList); +#endif if (priv->xshandle == NULL) return(-1); + fd = xs_fileno(priv->xshandle); + virEventRemoveHandle(fd); + close(fd); xs_daemon_close(priv->xshandle); + priv->xshandle = NULL; + return (0); } @@ -921,3 +993,359 @@ char *xenStoreDomainGetName(virConnectPtr conn, return xs_read(priv->xshandle, 0, prop, &len); } +#ifndef PROXY +char *xenStoreDomainGetUUID(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 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(!priv->activeDomainList->count) { + for (i=0 ; i < new_domain_cnt ; i++) { + if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { + continue; + } + if (!(uuid = xenStoreDomainGetUUID(conn, new_domids[i]))) { + VIR_FREE(name); + continue; + } + xenUnifiedAddDomainInfo(priv->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 = xenStoreDomainGetUUID(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 < priv->activeDomainList->count ; j++) { + if( !STREQ(name, priv->activeDomainList->doms[j]->name) || + !STREQ(uuid, priv->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); + + /* Add to the list */ + xenUnifiedAddDomainInfo(priv->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(!priv->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 < priv->activeDomainList->count ; j++) { + found = 0; + for (i=0 ; i < new_domain_cnt ; i++) { + if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) { + continue; + } + + if (!(uuid = xenStoreDomainGetUUID(conn, new_domids[i]))) { + VIR_FREE(name); + continue; + } + + if( STREQ(name, priv->activeDomainList->doms[j]->name) || + STREQ(uuid, priv->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, + priv->activeDomainList->doms[j]->name, + (unsigned char*)priv->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); + /* Remove from the list */ + xenUnifiedRemoveDomainInfo(priv->activeDomainList, + priv->activeDomainList->doms[j]->id, + priv->activeDomainList->doms[j]->name, + priv->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 6163ebc..006e0e1 100644 --- a/src/xs_internal.h +++ b/src/xs_internal.h @@ -52,5 +52,56 @@ char * xenStoreDomainGetDiskID(virConnectPtr conn, const char *dev); char * xenStoreDomainGetName(virConnectPtr conn, int id); +char * xenStoreDomainGetUUID(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 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