[libvirt] [PATCH] Xen Events Updated

Ben Guthro bguthro at virtualiron.com
Mon Nov 24 16:29:54 UTC 2008


All of these changes look good to me

+1 on this version

Daniel P. Berrange wrote on 11/24/2008 10:44 AM:
> On Fri, Nov 21, 2008 at 02:11:39PM -0500, Ben Guthro wrote:
>> 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?
> 
> It didn't break anything, but I found more edge cases & a couple of bugs
> and optimizations.
> 
> Changes in this version
> 
>  - We now also monitor IN_MOVED_TO and IN_MOVED_FROM, so that we catch
>    'mv foo /etc/xen' and 'mv /etc/xen/foo /tmp' as define & undefine
>    events
>  - On the @introduced watch handler we'll retry if we don't find the
>    UUID / name first time around, to deal with inevitable race while
>    XenD populates this info in xenstore.
>  - In the  @introduced & @removed handlers, simply to just check the
>    domain ID, not name/uuid, since running VMs are unique on ID.
>  - Fix UUID stored to be the raw unsigned char, not NULL terminated
>    string form, so we're passing correct data into virGetDomain.
> 
> This is now working reliably with my RHEL-5  Xen for all events, and
> should be a little more resilent on new Xen too.
> 
>  b/src/xen_inotify.c                          |  458 +++++++++++++++++++++++++++
>  b/src/xen_inotify.h                          |   31 +
>  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_unified.c                            |  197 +++++++++++
>  src/xen_unified.h                            |   63 +++
>  src/xm_internal.c                            |  198 +++++++----
>  src/xm_internal.h                            |    6 
>  src/xs_internal.c                            |  419 ++++++++++++++++++++++++
>  src/xs_internal.h                            |   52 +++
>  16 files changed, 1383 insertions(+), 73 deletions(-)
> 
> 
> Daniel
> 
> diff -r f10add69a53c configure.in
> --- a/configure.in	Mon Nov 24 09:08:00 2008 -0500
> +++ b/configure.in	Mon Nov 24 09:08:01 2008 -0500
> @@ -147,6 +147,8 @@ dnl Allow to build without Xen, QEMU/KVM
>  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],
> @@ -333,6 +335,17 @@ AM_CONDITIONAL([WITH_XEN], [test "$with_
>  AM_CONDITIONAL([WITH_XEN], [test "$with_xen" = "yes"])
>  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
> diff -r f10add69a53c examples/domain-events/events-c/event-test.c
> --- a/examples/domain-events/events-c/event-test.c	Mon Nov 24 09:08:00 2008 -0500
> +++ b/examples/domain-events/events-c/event-test.c	Mon Nov 24 09:08:01 2008 -0500
> @@ -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 -r f10add69a53c include/libvirt/virterror.h
> --- a/include/libvirt/virterror.h	Mon Nov 24 09:08:00 2008 -0500
> +++ b/include/libvirt/virterror.h	Mon Nov 24 09:08:01 2008 -0500
> @@ -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 -r f10add69a53c po/POTFILES.in
> --- a/po/POTFILES.in	Mon Nov 24 09:08:00 2008 -0500
> +++ b/po/POTFILES.in	Mon Nov 24 09:08:01 2008 -0500
> @@ -35,6 +35,7 @@ src/uuid.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 -r f10add69a53c python/libvir.c
> --- a/python/libvir.c	Mon Nov 24 09:08:00 2008 -0500
> +++ b/python/libvir.c	Mon Nov 24 09:08:01 2008 -0500
> @@ -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 -r f10add69a53c src/Makefile.am
> --- a/src/Makefile.am	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/Makefile.am	Mon Nov 24 09:08:01 2008 -0500
> @@ -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				\
> @@ -188,6 +191,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
> diff -r f10add69a53c src/util.c
> --- a/src/util.c	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/util.c	Mon Nov 24 09:08:01 2008 -0500
> @@ -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 -r f10add69a53c src/virterror.c
> --- a/src/virterror.c	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/virterror.c	Mon Nov 24 09:08:01 2008 -0500
> @@ -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 -r f10add69a53c src/xen_inotify.c
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/src/xen_inotify.c	Mon Nov 24 10:29:13 2008 -0500
> @@ -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 -r f10add69a53c src/xen_inotify.h
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/src/xen_inotify.h	Mon Nov 24 09:08:01 2008 -0500
> @@ -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 -r f10add69a53c src/xen_unified.c
> --- a/src/xen_unified.c	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/xen_unified.c	Mon Nov 24 09:46:55 2008 -0500
> @@ -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_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, virC
>  {
>      int i, ret = VIR_DRV_OPEN_DECLINED;
>      xenUnifiedPrivatePtr priv;
> +    virDomainEventCallbackListPtr cbList;
>  
>      if (conn->uri == NULL) {
>          if (!xenUnifiedProbe())
> @@ -259,6 +266,13 @@ xenUnifiedOpen (virConnectPtr conn, virC
>          return VIR_DRV_OPEN_ERROR;
>      }
>      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;
> @@ -333,6 +347,15 @@ xenUnifiedOpen (virConnectPtr conn, virC
>          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);
> @@ -1290,6 +1315,40 @@ xenUnifiedNodeGetFreeMemory (virConnectP
>  
>      xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
>      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. -----*/
> @@ -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 -r f10add69a53c src/xen_unified.h
> --- a/src/xen_unified.h	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/xen_unified.h	Mon Nov 24 10:18:38 2008 -0500
> @@ -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 xenNbCpus(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 -r f10add69a53c src/xm_internal.c
> --- a/src/xm_internal.c	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/xm_internal.c	Mon Nov 24 09:08:01 2008 -0500
> @@ -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
> @@ -53,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 */
> @@ -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, 
>      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 (virC
>      }
>  
>      while ((ent = readdir(dh))) {
> -        xenXMConfCachePtr entry;
>          struct stat st;
> -        int newborn = 0;
>          char path[PATH_MAX];
>  
>          /*
> @@ -447,62 +556,8 @@ static int xenXMConfigCacheRefresh (virC
>  
>          /* 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 (virC
>      virHashRemoveSet(configCache, xenXMConfigReaper, xenXMConfigFree, (const void*) &now);
>      ret = 0;
>  
> - cleanup:
>      if (dh)
>          closedir(dh);
>  
> @@ -1503,8 +1557,10 @@ virDomainPtr xenXMDomainLookupByName(vir
>          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(vir
>          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(virCon
>      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(virConnectPt
>          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(virConnectP
>          return (-1);
>      }
>  
> +#ifndef WITH_XEN_INOTIFY
>      if (xenXMConfigCacheRefresh (conn) < 0)
>          return (-1);
> +#endif
>  
>      return virHashSize(nameConfigMap);
>  }
> diff -r f10add69a53c src/xm_internal.h
> --- a/src/xm_internal.h	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/xm_internal.h	Mon Nov 24 09:08:01 2008 -0500
> @@ -32,6 +32,12 @@ extern struct xenUnifiedDriver xenXMDriv
>  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 -r f10add69a53c src/xs_internal.c
> --- a/src/xs_internal.c	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/xs_internal.c	Mon Nov 24 10:19:11 2008 -0500
> @@ -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 (priv->xshandle == NULL)
> -        return(-1);
> -
> +
> +    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(virConnectPt
>      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 -r f10add69a53c src/xs_internal.h
> --- a/src/xs_internal.h	Mon Nov 24 09:08:00 2008 -0500
> +++ b/src/xs_internal.h	Mon Nov 24 10:18:59 2008 -0500
> @@ -51,5 +51,57 @@ char *		xenStoreDomainGetDiskID(virConne
>                                           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__ */
> 
> --
> |: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
> |: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
> |: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
> |: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list