New patch attached with changes recommended in prior email from Daniel V. Ben
diff --git a/src/xen_unified.c b/src/xen_unified.c index 1ab204d..c5f84c8 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -222,6 +222,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 && @@ -248,6 +249,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; @@ -345,6 +353,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); @@ -1280,6 +1290,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 + \ @@ -1345,6 +1378,8 @@ static virDriver xenUnifiedDriver = { .domainBlockPeek = xenUnifiedDomainBlockPeek, .nodeGetCellsFreeMemory = xenUnifiedNodeGetCellsFreeMemory, .getFreeMemory = xenUnifiedNodeGetFreeMemory, + .domainEventRegister = xenUnifiedDomainEventRegister, + .domainEventDeregister = xenUnifiedDomainEventDeregister, }; /** @@ -1364,3 +1399,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..e0e5243 100644 --- a/src/xen_unified.h +++ b/src/xen_unified.h @@ -14,6 +14,8 @@ #include "internal.h" #include "capabilities.h" #include "driver.h" +#include "xs_internal.h" +#include "domain_event.h" #ifndef HAVE_WINSOCK2_H #include <sys/un.h> @@ -85,6 +87,26 @@ struct xenUnifiedDriver { virDrvDomainSetSchedulerParameters domainSetSchedulerParameters; }; +/* 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 +135,15 @@ struct _xenUnifiedPrivate { * xen_unified.c. */ int opened[XEN_UNIFIED_NR_DRIVERS]; + + /* A list of xenstore watches */ + xenStoreWatchListPtr xsWatchList; + + /* An list of active domain name/uuids */ + xenUnifiedDomainInfoListPtr domInfoList; + + /* An list of callbacks */ + virDomainEventCallbackListPtr domainEventCallbacks; }; typedef struct _xenUnifiedPrivate *xenUnifiedPrivatePtr; @@ -122,4 +153,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/xs_internal.c b/src/xs_internal.c index b096acd..68fda46 100644 --- a/src/xs_internal.c +++ b/src/xs_internal.c @@ -29,6 +29,8 @@ #include "virterror_internal.h" #include "datatypes.h" #include "driver.h" +#include "memory.h" +#include "event.h" #include "xen_unified.h" #include "xs_internal.h" #include "xen_internal.h" /* for xenHypervisorCheckID */ @@ -303,7 +305,53 @@ xenStoreOpen(virConnectPtr conn, } return (-1); } - return (0); + +#ifndef PROXY + /* Init domInfoList */ + if ( VIR_ALLOC(priv->domInfoList ) < 0) { + virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to allocate domInfoList")); + 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 ) + { + DEBUG0("Error Adding Watch @releaseDomain\n"); + return -1; + } + + /* The initial call of this will fill domInfoList */ + if( xenStoreAddWatch(conn, "@introduceDomain", + "introduceDomain", xenStoreDomainIntroduced, priv) < 0 ) + { + DEBUG0("Error Adding Watch @introduceDomain\n"); + 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 +365,7 @@ xenStoreOpen(virConnectPtr conn, int xenStoreClose(virConnectPtr conn) { + int fd; xenUnifiedPrivatePtr priv; if (conn == NULL) { @@ -325,10 +374,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->domInfoList); +#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 +990,357 @@ 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)) { + DEBUG0("WARNING: 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) { + DEBUG0("Error 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->domInfoList->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->domInfoList, 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->domInfoList->count ; j++) { + if( !STREQ(name, priv->domInfoList->doms[j]->name) || + !STREQ(uuid, priv->domInfoList->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->domInfoList, + 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->domInfoList->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->domInfoList->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->domInfoList->doms[j]->name) || + STREQ(uuid, priv->domInfoList->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->domInfoList->doms[j]->name, + (unsigned char*)priv->domInfoList->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->domInfoList, + priv->domInfoList->doms[j]->id, + priv->domInfoList->doms[j]->name, + priv->domInfoList->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