/* * domain_event.c: domain event queue processing helpers * * Copyright (C) 2010-2013 Red Hat, Inc. * 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, see * . * * Author: Ben Guthro */ #include #include "domain_event.h" #include "virlog.h" #include "datatypes.h" #include "viralloc.h" #include "virerror.h" #include "virstring.h" #define VIR_FROM_THIS VIR_FROM_NONE #define VIR_OBJECT_EVENT_CALLBACK(cb) ((virConnectObjectEventGenericCallback)(cb)) struct _virObjectMeta { int id; char *name; unsigned char uuid[VIR_UUID_BUFLEN]; }; typedef struct _virObjectMeta virObjectMeta; typedef virObjectMeta *virObjectMetaPtr; typedef struct _virObjectEventQueue virObjectEventQueue; typedef virObjectEventQueue *virObjectEventQueuePtr; struct _virObjectEventCallbackList { unsigned int nextID; unsigned int count; virObjectEventCallbackPtr *callbacks; }; typedef struct _virObjectEventCallbackList virObjectEventCallbackList; typedef virObjectEventCallbackList *virObjectEventCallbackListPtr; struct _virObjectEventQueue { unsigned int count; virDomainEventPtr *events; }; struct _virObjectEventState { /* The list of domain event callbacks */ virObjectEventCallbackListPtr callbacks; /* The queue of object events */ virObjectEventQueuePtr queue; /* Timer for flushing events queue */ int timer; /* Flag if we're in process of dispatching */ bool isDispatching; virMutex lock; }; struct _virObjectEventCallback { int callbackID; int eventID; virConnectPtr conn; virObjectMetaPtr meta; virConnectObjectEventGenericCallback cb; void *opaque; virFreeCallback freecb; int deleted; }; static virClassPtr virObjectEventClass; static virClassPtr virDomainEventClass; static virClassPtr virDomainEventLifecycleClass; static virClassPtr virDomainEventRTCChangeClass; static virClassPtr virDomainEventWatchdogClass; static virClassPtr virDomainEventIOErrorClass; static virClassPtr virDomainEventGraphicsClass; static virClassPtr virDomainEventBlockJobClass; static virClassPtr virDomainEventDiskChangeClass; static virClassPtr virDomainEventTrayChangeClass; static void virObjectEventDispose(void *obj); static void virDomainEventDispose(void *obj); static void virDomainEventLifecycleDispose(void *obj); static void virDomainEventRTCChangeDispose(void *obj); static void virDomainEventWatchdogDispose(void *obj); static void virDomainEventIOErrorDispose(void *obj); static void virDomainEventGraphicsDispose(void *obj); static void virDomainEventBlockJobDispose(void *obj); static void virDomainEventDiskChangeDispose(void *obj); static void virDomainEventTrayChangeDispose(void *obj); struct _virObjectEvent { virObject parent; int eventID; }; struct _virDomainEvent { virObjectEvent parent; virObjectMeta meta; union { struct { /* In unit of 1024 bytes */ unsigned long long actual; } balloonChange; struct { char *devAlias; } deviceRemoved; } data; }; struct _virDomainEventLifecycle { virDomainEvent parent; int type; int detail; }; typedef struct _virDomainEventLifecycle virDomainEventLifecycle; typedef virDomainEventLifecycle *virDomainEventLifecyclePtr; struct _virDomainEventRTCChange { virDomainEvent parent; long long offset; }; typedef struct _virDomainEventRTCChange virDomainEventRTCChange; typedef virDomainEventRTCChange *virDomainEventRTCChangePtr; struct _virDomainEventWatchdog { virDomainEvent parent; int action; }; typedef struct _virDomainEventWatchdog virDomainEventWatchdog; typedef virDomainEventWatchdog *virDomainEventWatchdogPtr; struct _virDomainEventIOError { virDomainEvent parent; char *srcPath; char *devAlias; int action; char *reason; }; typedef struct _virDomainEventIOError virDomainEventIOError; typedef virDomainEventIOError *virDomainEventIOErrorPtr; struct _virDomainEventBlockJob { virDomainEvent parent; char *path; int type; int status; }; typedef struct _virDomainEventBlockJob virDomainEventBlockJob; typedef virDomainEventBlockJob *virDomainEventBlockJobPtr; struct _virDomainEventGraphics { virDomainEvent parent; int phase; virDomainEventGraphicsAddressPtr local; virDomainEventGraphicsAddressPtr remote; char *authScheme; virDomainEventGraphicsSubjectPtr subject; }; typedef struct _virDomainEventGraphics virDomainEventGraphics; typedef virDomainEventGraphics *virDomainEventGraphicsPtr; struct _virDomainEventDiskChange { virDomainEvent parent; char *oldSrcPath; char *newSrcPath; char *devAlias; int reason; }; typedef struct _virDomainEventDiskChange virDomainEventDiskChange; typedef virDomainEventDiskChange *virDomainEventDiskChangePtr; struct _virDomainEventTrayChange { virDomainEvent parent; char *devAlias; int reason; }; typedef struct _virDomainEventTrayChange virDomainEventTrayChange; typedef virDomainEventTrayChange *virDomainEventTrayChangePtr; static int virObjectEventOnceInit(void) { if (!(virObjectEventClass = virClassNew(virClassForObject(), "virObjectEvent", sizeof(virObjectEvent), virObjectEventDispose))) return -1; if (!(virDomainEventClass = virClassNew(virObjectEventClass, "virDomainEvent", sizeof(virDomainEvent), virDomainEventDispose))) return -1; if (!(virDomainEventLifecycleClass = virClassNew(virDomainEventClass, "virDomainEventLifecycle", sizeof(virDomainEventLifecycle), virDomainEventLifecycleDispose))) return -1; if (!(virDomainEventRTCChangeClass = virClassNew(virDomainEventClass, "virDomainEventRTCChange", sizeof(virDomainEventRTCChange), virDomainEventRTCChangeDispose))) return -1; if (!(virDomainEventWatchdogClass = virClassNew(virDomainEventClass, "virDomainEventWatchdog", sizeof(virDomainEventWatchdog), virDomainEventWatchdogDispose))) return -1; if (!(virDomainEventIOErrorClass = virClassNew(virDomainEventClass, "virDomainEventIOError", sizeof(virDomainEventIOError), virDomainEventIOErrorDispose))) return -1; if (!(virDomainEventGraphicsClass = virClassNew(virDomainEventClass, "virDomainEventGraphics", sizeof(virDomainEventGraphics), virDomainEventGraphicsDispose))) return -1; if (!(virDomainEventBlockJobClass = virClassNew(virDomainEventClass, "virDomainEventBlockJob", sizeof(virDomainEventBlockJob), virDomainEventBlockJobDispose))) return -1; if (!(virDomainEventDiskChangeClass = virClassNew(virDomainEventClass, "virDomainEventDiskChange", sizeof(virDomainEventDiskChange), virDomainEventDiskChangeDispose))) return -1; if (!(virDomainEventTrayChangeClass = virClassNew(virDomainEventClass, "virDomainEventTrayChange", sizeof(virDomainEventTrayChange), virDomainEventTrayChangeDispose))) return -1; return 0; } VIR_ONCE_GLOBAL_INIT(virObjectEvent) static int virObjectEventGetEventID(void *anyobj) { virObjectEventPtr obj = anyobj; if (!virObjectIsClass(obj, virObjectEventClass)) { VIR_WARN("Object %p (%s) is not a virObjectEvent instance", obj, obj ? virClassName(obj->parent.klass) : "(unknown)"); return -1; } return obj->eventID; } static void virObjectEventDispose(void *obj) { virObjectEventPtr event = obj; VIR_DEBUG("obj=%p", event); } static void virDomainEventDispose(void *obj) { virDomainEventPtr event = obj; VIR_DEBUG("obj=%p", event); switch (virObjectEventGetEventID(event)) { case VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED: VIR_FREE(event->data.deviceRemoved.devAlias); break; } VIR_FREE(event->meta.name); } static void virDomainEventLifecycleDispose(void *obj) { virDomainEventLifecyclePtr event = obj; VIR_DEBUG("obj=%p", event); } static void virDomainEventRTCChangeDispose(void *obj) { virDomainEventRTCChangePtr event = obj; VIR_DEBUG("obj=%p", event); } static void virDomainEventWatchdogDispose(void *obj) { virDomainEventWatchdogPtr event = obj; VIR_DEBUG("obj=%p", event); } static void virDomainEventIOErrorDispose(void *obj) { virDomainEventIOErrorPtr event = obj; VIR_DEBUG("obj=%p", event); VIR_FREE(event->srcPath); VIR_FREE(event->devAlias); VIR_FREE(event->reason); } static void virDomainEventGraphicsDispose(void *obj) { virDomainEventGraphicsPtr event = obj; VIR_DEBUG("obj=%p", event); if (event->local) { VIR_FREE(event->local->node); VIR_FREE(event->local->service); VIR_FREE(event->local); } if (event->remote) { VIR_FREE(event->remote->node); VIR_FREE(event->remote->service); VIR_FREE(event->remote); } VIR_FREE(event->authScheme); if (event->subject) { size_t i; for (i = 0; i < event->subject->nidentity; i++) { VIR_FREE(event->subject->identities[i].type); VIR_FREE(event->subject->identities[i].name); } VIR_FREE(event->subject); } } static void virDomainEventBlockJobDispose(void *obj) { virDomainEventBlockJobPtr event = obj; VIR_DEBUG("obj=%p", event); VIR_FREE(event->path); } static void virDomainEventDiskChangeDispose(void *obj) { virDomainEventDiskChangePtr event = obj; VIR_DEBUG("obj=%p", event); VIR_FREE(event->oldSrcPath); VIR_FREE(event->newSrcPath); VIR_FREE(event->devAlias); } static void virDomainEventTrayChangeDispose(void *obj) { virDomainEventTrayChangePtr event = obj; VIR_DEBUG("obj=%p", event); VIR_FREE(event->devAlias); } /** * virObjectEventCallbackListFree: * @list: event callback list head * * Free the memory in the domain event callback list */ static void virObjectEventCallbackListFree(virObjectEventCallbackListPtr list) { size_t i; if (!list) return; for (i=0; icount; i++) { virFreeCallback freecb = list->callbacks[i]->freecb; if (freecb) (*freecb)(list->callbacks[i]->opaque); VIR_FREE(list->callbacks[i]); } VIR_FREE(list->callbacks); VIR_FREE(list); } /** * virDomainEventCallbackListRemove: * @conn: pointer to the connection * @cbList: the list * @callback: the callback to remove * * Internal function to remove a callback from a virObjectEventCallbackListPtr */ static int virDomainEventCallbackListRemove(virConnectPtr conn, virObjectEventCallbackListPtr cbList, virConnectDomainEventCallback callback) { int ret = 0; size_t i; for (i = 0; i < cbList->count; i++) { if (cbList->callbacks[i]->cb == VIR_OBJECT_EVENT_CALLBACK(callback) && cbList->callbacks[i]->eventID == VIR_DOMAIN_EVENT_ID_LIFECYCLE && cbList->callbacks[i]->conn == conn) { virFreeCallback freecb = cbList->callbacks[i]->freecb; if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virObjectUnref(cbList->callbacks[i]->conn); VIR_FREE(cbList->callbacks[i]); if (i < (cbList->count - 1)) memmove(cbList->callbacks + i, cbList->callbacks + i + 1, sizeof(*(cbList->callbacks)) * (cbList->count - (i + 1))); if (VIR_REALLOC_N(cbList->callbacks, cbList->count - 1) < 0) { ; /* Failure to reduce memory allocation isn't fatal */ } cbList->count--; for (i = 0; i < cbList->count; i++) { if (!cbList->callbacks[i]->deleted) ret++; } return ret; } } virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("could not find event callback for removal")); return -1; } /** * virObjectEventCallbackListRemoveID: * @conn: pointer to the connection * @cbList: the list * @callback: the callback to remove * * Internal function to remove a callback from a virObjectEventCallbackListPtr */ static int virObjectEventCallbackListRemoveID(virConnectPtr conn, virObjectEventCallbackListPtr cbList, int callbackID) { int ret = 0; size_t i; for (i = 0; i < cbList->count; i++) { if (cbList->callbacks[i]->callbackID == callbackID && cbList->callbacks[i]->conn == conn) { virFreeCallback freecb = cbList->callbacks[i]->freecb; if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virObjectUnref(cbList->callbacks[i]->conn); VIR_FREE(cbList->callbacks[i]); if (i < (cbList->count - 1)) memmove(cbList->callbacks + i, cbList->callbacks + i + 1, sizeof(*(cbList->callbacks)) * (cbList->count - (i + 1))); if (VIR_REALLOC_N(cbList->callbacks, cbList->count - 1) < 0) { ; /* Failure to reduce memory allocation isn't fatal */ } cbList->count--; for (i = 0; i < cbList->count; i++) { if (!cbList->callbacks[i]->deleted) ret++; } return ret; } } virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("could not find event callback for removal")); return -1; } static int virDomainEventCallbackListMarkDelete(virConnectPtr conn, virObjectEventCallbackListPtr cbList, virConnectDomainEventCallback callback) { int ret = 0; size_t i; for (i = 0; i < cbList->count; i++) { if (cbList->callbacks[i]->cb == VIR_OBJECT_EVENT_CALLBACK(callback) && cbList->callbacks[i]->eventID == VIR_DOMAIN_EVENT_ID_LIFECYCLE && cbList->callbacks[i]->conn == conn) { cbList->callbacks[i]->deleted = 1; for (i = 0; i < cbList->count; i++) { if (!cbList->callbacks[i]->deleted) ret++; } return ret; } } virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("could not find event callback for deletion")); return -1; } static int virObjectEventCallbackListMarkDeleteID(virConnectPtr conn, virObjectEventCallbackListPtr cbList, int callbackID) { int ret = 0; size_t i; for (i = 0; i < cbList->count; i++) { if (cbList->callbacks[i]->callbackID == callbackID && cbList->callbacks[i]->conn == conn) { cbList->callbacks[i]->deleted = 1; for (i = 0; i < cbList->count; i++) { if (!cbList->callbacks[i]->deleted) ret++; } return ret; } } virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("could not find event callback for deletion")); return -1; } static int virObjectEventCallbackListPurgeMarked(virObjectEventCallbackListPtr cbList) { int old_count = cbList->count; int n; for (n = 0; n < cbList->count; n++) { if (cbList->callbacks[n]->deleted) { virFreeCallback freecb = cbList->callbacks[n]->freecb; if (freecb) (*freecb)(cbList->callbacks[n]->opaque); virObjectUnref(cbList->callbacks[n]->conn); VIR_FREE(cbList->callbacks[n]); if (n < (cbList->count - 1)) memmove(cbList->callbacks + n, cbList->callbacks + n + 1, sizeof(*(cbList->callbacks)) * (cbList->count - (n + 1))); cbList->count--; n--; } } if (cbList->count < old_count && VIR_REALLOC_N(cbList->callbacks, cbList->count) < 0) { ; /* Failure to reduce memory allocation isn't fatal */ } return 0; } /** * virObjectEventCallbackListAddID: * @conn: pointer to the connection * @cbList: the list * @uuid: the uuid of the object to filter on * @name: the name of the object to filter on * @id: the ID of the object to filter on * @eventID: the event ID * @callback: the callback to add * @opaque: opaque data tio pass to callback * @callbackID: filled with callback ID * * Internal function to add a callback from a virObjectEventCallbackListPtr */ static int virObjectEventCallbackListAddID(virConnectPtr conn, virObjectEventCallbackListPtr cbList, unsigned char uuid[VIR_UUID_BUFLEN], const char *name, int id, int eventID, virConnectObjectEventGenericCallback callback, void *opaque, virFreeCallback freecb, int *callbackID) { virObjectEventCallbackPtr event; size_t i; int ret = 0; /* Check incoming */ if (!cbList) { return -1; } /* check if we already have this callback on our list */ for (i = 0; i < cbList->count; i++) { if (cbList->callbacks[i]->cb == VIR_OBJECT_EVENT_CALLBACK(callback) && cbList->callbacks[i]->eventID == eventID && cbList->callbacks[i]->conn == conn && ((uuid && cbList->callbacks[i]->meta && memcmp(cbList->callbacks[i]->meta->uuid, uuid, VIR_UUID_BUFLEN) == 0) || (!uuid && !cbList->callbacks[i]->meta))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("event callback already tracked")); return -1; } } /* Allocate new event */ if (VIR_ALLOC(event) < 0) goto error; event->conn = conn; event->cb = callback; event->eventID = eventID; event->opaque = opaque; event->freecb = freecb; if (name && uuid && id > 0) { if (VIR_ALLOC(event->meta) < 0) goto error; if (VIR_STRDUP(event->meta->name, name) < 0) goto error; memcpy(event->meta->uuid, uuid, VIR_UUID_BUFLEN); event->meta->id = id; } /* Make space on list */ if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) goto error; virObjectRef(event->conn); cbList->callbacks[cbList->count] = event; cbList->count++; event->callbackID = cbList->nextID++; for (i = 0; i < cbList->count; i++) { if (cbList->callbacks[i]->eventID == eventID && cbList->callbacks[i]->conn == conn && !cbList->callbacks[i]->deleted) ret++; } if (callbackID) *callbackID = event->callbackID; return ret; error: if (event) { if (event->meta) VIR_FREE(event->meta->name); VIR_FREE(event->meta); } VIR_FREE(event); return -1; } /** * virDomainEventCallbackListAdd: * @conn: pointer to the connection * @cbList: the list * @callback: the callback to add * @opaque: opaque data tio pass to callback * * Internal function to add a callback from a virObjectEventCallbackListPtr */ static int virDomainEventCallbackListAdd(virConnectPtr conn, virObjectEventCallbackListPtr cbList, virConnectDomainEventCallback callback, void *opaque, virFreeCallback freecb) { return virObjectEventCallbackListAddID(conn, cbList, NULL, NULL, 0, VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_OBJECT_EVENT_CALLBACK(callback), opaque, freecb, NULL); } static int virObjectEventCallbackListEventID(virConnectPtr conn, virObjectEventCallbackListPtr cbList, int callbackID) { size_t i; for (i = 0; i < cbList->count; i++) { if (cbList->callbacks[i]->deleted) continue; if (cbList->callbacks[i]->callbackID == callbackID && cbList->callbacks[i]->conn == conn) return cbList->callbacks[i]->eventID; } return -1; } /** * virObjectEventQueueClear: * @queue: pointer to the queue * * Removes all elements from the queue */ static void virObjectEventQueueClear(virObjectEventQueuePtr queue) { size_t i; if (!queue) return; for (i = 0; i < queue->count; i++) { virObjectUnref(queue->events[i]); } VIR_FREE(queue->events); queue->count = 0; } /** * virObjectEventQueueFree: * @queue: pointer to the queue * * Free the memory in the queue. We process this like a list here */ static void virObjectEventQueueFree(virObjectEventQueuePtr queue) { if (!queue) return; virObjectEventQueueClear(queue); VIR_FREE(queue); } static virObjectEventQueuePtr virObjectEventQueueNew(void) { virObjectEventQueuePtr ret; ignore_value(VIR_ALLOC(ret)); return ret; } static void virObjectEventStateLock(virObjectEventStatePtr state) { virMutexLock(&state->lock); } static void virObjectEventStateUnlock(virObjectEventStatePtr state) { virMutexUnlock(&state->lock); } /** * virObjectEventStateFree: * @list: virObjectEventStatePtr to free * * Free a virObjectEventStatePtr and its members, and unregister the timer. */ void virObjectEventStateFree(virObjectEventStatePtr state) { if (!state) return; virObjectEventCallbackListFree(state->callbacks); virObjectEventQueueFree(state->queue); if (state->timer != -1) virEventRemoveTimeout(state->timer); virMutexDestroy(&state->lock); VIR_FREE(state); } static void virObjectEventStateFlush(virObjectEventStatePtr state); static void virDomainEventTimer(int timer ATTRIBUTE_UNUSED, void *opaque) { virObjectEventStatePtr state = opaque; virObjectEventStateFlush(state); } /** * virObjectEventStateNew: */ virObjectEventStatePtr virObjectEventStateNew(void) { virObjectEventStatePtr state = NULL; if (VIR_ALLOC(state) < 0) goto error; if (virMutexInit(&state->lock) < 0) { virReportSystemError(errno, "%s", _("unable to initialize state mutex")); VIR_FREE(state); goto error; } if (VIR_ALLOC(state->callbacks) < 0) goto error; if (!(state->queue = virObjectEventQueueNew())) goto error; state->timer = -1; return state; error: virObjectEventStateFree(state); return NULL; } static void *virObjectEventNew(virClassPtr klass, int eventID) { virObjectEventPtr event; if (virObjectEventInitialize() < 0) return NULL; if (!virClassIsDerivedFrom(klass, virObjectEventClass)) { virReportInvalidArg(klass, _("Class %s must derive from virObjectEvent"), virClassName(klass)); return NULL; } if (!(event = virObjectNew(klass))) return NULL; event->eventID = eventID; VIR_DEBUG("obj=%p", event); return event; } static void *virDomainEventNew(virClassPtr klass, int eventID, int id, const char *name, const unsigned char *uuid) { virDomainEventPtr event; if (virObjectEventInitialize() < 0) return NULL; if (!virClassIsDerivedFrom(klass, virDomainEventClass)) { virReportInvalidArg(klass, _("Class %s must derive from virDomainEvent"), virClassName(klass)); return NULL; } if (!(event = virObjectEventNew(klass, eventID))) return NULL; if (VIR_STRDUP(event->meta.name, name) < 0) { VIR_FREE(event); return NULL; } event->meta.id = id; memcpy(event->meta.uuid, uuid, VIR_UUID_BUFLEN); return event; } virDomainEventPtr virDomainEventLifecycleNew(int id, const char *name, const unsigned char *uuid, int type, int detail) { virDomainEventLifecyclePtr event; if (virObjectEventInitialize() < 0) return NULL; if (!(event = virDomainEventNew(virDomainEventLifecycleClass, VIR_DOMAIN_EVENT_ID_LIFECYCLE, id, name, uuid))) return NULL; event->type = type; event->detail = detail; return (virDomainEventPtr)event; } virDomainEventPtr virDomainEventLifecycleNewFromDom(virDomainPtr dom, int type, int detail) { return virDomainEventLifecycleNew(dom->id, dom->name, dom->uuid, type, detail); } virDomainEventPtr virDomainEventLifecycleNewFromObj(virDomainObjPtr obj, int type, int detail) { return virDomainEventLifecycleNewFromDef(obj->def, type, detail); } virDomainEventPtr virDomainEventLifecycleNewFromDef(virDomainDefPtr def, int type, int detail) { return virDomainEventLifecycleNew(def->id, def->name, def->uuid, type, detail); } virDomainEventPtr virDomainEventRebootNew(int id, const char *name, const unsigned char *uuid) { if (virObjectEventInitialize() < 0) return NULL; return virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_REBOOT, id, name, uuid); } virDomainEventPtr virDomainEventRebootNewFromDom(virDomainPtr dom) { if (virObjectEventInitialize() < 0) return NULL; return virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_REBOOT, dom->id, dom->name, dom->uuid); } virDomainEventPtr virDomainEventRebootNewFromObj(virDomainObjPtr obj) { if (virObjectEventInitialize() < 0) return NULL; return virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_REBOOT, obj->def->id, obj->def->name, obj->def->uuid); } virDomainEventPtr virDomainEventRTCChangeNewFromDom(virDomainPtr dom, long long offset) { virDomainEventRTCChangePtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventRTCChangeClass, VIR_DOMAIN_EVENT_ID_RTC_CHANGE, dom->id, dom->name, dom->uuid))) return NULL; ev->offset = offset; return (virDomainEventPtr)ev; } virDomainEventPtr virDomainEventRTCChangeNewFromObj(virDomainObjPtr obj, long long offset) { virDomainEventRTCChangePtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventRTCChangeClass, VIR_DOMAIN_EVENT_ID_RTC_CHANGE, obj->def->id, obj->def->name, obj->def->uuid))) return NULL; ev->offset = offset; return (virDomainEventPtr)ev; } virDomainEventPtr virDomainEventWatchdogNewFromDom(virDomainPtr dom, int action) { virDomainEventWatchdogPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventWatchdogClass, VIR_DOMAIN_EVENT_ID_WATCHDOG, dom->id, dom->name, dom->uuid))) return NULL; ev->action = action; return (virDomainEventPtr)ev; } virDomainEventPtr virDomainEventWatchdogNewFromObj(virDomainObjPtr obj, int action) { virDomainEventWatchdogPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventWatchdogClass, VIR_DOMAIN_EVENT_ID_WATCHDOG, obj->def->id, obj->def->name, obj->def->uuid))) return NULL; ev->action = action; return (virDomainEventPtr)ev; } static virDomainEventPtr virDomainEventIOErrorNewFromDomImpl(int event, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, const char *reason) { virDomainEventIOErrorPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventIOErrorClass, event, dom->id, dom->name, dom->uuid))) return NULL; ev->action = action; if (VIR_STRDUP(ev->srcPath, srcPath) < 0 || VIR_STRDUP(ev->devAlias, devAlias) < 0 || VIR_STRDUP(ev->reason, reason) < 0) { virObjectUnref(ev); ev = NULL; } return (virDomainEventPtr)ev; } static virDomainEventPtr virDomainEventIOErrorNewFromObjImpl(int event, virDomainObjPtr obj, const char *srcPath, const char *devAlias, int action, const char *reason) { virDomainEventIOErrorPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventIOErrorClass, event, obj->def->id, obj->def->name, obj->def->uuid))) return NULL; ev->action = action; if (VIR_STRDUP(ev->srcPath, srcPath) < 0 || VIR_STRDUP(ev->devAlias, devAlias) < 0 || VIR_STRDUP(ev->reason, reason) < 0) { virObjectUnref(ev); ev = NULL; } return (virDomainEventPtr)ev; } virDomainEventPtr virDomainEventIOErrorNewFromDom(virDomainPtr dom, const char *srcPath, const char *devAlias, int action) { return virDomainEventIOErrorNewFromDomImpl(VIR_DOMAIN_EVENT_ID_IO_ERROR, dom, srcPath, devAlias, action, NULL); } virDomainEventPtr virDomainEventIOErrorNewFromObj(virDomainObjPtr obj, const char *srcPath, const char *devAlias, int action) { return virDomainEventIOErrorNewFromObjImpl(VIR_DOMAIN_EVENT_ID_IO_ERROR, obj, srcPath, devAlias, action, NULL); } virDomainEventPtr virDomainEventIOErrorReasonNewFromDom(virDomainPtr dom, const char *srcPath, const char *devAlias, int action, const char *reason) { return virDomainEventIOErrorNewFromDomImpl(VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON, dom, srcPath, devAlias, action, reason); } virDomainEventPtr virDomainEventIOErrorReasonNewFromObj(virDomainObjPtr obj, const char *srcPath, const char *devAlias, int action, const char *reason) { return virDomainEventIOErrorNewFromObjImpl(VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON, obj, srcPath, devAlias, action, reason); } virDomainEventPtr virDomainEventGraphicsNewFromDom(virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject) { virDomainEventGraphicsPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventGraphicsClass, VIR_DOMAIN_EVENT_ID_GRAPHICS, dom->id, dom->name, dom->uuid))) return NULL; ev->phase = phase; if (VIR_STRDUP(ev->authScheme, authScheme) < 0) { virObjectUnref(ev); return NULL; } ev->local = local; ev->remote = remote; ev->subject = subject; return (virDomainEventPtr)ev; } virDomainEventPtr virDomainEventGraphicsNewFromObj(virDomainObjPtr obj, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject) { virDomainEventGraphicsPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventGraphicsClass, VIR_DOMAIN_EVENT_ID_GRAPHICS, obj->def->id, obj->def->name, obj->def->uuid))) return NULL; ev->phase = phase; if (VIR_STRDUP(ev->authScheme, authScheme) < 0) { virObjectUnref(ev); return NULL; } ev->local = local; ev->remote = remote; ev->subject = subject; return (virDomainEventPtr)ev; } static virDomainEventPtr virDomainEventBlockJobNew(int id, const char *name, unsigned char *uuid, const char *path, int type, int status) { virDomainEventBlockJobPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventBlockJobClass, VIR_DOMAIN_EVENT_ID_BLOCK_JOB, id, name, uuid))) return NULL; if (VIR_STRDUP(ev->path, path) < 0) { virObjectUnref(ev); return NULL; } ev->type = type; ev->status = status; return (virDomainEventPtr)ev; } virDomainEventPtr virDomainEventBlockJobNewFromObj(virDomainObjPtr obj, const char *path, int type, int status) { return virDomainEventBlockJobNew(obj->def->id, obj->def->name, obj->def->uuid, path, type, status); } virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, const char *path, int type, int status) { return virDomainEventBlockJobNew(dom->id, dom->name, dom->uuid, path, type, status); } virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom) { virDomainEventPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_CONTROL_ERROR, dom->id, dom->name, dom->uuid))) return NULL; return ev; } virDomainEventPtr virDomainEventControlErrorNewFromObj(virDomainObjPtr obj) { virDomainEventPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_CONTROL_ERROR, obj->def->id, obj->def->name, obj->def->uuid))) return NULL; return ev; } static virDomainEventPtr virDomainEventDiskChangeNew(int id, const char *name, unsigned char *uuid, const char *oldSrcPath, const char *newSrcPath, const char *devAlias, int reason) { virDomainEventDiskChangePtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventDiskChangeClass, VIR_DOMAIN_EVENT_ID_DISK_CHANGE, id, name, uuid))) return NULL; if (VIR_STRDUP(ev->devAlias, devAlias) < 0) goto error; if (VIR_STRDUP(ev->oldSrcPath, oldSrcPath) < 0) goto error; if (VIR_STRDUP(ev->newSrcPath, newSrcPath) < 0) goto error; ev->reason = reason; return (virDomainEventPtr)ev; error: virObjectUnref(ev); return NULL; } virDomainEventPtr virDomainEventDiskChangeNewFromObj(virDomainObjPtr obj, const char *oldSrcPath, const char *newSrcPath, const char *devAlias, int reason) { return virDomainEventDiskChangeNew(obj->def->id, obj->def->name, obj->def->uuid, oldSrcPath, newSrcPath, devAlias, reason); } virDomainEventPtr virDomainEventDiskChangeNewFromDom(virDomainPtr dom, const char *oldSrcPath, const char *newSrcPath, const char *devAlias, int reason) { return virDomainEventDiskChangeNew(dom->id, dom->name, dom->uuid, oldSrcPath, newSrcPath, devAlias, reason); } static virDomainEventPtr virDomainEventTrayChangeNew(int id, const char *name, unsigned char *uuid, const char *devAlias, int reason) { virDomainEventTrayChangePtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventTrayChangeClass, VIR_DOMAIN_EVENT_ID_TRAY_CHANGE, id, name, uuid))) return NULL; if (VIR_STRDUP(ev->devAlias, devAlias) < 0) goto error; ev->reason = reason; return (virDomainEventPtr)ev; error: virObjectUnref(ev); return NULL; } virDomainEventPtr virDomainEventTrayChangeNewFromObj(virDomainObjPtr obj, const char *devAlias, int reason) { return virDomainEventTrayChangeNew(obj->def->id, obj->def->name, obj->def->uuid, devAlias, reason); } virDomainEventPtr virDomainEventTrayChangeNewFromDom(virDomainPtr dom, const char *devAlias, int reason) { return virDomainEventTrayChangeNew(dom->id, dom->name, dom->uuid, devAlias, reason); } static virDomainEventPtr virDomainEventPMWakeupNew(int id, const char *name, unsigned char *uuid) { virDomainEventPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_PMWAKEUP, id, name, uuid))) return NULL; return ev; } virDomainEventPtr virDomainEventPMWakeupNewFromObj(virDomainObjPtr obj) { return virDomainEventPMWakeupNew(obj->def->id, obj->def->name, obj->def->uuid); } virDomainEventPtr virDomainEventPMWakeupNewFromDom(virDomainPtr dom) { return virDomainEventPMWakeupNew(dom->id, dom->name, dom->uuid); } static virDomainEventPtr virDomainEventPMSuspendNew(int id, const char *name, unsigned char *uuid) { virDomainEventPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_PMSUSPEND, id, name, uuid))) return NULL; return ev; } virDomainEventPtr virDomainEventPMSuspendNewFromObj(virDomainObjPtr obj) { return virDomainEventPMSuspendNew(obj->def->id, obj->def->name, obj->def->uuid); } virDomainEventPtr virDomainEventPMSuspendNewFromDom(virDomainPtr dom) { return virDomainEventPMSuspendNew(dom->id, dom->name, dom->uuid); } static virDomainEventPtr virDomainEventPMSuspendDiskNew(int id, const char *name, unsigned char *uuid) { virDomainEventPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_PMSUSPEND_DISK, id, name, uuid))) return NULL; return ev; } virDomainEventPtr virDomainEventPMSuspendDiskNewFromObj(virDomainObjPtr obj) { return virDomainEventPMSuspendDiskNew(obj->def->id, obj->def->name, obj->def->uuid); } virDomainEventPtr virDomainEventPMSuspendDiskNewFromDom(virDomainPtr dom) { return virDomainEventPMSuspendDiskNew(dom->id, dom->name, dom->uuid); } virDomainEventPtr virDomainEventBalloonChangeNewFromDom(virDomainPtr dom, unsigned long long actual) { virDomainEventPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, dom->id, dom->name, dom->uuid))) return NULL; ev->data.balloonChange.actual = actual; return ev; } virDomainEventPtr virDomainEventBalloonChangeNewFromObj(virDomainObjPtr obj, unsigned long long actual) { virDomainEventPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE, obj->def->id, obj->def->name, obj->def->uuid))) return NULL; ev->data.balloonChange.actual = actual; return ev; } static virDomainEventPtr virDomainEventDeviceRemovedNew(int id, const char *name, unsigned char *uuid, const char *devAlias) { virDomainEventPtr ev; if (virObjectEventInitialize() < 0) return NULL; if (!(ev = virDomainEventNew(virDomainEventClass, VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED, id, name, uuid))) return NULL; if (VIR_STRDUP(ev->data.deviceRemoved.devAlias, devAlias) < 0) goto error; return ev; error: virObjectUnref(ev); return NULL; } virDomainEventPtr virDomainEventDeviceRemovedNewFromObj(virDomainObjPtr obj, const char *devAlias) { return virDomainEventDeviceRemovedNew(obj->def->id, obj->def->name, obj->def->uuid, devAlias); } virDomainEventPtr virDomainEventDeviceRemovedNewFromDom(virDomainPtr dom, const char *devAlias) { return virDomainEventDeviceRemovedNew(dom->id, dom->name, dom->uuid, devAlias); } /** * virObjectEventQueuePush: * @evtQueue: the object event queue * @event: the event to add * * Internal function to push to the back of a virObjectEventQueue * * Returns: 0 on success, -1 on failure */ static int virObjectEventQueuePush(virObjectEventQueuePtr evtQueue, virDomainEventPtr event) { if (!evtQueue) { return -1; } /* Make space on queue */ if (VIR_REALLOC_N(evtQueue->events, evtQueue->count + 1) < 0) return -1; evtQueue->events[evtQueue->count] = event; evtQueue->count++; return 0; } typedef void (*virObjectEventDispatchFunc)(virConnectPtr conn, virDomainEventPtr event, virConnectObjectEventGenericCallback cb, void *cbopaque, void *opaque); static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, virDomainEventPtr event, virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque ATTRIBUTE_UNUSED) { virDomainPtr dom = virGetDomain(conn, event->meta.name, event->meta.uuid); int eventID = virObjectEventGetEventID(event); if (!dom) return; dom->id = event->meta.id; switch ((virDomainEventID) eventID) { case VIR_DOMAIN_EVENT_ID_LIFECYCLE: { virDomainEventLifecyclePtr lifecycleEvent; lifecycleEvent = (virDomainEventLifecyclePtr)event; ((virConnectDomainEventCallback)cb)(conn, dom, lifecycleEvent->type, lifecycleEvent->detail, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_REBOOT: (cb)(conn, dom, cbopaque); goto cleanup; case VIR_DOMAIN_EVENT_ID_RTC_CHANGE: { virDomainEventRTCChangePtr rtcChangeEvent; rtcChangeEvent = (virDomainEventRTCChangePtr)event; ((virConnectDomainEventRTCChangeCallback)cb)(conn, dom, rtcChangeEvent->offset, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_WATCHDOG: { virDomainEventWatchdogPtr watchdogEvent; watchdogEvent = (virDomainEventWatchdogPtr)event; ((virConnectDomainEventWatchdogCallback)cb)(conn, dom, watchdogEvent->action, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_IO_ERROR: { virDomainEventIOErrorPtr ioErrorEvent; ioErrorEvent = (virDomainEventIOErrorPtr)event; ((virConnectDomainEventIOErrorCallback)cb)(conn, dom, ioErrorEvent->srcPath, ioErrorEvent->devAlias, ioErrorEvent->action, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON: { virDomainEventIOErrorPtr ioErrorEvent; ioErrorEvent = (virDomainEventIOErrorPtr)event; ((virConnectDomainEventIOErrorReasonCallback)cb)(conn, dom, ioErrorEvent->srcPath, ioErrorEvent->devAlias, ioErrorEvent->action, ioErrorEvent->reason, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_GRAPHICS: { virDomainEventGraphicsPtr graphicsEvent; graphicsEvent = (virDomainEventGraphicsPtr)event; ((virConnectDomainEventGraphicsCallback)cb)(conn, dom, graphicsEvent->phase, graphicsEvent->local, graphicsEvent->remote, graphicsEvent->authScheme, graphicsEvent->subject, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_CONTROL_ERROR: (cb)(conn, dom, cbopaque); goto cleanup; case VIR_DOMAIN_EVENT_ID_BLOCK_JOB: { virDomainEventBlockJobPtr blockJobEvent; blockJobEvent = (virDomainEventBlockJobPtr)event; ((virConnectDomainEventBlockJobCallback)cb)(conn, dom, blockJobEvent->path, blockJobEvent->type, blockJobEvent->status, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_DISK_CHANGE: { virDomainEventDiskChangePtr diskChangeEvent; diskChangeEvent = (virDomainEventDiskChangePtr)event; ((virConnectDomainEventDiskChangeCallback)cb)(conn, dom, diskChangeEvent->oldSrcPath, diskChangeEvent->newSrcPath, diskChangeEvent->devAlias, diskChangeEvent->reason, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_TRAY_CHANGE: { virDomainEventTrayChangePtr trayChangeEvent; trayChangeEvent = (virDomainEventTrayChangePtr)event; ((virConnectDomainEventTrayChangeCallback)cb)(conn, dom, trayChangeEvent->devAlias, trayChangeEvent->reason, cbopaque); goto cleanup; } case VIR_DOMAIN_EVENT_ID_PMWAKEUP: ((virConnectDomainEventPMWakeupCallback)cb)(conn, dom, 0, cbopaque); goto cleanup; case VIR_DOMAIN_EVENT_ID_PMSUSPEND: ((virConnectDomainEventPMSuspendCallback)cb)(conn, dom, 0, cbopaque); goto cleanup; case VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE: ((virConnectDomainEventBalloonChangeCallback)cb)(conn, dom, event->data.balloonChange.actual, cbopaque); goto cleanup; case VIR_DOMAIN_EVENT_ID_PMSUSPEND_DISK: ((virConnectDomainEventPMSuspendDiskCallback)cb)(conn, dom, 0, cbopaque); goto cleanup; case VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED: ((virConnectDomainEventDeviceRemovedCallback)cb)(conn, dom, event->data.deviceRemoved.devAlias, cbopaque); goto cleanup; case VIR_DOMAIN_EVENT_ID_LAST: break; } VIR_WARN("Unexpected event ID %d", eventID); cleanup: virDomainFree(dom); } static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, virObjectEventCallbackPtr cb) { if (!cb) return 0; if (cb->deleted) return 0; if (cb->eventID != virObjectEventGetEventID(event)) return 0; if (cb->meta) { /* Deliberately ignoring 'id' for matching, since that * will cause problems when a domain switches between * running & shutoff states & ignoring 'name' since * Xen sometimes renames guests during migration, thus * leaving 'uuid' as the only truly reliable ID we can use*/ if (memcmp(event->meta.uuid, cb->meta->uuid, VIR_UUID_BUFLEN) == 0) return 1; return 0; } else { return 1; } } static void virDomainEventDispatch(virDomainEventPtr event, virObjectEventCallbackListPtr callbacks, virObjectEventDispatchFunc dispatch, void *opaque) { size_t i; /* Cache this now, since we may be dropping the lock, and have more callbacks added. We're guaranteed not to have any removed */ int cbCount = callbacks->count; for (i = 0; i < cbCount; i++) { if (!virDomainEventDispatchMatchCallback(event, callbacks->callbacks[i])) continue; (*dispatch)(callbacks->callbacks[i]->conn, event, callbacks->callbacks[i]->cb, callbacks->callbacks[i]->opaque, opaque); } } static void virObjectEventQueueDispatch(virObjectEventQueuePtr queue, virObjectEventCallbackListPtr callbacks, virObjectEventDispatchFunc dispatch, void *opaque) { size_t i; for (i = 0; i < queue->count; i++) { virDomainEventDispatch(queue->events[i], callbacks, dispatch, opaque); virObjectUnref(queue->events[i]); } VIR_FREE(queue->events); queue->count = 0; } void virObjectEventStateQueue(virObjectEventStatePtr state, virDomainEventPtr event) { if (state->timer < 0) { virObjectUnref(event); return; } virObjectEventStateLock(state); if (virObjectEventQueuePush(state->queue, event) < 0) { VIR_DEBUG("Error adding event to queue"); virObjectUnref(event); } if (state->queue->count == 1) virEventUpdateTimeout(state->timer, 0); virObjectEventStateUnlock(state); } static void virObjectEventStateDispatchFunc(virConnectPtr conn, virDomainEventPtr event, virConnectObjectEventGenericCallback cb, void *cbopaque, void *opaque) { virObjectEventStatePtr state = opaque; /* Drop the lock whle dispatching, for sake of re-entrancy */ virObjectEventStateUnlock(state); virDomainEventDispatchDefaultFunc(conn, event, VIR_DOMAIN_EVENT_CALLBACK(cb), cbopaque, NULL); virObjectEventStateLock(state); } static void virObjectEventStateFlush(virObjectEventStatePtr state) { virObjectEventQueue tempQueue; virObjectEventStateLock(state); state->isDispatching = true; /* Copy the queue, so we're reentrant safe when dispatchFunc drops the * driver lock */ tempQueue.count = state->queue->count; tempQueue.events = state->queue->events; state->queue->count = 0; state->queue->events = NULL; virEventUpdateTimeout(state->timer, -1); virObjectEventQueueDispatch(&tempQueue, state->callbacks, virObjectEventStateDispatchFunc, state); /* Purge any deleted callbacks */ virObjectEventCallbackListPurgeMarked(state->callbacks); state->isDispatching = false; virObjectEventStateUnlock(state); } /** * virObjectEventStateRegisterID: * @conn: connection to associate with callback * @state: domain event state * @eventID: ID of the event type to register for * @cb: function to remove from event * @opaque: data blob to pass to callback * @freecb: callback to free @opaque * @callbackID: filled with callback ID * * Register the function @callbackID with connection @conn, * from @state, for events of type @eventID. * * Returns: the number of callbacks now registered, or -1 on error */ int virObjectEventStateRegisterID(virConnectPtr conn, virObjectEventStatePtr state, unsigned char *uuid, const char *name, int id, int eventID, virConnectObjectEventGenericCallback cb, void *opaque, virFreeCallback freecb, int *callbackID) { int ret = -1; virObjectEventStateLock(state); if ((state->callbacks->count == 0) && (state->timer == -1) && (state->timer = virEventAddTimeout(-1, virDomainEventTimer, state, NULL)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("could not initialize domain event timer")); goto cleanup; } ret = virObjectEventCallbackListAddID(conn, state->callbacks, uuid, name, id, eventID, cb, opaque, freecb, callbackID); if (ret == -1 && state->callbacks->count == 0 && state->timer != -1) { virEventRemoveTimeout(state->timer); state->timer = -1; } cleanup: virObjectEventStateUnlock(state); return ret; } /** * virDomainEventStateRegister: * @conn: connection to associate with callback * @state: object event state * @callback: function to remove from event * @opaque: data blob to pass to callback * @freecb: callback to free @opaque * * Register the function @callback with connection @conn, * from @state, for lifecycle events. * * Returns: the number of lifecycle callbacks now registered, or -1 on error */ int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, virConnectDomainEventCallback callback, void *opaque, virFreeCallback freecb) { int ret = -1; virObjectEventStateLock(state); if ((state->callbacks->count == 0) && (state->timer == -1) && (state->timer = virEventAddTimeout(-1, virDomainEventTimer, state, NULL)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("could not initialize domain event timer")); goto cleanup; } ret = virDomainEventCallbackListAdd(conn, state->callbacks, callback, opaque, freecb); if (ret == -1 && state->callbacks->count == 0 && state->timer != -1) { virEventRemoveTimeout(state->timer); state->timer = -1; } cleanup: virObjectEventStateUnlock(state); return ret; } /** * virDomainEventStateRegisterID: * @conn: connection to associate with callback * @state: object event state * @eventID: ID of the event type to register for * @cb: function to remove from event * @opaque: data blob to pass to callback * @freecb: callback to free @opaque * @callbackID: filled with callback ID * * Register the function @callbackID with connection @conn, * from @state, for events of type @eventID. * * Returns: the number of callbacks now registered, or -1 on error */ int virDomainEventStateRegisterID(virConnectPtr conn, virObjectEventStatePtr state, virDomainPtr dom, int eventID, virConnectDomainEventGenericCallback cb, void *opaque, virFreeCallback freecb, int *callbackID) { if (dom) return virObjectEventStateRegisterID(conn, state, dom->uuid, dom->name, dom->id, eventID, VIR_OBJECT_EVENT_CALLBACK(cb), opaque, freecb, callbackID); else return virObjectEventStateRegisterID(conn, state, NULL, NULL, 0, eventID, VIR_OBJECT_EVENT_CALLBACK(cb), opaque, freecb, callbackID); } /** * virDomainEventStateDeregister: * @conn: connection to associate with callback * @state: object event state * @callback: function to remove from event * * Unregister the function @callback with connection @conn, * from @state, for lifecycle events. * * Returns: the number of lifecycle callbacks still registered, or -1 on error */ int virDomainEventStateDeregister(virConnectPtr conn, virObjectEventStatePtr state, virConnectDomainEventCallback callback) { int ret; virObjectEventStateLock(state); if (state->isDispatching) ret = virDomainEventCallbackListMarkDelete(conn, state->callbacks, callback); else ret = virDomainEventCallbackListRemove(conn, state->callbacks, callback); if (state->callbacks->count == 0 && state->timer != -1) { virEventRemoveTimeout(state->timer); state->timer = -1; virObjectEventQueueClear(state->queue); } virObjectEventStateUnlock(state); return ret; } /** * virObjectEventStateDeregisterID: * @conn: connection to associate with callback * @state: object event state * @callbackID: ID of the function to remove from event * * Unregister the function @callbackID with connection @conn, * from @state, for events. * * Returns: the number of callbacks still registered, or -1 on error */ int virObjectEventStateDeregisterID(virConnectPtr conn, virObjectEventStatePtr state, int callbackID) { int ret; virObjectEventStateLock(state); if (state->isDispatching) ret = virObjectEventCallbackListMarkDeleteID(conn, state->callbacks, callbackID); else ret = virObjectEventCallbackListRemoveID(conn, state->callbacks, callbackID); if (state->callbacks->count == 0 && state->timer != -1) { virEventRemoveTimeout(state->timer); state->timer = -1; virObjectEventQueueClear(state->queue); } virObjectEventStateUnlock(state); return ret; } /** * virObjectEventStateEventID: * @conn: connection associated with the callback * @state: object event state * @callbackID: the callback to query * * Query what event ID type is associated with the * callback @callbackID for connection @conn * * Returns 0 on success, -1 on error */ int virObjectEventStateEventID(virConnectPtr conn, virObjectEventStatePtr state, int callbackID) { int ret; virObjectEventStateLock(state); ret = virObjectEventCallbackListEventID(conn, state->callbacks, callbackID); virObjectEventStateUnlock(state); return ret; }