[libvirt] [PATCH 1/4] add a qemu-specific event register API, to passthough the new events come from qemu

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Basically, this feature can go along with qemu monitor passthrough. That way, if we use new commands in the monitor that generate new events, we want some way to receive those new events too. Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- include/libvirt/libvirt-qemu.h | 27 ++++ include/libvirt/libvirt.h.in | 2 +- src/conf/domain_event.c | 293 ++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 50 ++++++- src/driver.h | 14 ++ src/libvirt-qemu.c | 189 ++++++++++++++++++++++++++ src/libvirt_private.syms | 6 + src/libvirt_qemu.syms | 5 + 8 files changed, 571 insertions(+), 15 deletions(-) diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 7f12e4f..3aa944a 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -32,6 +32,33 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid, unsigned int flags); +/** + * virConnectDomainQemuEventCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @eventName : the name of the unknow or un-implementation event + * @eventArgs: the content of the unknow or un-implementation event + * + * The callback signature to use when registering for an event of type + * VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN with virConnectDomainQemuEventRegister() + */ +typedef void (*virConnectDomainQemuEventCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque); + +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb); +int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID); + # ifdef __cplusplus } # endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2480add..9fcb400 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3207,7 +3207,6 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, int type, int status, void *opaque); - /** * virConnectDomainEventDiskChangeReason: * @@ -3263,6 +3262,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */ VIR_DOMAIN_EVENT_ID_DISK_CHANGE = 9, /* virConnectDomainEventDiskChangeCallback */ + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN = 10, /* virConnectDomainEventDefaultCallback */ /* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 614ab97..0388a66 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -45,7 +45,9 @@ typedef virDomainMeta *virDomainMetaPtr; struct _virDomainEventCallback { int callbackID; + int qemuCallbackID; int eventID; + char *eventName; virConnectPtr conn; virDomainMetaPtr dom; virConnectDomainEventGenericCallback cb; @@ -94,6 +96,10 @@ struct _virDomainEvent { char *devAlias; int reason; } diskChange; + struct { + char *eventName; + char *eventArgs; + }qemuUnknownEvent; } data; }; @@ -112,6 +118,7 @@ virDomainEventCallbackListFree(virDomainEventCallbackListPtr list) for (i=0; i<list->count; i++) { virFreeCallback freecb = list->callbacks[i]->freecb; + VIR_FREE(list->callbacks[i]->eventName); if (freecb) (*freecb)(list->callbacks[i]->opaque); VIR_FREE(list->callbacks[i]); @@ -187,8 +194,10 @@ virDomainEventCallbackListRemoveID(virConnectPtr conn, if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]); - if (i < (cbList->count - 1)) memmove(cbList->callbacks + i, cbList->callbacks + i + 1, @@ -231,6 +240,9 @@ virDomainEventCallbackListRemoveConn(virConnectPtr conn, if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]); if (i < (cbList->count - 1)) @@ -299,6 +311,9 @@ int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList) if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]); if (i < (cbList->count - 1)) @@ -404,7 +419,98 @@ virDomainEventCallbackListAddID(virConnectPtr conn, cbList->callbacks[cbList->count] = event; cbList->count++; + event->callbackID = cbList->nextID++; + + return event->callbackID; + +no_memory: + virReportOOMError(); + + if (event) { + if (event->dom) + VIR_FREE(event->dom->name); + VIR_FREE(event->dom); + } + VIR_FREE(event); + return -1; +} + + + +/** + * virDomainEventCallbackListAddName: + * @conn: pointer to the connection + * @cbList: the list + * @eventName: the event eventName + * @callback: the callback to add + * @eventID: the specific eventID + * @opaque: opaque data tio pass to callback + * + * Internal function to add a callback from a virDomainEventCallbackListPtr + */ +int +virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + virDomainEventCallbackPtr event; + int i; + + /* 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_DOMAIN_EVENT_CALLBACK(callback) && + STREQ(cbList->callbacks[i]->eventName, eventName) && + cbList->callbacks[i]->eventID == eventID && + cbList->callbacks[i]->conn == conn) { + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("event callback already tracked")); + return -1; + } + } + if (eventID > VIR_DOMAIN_EVENT_ID_LAST || eventID < VIR_DOMAIN_EVENT_ID_LIFECYCLE) { + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("not suport this kind of eventID: %d"), eventID); + } + /* Allocate new event */ + if (VIR_ALLOC(event) < 0) + goto no_memory; + event->conn = conn; + event->cb = callback; + if (eventName == NULL) + goto no_memory; + event->eventName = strdup(eventName); + if ( event->eventName == NULL) + goto no_memory; + event->opaque = opaque; + event->freecb = freecb; + event->eventID = eventID; + if (dom) { + if (VIR_ALLOC(event->dom) < 0) + goto no_memory; + if (!(event->dom->name = strdup(dom->name))) + goto no_memory; + memcpy(event->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); + event->dom->id = dom->id; + } + /* Make space on list */ + if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) + goto no_memory; + + event->conn->refs++; + + cbList->callbacks[cbList->count] = event; + cbList->count++; event->callbackID = cbList->nextID++; return event->callbackID; @@ -416,11 +522,40 @@ no_memory: if (event->dom) VIR_FREE(event->dom->name); VIR_FREE(event->dom); + VIR_FREE(event->eventName); } VIR_FREE(event); return -1; } +/** + * virDomainEventCallbackListAddQemuCallbackID: + * @conn: pointer to the connection + * @cbList: the list + * @callbackID: the libvirt callback ID + * @qemuCallbackID: the libvirtd callback ID to add + * + * Internal function to add a Daemon libvirtd callbackID + */ +int +virDomainEventCallbackListAddQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID, + int qemuCallbackID) +{ + int i; + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->callbackID == callbackID && + cbList->callbacks[i]->conn == conn) { + cbList->callbacks[i]->qemuCallbackID = qemuCallbackID; + return 0; + } + } + + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not find event callback for deletion")); + return -1; +} int virDomainEventCallbackListCountID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, @@ -442,6 +577,27 @@ int virDomainEventCallbackListCountID(virConnectPtr conn, } +int +virDomainEventCallbackListCountName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + const char *eventName) +{ + int i; + int count = 0; + + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->deleted) + continue; + + if (STREQ(cbList->callbacks[i]->eventName,eventName) && + cbList->callbacks[i]->conn == conn) + count++; + } + + return count; +} + + int virDomainEventCallbackListEventID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, int callbackID) @@ -461,6 +617,44 @@ int virDomainEventCallbackListEventID(virConnectPtr conn, } +const char* +virDomainEventCallbackListEventName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int 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]->eventName; + } + + return NULL; +} + +int +virDomainEventCallbackListEventQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int 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]->qemuCallbackID; + } + + return -1; +} + int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList) { int i; @@ -521,6 +715,11 @@ void virDomainEventFree(virDomainEventPtr event) VIR_FREE(event->data.diskChange.newSrcPath); VIR_FREE(event->data.diskChange.devAlias); break; + + case VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN: + VIR_FREE(event->data.qemuUnknownEvent.eventName); + VIR_FREE(event->data.qemuUnknownEvent.eventArgs); + break; } VIR_FREE(event->dom.name); @@ -956,6 +1155,51 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, path, type, status); } +static virDomainEventPtr +virDomainEventUnknownNew(int id, const char *name, unsigned char *uuid, + const char *eventName, const char *eventArgs) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN, + id, name, uuid); + if (ev) { + if (!(ev->data.qemuUnknownEvent.eventName = strdup(eventName))) { + virReportOOMError(); + VIR_FREE(ev->dom.name); + VIR_FREE(ev); + return NULL; + } + if (eventArgs) { + if (!(ev->data.qemuUnknownEvent.eventArgs = strdup(eventArgs))) { + virReportOOMError(); + VIR_FREE(ev->data.qemuUnknownEvent.eventName); + VIR_FREE(ev->dom.name); + VIR_FREE(ev); + return NULL; + } + } + } + + return ev; +} + +virDomainEventPtr virDomainEventUnknownNewFromObj(virDomainObjPtr obj, + const char *eventName, + const char *eventArgs) +{ + + return virDomainEventUnknownNew(obj->def->id, obj->def->name, + obj->def->uuid, eventName, eventArgs); +} + +virDomainEventPtr virDomainEventUnknownNewFromDom(virDomainPtr dom, + const char *eventName, + const char *eventArgs) +{ + return virDomainEventUnknownNew(dom->id, dom->name, dom->uuid, + eventName, eventArgs); +} + virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom) { virDomainEventPtr ev = @@ -1095,11 +1339,12 @@ virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, } -void virDomainEventDispatchDefaultFunc(virConnectPtr conn, - virDomainEventPtr event, - virConnectDomainEventGenericCallback cb, - void *cbopaque, - void *opaque ATTRIBUTE_UNUSED) +void +virDomainEventDispatchDefaultFunc(virConnectPtr conn, + virDomainEventPtr event, + virConnectDomainEventGenericCallback cb, + void *cbopaque, + void *opaque ATTRIBUTE_UNUSED) { virDomainPtr dom = virGetDomain(conn, event->dom.name, event->dom.uuid); if (!dom) @@ -1180,6 +1425,13 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN: + ((virConnectDomainQemuEventCallback)cb)(conn, dom, + event->data.qemuUnknownEvent.eventName, + event->data.qemuUnknownEvent.eventArgs, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; @@ -1189,8 +1441,9 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, } -static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, - virDomainEventCallbackPtr cb) +static int +virDomainEventDispatchMatchCallback(virDomainEventPtr event, + virDomainEventCallbackPtr cb) { if (!cb) return 0; @@ -1198,7 +1451,12 @@ static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, return 0; if (cb->eventID != event->eventID) return 0; - + if (event->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + if (event->data.qemuUnknownEvent.eventName == NULL || + cb->eventName == NULL || + STRNEQ(cb->eventName, event->data.qemuUnknownEvent.eventName)) + return 0; + } if (cb->dom) { /* Deliberately ignoring 'id' for matching, since that * will cause problems when a domain switches between @@ -1341,3 +1599,20 @@ virDomainEventStateDeregisterAny(virConnectPtr conn, virDomainEventStateUnlock(state); return ret; } +int +virDomainQemuEventStateDeregister(virConnectPtr conn, + virDomainEventStatePtr state, + int callbackID) +{ + int ret; + + virDomainEventStateLock(state); + if (state->isDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, + state->callbacks, callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, + state->callbacks, callbackID); + virDomainEventStateUnlock(state); + return ret; +} diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 3ba418e..f2fe847 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -83,14 +83,23 @@ int virDomainEventCallbackListAddID(virConnectPtr conn, virFreeCallback freecb) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(5); +int virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(6); int virDomainEventCallbackListRemove(virConnectPtr conn, virDomainEventCallbackListPtr cbList, virConnectDomainEventCallback callback) ATTRIBUTE_NONNULL(1); -int virDomainEventCallbackListRemoveID(virConnectPtr conn, - virDomainEventCallbackListPtr cbList, - int callbackID) +int virDomainQemuEventCallbackListRemoveID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) ATTRIBUTE_NONNULL(1); int virDomainEventCallbackListRemoveConn(virConnectPtr conn, virDomainEventCallbackListPtr cbList) @@ -106,9 +115,14 @@ int virDomainEventCallbackListMarkDeleteID(virConnectPtr conn, int callbackID) ATTRIBUTE_NONNULL(1); - int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList); +int virDomainEventCallbackListAddQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID, + int qemuCallbackID) + ATTRIBUTE_NONNULL(1); + int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList); int virDomainEventCallbackListCountID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, @@ -119,6 +133,21 @@ int virDomainEventCallbackListEventID(virConnectPtr conn, int callbackID) ATTRIBUTE_NONNULL(1); +int virDomainEventCallbackListCountName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + const char *eventName) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); + +int virDomainEventCallbackListEventQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + +const char* virDomainEventCallbackListEventName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + virDomainEventQueuePtr virDomainEventQueueNew(void); virDomainEventPtr virDomainEventNew(int id, const char *name, const unsigned char *uuid, int type, int detail); @@ -190,6 +219,13 @@ virDomainEventPtr virDomainEventDiskChangeNewFromDom(virDomainPtr dom, const char *devAlias, int reason); +virDomainEventPtr virDomainEventUnknownNewFromObj(virDomainObjPtr obj, + const char *eventName, + const char *eventArgs); +virDomainEventPtr virDomainEventUnknownNewFromDom(virDomainPtr dom, + const char *eventName, + const char *eventArgs); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); @@ -246,5 +282,9 @@ virDomainEventStateDeregisterAny(virConnectPtr conn, virDomainEventStatePtr state, int callbackID) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - +int +virDomainQemuEventStateDeregister(virConnectPtr conn, + virDomainEventStatePtr state, + int callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); #endif diff --git a/src/driver.h b/src/driver.h index 941ff51..51164a9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -635,6 +635,18 @@ typedef virDomainPtr unsigned int flags); typedef int + (*virDrvDomainQemuEventRegister)(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +typedef int + (*virDrvDomainQemuEventDeregister)(virConnectPtr conn, + int callbackID); + +typedef int (*virDrvDomainOpenConsole)(virDomainPtr dom, const char *dev_name, virStreamPtr st, @@ -915,6 +927,8 @@ struct _virDriver { virDrvDomainSnapshotDelete domainSnapshotDelete; virDrvDomainQemuMonitorCommand qemuDomainMonitorCommand; virDrvDomainQemuAttach qemuDomainAttach; + virDrvDomainQemuEventRegister qemuDomainQemuEventRegister; + virDrvDomainQemuEventDeregister qemuDomainQemuEventDeregister; virDrvDomainOpenConsole domainOpenConsole; virDrvDomainOpenGraphics domainOpenGraphics; virDrvDomainInjectNMI domainInjectNMI; diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index 248cc33..7722b7b 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -36,6 +36,77 @@ virReportErrorHelper(VIR_FROM_DOM, error, NULL, __FUNCTION__, \ __LINE__, info) +/* Helper macros to implement VIR_DOMAIN_DEBUG using just C99. This + * assumes you pass fewer than 15 arguments to VIR_DOMAIN_DEBUG, but + * can easily be expanded if needed. + * + * Note that gcc provides extensions of "define a(b...) b" or + * "define a(b,...) b,##__VA_ARGS__" as a means of eliding a comma + * when no var-args are present, but we don't want to require gcc. + */ +#define VIR_ARG15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 +#define VIR_HAS_COMMA(...) VIR_ARG15(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) + +/* Form the name VIR_DOMAIN_DEBUG_[01], then call that macro, + * according to how many arguments are present. Two-phase due to + * macro expansion rules. */ +#define VIR_DOMAIN_DEBUG_EXPAND(a, b, ...) \ + VIR_DOMAIN_DEBUG_PASTE(a, b, __VA_ARGS__) +#define VIR_DOMAIN_DEBUG_PASTE(a, b, ...) \ + a##b(__VA_ARGS__) + +/* Internal use only, when VIR_DOMAIN_DEBUG has one argument. */ +#define VIR_DOMAIN_DEBUG_0(dom) \ + VIR_DOMAIN_DEBUG_2(dom, "%s", "") + +/* Internal use only, when VIR_DOMAIN_DEBUG has three or more arguments. */ +#define VIR_DOMAIN_DEBUG_1(dom, fmt, ...) \ + VIR_DOMAIN_DEBUG_2(dom, ", " fmt, __VA_ARGS__) + +/* Internal use only, with final format. */ +#define VIR_DOMAIN_DEBUG_2(dom, fmt, ...) \ + do { \ + char _uuidstr[VIR_UUID_STRING_BUFLEN]; \ + const char *_domname = NULL; \ + \ + if (!VIR_IS_DOMAIN(dom)) { \ + memset(_uuidstr, 0, sizeof(_uuidstr)); \ + } else { \ + virUUIDFormat((dom)->uuid, _uuidstr); \ + _domname = (dom)->name; \ + } \ + \ + VIR_DEBUG("dom=%p, (VM: name=%s, uuid=%s)" fmt, \ + dom, NULLSTR(_domname), _uuidstr, __VA_ARGS__); \ + } while (0) + +/** + * VIR_DOMAIN_DEBUG: + * @dom: domain + * @fmt: optional format for additional information + * @...: optional arguments corresponding to @fmt. + */ +#define VIR_DOMAIN_DEBUG(...) \ + VIR_DOMAIN_DEBUG_EXPAND(VIR_DOMAIN_DEBUG_, \ + VIR_HAS_COMMA(__VA_ARGS__), \ + __VA_ARGS__) + +/** + * VIR_UUID_DEBUG: + * @conn: connection + * @uuid: possibly null UUID array + */ +#define VIR_UUID_DEBUG(conn, uuid) \ + do { \ + if (uuid) { \ + char _uuidstr[VIR_UUID_STRING_BUFLEN]; \ + virUUIDFormat(uuid, _uuidstr); \ + VIR_DEBUG("conn=%p, uuid=%s", conn, _uuidstr); \ + } else { \ + VIR_DEBUG("conn=%p, uuid=(null)", conn); \ + } \ + } while (0) + /** * virDomainQemuMonitorCommand: * @domain: a domain object @@ -178,3 +249,121 @@ error: virDispatchError(conn); return NULL; } + +/** + * virConnectDomainQemuEventRegister: + * @conn: pointer to the connection + * @dom: pointer to the domain + * @eventName: the event Name to receive + * @cb: callback to the function handling domain events + * @opaque: opaque data to pass on to the callback + * @freecb: optional function to deallocate opaque when not used anymore + * + * Adds a callback to receive notifications of arbitrary qemu domain events + * occurring on a domain. + * + * If dom is NULL, then events will be monitored for any domain. If dom + * is non-NULL, then only the specific domain will be monitored + * + * Most types of event have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus neccessary to use + * the VIR_DOMAIN_EVENT_CALLBACK() macro to cast the supplied function pointer + * to match the signature of this method. + * + * The virDomainPtr object handle passed into the callback upon delivery + * of an event is only valid for the duration of execution of the callback. + * If the callback wishes to keep the domain object after the callback returns, + * it shall take a reference to it, by calling virDomainRef. + * The reference can be released once the object is no longer required + * by calling virDomainFree. + * + * The return value from this method is a positive integer identifier + * for the callback. To unregister a callback, this callback ID should + * be passed to the virConnectDomainQemuEventDeregister method + * + * Returns a callback identifier on success, -1 on failure + */ +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DOMAIN_DEBUG(dom, "conn=%p, eventName=%s, cb=%p, opaque=%p, freecb=%p", + conn, eventName, cb, opaque, freecb); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (dom != NULL && + !(VIR_IS_CONNECTED_DOMAIN(dom) && dom->conn == conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(conn); + return -1; + } + if (eventName == NULL || cb == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if ((conn->driver) && (conn->driver->qemuDomainQemuEventRegister)) { + int ret; + ret = conn->driver->qemuDomainQemuEventRegister(conn, dom, eventName, cb, opaque, freecb); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectDomainQemuEventDeregister: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * vaule obtained from a previous virConnectDomainQemuEventDeregister method. + * + * Returns 0 on success, -1 on failure + */ +int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID) +{ + + VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (callbackID < 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if ((conn->driver) && (conn->driver->qemuDomainQemuEventDeregister)) { + int ret; + ret = conn->driver->qemuDomainQemuEventDeregister(conn, callbackID); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 48ffdf2..75e544a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -471,11 +471,16 @@ virDomainWatchdogModelTypeToString; # domain_event.h virDomainEventBlockJobNewFromObj; virDomainEventBlockJobNewFromDom; +virDomainEventUnknownNewFromObj; +virDomainEventunknownNewFromDom; virDomainEventCallbackListAdd; virDomainEventCallbackListAddID; +virDomainEventCallbackListAddName; virDomainEventCallbackListCount; virDomainEventCallbackListCountID; +virDomainEventCallbackListCountName; virDomainEventCallbackListEventID; +virDomainEventCallbackListEventName; virDomainEventCallbackListFree; virDomainEventCallbackListMarkDelete; virDomainEventCallbackListMarkDeleteID; @@ -512,6 +517,7 @@ virDomainEventRebootNewFromDom; virDomainEventRebootNewFromObj; virDomainEventStateDeregister; virDomainEventStateDeregisterAny; +virDomainQemuEventStateDeregister; virDomainEventStateFlush; virDomainEventStateFree; virDomainEventStateNew; diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms index 8447730..a17e387 100644 --- a/src/libvirt_qemu.syms +++ b/src/libvirt_qemu.syms @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; +LIBVIRT_QEMU_0.9.9 { + global: + virConnectDomainQemuEventRegister; + virConnectDomainQemuEventDeregister; +} LIBVIRT_QEMU_0.9.4; -- 1.7.5.4

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- daemon/libvirtd.h | 10 +++ daemon/remote.c | 172 +++++++++++++++++++++++++++++++++++++++++- src/remote/qemu_protocol.x | 29 +++++++- src/remote/remote_driver.c | 162 ++++++++++++++++++++++++++++++++++++++- src/remote/remote_protocol.x | 6 ++ src/remote_protocol-structs | 5 + 6 files changed, 376 insertions(+), 8 deletions(-) diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index c8d3ca2..fab7290 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -38,6 +38,15 @@ # endif # include "virnetserverprogram.h" +/* limit the number unknow event of an conncet can register */ +#define VIR_DOMAIN_EVENT_NAME_LAST 512 +struct domainEventNameCallBackStatus { + /* counter the number of unknow event registered */ + int eventNameCallBackCounter; + /* Stores the ID of the unknow event registered */ + int eventNameCallback[VIR_DOMAIN_EVENT_NAME_LAST]; +}; +typedef struct domainEventNameCallBackStatus domainEventNameCallBackStatus; typedef struct daemonClientStream daemonClientStream; typedef daemonClientStream *daemonClientStreamPtr; typedef struct daemonClientPrivate daemonClientPrivate; @@ -49,6 +58,7 @@ struct daemonClientPrivate { virMutex lock; int domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LAST]; + domainEventNameCallBackStatus domainEventNameCallBack; # if HAVE_SASL virNetSASLSessionPtr sasl; diff --git a/daemon/remote.c b/daemon/remote.c index e1d208c..f444c3d 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -421,6 +421,53 @@ mem_error: return -1; } +static int remoteRelayDomainEventUnknown(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque) +{ + virNetServerClientPtr client = opaque; + qemu_domain_events_unknown_event_msg data; + + if (!client) + return -1; + + VIR_DEBUG("Relaying domain: %s id: %d, unknown event: %s arguments: %s", + dom->name, dom->id, eventName, eventArgs); + + /* build return data */ + memset(&data, 0, sizeof data); + if (eventName == NULL) + goto mem_error3; + data.eventName = (char *)strdup(eventName); + if (data.eventName == NULL) + goto mem_error2; + if (eventArgs != NULL) { + data.eventArgs = (char *)strdup(eventArgs); + if (data.eventArgs == NULL) + goto mem_error1; + } + else { + data.eventArgs = (char *)strdup("NULL"); + if (data.eventArgs == NULL) + goto mem_error1; + } + make_nonnull_domain(&data.dom, dom); + remoteDispatchDomainEventSend(client, qemuProgram, + QEMU_PROC_DOMAIN_EVENTS_UNKNOWN_EVENT, + (xdrproc_t)xdr_qemu_domain_events_unknown_event_msg, &data); + + return 0; + +mem_error1: + VIR_FREE(data.eventArgs); +mem_error2: + VIR_FREE(data.eventName); +mem_error3: + virReportOOMError(); + return -1; +} static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -509,6 +556,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDiskChange), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventUnknown), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); @@ -526,10 +574,21 @@ static void remoteClientFreeFunc(void *data) /* Deregister event delivery callback */ if (priv->conn) { - int i; + int i, j; for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) { - if (priv->domainEventCallbackID[i] != -1) { + if (i == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN && priv->domainEventCallbackID[i] != -1) { + for (j = 0 ; j < VIR_DOMAIN_EVENT_NAME_LAST ; j++){ + if (priv->domainEventNameCallBack.eventNameCallback[j] != -1) { + VIR_DEBUG("Deregistering to relay remote events %d", i); + virConnectDomainQemuEventDeregister(priv->conn, + priv->domainEventNameCallBack.eventNameCallback[j]); + } + priv->domainEventNameCallBack.eventNameCallback[j] == -1; + } + priv->domainEventNameCallBack.eventNameCallBackCounter = 0; + } + else if (priv->domainEventCallbackID[i] != -1) { VIR_DEBUG("Deregistering to relay remote events %d", i); virConnectDomainEventDeregisterAny(priv->conn, priv->domainEventCallbackID[i]); @@ -572,6 +631,10 @@ int remoteClientInitHook(virNetServerPtr srv ATTRIBUTE_UNUSED, for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) priv->domainEventCallbackID[i] = -1; + priv->domainEventNameCallBack.eventNameCallBackCounter = 0; + for (i = 0 ; i < VIR_DOMAIN_EVENT_NAME_LAST ; i++) + priv->domainEventNameCallBack.eventNameCallback[i] = -1; + virNetServerClientSetPrivateData(client, priv, remoteClientFreeFunc); virNetServerClientSetCloseHook(client, remoteClientCloseFunc); @@ -2991,6 +3054,111 @@ cleanup: } static int +qemuDispatchDomainEventsRegister(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + qemu_domain_events_register_args *args, + qemu_domain_events_register_ret *ret ATTRIBUTE_UNUSED) +{ + int callbackID = -1; + int rv = -1; + int eventIdx = 0; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + if (!priv->conn) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + virMutexLock(&priv->lock); + + if (args->eventName == NULL) { + virNetError(VIR_ERR_INTERNAL_ERROR, _("event Name is NULL")); + goto cleanup; + } + if (priv->domainEventNameCallBack.eventNameCallBackCounter >= VIR_DOMAIN_EVENT_NAME_LAST) { + virNetError(VIR_ERR_INTERNAL_ERROR, + _("domain event %s is not registered, the register number has exceeded limit number %d"), + args->eventName, + VIR_DOMAIN_EVENT_NAME_LAST); + goto cleanup; + } + + if ((callbackID = virConnectDomainQemuEventRegister(priv->conn, + NULL, + args->eventName, + (virConnectDomainQemuEventCallback)remoteRelayDomainEventUnknown, + client, + NULL)) < 0) + goto cleanup; + + for (eventIdx = 0 ; eventIdx < VIR_DOMAIN_EVENT_NAME_LAST ; eventIdx++) { + if (priv->domainEventNameCallBack.eventNameCallback[eventIdx] == -1) { + priv->domainEventNameCallBack.eventNameCallback[eventIdx] = callbackID; + priv->domainEventNameCallBack.eventNameCallBackCounter++; + ret->callbackID = eventIdx; + break; + } + } + priv->domainEventCallbackID[VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN] = callbackID; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + +static int +qemuDispatchDomainEventsDeregister(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + qemu_domain_events_deregister_args *args, + qemu_domain_events_deregister_ret *ret ATTRIBUTE_UNUSED) +{ + int callbackID = -1; + int rv = -1; + int eventIdx = args->callbackID; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + virMutexLock(&priv->lock); + + if (eventIdx >= VIR_DOMAIN_EVENT_NAME_LAST || + (callbackID = priv->domainEventNameCallBack.eventNameCallback[eventIdx]) < 0) { + + virNetError(VIR_ERR_INTERNAL_ERROR, _("callbakcID %d is not register"), eventIdx); + goto cleanup; + } + + if (virConnectDomainQemuEventDeregister(priv->conn, callbackID) < 0) + goto cleanup; + ret->callbackID = callbackID; + + priv->domainEventNameCallBack.eventNameCallback[eventIdx] = -1; + priv->domainEventNameCallBack.eventNameCallBackCounter--; + if (priv->domainEventNameCallBack.eventNameCallBackCounter == 0) + priv->domainEventCallbackID[VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN] = -1; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + +static int qemuDispatchMonitorCommand(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client ATTRIBUTE_UNUSED, virNetMessagePtr msg ATTRIBUTE_UNUSED, diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x index 39f9adf..54f5734 100644 --- a/src/remote/qemu_protocol.x +++ b/src/remote/qemu_protocol.x @@ -47,6 +47,30 @@ struct qemu_domain_attach_ret { remote_nonnull_domain dom; }; +struct qemu_domain_events_register_args { + remote_nonnull_string eventName; +}; + +struct qemu_domain_events_deregister_args { + remote_nonnull_string eventName; + int callbackID; +}; + +struct qemu_domain_events_register_ret { + int callbackID; +}; + +struct qemu_domain_events_deregister_ret { + int callbackID; +}; + +struct qemu_domain_events_unknown_event_msg { + remote_nonnull_domain dom; + remote_nonnull_string eventName; + remote_nonnull_string eventArgs; +}; + + /* Define the program number, protocol version and procedure numbers here. */ const QEMU_PROGRAM = 0x20008087; const QEMU_PROTOCOL_VERSION = 1; @@ -61,5 +85,8 @@ enum qemu_procedure { * are some exceptions to this rule, e.g. domainDestroy. Other APIs MAY * be marked as high priority. If in doubt, it's safe to choose low. */ QEMU_PROC_MONITOR_COMMAND = 1, /* skipgen skipgen priority:low */ - QEMU_PROC_DOMAIN_ATTACH = 2 /* autogen autogen priority:low */ + QEMU_PROC_DOMAIN_ATTACH = 2, /* autogen autogen priority:low */ + QEMU_PROC_DOMAIN_EVENTS_REGISTER = 3, /* skipgen skipgen priority:low */ + QEMU_PROC_DOMAIN_EVENTS_DEREGISTER = 4, /* skipgen skipgen priority:low */ + QEMU_PROC_DOMAIN_EVENTS_UNKNOWN_EVENT = 5 /* skipgen skipgen */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ff2d4b4..a347eb2 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -238,6 +238,10 @@ static void remoteDomainBuildEventDiskChange(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteQemuDomainBuildEventDefaultEvent(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); static virNetClientProgramEvent remoteDomainEvents[] = { { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, @@ -281,7 +285,12 @@ static virNetClientProgramEvent remoteDomainEvents[] = { sizeof(remote_domain_event_disk_change_msg), (xdrproc_t)xdr_remote_domain_event_disk_change_msg }, }; - +static virNetClientProgramEvent qemuDomainEvents[] = { + { QEMU_PROC_DOMAIN_EVENTS_UNKNOWN_EVENT, + remoteQemuDomainBuildEventDefaultEvent, + sizeof(qemu_domain_events_unknown_event_msg), + (xdrproc_t)xdr_qemu_domain_events_unknown_event_msg}, +}; enum virDrvOpenRemoteFlags { VIR_DRV_OPEN_REMOTE_RO = (1 << 0), VIR_DRV_OPEN_REMOTE_USER = (1 << 1), /* Use the per-user socket path */ @@ -663,9 +672,9 @@ doRemoteOpen (virConnectPtr conn, goto failed; if (!(priv->qemuProgram = virNetClientProgramNew(QEMU_PROGRAM, QEMU_PROTOCOL_VERSION, - NULL, - 0, - NULL))) + qemuDomainEvents, + ARRAY_CARDINALITY(remoteDomainEvents), + conn))) goto failed; if (virNetClientAddProgram(priv->client, priv->remoteProgram) < 0 || @@ -3345,6 +3354,29 @@ remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, } static void +remoteQemuDomainBuildEventDefaultEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + //remote_domain_event_default_event_msg *msg = evdata; + qemu_domain_events_unknown_event_msg *msg = evdata; + virDomainPtr dom; + virDomainEventPtr event = NULL; + + dom = get_nonnull_domain(conn, msg->dom); + if (!dom) + return; + + event = virDomainEventUnknownNewFromDom(dom, msg->eventName, msg->eventArgs); + + virDomainFree(dom); + + remoteDomainEventQueue(priv, event); +} + +static void remoteDomainBuildEventGraphics(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque) @@ -3800,7 +3832,6 @@ done: return rv; } - static int remoteDomainEventDeregisterAny(virConnectPtr conn, int callbackID) { @@ -3843,6 +3874,125 @@ done: return rv; } +static int +remoteDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + int rv = -1; + struct private_data *priv = conn->privateData; + qemu_domain_events_register_args args; + qemu_domain_events_register_ret ret; + int callbackID; + + remoteDriverLock(priv); + + if (priv->domainEventState->timer < 0) { + remoteError(VIR_ERR_NO_SUPPORT, "%s", _("no event support")); + goto done; + } + + if ((callbackID = virDomainEventCallbackListAddName(conn, + priv->domainEventState->callbacks, + dom, eventName, + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN, + callback, opaque, freecb)) < 0) { + remoteError(VIR_ERR_RPC, "%s", _("adding cb to list")); + goto done; + } + + /* If this is the first callback for this eventID, we need to enable + * events on the server */ + if (virDomainEventCallbackListCountName(conn, + priv->domainEventState->callbacks, + eventName) == 1) { + args.eventName= (char *)eventName; + + if (call (conn, priv, REMOTE_CALL_QEMU, QEMU_PROC_DOMAIN_EVENTS_REGISTER, + (xdrproc_t) xdr_qemu_domain_events_register_args, (char *) &args, + (xdrproc_t) xdr_qemu_domain_events_register_ret, (char *) &ret) == -1) { + virDomainEventCallbackListRemoveID(conn, + priv->domainEventState->callbacks, + callbackID); + goto done; + } + } + virDomainEventCallbackListAddQemuCallbackID(conn, + priv->domainEventState->callbacks, + callbackID, + ret.callbackID); + rv = callbackID; + +done: + remoteDriverUnlock(priv); + return rv; +} + +static int remoteDomainQemuEventDeregister(virConnectPtr conn, + int callbackID) +{ + struct private_data *priv = conn->privateData; + int rv = -1; + qemu_domain_events_deregister_args args; + qemu_domain_events_deregister_ret ret; + char *eventName = NULL; + ret.callbackID = -1; + + remoteDriverLock(priv); + + if ((eventName = (char *)virDomainEventCallbackListEventName(conn, + priv->domainEventState->callbacks, + callbackID)) == NULL) { + remoteError(VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID); + goto done; + } + eventName = strdup(eventName); + if (eventName == NULL) + goto done; + + if ((args.callbackID = virDomainEventCallbackListEventQemuCallbackID(conn, + priv->domainEventState->callbacks, + callbackID)) == -1) { + remoteError(VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID); + goto done; + } + + if (virDomainQemuEventStateDeregister(conn, + priv->domainEventState, + callbackID) < 0) + goto done; + + /* If that was the last callback for this eventName, we need to disable + * events on the server */ + if (virDomainEventCallbackListCountName(conn, + priv->domainEventState->callbacks, + eventName) == 0) { + args.eventName = eventName; + + if (call (conn, priv, REMOTE_CALL_QEMU, QEMU_PROC_DOMAIN_EVENTS_DEREGISTER, + (xdrproc_t) xdr_qemu_domain_events_deregister_args, (char *) &args, + (xdrproc_t) xdr_qemu_domain_events_deregister_ret, (char *) &ret) == -1) { + goto done; + } + + if ( ret.callbackID == -1 ) { + remoteError(VIR_ERR_RPC, _("remote sever deregeiter remote callbackID:%d, and the client callbackID%d"), + args.callbackID, + callbackID); + goto done; + } + rv = ret.callbackID; + } + + +done: + remoteDriverUnlock(priv); + VIR_FREE(eventName); + return rv; +} /*----------------------------------------------------------------------*/ @@ -4627,6 +4777,8 @@ static virDriver remote_driver = { .nodeGetFreeMemory = remoteNodeGetFreeMemory, /* 0.3.3 */ .domainEventRegister = remoteDomainEventRegister, /* 0.5.0 */ .domainEventDeregister = remoteDomainEventDeregister, /* 0.5.0 */ + .qemuDomainQemuEventRegister = remoteDomainQemuEventRegister, /* 0.9.9 */ + .qemuDomainQemuEventDeregister = remoteDomainQemuEventDeregister, /* 0.9.9 */ .domainMigratePrepare2 = remoteDomainMigratePrepare2, /* 0.5.0 */ .domainMigrateFinish2 = remoteDomainMigrateFinish2, /* 0.5.0 */ .nodeDeviceDettach = remoteNodeDeviceDettach, /* 0.6.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 509a20b..02154bc 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2049,6 +2049,12 @@ struct remote_domain_event_disk_change_msg { int reason; }; +struct remote_domain_event_default_event_msg { + remote_nonnull_domain dom; + remote_nonnull_string eventName; + remote_nonnull_string eventArgs; +}; + struct remote_domain_managed_save_args { remote_nonnull_domain dom; unsigned int flags; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index a9d4296..17b3896 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1546,6 +1546,10 @@ struct remote_domain_event_disk_change_msg { remote_nonnull_string devAlias; int reason; }; +struct remote_domain_event_default_event_msg { + remote_nonnull_domain dom; + remote_nonnull_string rawEvent; +}; struct remote_domain_managed_save_args { remote_nonnull_domain dom; u_int flags; @@ -2044,4 +2048,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_BLOCK_RESIZE = 251, REMOTE_PROC_DOMAIN_SET_BLOCK_IO_TUNE = 252, REMOTE_PROC_DOMAIN_GET_BLOCK_IO_TUNE = 253, + REMOTE_PROC_QEMU_DOMAIN_EVENT_UNKNOWN_EVENT = 254, }; -- 1.7.5.4

On 12/16/2011 09:58 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Pretty light on the commit message.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- daemon/libvirtd.h | 10 +++ daemon/remote.c | 172 +++++++++++++++++++++++++++++++++++++++++- src/remote/qemu_protocol.x | 29 +++++++- src/remote/remote_driver.c | 162 ++++++++++++++++++++++++++++++++++++++- src/remote/remote_protocol.x | 6 ++ src/remote_protocol-structs | 5 + 6 files changed, 376 insertions(+), 8 deletions(-)
diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index c8d3ca2..fab7290 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -38,6 +38,15 @@ # endif # include "virnetserverprogram.h"
+/* limit the number unknow event of an conncet can register */
s/unknow/unknown/ s/conncet/connection/ But based on my review of 1/4, this has several problems. They aren't unknown events, so much as qemu events, which might be sent in parallel to a libvirt event. Is the goal to hard-code the number of event names we allow to be registered in order to avoid a DoS where someone just registers an indefinite stream of arbitrary event names to exhaust libvirtd's memory? But since we don't limit the number of event registration ids you can create, I think we're better off tracking this as a growable hash table, rather than capping it to a hard limit in the number of names.
+++ b/daemon/remote.c @@ -421,6 +421,53 @@ mem_error: return -1; }
+static int remoteRelayDomainEventUnknown(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque)
static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -509,6 +556,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDiskChange), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventUnknown), };
I think you want to separate things; since registering a qemu callback should not interfere with libvirt events, this will not be in the table of domainEventCallbacks. I think there's going to be a bit of work to refactor this so that it is triggered in the right code paths for libvirt-qemu.so, but doesn't impact libvirt.so.
+++ b/src/remote/qemu_protocol.x @@ -47,6 +47,30 @@ struct qemu_domain_attach_ret { remote_nonnull_domain dom; };
+struct qemu_domain_events_register_args { + remote_nonnull_string eventName; +}; + +struct qemu_domain_events_deregister_args { + remote_nonnull_string eventName;
Shouldn't need the eventName on deregister.
+ int callbackID; +}; + +struct qemu_domain_events_register_ret { + int callbackID; +};
I'd stick the related args and ret structs next to one another.
+ +struct qemu_domain_events_deregister_ret { + int callbackID; +};
Deregistration doesn't need a _ret.
+ +struct qemu_domain_events_unknown_event_msg { + remote_nonnull_domain dom; + remote_nonnull_string eventName; + remote_nonnull_string eventArgs; +}; + + /* Define the program number, protocol version and procedure numbers here. */ const QEMU_PROGRAM = 0x20008087; const QEMU_PROTOCOL_VERSION = 1; @@ -61,5 +85,8 @@ enum qemu_procedure { * are some exceptions to this rule, e.g. domainDestroy. Other APIs MAY * be marked as high priority. If in doubt, it's safe to choose low. */ QEMU_PROC_MONITOR_COMMAND = 1, /* skipgen skipgen priority:low */ - QEMU_PROC_DOMAIN_ATTACH = 2 /* autogen autogen priority:low */ + QEMU_PROC_DOMAIN_ATTACH = 2, /* autogen autogen priority:low */ + QEMU_PROC_DOMAIN_EVENTS_REGISTER = 3, /* skipgen skipgen priority:low */ + QEMU_PROC_DOMAIN_EVENTS_DEREGISTER = 4, /* skipgen skipgen priority:low */ + QEMU_PROC_DOMAIN_EVENTS_UNKNOWN_EVENT = 5 /* skipgen skipgen */
I still don't like the name UNKNOWN everywhere.
@@ -4627,6 +4777,8 @@ static virDriver remote_driver = { .nodeGetFreeMemory = remoteNodeGetFreeMemory, /* 0.3.3 */ .domainEventRegister = remoteDomainEventRegister, /* 0.5.0 */ .domainEventDeregister = remoteDomainEventDeregister, /* 0.5.0 */ + .qemuDomainQemuEventRegister = remoteDomainQemuEventRegister, /* 0.9.9 */ + .qemuDomainQemuEventDeregister = remoteDomainQemuEventDeregister, /* 0.9.9 */
0.9.10, now. It looks like a lot of this will be copy and paste from existing event codes, but I didn't review it closely.
+++ b/src/remote/remote_protocol.x @@ -2049,6 +2049,12 @@ struct remote_domain_event_disk_change_msg { int reason; };
+struct remote_domain_event_default_event_msg { + remote_nonnull_domain dom; + remote_nonnull_string eventName; + remote_nonnull_string eventArgs; +};
No. We should _not_ be sending any qemu-specific event over remote_protocol.x. It should all be handled solely by qemu_protocol.x. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/qemu/qemu_driver.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 10 ++++++++++ src/qemu/qemu_monitor.h | 8 ++++++++ src/qemu/qemu_monitor_json.c | 35 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_process.c | 24 ++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 725b593..b9f1ec4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8015,6 +8015,46 @@ qemuDomainEventDeregisterAny(virConnectPtr conn, } +static int +qemuDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + struct qemud_driver *driver = conn->privateData; + int ret; + + qemuDriverLock(driver); + ret = virDomainEventCallbackListAddName(conn, + driver->domainEventState->callbacks, + dom, eventName, + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN, + callback, opaque, freecb); + qemuDriverUnlock(driver); + + return ret; +} + + +static int +qemuDomainQemuEventDeregister(virConnectPtr conn, + int callbackID) +{ + struct qemud_driver *driver = conn->privateData; + int ret; + + qemuDriverLock(driver); + ret = virDomainQemuEventStateDeregister(conn, + driver->domainEventState, + callbackID); + qemuDriverUnlock(driver); + + return ret; +} + + /******************************************************************* * Migration Protocol Version 2 *******************************************************************/ @@ -11320,6 +11360,8 @@ static virDriver qemuDriver = { .domainMigrateGetMaxSpeed = qemuDomainMigrateGetMaxSpeed, /* 0.9.5 */ .domainEventRegisterAny = qemuDomainEventRegisterAny, /* 0.8.0 */ .domainEventDeregisterAny = qemuDomainEventDeregisterAny, /* 0.8.0 */ + .qemuDomainQemuEventRegister = qemuDomainQemuEventRegister, /* 0.9.9 */ + .qemuDomainQemuEventDeregister = qemuDomainQemuEventDeregister, /* 0.9.9 */ .domainManagedSave = qemuDomainManagedSave, /* 0.8.0 */ .domainHasManagedSaveImage = qemuDomainHasManagedSaveImage, /* 0.8.0 */ .domainManagedSaveRemove = qemuDomainManagedSaveRemove, /* 0.8.0 */ diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index ad7e2a5..ab090b9 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -980,6 +980,16 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, return ret; } +int qemuMonitorEmitUnknownEvent(qemuMonitorPtr mon, + const char *eventName, + const char *eventArgs) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + QEMU_MONITOR_CALLBACK(mon, ret, domainUnknownEvent, mon->vm, + eventName, eventArgs); + return ret; +} int qemuMonitorSetCapabilities(qemuMonitorPtr mon) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 15acf8b..d3685b4 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -123,6 +123,10 @@ struct _qemuMonitorCallbacks { const char *diskAlias, int type, int status); + int (*domainUnknownEvent)(qemuMonitorPtr mon, + virDomainObjPtr vm, + const char *eventName, + const char *eventArgs); }; @@ -193,6 +197,10 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, int type, int status); +int qemuMonitorEmitUnknownEvent(qemuMonitorPtr mon, + const char *eventName, + const char *eventArgs); + int qemuMonitorStartCPUs(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 1ef3e84..8931332 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -58,6 +58,7 @@ static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleUnmatchedEvent(qemuMonitorPtr mon, virJSONValuePtr obj); struct { const char *type; @@ -74,6 +75,7 @@ struct { { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, }, { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, }, { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJob, }, + { "DEFAULT_UNKNOW_EVENT", qemuMonitorJSONHandleUnmatchedEvent, }, }; @@ -83,6 +85,7 @@ qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon, { const char *type; int i; + int findEventFlag = -1; VIR_DEBUG("mon=%p obj=%p", mon, obj); type = virJSONValueObjectGetString(obj, "event"); @@ -98,9 +101,24 @@ qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon, VIR_DEBUG("handle %s handler=%p data=%p", type, eventHandlers[i].handler, data); (eventHandlers[i].handler)(mon, data); + findEventFlag = 0; break; } } + if (findEventFlag != 0) { + if (!STREQ(eventHandlers[ARRAY_CARDINALITY(eventHandlers)-1].type, "DEFAULT_UNKNOW_EVENT")) { + VIR_ERROR(_("the last element is not the default event handler")); + } + else { + char *event = NULL; + event = virJSONValueToString(obj); + if (event != NULL) { + VIR_DEBUG("Unknow event,call default event handler %s",event); + free(event); + } + (eventHandlers[ARRAY_CARDINALITY(eventHandlers)-1].handler)(mon, obj); + } + } return 0; } @@ -724,6 +742,23 @@ out: } +static void +qemuMonitorJSONHandleUnmatchedEvent(qemuMonitorPtr mon, virJSONValuePtr obj) +{ + const char *eventArgsStr = NULL; + const char *type = NULL; + virJSONValuePtr data = NULL; + type = virJSONValueObjectGetString(obj, "event"); + data = virJSONValueObjectGet(obj, "data"); + if (data) { + eventArgsStr = virJSONValueToString(data); + if (eventArgsStr == NULL) + VIR_ERROR(_("no data string from JSONValue")); + } + qemuMonitorEmitUnknownEvent(mon, type, eventArgsStr); +} + + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, const char *cmd_str, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 9123f4c..55e5464 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -755,6 +755,29 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } static int +qemuProcessHandleUnknownEvent(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *eventName, + const char *eventArgs) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event = NULL; + + virDomainObjLock(vm); + event = virDomainEventUnknownNewFromObj(vm, eventName, eventArgs); + + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} + +static int qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainObjPtr vm, int phase, @@ -871,6 +894,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainIOError = qemuProcessHandleIOError, .domainGraphics = qemuProcessHandleGraphics, .domainBlockJob = qemuProcessHandleBlockJob, + .domainUnknownEvent = qemuProcessHandleUnknownEvent, }; static int -- 1.7.5.4

On 12/16/2011 09:59 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- src/qemu/qemu_driver.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 10 ++++++++++ src/qemu/qemu_monitor.h | 8 ++++++++ src/qemu/qemu_monitor_json.c | 35 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_process.c | 24 ++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 0 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 725b593..b9f1ec4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8015,6 +8015,46 @@ qemuDomainEventDeregisterAny(virConnectPtr conn, }
+static int +qemuDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + struct qemud_driver *driver = conn->privateData; + int ret; + + qemuDriverLock(driver); + ret = virDomainEventCallbackListAddName(conn, + driver->domainEventState->callbacks, + dom, eventName, + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN, + callback, opaque, freecb); + qemuDriverUnlock(driver);
This implementation looks quite simple, but I guess it depends on whether the work I mentioned in 1/4 about domain_event changes should be moved somewhere separate.
+++ b/src/qemu/qemu_monitor_json.c @@ -58,6 +58,7 @@ static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleUnmatchedEvent(qemuMonitorPtr mon, virJSONValuePtr obj);
struct { const char *type; @@ -74,6 +75,7 @@ struct { { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, }, { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, }, { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJob, }, + { "DEFAULT_UNKNOW_EVENT", qemuMonitorJSONHandleUnmatchedEvent, },
s/UNKNOW/UNKNOWN/ but again, I think we should be checking _every_ qemu event for registration through libvirt-qemu, even the ones known by libvirt.
};
@@ -83,6 +85,7 @@ qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon, { const char *type; int i; + int findEventFlag = -1; VIR_DEBUG("mon=%p obj=%p", mon, obj);
type = virJSONValueObjectGetString(obj, "event"); @@ -98,9 +101,24 @@ qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon, VIR_DEBUG("handle %s handler=%p data=%p", type, eventHandlers[i].handler, data); (eventHandlers[i].handler)(mon, data); + findEventFlag = 0; break; } }
Which means this code should really be doing: check hash table, if name found, then send qemu event to libvirt-qemu check table of known events, if found, then call registered eventHandler -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- python/generator.py | 28 +++++---- python/libvirt-qemu-override.c | 140 +++++++++++++++++++++++++++++++++++++++ python/libvirt-qemu-override.py | 55 +++++++++++++++ python/libvirt_qemu.py | 105 +++++++++++++++++++++++++++++ 4 files changed, 316 insertions(+), 12 deletions(-) create mode 100644 python/libvirt-qemu-override.py create mode 100644 python/libvirt_qemu.py diff --git a/python/generator.py b/python/generator.py index 1657f4f..9a4cf83 100755 --- a/python/generator.py +++ b/python/generator.py @@ -234,6 +234,7 @@ skipped_types = { # 'int *': "usually a return type", 'virConnectDomainEventCallback': "No function types in python", 'virConnectDomainEventGenericCallback': "No function types in python", + 'virConnectDomainQemuEventCallback': "No function types in python", 'virConnectDomainEventRTCChangeCallback': "No function types in python", 'virConnectDomainEventWatchdogCallback': "No function types in python", 'virConnectDomainEventIOErrorCallback': "No function types in python", @@ -477,6 +478,9 @@ skip_function = ( qemu_skip_function = ( #"virDomainQemuAttach", + 'virConnectDomainQemuEventCallback', # overridden in libvirt_qemu_override.py + 'virConnectDomainQemuEventRegister', # overridden in libvirt_qemu_override.py + 'virConnectDomainQemuEventDeregister', # overridden in libvirt_qemu_override.py ) # Generate C code, but skip python impl @@ -1657,17 +1661,18 @@ def qemuBuildWrappers(module): if extra != None: extra.close() - fd.write("try:\n") - fd.write(" import libvirtmod_qemu\n") - fd.write("except ImportError, lib_e:\n") - fd.write(" try:\n") - fd.write(" import cygvirtmod_qemu as libvirtmod_qemu\n") - fd.write(" except ImportError, cyg_e:\n") - fd.write(" if str(cyg_e).count(\"No module named\"):\n") - fd.write(" raise lib_e\n\n") - - fd.write("import libvirt\n\n"); - fd.write("#\n# Functions from module %s\n#\n\n" % module) + if extra == None: + fd.write("try:\n") + fd.write(" import libvirtmod_qemu\n") + fd.write("except ImportError, lib_e:\n") + fd.write(" try:\n") + fd.write(" import cygvirtmod_qemu as libvirtmod_qemu\n") + fd.write(" except ImportError, cyg_e:\n") + fd.write(" if str(cyg_e).count(\"No module named\"):\n") + fd.write(" raise lib_e\n\n") + + fd.write("import libvirt\n\n"); + fd.write("#\n# Functions from module %s\n#\n\n" % module) # # Generate functions directly, no classes # @@ -1727,7 +1732,6 @@ def qemuBuildWrappers(module): items.sort(lambda i1,i2: cmp(long(i1[1]),long(i2[1]))) for name,value in items: fd.write("%s = %s\n" % (name,value)) - fd.write("\n"); fd.close() diff --git a/python/libvirt-qemu-override.c b/python/libvirt-qemu-override.c index 485c809..f5a8c09 100644 --- a/python/libvirt-qemu-override.c +++ b/python/libvirt-qemu-override.c @@ -18,6 +18,7 @@ #include <Python.h> #include "libvirt/libvirt-qemu.h" +#include "libvirt/libvirt.h" #include "libvirt/virterror.h" #include "typewrappers.h" #include "libvirt-qemu.h" @@ -96,6 +97,143 @@ libvirt_qemu_virDomainQemuMonitorCommand(PyObject *self ATTRIBUTE_UNUSED, return(py_retval); } +static void +libvirt_qemu_virConnectDomainEventFreeFunc(void *opaque) +{ + PyObject *pyobj_conn = (PyObject*)opaque; + LIBVIRT_ENSURE_THREAD_STATE; + Py_DECREF(pyobj_conn); + LIBVIRT_RELEASE_THREAD_STATE; +} + +static int +libvirt_qemu_virConnectDomainEventUnknownCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *eventName, + const char *eventArgs, + void *opaque) +{ + PyObject *pyobj_cbData = (PyObject*)opaque; + PyObject *pyobj_dom; + PyObject *pyobj_ret; + PyObject *pyobj_conn; + PyObject *dictKey; + int ret = -1; + + LIBVIRT_ENSURE_THREAD_STATE; + + /* Create a python instance of this virDomainPtr */ + virDomainRef(dom); + pyobj_dom = libvirt_virDomainPtrWrap(dom); + Py_INCREF(pyobj_cbData); + + dictKey = libvirt_constcharPtrWrap("conn"); + pyobj_conn = PyDict_GetItem(pyobj_cbData, dictKey); + Py_DECREF(dictKey); + + /* Call the Callback Dispatcher */ + pyobj_ret = PyObject_CallMethod(pyobj_conn, + (char*)"_dispatchDomainEventUnknownCallback", + (char*)"OssO", + pyobj_dom, eventName, eventArgs, pyobj_cbData); + + Py_DECREF(pyobj_cbData); + Py_DECREF(pyobj_dom); + + if (!pyobj_ret) { +#if DEBUG_ERROR + printf("%s - ret:%p\n", __FUNCTION__, pyobj_ret); +#endif + PyErr_Print(); + } else { + Py_DECREF(pyobj_ret); + ret = 0; + } + + LIBVIRT_RELEASE_THREAD_STATE; + return ret; +} + +static PyObject * +libvirt_qemu_virConnectDomainQemuEventRegister(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *py_retval; /* return value */ + PyObject *pyobj_conn; /* virConnectPtr */ + PyObject *pyobj_dom; + PyObject *pyobj_cbData; /* hash of callback data */ + char *eventName; + virConnectPtr conn; + int ret = 0; + + virConnectDomainQemuEventCallback cb = NULL; + virDomainPtr dom; + + if (!PyArg_ParseTuple + (args, (char *) "OOsO:virConnectDomainQemuEventRegister", + &pyobj_conn, &pyobj_dom, &eventName, &pyobj_cbData)) { + DEBUG("%s failed parsing tuple\n", __FUNCTION__); + return VIR_PY_INT_FAIL; + } + + if (eventName == NULL) + return VIR_PY_INT_FAIL; + + DEBUG("libvirt_qemu_virConnectDomainQemuEventRegister(%p %p %s %p) called\n", + pyobj_conn, pyobj_dom, eventName, pyobj_cbData); + conn = PyvirConnect_Get(pyobj_conn); + if (pyobj_dom == Py_None) + dom = NULL; + else + dom = PyvirDomain_Get(pyobj_dom); + + cb = (virConnectDomainQemuEventCallback)(libvirt_qemu_virConnectDomainEventUnknownCallback); + if (!cb) { + return VIR_PY_INT_FAIL; + } + + Py_INCREF(pyobj_cbData); + + LIBVIRT_BEGIN_ALLOW_THREADS; + ret = virConnectDomainQemuEventRegister(conn, dom, eventName, + cb, pyobj_cbData, + libvirt_qemu_virConnectDomainEventFreeFunc); + LIBVIRT_END_ALLOW_THREADS; + if (ret < 0) { + Py_DECREF(pyobj_cbData); + } + py_retval = libvirt_intWrap(ret); + return (py_retval); +} + +static PyObject * +libvirt_qemu_virConnectDomainQemuEventDeregister(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *py_retval; + PyObject *pyobj_conn; + int callbackID; + virConnectPtr conn; + int ret = 0; + + if (!PyArg_ParseTuple + (args, (char *) "Oi:virConnectDomainQemuEventDeregister", + &pyobj_conn, &callbackID)) + return (NULL); + + DEBUG("libvirt_qemu_virConnectDomainQemuEventDeregister(%p) called\n", pyobj_conn); + + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + + ret = virConnectDomainQemuEventDeregister(conn, callbackID); + + LIBVIRT_END_ALLOW_THREADS; + py_retval = libvirt_intWrap(ret); + return (py_retval); +} + /************************************************************************ * * * The registration stuff * @@ -104,6 +242,8 @@ libvirt_qemu_virDomainQemuMonitorCommand(PyObject *self ATTRIBUTE_UNUSED, static PyMethodDef libvirtQemuMethods[] = { #include "libvirt-qemu-export.c" {(char *) "virDomainQemuMonitorCommand", libvirt_qemu_virDomainQemuMonitorCommand, METH_VARARGS, NULL}, + {(char *) "virConnectDomainQemuEventRegister", libvirt_qemu_virConnectDomainQemuEventRegister, METH_VARARGS, NULL}, + {(char *) "virConnectDomainQemuEventDeregister", libvirt_qemu_virConnectDomainQemuEventDeregister, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; diff --git a/python/libvirt-qemu-override.py b/python/libvirt-qemu-override.py new file mode 100644 index 0000000..9838941 --- /dev/null +++ b/python/libvirt-qemu-override.py @@ -0,0 +1,55 @@ +# +# Manually written part of python bindings for libvirt_qemu +# + +# On cygwin, the DLL is called cygvirtmod.dll +try: + import libvirtmod_qemu +except ImportError, lib_e: + try: + import cygvirtmod_qemu as libvirtmod_qemu + except ImportError, cyg_e: + if str(cyg_e).count("No module named"): + raise lib_e + +import libvirt +import types + +def _dispatchDomainEventUnknownCallback(dom, eventName, eventArgs, cbData): + """Dispatches events to python user unknown event callbacks + """ + try: + cb = cbData["cb"] + opaque = cbData["opaque"] + conn = cbData["conn"] + cb(conn, libvirt.virDomain(conn, _obj=dom), eventName, eventArgs, opaque) + return 0 + except AttributeError: + pass + +def domainQemuEventDeregister(conn, callbackID): + """Removes a Domain Event Callback. De-registering for a + domain callback will disable delivery of this event type """ + try: + ret = libvirtmod_qemu.virConnectDomainQemuEventDeregister(conn._o, callbackID) + if ret == -1: raise libvirt.libvirtError ('virConnectDomainQemuEventDeregister() failed', conn) + del conn.domainEventCallbackID[callbackID] + except AttributeError: + pass + +def domainQemuEventRegister(conn, dom, eventName, cb, opaque): + """Adds a Domain Event Callback. Registering for a domain + callback will enable delivery of the events """ + if not hasattr(conn, 'domainEventCallbackID'): + conn.domainEventCallbackID = {} + if not hasattr(conn, '_dispatchDomainEventUnknownCallback'): + conn._dispatchDomainEventUnknownCallback = _dispatchDomainEventUnknownCallback + cbData = { "cb": cb, "conn": conn, "opaque": opaque } + if dom is None: + ret = libvirtmod_qemu.virConnectDomainQemuEventRegister(conn._o, None, eventName, cbData) + else: + ret = libvirtmod_qemu.virConnectDomainQemuEventRegister(conn._o, dom._o, eventName, cbData) + if ret == -1: + raise libvirt.libvirtError ('virConnectDomainQemuEventRegister() failed', conn) + conn.domainEventCallbackID[ret] = opaque + return ret diff --git a/python/libvirt_qemu.py b/python/libvirt_qemu.py new file mode 100644 index 0000000..9a2a8fd --- /dev/null +++ b/python/libvirt_qemu.py @@ -0,0 +1,105 @@ +#! /usr/bin/python -i +# +# WARNING WARNING WARNING WARNING +# +# This file is automatically written by generator.py. Any changes +# made here will be lost. +# +# To change the manually written methods edit libvirt-qemu-override.py +# To change the automatically written methods edit generator.py +# +# WARNING WARNING WARNING WARNING +# +# +# Manually written part of python bindings for libvirt_qemu +# + +# On cygwin, the DLL is called cygvirtmod.dll +try: + import libvirtmod_qemu +except ImportError, lib_e: + try: + import cygvirtmod_qemu as libvirtmod_qemu + except ImportError, cyg_e: + if str(cyg_e).count("No module named"): + raise lib_e + +import libvirt +import types + +def _dispatchDomainEventUnknownCallback(dom, eventName, eventArgs, cbData): + """Dispatches events to python user unknown event callbacks + """ + try: + cb = cbData["cb"] + opaque = cbData["opaque"] + conn = cbData["conn"] + cb(conn, libvirt.virDomain(conn, _obj=dom), eventName, eventArgs, opaque) + return 0 + except AttributeError: + pass + +def domainQemuEventDeregister(conn, callbackID): + """Removes a Domain Event Callback. De-registering for a + domain callback will disable delivery of this event type """ + try: + ret = libvirtmod_qemu.virConnectDomainQemuEventDeregister(conn._o, callbackID) + if ret == -1: raise libvirt.libvirtError ('virConnectDomainQemuEventDeregister() failed', conn) + del conn.domainEventCallbackID[callbackID] + except AttributeError: + pass + +def domainQemuEventRegister(conn, dom, eventName, cb, opaque): + """Adds a Domain Event Callback. Registering for a domain + callback will enable delivery of the events """ + if not hasattr(conn, 'domainEventCallbackID'): + conn.domainEventCallbackID = {} + if not hasattr(conn, '_dispatchDomainEventUnknownCallback'): + conn._dispatchDomainEventUnknownCallback = _dispatchDomainEventUnknownCallback + cbData = { "cb": cb, "conn": conn, "opaque": opaque } + if dom is None: + ret = libvirtmod_qemu.virConnectDomainQemuEventRegister(conn._o, None, eventName, cbData) + else: + ret = libvirtmod_qemu.virConnectDomainQemuEventRegister(conn._o, dom._o, eventName, cbData) + if ret == -1: + raise libvirt.libvirtError ('virConnectDomainQemuEventRegister() failed', conn) + conn.domainEventCallbackID[ret] = opaque + return ret +# +# WARNING WARNING WARNING WARNING +# +# Automatically written part of python bindings for libvirt +# +# WARNING WARNING WARNING WARNING +def qemuMonitorCommand(domain, cmd, flags): + """Send an arbitrary monitor command through qemu monitor of domain """ + ret = libvirtmod_qemu.virDomainQemuMonitorCommand(domain._o, cmd, flags) + if ret is None: raise libvirt.libvirtError('virDomainQemuMonitorCommand() failed') + return ret + +def qemuAttach(conn, pid, flags): + """This API is QEMU specific, so it will only work with hypervisor + connections to the QEMU driver. + + This API will attach to an externally launched QEMU process + identified by @pid. There are several requirements to successfully + attach to an external QEMU process: + + - It must have been started with a monitor socket using the UNIX + domain socket protocol. + - No device hotplug/unplug, or other configuration changes can + have been made via the monitor since it started. + - The '-name' and '-uuid' arguments should have been set (not + mandatory, but strongly recommended) + + If successful, then the guest will appear in the list of running + domains for this connection, and other APIs should operate + normally (provided the above requirements were honored). """ + ret = libvirtmod_qemu.virDomainQemuAttach(conn._o, pid, flags) + if ret is None: raise libvirt.libvirtError('virDomainQemuAttach() failed') + __tmp = virDomain(conn,_obj=ret) + return __tmp + +# virDomainQemuMonitorCommandFlags +VIR_DOMAIN_QEMU_MONITOR_COMMAND_DEFAULT = 0 +VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP = 1 -- 1.7.5.4

On 12/16/2011 09:59 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- python/generator.py | 28 +++++---- python/libvirt-qemu-override.c | 140 +++++++++++++++++++++++++++++++++++++++ python/libvirt-qemu-override.py | 55 +++++++++++++++ python/libvirt_qemu.py | 105 +++++++++++++++++++++++++++++ 4 files changed, 316 insertions(+), 12 deletions(-) create mode 100644 python/libvirt-qemu-override.py create mode 100644 python/libvirt_qemu.py
I haven't read this closely; I suspect it is similar to existing event handling, which helps. I also wonder if it is possible to set up a virsh binding for event registration, although I don't know that we have really done that in the past. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> this patch is just used to the new register API add two command, register-event and deregister-event in virsh in order to test the new API. there will come an "RESUME" events when qemu domains are started. virish starts a qemu domain and to catch "RESUME" event. virsh# register-event RESUME virsh# start domain Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- tools/virsh.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 106 insertions(+), 0 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 3654589..392768f 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -36,6 +36,7 @@ #include <libxml/parser.h> #include <libxml/tree.h> +#include <libxml/uri.h> #include <libxml/xpath.h> #include <libxml/xmlsave.h> @@ -53,6 +54,8 @@ #include "memory.h" #include "xml.h" #include "libvirt/libvirt-qemu.h" +#include "libvirt/libvirt.h" +#include "datatypes.h" #include "virfile.h" #include "event_poll.h" #include "configmake.h" @@ -11976,6 +11979,107 @@ cmdVNCDisplay(vshControl *ctl, const vshCmd *cmd) return ret; } +static const vshCmdInfo info_deregevent[] = { + {"help", N_("deregister an qemu event")}, + {"desc", N_("you please input you eventID, that the register-event returns.")}, + {NULL, NULL} +}; +static const vshCmdOptDef opts_deregevent[] = { + {"eventID", VSH_OT_INT, VSH_OFLAG_REQ, N_("deregister qemu spici eventID ")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdDeregisterEvent(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + bool ret = false; + int eventID = -1; + int retEventID = -1; + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (vshCommandOptInt(cmd, "eventID", &eventID) < 0) { + vshError(ctl, "%s", _("Please specify valid eventID")); + return false; + } + if (eventID < 0) { + fprintf(stdout, "please input a positive Int\n"); + return false; + } + + if ((retEventID = virConnectDomainQemuEventDeregister(ctl->conn, eventID)) < 0) { + fprintf(stdout, "eventID: %d Deregister error.\n", eventID); + return false; + } + fprintf(stdout, "event Deregister success, the remote callbackID is %d.\n", retEventID); + ret = true; + cleanup: + return ret; +} + +static const vshCmdInfo info_regevent[] = { + {"help", N_("register an qemu event")}, + {"desc", N_("you please input you event name.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_regevent[] = { + {"event", VSH_OT_DATA, VSH_OFLAG_REQ, N_("register qemu spici event ")}, + {NULL, 0, 0, NULL} +}; + +void printRegistEvent(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque){ + char *uriName = virConnectGetURI (conn); + fputc('\n', stdout); + if (uriName != NULL) + fprintf(stdout, "connect URI: %s, ",uriName); + + if (dom != NULL) + fprintf(stdout, "dom: %s(%d) receive an event:\n", dom->name, dom->id); + + if (eventName != NULL) + fprintf(stdout, "{ event: %s", eventName); + if (eventArgs != NULL) + fprintf(stdout, ", data: %s }\n", eventArgs); + else + fprintf(stdout, " }\n"); + fprintf(stdout, "----------------------------------------------"); + fputc('\n', stdout); +} + +static bool +cmdRegisterEvent(vshControl *ctl, const vshCmd *cmd) +{ + const char *name = NULL; + virDomainPtr dom; + bool ret = false; + int eventID = -1; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (vshCommandOptString(cmd, "event", &name) < 0) { + vshError(ctl, "%s", _("Please specify valid event name")); + return false; + } + + dom = NULL; + eventID = virConnectDomainQemuEventRegister(ctl->conn, dom, name, printRegistEvent, NULL, NULL); + if (eventID < 0) { + fprintf(stdout, "%s event register error.\n", name); + return false; + } + fprintf(stdout, "%s event call back ID is %d, you can use it to deregister the event.\n", name, eventID); + ret = true; + cleanup: + return ret; +} + /* * "ttyconsole" command */ @@ -15363,6 +15467,8 @@ static const vshCmdDef domManagementCmds[] = { {"vcpupin", cmdVcpuPin, opts_vcpupin, info_vcpupin, 0}, {"version", cmdVersion, opts_version, info_version, 0}, {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay, 0}, + {"register-event", cmdRegisterEvent, opts_regevent, info_regevent, 0}, + {"deregister-event", cmdDeregisterEvent, opts_deregevent, info_deregevent, 0}, {NULL, NULL, NULL, NULL, 0} }; -- 1.7.5.4

On 12/16/2011 09:59 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
this patch is just used to the new register API
add two command, register-event and deregister-event in virsh in order to test the new API.
there will come an "RESUME" events when qemu domains are started.
virish starts a qemu domain and to catch "RESUME" event. virsh# register-event RESUME virsh# start domain
I'd name this qemu-register-event, to make it obvious that it depends on libvirt-qemu.
+void printRegistEvent(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque){ + char *uriName = virConnectGetURI (conn); + fputc('\n', stdout); + if (uriName != NULL) + fprintf(stdout, "connect URI: %s, ",uriName); + + if (dom != NULL) + fprintf(stdout, "dom: %s(%d) receive an event:\n", dom->name, dom->id); + + if (eventName != NULL) + fprintf(stdout, "{ event: %s", eventName); + if (eventArgs != NULL) + fprintf(stdout, ", data: %s }\n", eventArgs); + else + fprintf(stdout, " }\n"); + fprintf(stdout, "----------------------------------------------"); + fputc('\n', stdout); +}
Hmm, looks like it does as much as can be expected when receiving an event.
+ +static bool +cmdRegisterEvent(vshControl *ctl, const vshCmd *cmd) +{ + const char *name = NULL; + virDomainPtr dom; + bool ret = false; + int eventID = -1; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (vshCommandOptString(cmd, "event", &name) < 0) { + vshError(ctl, "%s", _("Please specify valid event name")); + return false; + } + + dom = NULL; + eventID = virConnectDomainQemuEventRegister(ctl->conn, dom, name, printRegistEvent, NULL, NULL); + if (eventID < 0) { + fprintf(stdout, "%s event register error.\n", name); + return false; + } + fprintf(stdout, "%s event call back ID is %d, you can use it to deregister the event.\n", name, eventID); + ret = true; + cleanup: + return ret; +}
What happens if virsh dies without deregistering the event? I'm guessing this is the sort of task where you'd either have to run virsh in command mode (not batch mode). Perhaps it might also be worth setting up a mode where you can do: virsh qemu-register-event --wait domain RESUME & which then blocks that virsh instance until a RESUME event arrives, so that you can use backgrounded virsh tasks as a one-shot view into an event firing.
@@ -15363,6 +15467,8 @@ static const vshCmdDef domManagementCmds[] = { {"vcpupin", cmdVcpuPin, opts_vcpupin, info_vcpupin, 0}, {"version", cmdVersion, opts_version, info_version, 0}, {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay, 0}, + {"register-event", cmdRegisterEvent, opts_regevent, info_regevent, 0}, + {"deregister-event", cmdDeregisterEvent, opts_deregevent, info_deregevent, 0}, {NULL, NULL, NULL, NULL, 0}
Sort these new commands with the other qemu-* commands. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

From: ShaoHe Feng <shaohef@linux.vnet.ibm.com> there will come an "RESUME" events when qemu domains are started. this python test can catch the "RESUME" event Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- qemu-event-test.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 57 insertions(+), 0 deletions(-) create mode 100755 qemu-event-test.py diff --git a/qemu-event-test.py b/qemu-event-test.py new file mode 100755 index 0000000..cf33d04 --- /dev/null +++ b/qemu-event-test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +import libvirt +import libvirt_qemu +import threading +import time +import signal +import os +import sys + +eventLoopThread = None + +def eventLoopRun(): + while True: + libvirt.virEventRunDefaultImpl() + +def eventLoopStart(): + global eventLoopThread + + libvirt.virEventRegisterDefaultImpl() + eventLoopThread = threading.Thread(target=eventLoopRun, name="libvirtEventLoop") + eventLoopThread.setDaemon(True) + eventLoopThread.start() + + +def reportDefault(conn, dom, eventName,eventArgs,opaque): + print "DEFAULT EVENT: Domain %s(%s), pass the unknown event:%s, arguments is: %s" % (dom.name(), dom.ID(), eventName, eventArgs) + +eventStrings = ( + "RESUME", + "VNC_CONNECTED", + "VNC_DISCONNECTED", + "VNC_INITIALIZED", + "RESET", + "SHUTDOWN", + "HELLO" ); +eventID = {}; + +def signal_handler(signal, frame): + print "deregister event: %d" %(eventID[0]) + #libvirt_qemu.domainQemuEventDeregister(conn, eventID[0]) + print "test safely exit" + sys.exit(0) + +def main(): + eventLoopStart() + signal.signal(signal.SIGINT, signal_handler) + conn = libvirt.openReadOnly('qemu:///system') + eventID[0] = libvirt_qemu.domainQemuEventRegister(conn, None, eventStrings[0], reportDefault, None) + + while 1: + time.sleep(1) + +if __name__ == "__main__": + main() + + -- 1.7.5.4

there are still some questions: In my code the add new libvirt_qemu specific events and the former libvirt events share the same event queue. so in libvirtd, the qemuDomainEventFlush will dispatch these two kinds of event. so should I add new event queue for the new libvirt_qemu specific events, in order to isolate them from the rest events of libvirt? I think the qemuMonitorJSONIOProcessEvent has better to filter the register event names, but I did not know the best way to add these register event names list. , shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Basically, this feature can go along with qemu monitor passthrough. That way, if we use new commands in the monitor that generate new events, we want some way to receive those new events too.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- include/libvirt/libvirt-qemu.h | 27 ++++ include/libvirt/libvirt.h.in | 2 +- src/conf/domain_event.c | 293 ++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 50 ++++++- src/driver.h | 14 ++ src/libvirt-qemu.c | 189 ++++++++++++++++++++++++++ src/libvirt_private.syms | 6 + src/libvirt_qemu.syms | 5 + 8 files changed, 571 insertions(+), 15 deletions(-)
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 7f12e4f..3aa944a 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -32,6 +32,33 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid, unsigned int flags);
+/** + * virConnectDomainQemuEventCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @eventName : the name of the unknow or un-implementation event + * @eventArgs: the content of the unknow or un-implementation event + * + * The callback signature to use when registering for an event of type + * VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN with virConnectDomainQemuEventRegister() + */ +typedef void (*virConnectDomainQemuEventCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque); + +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb); +int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID); + # ifdef __cplusplus } # endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2480add..9fcb400 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3207,7 +3207,6 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, int type, int status, void *opaque); - /** * virConnectDomainEventDiskChangeReason: * @@ -3263,6 +3262,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */ VIR_DOMAIN_EVENT_ID_DISK_CHANGE = 9, /* virConnectDomainEventDiskChangeCallback */ + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN = 10, /* virConnectDomainEventDefaultCallback */
/* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 614ab97..0388a66 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -45,7 +45,9 @@ typedef virDomainMeta *virDomainMetaPtr;
struct _virDomainEventCallback { int callbackID; + int qemuCallbackID; int eventID; + char *eventName; virConnectPtr conn; virDomainMetaPtr dom; virConnectDomainEventGenericCallback cb; @@ -94,6 +96,10 @@ struct _virDomainEvent { char *devAlias; int reason; } diskChange; + struct { + char *eventName; + char *eventArgs; + }qemuUnknownEvent; } data; };
@@ -112,6 +118,7 @@ virDomainEventCallbackListFree(virDomainEventCallbackListPtr list)
for (i=0; i<list->count; i++) { virFreeCallback freecb = list->callbacks[i]->freecb; + VIR_FREE(list->callbacks[i]->eventName); if (freecb) (*freecb)(list->callbacks[i]->opaque); VIR_FREE(list->callbacks[i]); @@ -187,8 +194,10 @@ virDomainEventCallbackListRemoveID(virConnectPtr conn, if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]); - if (i < (cbList->count - 1)) memmove(cbList->callbacks + i, cbList->callbacks + i + 1, @@ -231,6 +240,9 @@ virDomainEventCallbackListRemoveConn(virConnectPtr conn, if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]);
if (i < (cbList->count - 1)) @@ -299,6 +311,9 @@ int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList) if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]);
if (i < (cbList->count - 1)) @@ -404,7 +419,98 @@ virDomainEventCallbackListAddID(virConnectPtr conn,
cbList->callbacks[cbList->count] = event; cbList->count++; + event->callbackID = cbList->nextID++; + + return event->callbackID; + +no_memory: + virReportOOMError(); + + if (event) { + if (event->dom) + VIR_FREE(event->dom->name); + VIR_FREE(event->dom); + } + VIR_FREE(event); + return -1; +} + + + +/** + * virDomainEventCallbackListAddName: + * @conn: pointer to the connection + * @cbList: the list + * @eventName: the event eventName + * @callback: the callback to add + * @eventID: the specific eventID + * @opaque: opaque data tio pass to callback + * + * Internal function to add a callback from a virDomainEventCallbackListPtr + */ +int +virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + virDomainEventCallbackPtr event; + int i; + + /* 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_DOMAIN_EVENT_CALLBACK(callback) && + STREQ(cbList->callbacks[i]->eventName, eventName) && + cbList->callbacks[i]->eventID == eventID && + cbList->callbacks[i]->conn == conn) { + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("event callback already tracked")); + return -1; + } + } + if (eventID > VIR_DOMAIN_EVENT_ID_LAST || eventID < VIR_DOMAIN_EVENT_ID_LIFECYCLE) { + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("not suport this kind of eventID: %d"), eventID); + } + /* Allocate new event */ + if (VIR_ALLOC(event) < 0) + goto no_memory; + event->conn = conn; + event->cb = callback; + if (eventName == NULL) + goto no_memory; + event->eventName = strdup(eventName); + if ( event->eventName == NULL) + goto no_memory; + event->opaque = opaque; + event->freecb = freecb; + event->eventID = eventID; + if (dom) { + if (VIR_ALLOC(event->dom) < 0) + goto no_memory; + if (!(event->dom->name = strdup(dom->name))) + goto no_memory; + memcpy(event->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); + event->dom->id = dom->id; + }
+ /* Make space on list */ + if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) + goto no_memory; + + event->conn->refs++; + + cbList->callbacks[cbList->count] = event; + cbList->count++; event->callbackID = cbList->nextID++;
return event->callbackID; @@ -416,11 +522,40 @@ no_memory: if (event->dom) VIR_FREE(event->dom->name); VIR_FREE(event->dom); + VIR_FREE(event->eventName); } VIR_FREE(event); return -1; }
+/** + * virDomainEventCallbackListAddQemuCallbackID: + * @conn: pointer to the connection + * @cbList: the list + * @callbackID: the libvirt callback ID + * @qemuCallbackID: the libvirtd callback ID to add + * + * Internal function to add a Daemon libvirtd callbackID + */ +int +virDomainEventCallbackListAddQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID, + int qemuCallbackID) +{ + int i; + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->callbackID == callbackID && + cbList->callbacks[i]->conn == conn) { + cbList->callbacks[i]->qemuCallbackID = qemuCallbackID; + return 0; + } + } + + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not find event callback for deletion")); + return -1; +}
int virDomainEventCallbackListCountID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, @@ -442,6 +577,27 @@ int virDomainEventCallbackListCountID(virConnectPtr conn, }
+int +virDomainEventCallbackListCountName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + const char *eventName) +{ + int i; + int count = 0; + + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->deleted) + continue; + + if (STREQ(cbList->callbacks[i]->eventName,eventName) && + cbList->callbacks[i]->conn == conn) + count++; + } + + return count; +} + + int virDomainEventCallbackListEventID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, int callbackID) @@ -461,6 +617,44 @@ int virDomainEventCallbackListEventID(virConnectPtr conn, }
+const char* +virDomainEventCallbackListEventName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int 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]->eventName; + } + + return NULL; +} + +int +virDomainEventCallbackListEventQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int 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]->qemuCallbackID; + } + + return -1; +} + int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList) { int i; @@ -521,6 +715,11 @@ void virDomainEventFree(virDomainEventPtr event) VIR_FREE(event->data.diskChange.newSrcPath); VIR_FREE(event->data.diskChange.devAlias); break; + + case VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN: + VIR_FREE(event->data.qemuUnknownEvent.eventName); + VIR_FREE(event->data.qemuUnknownEvent.eventArgs); + break; }
VIR_FREE(event->dom.name); @@ -956,6 +1155,51 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, path, type, status); }
+static virDomainEventPtr +virDomainEventUnknownNew(int id, const char *name, unsigned char *uuid, + const char *eventName, const char *eventArgs) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN, + id, name, uuid); + if (ev) { + if (!(ev->data.qemuUnknownEvent.eventName = strdup(eventName))) { + virReportOOMError(); + VIR_FREE(ev->dom.name); + VIR_FREE(ev); + return NULL; + } + if (eventArgs) { + if (!(ev->data.qemuUnknownEvent.eventArgs = strdup(eventArgs))) { + virReportOOMError(); + VIR_FREE(ev->data.qemuUnknownEvent.eventName); + VIR_FREE(ev->dom.name); + VIR_FREE(ev); + return NULL; + } + } + } + + return ev; +} + +virDomainEventPtr virDomainEventUnknownNewFromObj(virDomainObjPtr obj, + const char *eventName, + const char *eventArgs) +{ + + return virDomainEventUnknownNew(obj->def->id, obj->def->name, + obj->def->uuid, eventName, eventArgs); +} + +virDomainEventPtr virDomainEventUnknownNewFromDom(virDomainPtr dom, + const char *eventName, + const char *eventArgs) +{ + return virDomainEventUnknownNew(dom->id, dom->name, dom->uuid, + eventName, eventArgs); +} + virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom) { virDomainEventPtr ev = @@ -1095,11 +1339,12 @@ virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, }
-void virDomainEventDispatchDefaultFunc(virConnectPtr conn, - virDomainEventPtr event, - virConnectDomainEventGenericCallback cb, - void *cbopaque, - void *opaque ATTRIBUTE_UNUSED) +void +virDomainEventDispatchDefaultFunc(virConnectPtr conn, + virDomainEventPtr event, + virConnectDomainEventGenericCallback cb, + void *cbopaque, + void *opaque ATTRIBUTE_UNUSED) { virDomainPtr dom = virGetDomain(conn, event->dom.name, event->dom.uuid); if (!dom) @@ -1180,6 +1425,13 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break;
+ case VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN: + ((virConnectDomainQemuEventCallback)cb)(conn, dom, + event->data.qemuUnknownEvent.eventName, + event->data.qemuUnknownEvent.eventArgs, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; @@ -1189,8 +1441,9 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, }
-static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, - virDomainEventCallbackPtr cb) +static int +virDomainEventDispatchMatchCallback(virDomainEventPtr event, + virDomainEventCallbackPtr cb) { if (!cb) return 0; @@ -1198,7 +1451,12 @@ static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, return 0; if (cb->eventID != event->eventID) return 0; - + if (event->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + if (event->data.qemuUnknownEvent.eventName == NULL || + cb->eventName == NULL || + STRNEQ(cb->eventName, event->data.qemuUnknownEvent.eventName)) + return 0; + } if (cb->dom) { /* Deliberately ignoring 'id' for matching, since that * will cause problems when a domain switches between @@ -1341,3 +1599,20 @@ virDomainEventStateDeregisterAny(virConnectPtr conn, virDomainEventStateUnlock(state); return ret; } +int +virDomainQemuEventStateDeregister(virConnectPtr conn, + virDomainEventStatePtr state, + int callbackID) +{ + int ret; + + virDomainEventStateLock(state); + if (state->isDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, + state->callbacks, callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, + state->callbacks, callbackID); + virDomainEventStateUnlock(state); + return ret; +} diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 3ba418e..f2fe847 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -83,14 +83,23 @@ int virDomainEventCallbackListAddID(virConnectPtr conn, virFreeCallback freecb) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(5);
+int virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(6);
int virDomainEventCallbackListRemove(virConnectPtr conn, virDomainEventCallbackListPtr cbList, virConnectDomainEventCallback callback) ATTRIBUTE_NONNULL(1); -int virDomainEventCallbackListRemoveID(virConnectPtr conn, - virDomainEventCallbackListPtr cbList, - int callbackID) +int virDomainQemuEventCallbackListRemoveID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) ATTRIBUTE_NONNULL(1); int virDomainEventCallbackListRemoveConn(virConnectPtr conn, virDomainEventCallbackListPtr cbList) @@ -106,9 +115,14 @@ int virDomainEventCallbackListMarkDeleteID(virConnectPtr conn, int callbackID) ATTRIBUTE_NONNULL(1);
- int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList);
+int virDomainEventCallbackListAddQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID, + int qemuCallbackID) + ATTRIBUTE_NONNULL(1); + int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList); int virDomainEventCallbackListCountID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, @@ -119,6 +133,21 @@ int virDomainEventCallbackListEventID(virConnectPtr conn, int callbackID) ATTRIBUTE_NONNULL(1);
+int virDomainEventCallbackListCountName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + const char *eventName) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); + +int virDomainEventCallbackListEventQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + +const char* virDomainEventCallbackListEventName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + virDomainEventQueuePtr virDomainEventQueueNew(void);
virDomainEventPtr virDomainEventNew(int id, const char *name, const unsigned char *uuid, int type, int detail); @@ -190,6 +219,13 @@ virDomainEventPtr virDomainEventDiskChangeNewFromDom(virDomainPtr dom, const char *devAlias, int reason);
+virDomainEventPtr virDomainEventUnknownNewFromObj(virDomainObjPtr obj, + const char *eventName, + const char *eventArgs); +virDomainEventPtr virDomainEventUnknownNewFromDom(virDomainPtr dom, + const char *eventName, + const char *eventArgs); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event);
@@ -246,5 +282,9 @@ virDomainEventStateDeregisterAny(virConnectPtr conn, virDomainEventStatePtr state, int callbackID) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - +int +virDomainQemuEventStateDeregister(virConnectPtr conn, + virDomainEventStatePtr state, + int callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); #endif diff --git a/src/driver.h b/src/driver.h index 941ff51..51164a9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -635,6 +635,18 @@ typedef virDomainPtr unsigned int flags);
typedef int + (*virDrvDomainQemuEventRegister)(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +typedef int + (*virDrvDomainQemuEventDeregister)(virConnectPtr conn, + int callbackID); + +typedef int (*virDrvDomainOpenConsole)(virDomainPtr dom, const char *dev_name, virStreamPtr st, @@ -915,6 +927,8 @@ struct _virDriver { virDrvDomainSnapshotDelete domainSnapshotDelete; virDrvDomainQemuMonitorCommand qemuDomainMonitorCommand; virDrvDomainQemuAttach qemuDomainAttach; + virDrvDomainQemuEventRegister qemuDomainQemuEventRegister; + virDrvDomainQemuEventDeregister qemuDomainQemuEventDeregister; virDrvDomainOpenConsole domainOpenConsole; virDrvDomainOpenGraphics domainOpenGraphics; virDrvDomainInjectNMI domainInjectNMI; diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index 248cc33..7722b7b 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -36,6 +36,77 @@ virReportErrorHelper(VIR_FROM_DOM, error, NULL, __FUNCTION__, \ __LINE__, info)
+/* Helper macros to implement VIR_DOMAIN_DEBUG using just C99. This + * assumes you pass fewer than 15 arguments to VIR_DOMAIN_DEBUG, but + * can easily be expanded if needed. + * + * Note that gcc provides extensions of "define a(b...) b" or + * "define a(b,...) b,##__VA_ARGS__" as a means of eliding a comma + * when no var-args are present, but we don't want to require gcc. + */ +#define VIR_ARG15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 +#define VIR_HAS_COMMA(...) VIR_ARG15(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) + +/* Form the name VIR_DOMAIN_DEBUG_[01], then call that macro, + * according to how many arguments are present. Two-phase due to + * macro expansion rules. */ +#define VIR_DOMAIN_DEBUG_EXPAND(a, b, ...) \ + VIR_DOMAIN_DEBUG_PASTE(a, b, __VA_ARGS__) +#define VIR_DOMAIN_DEBUG_PASTE(a, b, ...) \ + a##b(__VA_ARGS__) + +/* Internal use only, when VIR_DOMAIN_DEBUG has one argument. */ +#define VIR_DOMAIN_DEBUG_0(dom) \ + VIR_DOMAIN_DEBUG_2(dom, "%s", "") + +/* Internal use only, when VIR_DOMAIN_DEBUG has three or more arguments. */ +#define VIR_DOMAIN_DEBUG_1(dom, fmt, ...) \ + VIR_DOMAIN_DEBUG_2(dom, ", " fmt, __VA_ARGS__) + +/* Internal use only, with final format. */ +#define VIR_DOMAIN_DEBUG_2(dom, fmt, ...) \ + do { \ + char _uuidstr[VIR_UUID_STRING_BUFLEN]; \ + const char *_domname = NULL; \ + \ + if (!VIR_IS_DOMAIN(dom)) { \ + memset(_uuidstr, 0, sizeof(_uuidstr)); \ + } else { \ + virUUIDFormat((dom)->uuid, _uuidstr); \ + _domname = (dom)->name; \ + } \ + \ + VIR_DEBUG("dom=%p, (VM: name=%s, uuid=%s)" fmt, \ + dom, NULLSTR(_domname), _uuidstr, __VA_ARGS__); \ + } while (0) + +/** + * VIR_DOMAIN_DEBUG: + * @dom: domain + * @fmt: optional format for additional information + * @...: optional arguments corresponding to @fmt. + */ +#define VIR_DOMAIN_DEBUG(...) \ + VIR_DOMAIN_DEBUG_EXPAND(VIR_DOMAIN_DEBUG_, \ + VIR_HAS_COMMA(__VA_ARGS__), \ + __VA_ARGS__) + +/** + * VIR_UUID_DEBUG: + * @conn: connection + * @uuid: possibly null UUID array + */ +#define VIR_UUID_DEBUG(conn, uuid) \ + do { \ + if (uuid) { \ + char _uuidstr[VIR_UUID_STRING_BUFLEN]; \ + virUUIDFormat(uuid, _uuidstr); \ + VIR_DEBUG("conn=%p, uuid=%s", conn, _uuidstr); \ + } else { \ + VIR_DEBUG("conn=%p, uuid=(null)", conn); \ + } \ + } while (0) + /** * virDomainQemuMonitorCommand: * @domain: a domain object @@ -178,3 +249,121 @@ error: virDispatchError(conn); return NULL; } + +/** + * virConnectDomainQemuEventRegister: + * @conn: pointer to the connection + * @dom: pointer to the domain + * @eventName: the event Name to receive + * @cb: callback to the function handling domain events + * @opaque: opaque data to pass on to the callback + * @freecb: optional function to deallocate opaque when not used anymore + * + * Adds a callback to receive notifications of arbitrary qemu domain events + * occurring on a domain. + * + * If dom is NULL, then events will be monitored for any domain. If dom + * is non-NULL, then only the specific domain will be monitored + * + * Most types of event have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus neccessary to use + * the VIR_DOMAIN_EVENT_CALLBACK() macro to cast the supplied function pointer + * to match the signature of this method. + * + * The virDomainPtr object handle passed into the callback upon delivery + * of an event is only valid for the duration of execution of the callback. + * If the callback wishes to keep the domain object after the callback returns, + * it shall take a reference to it, by calling virDomainRef. + * The reference can be released once the object is no longer required + * by calling virDomainFree. + * + * The return value from this method is a positive integer identifier + * for the callback. To unregister a callback, this callback ID should + * be passed to the virConnectDomainQemuEventDeregister method + * + * Returns a callback identifier on success, -1 on failure + */ +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DOMAIN_DEBUG(dom, "conn=%p, eventName=%s, cb=%p, opaque=%p, freecb=%p", + conn, eventName, cb, opaque, freecb); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (dom != NULL && + !(VIR_IS_CONNECTED_DOMAIN(dom) && dom->conn == conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(conn); + return -1; + } + if (eventName == NULL || cb == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if ((conn->driver) && (conn->driver->qemuDomainQemuEventRegister)) { + int ret; + ret = conn->driver->qemuDomainQemuEventRegister(conn, dom, eventName, cb, opaque, freecb); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectDomainQemuEventDeregister: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * vaule obtained from a previous virConnectDomainQemuEventDeregister method. + * + * Returns 0 on success, -1 on failure + */ +int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID) +{ + + VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (callbackID < 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if ((conn->driver) && (conn->driver->qemuDomainQemuEventDeregister)) { + int ret; + ret = conn->driver->qemuDomainQemuEventDeregister(conn, callbackID); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 48ffdf2..75e544a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -471,11 +471,16 @@ virDomainWatchdogModelTypeToString; # domain_event.h virDomainEventBlockJobNewFromObj; virDomainEventBlockJobNewFromDom; +virDomainEventUnknownNewFromObj; +virDomainEventunknownNewFromDom; virDomainEventCallbackListAdd; virDomainEventCallbackListAddID; +virDomainEventCallbackListAddName; virDomainEventCallbackListCount; virDomainEventCallbackListCountID; +virDomainEventCallbackListCountName; virDomainEventCallbackListEventID; +virDomainEventCallbackListEventName; virDomainEventCallbackListFree; virDomainEventCallbackListMarkDelete; virDomainEventCallbackListMarkDeleteID; @@ -512,6 +517,7 @@ virDomainEventRebootNewFromDom; virDomainEventRebootNewFromObj; virDomainEventStateDeregister; virDomainEventStateDeregisterAny; +virDomainQemuEventStateDeregister; virDomainEventStateFlush; virDomainEventStateFree; virDomainEventStateNew; diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms index 8447730..a17e387 100644 --- a/src/libvirt_qemu.syms +++ b/src/libvirt_qemu.syms @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; +LIBVIRT_QEMU_0.9.9 { + global: + virConnectDomainQemuEventRegister; + virConnectDomainQemuEventDeregister; +} LIBVIRT_QEMU_0.9.4;

I want to improve this patch. Is it the right place that I add an event name list as an attribute of _virDomainEventState. if the virConnectDomainQemuEventRegister() registers an eventName, then I add the eventName to this name list. and if virConnectDomainQemuEventDeregister() deregisters an eventName, then I delete the eventName from this name list. Different connects may register the same eventName. Yes, I should mark the times of the eventName registered. when an qemu monitor receives an event, so it can traversal this event name list. if it can find the event name list, then it can add to the qemu_driver event queue. should I also add the dom to this event name list to filter the event, for the dom argument of virConnectDomainQemuEventRegister may not be NULL. And should I limit the times of the event name registered. for user may register an event name that does not exist in qemu at all. there is the two API, that we have discussed before. virConnectDomainQemuEventRegister(virConnectPtr conn, virDomainPtr dom, /* option to filter */ const char *eventName, /* JSON event name */ virConnectDomainQemuEventCallback cb, void *opaque, virFreeCallback freecb); virConnectDomainQemuEventDeregister(virConnectPtr conn, int callbackID); , ShaoHe Feng wrote:
there are still some questions:
In my code the add new libvirt_qemu specific events and the former libvirt events share the same event queue. so in libvirtd, the qemuDomainEventFlush will dispatch these two kinds of event. so should I add new event queue for the new libvirt_qemu specific events, in order to isolate them from the rest events of libvirt?
I think the qemuMonitorJSONIOProcessEvent has better to filter the register event names, but I did not know the best way to add these register event names list.
, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Basically, this feature can go along with qemu monitor passthrough. That way, if we use new commands in the monitor that generate new events, we want some way to receive those new events too.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- include/libvirt/libvirt-qemu.h | 27 ++++ include/libvirt/libvirt.h.in | 2 +- src/conf/domain_event.c | 293 ++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 50 ++++++- src/driver.h | 14 ++ src/libvirt-qemu.c | 189 ++++++++++++++++++++++++++ src/libvirt_private.syms | 6 + src/libvirt_qemu.syms | 5 + 8 files changed, 571 insertions(+), 15 deletions(-)
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 7f12e4f..3aa944a 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -32,6 +32,33 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid, unsigned int flags);
+/** + * virConnectDomainQemuEventCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @eventName : the name of the unknow or un-implementation event + * @eventArgs: the content of the unknow or un-implementation event + * + * The callback signature to use when registering for an event of type + * VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN with virConnectDomainQemuEventRegister() + */ +typedef void (*virConnectDomainQemuEventCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque); + +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb); +int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID); + # ifdef __cplusplus } # endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2480add..9fcb400 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3207,7 +3207,6 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, int type, int status, void *opaque); - /** * virConnectDomainEventDiskChangeReason: * @@ -3263,6 +3262,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */ VIR_DOMAIN_EVENT_ID_DISK_CHANGE = 9, /* virConnectDomainEventDiskChangeCallback */ + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN = 10, /* virConnectDomainEventDefaultCallback */
/* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 614ab97..0388a66 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -45,7 +45,9 @@ typedef virDomainMeta *virDomainMetaPtr;
struct _virDomainEventCallback { int callbackID; + int qemuCallbackID; int eventID; + char *eventName; virConnectPtr conn; virDomainMetaPtr dom; virConnectDomainEventGenericCallback cb; @@ -94,6 +96,10 @@ struct _virDomainEvent { char *devAlias; int reason; } diskChange; + struct { + char *eventName; + char *eventArgs; + }qemuUnknownEvent; } data; };
@@ -112,6 +118,7 @@ virDomainEventCallbackListFree(virDomainEventCallbackListPtr list)
for (i=0; i<list->count; i++) { virFreeCallback freecb = list->callbacks[i]->freecb; + VIR_FREE(list->callbacks[i]->eventName); if (freecb) (*freecb)(list->callbacks[i]->opaque); VIR_FREE(list->callbacks[i]); @@ -187,8 +194,10 @@ virDomainEventCallbackListRemoveID(virConnectPtr conn, if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]); - if (i < (cbList->count - 1)) memmove(cbList->callbacks + i, cbList->callbacks + i + 1, @@ -231,6 +240,9 @@ virDomainEventCallbackListRemoveConn(virConnectPtr conn, if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]);
if (i < (cbList->count - 1)) @@ -299,6 +311,9 @@ int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList) if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]);
if (i < (cbList->count - 1)) @@ -404,7 +419,98 @@ virDomainEventCallbackListAddID(virConnectPtr conn,
cbList->callbacks[cbList->count] = event; cbList->count++; + event->callbackID = cbList->nextID++; + + return event->callbackID; + +no_memory: + virReportOOMError(); + + if (event) { + if (event->dom) + VIR_FREE(event->dom->name); + VIR_FREE(event->dom); + } + VIR_FREE(event); + return -1; +} + + + +/** + * virDomainEventCallbackListAddName: + * @conn: pointer to the connection + * @cbList: the list + * @eventName: the event eventName + * @callback: the callback to add + * @eventID: the specific eventID + * @opaque: opaque data tio pass to callback + * + * Internal function to add a callback from a virDomainEventCallbackListPtr + */ +int +virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + virDomainEventCallbackPtr event; + int i; + + /* 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_DOMAIN_EVENT_CALLBACK(callback) && + STREQ(cbList->callbacks[i]->eventName, eventName) && + cbList->callbacks[i]->eventID == eventID && + cbList->callbacks[i]->conn == conn) { + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("event callback already tracked")); + return -1; + } + } + if (eventID > VIR_DOMAIN_EVENT_ID_LAST || eventID < VIR_DOMAIN_EVENT_ID_LIFECYCLE) { + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("not suport this kind of eventID: %d"), eventID); + } + /* Allocate new event */ + if (VIR_ALLOC(event) < 0) + goto no_memory; + event->conn = conn; + event->cb = callback; + if (eventName == NULL) + goto no_memory; + event->eventName = strdup(eventName); + if ( event->eventName == NULL) + goto no_memory; + event->opaque = opaque; + event->freecb = freecb; + event->eventID = eventID; + if (dom) { + if (VIR_ALLOC(event->dom) < 0) + goto no_memory; + if (!(event->dom->name = strdup(dom->name))) + goto no_memory; + memcpy(event->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); + event->dom->id = dom->id; + }
+ /* Make space on list */ + if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) + goto no_memory; + + event->conn->refs++; + + cbList->callbacks[cbList->count] = event; + cbList->count++; event->callbackID = cbList->nextID++;
return event->callbackID; @@ -416,11 +522,40 @@ no_memory: if (event->dom) VIR_FREE(event->dom->name); VIR_FREE(event->dom); + VIR_FREE(event->eventName); } VIR_FREE(event); return -1; }
+/** + * virDomainEventCallbackListAddQemuCallbackID: + * @conn: pointer to the connection + * @cbList: the list + * @callbackID: the libvirt callback ID + * @qemuCallbackID: the libvirtd callback ID to add + * + * Internal function to add a Daemon libvirtd callbackID + */ +int +virDomainEventCallbackListAddQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID, + int qemuCallbackID) +{ + int i; + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->callbackID == callbackID && + cbList->callbacks[i]->conn == conn) { + cbList->callbacks[i]->qemuCallbackID = qemuCallbackID; + return 0; + } + } + + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not find event callback for deletion")); + return -1; +}
int virDomainEventCallbackListCountID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, @@ -442,6 +577,27 @@ int virDomainEventCallbackListCountID(virConnectPtr conn, }
+int +virDomainEventCallbackListCountName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + const char *eventName) +{ + int i; + int count = 0; + + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->deleted) + continue; + + if (STREQ(cbList->callbacks[i]->eventName,eventName) && + cbList->callbacks[i]->conn == conn) + count++; + } + + return count; +} + + int virDomainEventCallbackListEventID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, int callbackID) @@ -461,6 +617,44 @@ int virDomainEventCallbackListEventID(virConnectPtr conn, }
+const char* +virDomainEventCallbackListEventName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int 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]->eventName; + } + + return NULL; +} + +int +virDomainEventCallbackListEventQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int 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]->qemuCallbackID; + } + + return -1; +} + int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList) { int i; @@ -521,6 +715,11 @@ void virDomainEventFree(virDomainEventPtr event) VIR_FREE(event->data.diskChange.newSrcPath); VIR_FREE(event->data.diskChange.devAlias); break; + + case VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN: + VIR_FREE(event->data.qemuUnknownEvent.eventName); + VIR_FREE(event->data.qemuUnknownEvent.eventArgs); + break; }
VIR_FREE(event->dom.name); @@ -956,6 +1155,51 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, path, type, status); }
+static virDomainEventPtr +virDomainEventUnknownNew(int id, const char *name, unsigned char *uuid, + const char *eventName, const char *eventArgs) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN, + id, name, uuid); + if (ev) { + if (!(ev->data.qemuUnknownEvent.eventName = strdup(eventName))) { + virReportOOMError(); + VIR_FREE(ev->dom.name); + VIR_FREE(ev); + return NULL; + } + if (eventArgs) { + if (!(ev->data.qemuUnknownEvent.eventArgs = strdup(eventArgs))) { + virReportOOMError(); + VIR_FREE(ev->data.qemuUnknownEvent.eventName); + VIR_FREE(ev->dom.name); + VIR_FREE(ev); + return NULL; + } + } + } + + return ev; +} + +virDomainEventPtr virDomainEventUnknownNewFromObj(virDomainObjPtr obj, + const char *eventName, + const char *eventArgs) +{ + + return virDomainEventUnknownNew(obj->def->id, obj->def->name, + obj->def->uuid, eventName, eventArgs); +} + +virDomainEventPtr virDomainEventUnknownNewFromDom(virDomainPtr dom, + const char *eventName, + const char *eventArgs) +{ + return virDomainEventUnknownNew(dom->id, dom->name, dom->uuid, + eventName, eventArgs); +} + virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom) { virDomainEventPtr ev = @@ -1095,11 +1339,12 @@ virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, }
-void virDomainEventDispatchDefaultFunc(virConnectPtr conn, - virDomainEventPtr event, - virConnectDomainEventGenericCallback cb, - void *cbopaque, - void *opaque ATTRIBUTE_UNUSED) +void +virDomainEventDispatchDefaultFunc(virConnectPtr conn, + virDomainEventPtr event, + virConnectDomainEventGenericCallback cb, + void *cbopaque, + void *opaque ATTRIBUTE_UNUSED) { virDomainPtr dom = virGetDomain(conn, event->dom.name, event->dom.uuid); if (!dom) @@ -1180,6 +1425,13 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break;
+ case VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN: + ((virConnectDomainQemuEventCallback)cb)(conn, dom, + event->data.qemuUnknownEvent.eventName, + event->data.qemuUnknownEvent.eventArgs, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; @@ -1189,8 +1441,9 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, }
-static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, - virDomainEventCallbackPtr cb) +static int +virDomainEventDispatchMatchCallback(virDomainEventPtr event, + virDomainEventCallbackPtr cb) { if (!cb) return 0; @@ -1198,7 +1451,12 @@ static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, return 0; if (cb->eventID != event->eventID) return 0; - + if (event->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + if (event->data.qemuUnknownEvent.eventName == NULL || + cb->eventName == NULL || + STRNEQ(cb->eventName, event->data.qemuUnknownEvent.eventName)) + return 0; + } if (cb->dom) { /* Deliberately ignoring 'id' for matching, since that * will cause problems when a domain switches between @@ -1341,3 +1599,20 @@ virDomainEventStateDeregisterAny(virConnectPtr conn, virDomainEventStateUnlock(state); return ret; } +int +virDomainQemuEventStateDeregister(virConnectPtr conn, + virDomainEventStatePtr state, + int callbackID) +{ + int ret; + + virDomainEventStateLock(state); + if (state->isDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, + state->callbacks, callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, + state->callbacks, callbackID); + virDomainEventStateUnlock(state); + return ret; +} diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 3ba418e..f2fe847 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -83,14 +83,23 @@ int virDomainEventCallbackListAddID(virConnectPtr conn, virFreeCallback freecb) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(5);
+int virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(6);
int virDomainEventCallbackListRemove(virConnectPtr conn, virDomainEventCallbackListPtr cbList, virConnectDomainEventCallback callback) ATTRIBUTE_NONNULL(1); -int virDomainEventCallbackListRemoveID(virConnectPtr conn, - virDomainEventCallbackListPtr cbList, - int callbackID) +int virDomainQemuEventCallbackListRemoveID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) ATTRIBUTE_NONNULL(1); int virDomainEventCallbackListRemoveConn(virConnectPtr conn, virDomainEventCallbackListPtr cbList) @@ -106,9 +115,14 @@ int virDomainEventCallbackListMarkDeleteID(virConnectPtr conn, int callbackID) ATTRIBUTE_NONNULL(1);
- int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList);
+int virDomainEventCallbackListAddQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID, + int qemuCallbackID) + ATTRIBUTE_NONNULL(1); + int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList); int virDomainEventCallbackListCountID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, @@ -119,6 +133,21 @@ int virDomainEventCallbackListEventID(virConnectPtr conn, int callbackID) ATTRIBUTE_NONNULL(1);
+int virDomainEventCallbackListCountName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + const char *eventName) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); + +int virDomainEventCallbackListEventQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + +const char* virDomainEventCallbackListEventName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + virDomainEventQueuePtr virDomainEventQueueNew(void);
virDomainEventPtr virDomainEventNew(int id, const char *name, const unsigned char *uuid, int type, int detail); @@ -190,6 +219,13 @@ virDomainEventPtr virDomainEventDiskChangeNewFromDom(virDomainPtr dom, const char *devAlias, int reason);
+virDomainEventPtr virDomainEventUnknownNewFromObj(virDomainObjPtr obj, + const char *eventName, + const char *eventArgs); +virDomainEventPtr virDomainEventUnknownNewFromDom(virDomainPtr dom, + const char *eventName, + const char *eventArgs); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event);
@@ -246,5 +282,9 @@ virDomainEventStateDeregisterAny(virConnectPtr conn, virDomainEventStatePtr state, int callbackID) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - +int +virDomainQemuEventStateDeregister(virConnectPtr conn, + virDomainEventStatePtr state, + int callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); #endif diff --git a/src/driver.h b/src/driver.h index 941ff51..51164a9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -635,6 +635,18 @@ typedef virDomainPtr unsigned int flags);
typedef int + (*virDrvDomainQemuEventRegister)(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +typedef int + (*virDrvDomainQemuEventDeregister)(virConnectPtr conn, + int callbackID); + +typedef int (*virDrvDomainOpenConsole)(virDomainPtr dom, const char *dev_name, virStreamPtr st, @@ -915,6 +927,8 @@ struct _virDriver { virDrvDomainSnapshotDelete domainSnapshotDelete; virDrvDomainQemuMonitorCommand qemuDomainMonitorCommand; virDrvDomainQemuAttach qemuDomainAttach; + virDrvDomainQemuEventRegister qemuDomainQemuEventRegister; + virDrvDomainQemuEventDeregister qemuDomainQemuEventDeregister; virDrvDomainOpenConsole domainOpenConsole; virDrvDomainOpenGraphics domainOpenGraphics; virDrvDomainInjectNMI domainInjectNMI; diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index 248cc33..7722b7b 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -36,6 +36,77 @@ virReportErrorHelper(VIR_FROM_DOM, error, NULL, __FUNCTION__, \ __LINE__, info)
+/* Helper macros to implement VIR_DOMAIN_DEBUG using just C99. This + * assumes you pass fewer than 15 arguments to VIR_DOMAIN_DEBUG, but + * can easily be expanded if needed. + * + * Note that gcc provides extensions of "define a(b...) b" or + * "define a(b,...) b,##__VA_ARGS__" as a means of eliding a comma + * when no var-args are present, but we don't want to require gcc. + */ +#define VIR_ARG15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 +#define VIR_HAS_COMMA(...) VIR_ARG15(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) + +/* Form the name VIR_DOMAIN_DEBUG_[01], then call that macro, + * according to how many arguments are present. Two-phase due to + * macro expansion rules. */ +#define VIR_DOMAIN_DEBUG_EXPAND(a, b, ...) \ + VIR_DOMAIN_DEBUG_PASTE(a, b, __VA_ARGS__) +#define VIR_DOMAIN_DEBUG_PASTE(a, b, ...) \ + a##b(__VA_ARGS__) + +/* Internal use only, when VIR_DOMAIN_DEBUG has one argument. */ +#define VIR_DOMAIN_DEBUG_0(dom) \ + VIR_DOMAIN_DEBUG_2(dom, "%s", "") + +/* Internal use only, when VIR_DOMAIN_DEBUG has three or more arguments. */ +#define VIR_DOMAIN_DEBUG_1(dom, fmt, ...) \ + VIR_DOMAIN_DEBUG_2(dom, ", " fmt, __VA_ARGS__) + +/* Internal use only, with final format. */ +#define VIR_DOMAIN_DEBUG_2(dom, fmt, ...) \ + do { \ + char _uuidstr[VIR_UUID_STRING_BUFLEN]; \ + const char *_domname = NULL; \ + \ + if (!VIR_IS_DOMAIN(dom)) { \ + memset(_uuidstr, 0, sizeof(_uuidstr)); \ + } else { \ + virUUIDFormat((dom)->uuid, _uuidstr); \ + _domname = (dom)->name; \ + } \ + \ + VIR_DEBUG("dom=%p, (VM: name=%s, uuid=%s)" fmt, \ + dom, NULLSTR(_domname), _uuidstr, __VA_ARGS__); \ + } while (0) + +/** + * VIR_DOMAIN_DEBUG: + * @dom: domain + * @fmt: optional format for additional information + * @...: optional arguments corresponding to @fmt. + */ +#define VIR_DOMAIN_DEBUG(...) \ + VIR_DOMAIN_DEBUG_EXPAND(VIR_DOMAIN_DEBUG_, \ + VIR_HAS_COMMA(__VA_ARGS__), \ + __VA_ARGS__) + +/** + * VIR_UUID_DEBUG: + * @conn: connection + * @uuid: possibly null UUID array + */ +#define VIR_UUID_DEBUG(conn, uuid) \ + do { \ + if (uuid) { \ + char _uuidstr[VIR_UUID_STRING_BUFLEN]; \ + virUUIDFormat(uuid, _uuidstr); \ + VIR_DEBUG("conn=%p, uuid=%s", conn, _uuidstr); \ + } else { \ + VIR_DEBUG("conn=%p, uuid=(null)", conn); \ + } \ + } while (0) + /** * virDomainQemuMonitorCommand: * @domain: a domain object @@ -178,3 +249,121 @@ error: virDispatchError(conn); return NULL; } + +/** + * virConnectDomainQemuEventRegister: + * @conn: pointer to the connection + * @dom: pointer to the domain + * @eventName: the event Name to receive + * @cb: callback to the function handling domain events + * @opaque: opaque data to pass on to the callback + * @freecb: optional function to deallocate opaque when not used anymore + * + * Adds a callback to receive notifications of arbitrary qemu domain events + * occurring on a domain. + * + * If dom is NULL, then events will be monitored for any domain. If dom + * is non-NULL, then only the specific domain will be monitored + * + * Most types of event have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus neccessary to use + * the VIR_DOMAIN_EVENT_CALLBACK() macro to cast the supplied function pointer + * to match the signature of this method. + * + * The virDomainPtr object handle passed into the callback upon delivery + * of an event is only valid for the duration of execution of the callback. + * If the callback wishes to keep the domain object after the callback returns, + * it shall take a reference to it, by calling virDomainRef. + * The reference can be released once the object is no longer required + * by calling virDomainFree. + * + * The return value from this method is a positive integer identifier + * for the callback. To unregister a callback, this callback ID should + * be passed to the virConnectDomainQemuEventDeregister method + * + * Returns a callback identifier on success, -1 on failure + */ +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DOMAIN_DEBUG(dom, "conn=%p, eventName=%s, cb=%p, opaque=%p, freecb=%p", + conn, eventName, cb, opaque, freecb); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (dom != NULL && + !(VIR_IS_CONNECTED_DOMAIN(dom) && dom->conn == conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(conn); + return -1; + } + if (eventName == NULL || cb == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if ((conn->driver) && (conn->driver->qemuDomainQemuEventRegister)) { + int ret; + ret = conn->driver->qemuDomainQemuEventRegister(conn, dom, eventName, cb, opaque, freecb); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectDomainQemuEventDeregister: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * vaule obtained from a previous virConnectDomainQemuEventDeregister method. + * + * Returns 0 on success, -1 on failure + */ +int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID) +{ + + VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (callbackID < 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if ((conn->driver) && (conn->driver->qemuDomainQemuEventDeregister)) { + int ret; + ret = conn->driver->qemuDomainQemuEventDeregister(conn, callbackID); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 48ffdf2..75e544a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -471,11 +471,16 @@ virDomainWatchdogModelTypeToString; # domain_event.h virDomainEventBlockJobNewFromObj; virDomainEventBlockJobNewFromDom; +virDomainEventUnknownNewFromObj; +virDomainEventunknownNewFromDom; virDomainEventCallbackListAdd; virDomainEventCallbackListAddID; +virDomainEventCallbackListAddName; virDomainEventCallbackListCount; virDomainEventCallbackListCountID; +virDomainEventCallbackListCountName; virDomainEventCallbackListEventID; +virDomainEventCallbackListEventName; virDomainEventCallbackListFree; virDomainEventCallbackListMarkDelete; virDomainEventCallbackListMarkDeleteID; @@ -512,6 +517,7 @@ virDomainEventRebootNewFromDom; virDomainEventRebootNewFromObj; virDomainEventStateDeregister; virDomainEventStateDeregisterAny; +virDomainQemuEventStateDeregister; virDomainEventStateFlush; virDomainEventStateFree; virDomainEventStateNew; diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms index 8447730..a17e387 100644 --- a/src/libvirt_qemu.syms +++ b/src/libvirt_qemu.syms @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; +LIBVIRT_QEMU_0.9.9 { + global: + virConnectDomainQemuEventRegister; + virConnectDomainQemuEventDeregister; +} LIBVIRT_QEMU_0.9.4;

Please remember to Cc: the libvirt list as well so that others may contribute to the discussion. On Sat, Dec 17, 2011 at 12:58:58AM +0800, ShaoHe Feng wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Basically, this feature can go along with qemu monitor passthrough. That way, if we use new commands in the monitor that generate new events, we want some way to receive those new events too.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- include/libvirt/libvirt-qemu.h | 27 ++++ include/libvirt/libvirt.h.in | 2 +- src/conf/domain_event.c | 293 ++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 50 ++++++- src/driver.h | 14 ++ src/libvirt-qemu.c | 189 ++++++++++++++++++++++++++ src/libvirt_private.syms | 6 + src/libvirt_qemu.syms | 5 + 8 files changed, 571 insertions(+), 15 deletions(-)
diff --git a/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h index 7f12e4f..3aa944a 100644 --- a/include/libvirt/libvirt-qemu.h +++ b/include/libvirt/libvirt-qemu.h @@ -32,6 +32,33 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain, unsigned int pid, unsigned int flags);
+/** + * virConnectDomainQemuEventCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @eventName : the name of the unknow or un-implementation event + * @eventArgs: the content of the unknow or un-implementation event + * + * The callback signature to use when registering for an event of type + * VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN with virConnectDomainQemuEventRegister() + */ +typedef void (*virConnectDomainQemuEventCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque); + +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb); +int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID); + # ifdef __cplusplus } # endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2480add..9fcb400 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3207,7 +3207,6 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, int type, int status, void *opaque); - /** * virConnectDomainEventDiskChangeReason: * @@ -3263,6 +3262,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */ VIR_DOMAIN_EVENT_ID_DISK_CHANGE = 9, /* virConnectDomainEventDiskChangeCallback */ + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN = 10, /* virConnectDomainEventDefaultCallback */
/* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 614ab97..0388a66 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -45,7 +45,9 @@ typedef virDomainMeta *virDomainMetaPtr;
struct _virDomainEventCallback { int callbackID; + int qemuCallbackID; int eventID; + char *eventName; virConnectPtr conn; virDomainMetaPtr dom; virConnectDomainEventGenericCallback cb; @@ -94,6 +96,10 @@ struct _virDomainEvent { char *devAlias; int reason; } diskChange; + struct { + char *eventName; + char *eventArgs; + }qemuUnknownEvent; } data; };
@@ -112,6 +118,7 @@ virDomainEventCallbackListFree(virDomainEventCallbackListPtr list)
for (i=0; i<list->count; i++) { virFreeCallback freecb = list->callbacks[i]->freecb; + VIR_FREE(list->callbacks[i]->eventName); if (freecb) (*freecb)(list->callbacks[i]->opaque); VIR_FREE(list->callbacks[i]); @@ -187,8 +194,10 @@ virDomainEventCallbackListRemoveID(virConnectPtr conn, if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]); - if (i < (cbList->count - 1)) memmove(cbList->callbacks + i, cbList->callbacks + i + 1, @@ -231,6 +240,9 @@ virDomainEventCallbackListRemoveConn(virConnectPtr conn, if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]);
if (i < (cbList->count - 1)) @@ -299,6 +311,9 @@ int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList) if (freecb) (*freecb)(cbList->callbacks[i]->opaque); virUnrefConnect(cbList->callbacks[i]->conn); + if (cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + VIR_FREE(cbList->callbacks[i]->eventName); + } VIR_FREE(cbList->callbacks[i]);
if (i < (cbList->count - 1)) @@ -404,7 +419,98 @@ virDomainEventCallbackListAddID(virConnectPtr conn,
cbList->callbacks[cbList->count] = event; cbList->count++; + event->callbackID = cbList->nextID++; + + return event->callbackID; + +no_memory: + virReportOOMError(); + + if (event) { + if (event->dom) + VIR_FREE(event->dom->name); + VIR_FREE(event->dom); + } + VIR_FREE(event); + return -1; +} + + + +/** + * virDomainEventCallbackListAddName: + * @conn: pointer to the connection + * @cbList: the list + * @eventName: the event eventName + * @callback: the callback to add + * @eventID: the specific eventID + * @opaque: opaque data tio pass to callback + * + * Internal function to add a callback from a virDomainEventCallbackListPtr + */ +int +virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + virDomainEventCallbackPtr event; + int i; + + /* 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_DOMAIN_EVENT_CALLBACK(callback) && + STREQ(cbList->callbacks[i]->eventName, eventName) && + cbList->callbacks[i]->eventID == eventID && + cbList->callbacks[i]->conn == conn) { + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("event callback already tracked")); + return -1; + } + } + if (eventID > VIR_DOMAIN_EVENT_ID_LAST || eventID < VIR_DOMAIN_EVENT_ID_LIFECYCLE) { + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("not suport this kind of eventID: %d"), eventID); + } + /* Allocate new event */ + if (VIR_ALLOC(event) < 0) + goto no_memory; + event->conn = conn; + event->cb = callback; + if (eventName == NULL) + goto no_memory; + event->eventName = strdup(eventName); + if ( event->eventName == NULL) + goto no_memory; + event->opaque = opaque; + event->freecb = freecb; + event->eventID = eventID; + if (dom) { + if (VIR_ALLOC(event->dom) < 0) + goto no_memory; + if (!(event->dom->name = strdup(dom->name))) + goto no_memory; + memcpy(event->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); + event->dom->id = dom->id; + }
+ /* Make space on list */ + if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) + goto no_memory; + + event->conn->refs++; + + cbList->callbacks[cbList->count] = event; + cbList->count++; event->callbackID = cbList->nextID++;
return event->callbackID; @@ -416,11 +522,40 @@ no_memory: if (event->dom) VIR_FREE(event->dom->name); VIR_FREE(event->dom); + VIR_FREE(event->eventName); } VIR_FREE(event); return -1; }
+/** + * virDomainEventCallbackListAddQemuCallbackID: + * @conn: pointer to the connection + * @cbList: the list + * @callbackID: the libvirt callback ID + * @qemuCallbackID: the libvirtd callback ID to add + * + * Internal function to add a Daemon libvirtd callbackID + */ +int +virDomainEventCallbackListAddQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID, + int qemuCallbackID) +{ + int i; + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->callbackID == callbackID && + cbList->callbacks[i]->conn == conn) { + cbList->callbacks[i]->qemuCallbackID = qemuCallbackID; + return 0; + } + } + + eventReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("could not find event callback for deletion")); + return -1; +}
int virDomainEventCallbackListCountID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, @@ -442,6 +577,27 @@ int virDomainEventCallbackListCountID(virConnectPtr conn, }
+int +virDomainEventCallbackListCountName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + const char *eventName) +{ + int i; + int count = 0; + + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->deleted) + continue; + + if (STREQ(cbList->callbacks[i]->eventName,eventName) && + cbList->callbacks[i]->conn == conn) + count++; + } + + return count; +} + + int virDomainEventCallbackListEventID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, int callbackID) @@ -461,6 +617,44 @@ int virDomainEventCallbackListEventID(virConnectPtr conn, }
+const char* +virDomainEventCallbackListEventName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int 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]->eventName; + } + + return NULL; +} + +int +virDomainEventCallbackListEventQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int 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]->qemuCallbackID; + } + + return -1; +} + int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList) { int i; @@ -521,6 +715,11 @@ void virDomainEventFree(virDomainEventPtr event) VIR_FREE(event->data.diskChange.newSrcPath); VIR_FREE(event->data.diskChange.devAlias); break; + + case VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN: + VIR_FREE(event->data.qemuUnknownEvent.eventName); + VIR_FREE(event->data.qemuUnknownEvent.eventArgs); + break; }
VIR_FREE(event->dom.name); @@ -956,6 +1155,51 @@ virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, path, type, status); }
+static virDomainEventPtr +virDomainEventUnknownNew(int id, const char *name, unsigned char *uuid, + const char *eventName, const char *eventArgs) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN, + id, name, uuid); + if (ev) { + if (!(ev->data.qemuUnknownEvent.eventName = strdup(eventName))) { + virReportOOMError(); + VIR_FREE(ev->dom.name); + VIR_FREE(ev); + return NULL; + } + if (eventArgs) { + if (!(ev->data.qemuUnknownEvent.eventArgs = strdup(eventArgs))) { + virReportOOMError(); + VIR_FREE(ev->data.qemuUnknownEvent.eventName); + VIR_FREE(ev->dom.name); + VIR_FREE(ev); + return NULL; + } + } + } + + return ev; +} + +virDomainEventPtr virDomainEventUnknownNewFromObj(virDomainObjPtr obj, + const char *eventName, + const char *eventArgs) +{ + + return virDomainEventUnknownNew(obj->def->id, obj->def->name, + obj->def->uuid, eventName, eventArgs); +} + +virDomainEventPtr virDomainEventUnknownNewFromDom(virDomainPtr dom, + const char *eventName, + const char *eventArgs) +{ + return virDomainEventUnknownNew(dom->id, dom->name, dom->uuid, + eventName, eventArgs); +} + virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom) { virDomainEventPtr ev = @@ -1095,11 +1339,12 @@ virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, }
-void virDomainEventDispatchDefaultFunc(virConnectPtr conn, - virDomainEventPtr event, - virConnectDomainEventGenericCallback cb, - void *cbopaque, - void *opaque ATTRIBUTE_UNUSED) +void +virDomainEventDispatchDefaultFunc(virConnectPtr conn, + virDomainEventPtr event, + virConnectDomainEventGenericCallback cb, + void *cbopaque, + void *opaque ATTRIBUTE_UNUSED) { virDomainPtr dom = virGetDomain(conn, event->dom.name, event->dom.uuid); if (!dom) @@ -1180,6 +1425,13 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break;
+ case VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN: + ((virConnectDomainQemuEventCallback)cb)(conn, dom, + event->data.qemuUnknownEvent.eventName, + event->data.qemuUnknownEvent.eventArgs, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; @@ -1189,8 +1441,9 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, }
-static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, - virDomainEventCallbackPtr cb) +static int +virDomainEventDispatchMatchCallback(virDomainEventPtr event, + virDomainEventCallbackPtr cb) { if (!cb) return 0; @@ -1198,7 +1451,12 @@ static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, return 0; if (cb->eventID != event->eventID) return 0; - + if (event->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) { + if (event->data.qemuUnknownEvent.eventName == NULL || + cb->eventName == NULL || + STRNEQ(cb->eventName, event->data.qemuUnknownEvent.eventName)) + return 0; + } if (cb->dom) { /* Deliberately ignoring 'id' for matching, since that * will cause problems when a domain switches between @@ -1341,3 +1599,20 @@ virDomainEventStateDeregisterAny(virConnectPtr conn, virDomainEventStateUnlock(state); return ret; } +int +virDomainQemuEventStateDeregister(virConnectPtr conn, + virDomainEventStatePtr state, + int callbackID) +{ + int ret; + + virDomainEventStateLock(state); + if (state->isDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, + state->callbacks, callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, + state->callbacks, callbackID); + virDomainEventStateUnlock(state); + return ret; +} diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 3ba418e..f2fe847 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -83,14 +83,23 @@ int virDomainEventCallbackListAddID(virConnectPtr conn, virFreeCallback freecb) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(5);
+int virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(6);
int virDomainEventCallbackListRemove(virConnectPtr conn, virDomainEventCallbackListPtr cbList, virConnectDomainEventCallback callback) ATTRIBUTE_NONNULL(1); -int virDomainEventCallbackListRemoveID(virConnectPtr conn, - virDomainEventCallbackListPtr cbList, - int callbackID) +int virDomainQemuEventCallbackListRemoveID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) ATTRIBUTE_NONNULL(1); int virDomainEventCallbackListRemoveConn(virConnectPtr conn, virDomainEventCallbackListPtr cbList) @@ -106,9 +115,14 @@ int virDomainEventCallbackListMarkDeleteID(virConnectPtr conn, int callbackID) ATTRIBUTE_NONNULL(1);
- int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList);
+int virDomainEventCallbackListAddQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID, + int qemuCallbackID) + ATTRIBUTE_NONNULL(1); + int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList); int virDomainEventCallbackListCountID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, @@ -119,6 +133,21 @@ int virDomainEventCallbackListEventID(virConnectPtr conn, int callbackID) ATTRIBUTE_NONNULL(1);
+int virDomainEventCallbackListCountName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + const char *eventName) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); + +int virDomainEventCallbackListEventQemuCallbackID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + +const char* virDomainEventCallbackListEventName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + virDomainEventQueuePtr virDomainEventQueueNew(void);
virDomainEventPtr virDomainEventNew(int id, const char *name, const unsigned char *uuid, int type, int detail); @@ -190,6 +219,13 @@ virDomainEventPtr virDomainEventDiskChangeNewFromDom(virDomainPtr dom, const char *devAlias, int reason);
+virDomainEventPtr virDomainEventUnknownNewFromObj(virDomainObjPtr obj, + const char *eventName, + const char *eventArgs); +virDomainEventPtr virDomainEventUnknownNewFromDom(virDomainPtr dom, + const char *eventName, + const char *eventArgs); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event);
@@ -246,5 +282,9 @@ virDomainEventStateDeregisterAny(virConnectPtr conn, virDomainEventStatePtr state, int callbackID) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - +int +virDomainQemuEventStateDeregister(virConnectPtr conn, + virDomainEventStatePtr state, + int callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); #endif diff --git a/src/driver.h b/src/driver.h index 941ff51..51164a9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -635,6 +635,18 @@ typedef virDomainPtr unsigned int flags);
typedef int + (*virDrvDomainQemuEventRegister)(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +typedef int + (*virDrvDomainQemuEventDeregister)(virConnectPtr conn, + int callbackID); + +typedef int (*virDrvDomainOpenConsole)(virDomainPtr dom, const char *dev_name, virStreamPtr st, @@ -915,6 +927,8 @@ struct _virDriver { virDrvDomainSnapshotDelete domainSnapshotDelete; virDrvDomainQemuMonitorCommand qemuDomainMonitorCommand; virDrvDomainQemuAttach qemuDomainAttach; + virDrvDomainQemuEventRegister qemuDomainQemuEventRegister; + virDrvDomainQemuEventDeregister qemuDomainQemuEventDeregister; virDrvDomainOpenConsole domainOpenConsole; virDrvDomainOpenGraphics domainOpenGraphics; virDrvDomainInjectNMI domainInjectNMI; diff --git a/src/libvirt-qemu.c b/src/libvirt-qemu.c index 248cc33..7722b7b 100644 --- a/src/libvirt-qemu.c +++ b/src/libvirt-qemu.c @@ -36,6 +36,77 @@ virReportErrorHelper(VIR_FROM_DOM, error, NULL, __FUNCTION__, \ __LINE__, info)
+/* Helper macros to implement VIR_DOMAIN_DEBUG using just C99. This + * assumes you pass fewer than 15 arguments to VIR_DOMAIN_DEBUG, but + * can easily be expanded if needed. + * + * Note that gcc provides extensions of "define a(b...) b" or + * "define a(b,...) b,##__VA_ARGS__" as a means of eliding a comma + * when no var-args are present, but we don't want to require gcc. + */ +#define VIR_ARG15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 +#define VIR_HAS_COMMA(...) VIR_ARG15(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) + +/* Form the name VIR_DOMAIN_DEBUG_[01], then call that macro, + * according to how many arguments are present. Two-phase due to + * macro expansion rules. */ +#define VIR_DOMAIN_DEBUG_EXPAND(a, b, ...) \ + VIR_DOMAIN_DEBUG_PASTE(a, b, __VA_ARGS__) +#define VIR_DOMAIN_DEBUG_PASTE(a, b, ...) \ + a##b(__VA_ARGS__) + +/* Internal use only, when VIR_DOMAIN_DEBUG has one argument. */ +#define VIR_DOMAIN_DEBUG_0(dom) \ + VIR_DOMAIN_DEBUG_2(dom, "%s", "") + +/* Internal use only, when VIR_DOMAIN_DEBUG has three or more arguments. */ +#define VIR_DOMAIN_DEBUG_1(dom, fmt, ...) \ + VIR_DOMAIN_DEBUG_2(dom, ", " fmt, __VA_ARGS__) + +/* Internal use only, with final format. */ +#define VIR_DOMAIN_DEBUG_2(dom, fmt, ...) \ + do { \ + char _uuidstr[VIR_UUID_STRING_BUFLEN]; \ + const char *_domname = NULL; \ + \ + if (!VIR_IS_DOMAIN(dom)) { \ + memset(_uuidstr, 0, sizeof(_uuidstr)); \ + } else { \ + virUUIDFormat((dom)->uuid, _uuidstr); \ + _domname = (dom)->name; \ + } \ + \ + VIR_DEBUG("dom=%p, (VM: name=%s, uuid=%s)" fmt, \ + dom, NULLSTR(_domname), _uuidstr, __VA_ARGS__); \ + } while (0) + +/** + * VIR_DOMAIN_DEBUG: + * @dom: domain + * @fmt: optional format for additional information + * @...: optional arguments corresponding to @fmt. + */ +#define VIR_DOMAIN_DEBUG(...) \ + VIR_DOMAIN_DEBUG_EXPAND(VIR_DOMAIN_DEBUG_, \ + VIR_HAS_COMMA(__VA_ARGS__), \ + __VA_ARGS__) + +/** + * VIR_UUID_DEBUG: + * @conn: connection + * @uuid: possibly null UUID array + */ +#define VIR_UUID_DEBUG(conn, uuid) \ + do { \ + if (uuid) { \ + char _uuidstr[VIR_UUID_STRING_BUFLEN]; \ + virUUIDFormat(uuid, _uuidstr); \ + VIR_DEBUG("conn=%p, uuid=%s", conn, _uuidstr); \ + } else { \ + VIR_DEBUG("conn=%p, uuid=(null)", conn); \ + } \ + } while (0) + /** * virDomainQemuMonitorCommand: * @domain: a domain object @@ -178,3 +249,121 @@ error: virDispatchError(conn); return NULL; } + +/** + * virConnectDomainQemuEventRegister: + * @conn: pointer to the connection + * @dom: pointer to the domain + * @eventName: the event Name to receive + * @cb: callback to the function handling domain events + * @opaque: opaque data to pass on to the callback + * @freecb: optional function to deallocate opaque when not used anymore + * + * Adds a callback to receive notifications of arbitrary qemu domain events + * occurring on a domain. + * + * If dom is NULL, then events will be monitored for any domain. If dom + * is non-NULL, then only the specific domain will be monitored + * + * Most types of event have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus neccessary to use + * the VIR_DOMAIN_EVENT_CALLBACK() macro to cast the supplied function pointer + * to match the signature of this method. + * + * The virDomainPtr object handle passed into the callback upon delivery + * of an event is only valid for the duration of execution of the callback. + * If the callback wishes to keep the domain object after the callback returns, + * it shall take a reference to it, by calling virDomainRef. + * The reference can be released once the object is no longer required + * by calling virDomainFree. + * + * The return value from this method is a positive integer identifier + * for the callback. To unregister a callback, this callback ID should + * be passed to the virConnectDomainQemuEventDeregister method + * + * Returns a callback identifier on success, -1 on failure + */ +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DOMAIN_DEBUG(dom, "conn=%p, eventName=%s, cb=%p, opaque=%p, freecb=%p", + conn, eventName, cb, opaque, freecb); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (dom != NULL && + !(VIR_IS_CONNECTED_DOMAIN(dom) && dom->conn == conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(conn); + return -1; + } + if (eventName == NULL || cb == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if ((conn->driver) && (conn->driver->qemuDomainQemuEventRegister)) { + int ret; + ret = conn->driver->qemuDomainQemuEventRegister(conn, dom, eventName, cb, opaque, freecb); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectDomainQemuEventDeregister: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * vaule obtained from a previous virConnectDomainQemuEventDeregister method. + * + * Returns 0 on success, -1 on failure + */ +int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID) +{ + + VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + if (callbackID < 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if ((conn->driver) && (conn->driver->qemuDomainQemuEventDeregister)) { + int ret; + ret = conn->driver->qemuDomainQemuEventDeregister(conn, callbackID); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 48ffdf2..75e544a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -471,11 +471,16 @@ virDomainWatchdogModelTypeToString; # domain_event.h virDomainEventBlockJobNewFromObj; virDomainEventBlockJobNewFromDom; +virDomainEventUnknownNewFromObj; +virDomainEventunknownNewFromDom; virDomainEventCallbackListAdd; virDomainEventCallbackListAddID; +virDomainEventCallbackListAddName; virDomainEventCallbackListCount; virDomainEventCallbackListCountID; +virDomainEventCallbackListCountName; virDomainEventCallbackListEventID; +virDomainEventCallbackListEventName; virDomainEventCallbackListFree; virDomainEventCallbackListMarkDelete; virDomainEventCallbackListMarkDeleteID; @@ -512,6 +517,7 @@ virDomainEventRebootNewFromDom; virDomainEventRebootNewFromObj; virDomainEventStateDeregister; virDomainEventStateDeregisterAny; +virDomainQemuEventStateDeregister; virDomainEventStateFlush; virDomainEventStateFree; virDomainEventStateNew; diff --git a/src/libvirt_qemu.syms b/src/libvirt_qemu.syms index 8447730..a17e387 100644 --- a/src/libvirt_qemu.syms +++ b/src/libvirt_qemu.syms @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; +LIBVIRT_QEMU_0.9.9 { + global: + virConnectDomainQemuEventRegister; + virConnectDomainQemuEventDeregister; +} LIBVIRT_QEMU_0.9.4; -- 1.7.5.4
-- Adam Litke <agl@us.ibm.com> IBM Linux Technology Center

On 12/16/2011 09:58 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Basically, this feature can go along with qemu monitor passthrough. That way, if we use new commands in the monitor that generate new events, we want some way to receive those new events too.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- include/libvirt/libvirt-qemu.h | 27 ++++ include/libvirt/libvirt.h.in | 2 +- src/conf/domain_event.c | 293 ++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 50 ++++++- src/driver.h | 14 ++ src/libvirt-qemu.c | 189 ++++++++++++++++++++++++++ src/libvirt_private.syms | 6 + src/libvirt_qemu.syms | 5 + 8 files changed, 571 insertions(+), 15 deletions(-)
Where do we stand on this series? It seems like something worth including in 0.9.10; can we get it rebased to the latest libvirt so it can get a good review? -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Wed, Jan 25, 2012 at 02:16:14PM -0700, Eric Blake wrote:
On 12/16/2011 09:58 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Basically, this feature can go along with qemu monitor passthrough. That way, if we use new commands in the monitor that generate new events, we want some way to receive those new events too.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- include/libvirt/libvirt-qemu.h | 27 ++++ include/libvirt/libvirt.h.in | 2 +- src/conf/domain_event.c | 293 ++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 50 ++++++- src/driver.h | 14 ++ src/libvirt-qemu.c | 189 ++++++++++++++++++++++++++ src/libvirt_private.syms | 6 + src/libvirt_qemu.syms | 5 + 8 files changed, 571 insertions(+), 15 deletions(-)
Where do we stand on this series? It seems like something worth including in 0.9.10; can we get it rebased to the latest libvirt so it can get a good review?
Thanks for asking about this... Shaohe is on vacation for two weeks so I will do my best to answer in his absence. I believe the process of moving the event code into libvirt_qemu.so had increased code size, complexity, and duplication. He is still looking at improving the series and will be posting another RFC soon after he returns from vacation. Therefore, we'll have to punt this one to the next release. -- Adam Litke <agl@us.ibm.com> IBM Linux Technology Center

On 12/16/2011 09:58 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng <shaohef@linux.vnet.ibm.com>
Basically, this feature can go along with qemu monitor passthrough. That way, if we use new commands in the monitor that generate new events, we want some way to receive those new events too.
Signed-off-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> --- include/libvirt/libvirt-qemu.h | 27 ++++ include/libvirt/libvirt.h.in | 2 +- src/conf/domain_event.c | 293 ++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 50 ++++++- src/driver.h | 14 ++ src/libvirt-qemu.c | 189 ++++++++++++++++++++++++++ src/libvirt_private.syms | 6 + src/libvirt_qemu.syms | 5 + 8 files changed, 571 insertions(+), 15 deletions(-)
+/** + * virConnectDomainQemuEventCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @eventName : the name of the unknow or un-implementation event
s/unknow/unknown/ except that I think this interface should be usable to get the JSON argument string of _any_ qemu monitor event, even if libvirt is already handling it. That is, in qemu, I think we want to parse every incoming event, and do two things with it: 1. if the event name has been registered with the qemu-specific event registration, send a libvirt-qemu event 2. if the event name is known and has a libvirt callback, then do the libvirt callback (which might send a libvirt event). So UNKNOWN is really not the right word to use. Rather, it is an arbitrary qemu event, whether or not libvirt knows about it.
+ * @eventArgs: the content of the unknow or un-implementation event + * + * The callback signature to use when registering for an event of type + * VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN with virConnectDomainQemuEventRegister()
This sentence would then be: The callback signature to use when registering for an arbitrary qemu event with virConnectDomainQemuEventRegister().
+ */ +typedef void (*virConnectDomainQemuEventCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque);
This signature looks reasonable.
+ +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb);
As does this API.
+int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID); + # ifdef __cplusplus } # endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2480add..9fcb400 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3207,7 +3207,6 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, int type, int status, void *opaque); - /** * virConnectDomainEventDiskChangeReason: * @@ -3263,6 +3262,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */ VIR_DOMAIN_EVENT_ID_DISK_CHANGE = 9, /* virConnectDomainEventDiskChangeCallback */ + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN = 10, /* virConnectDomainEventDefaultCallback */
No. We do not want to be adding this event type to libvirt.so. Qemu events should be sent on a different channel of the RPC protocol, and never interfere with libvirt events.
/* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 614ab97..0388a66 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -45,7 +45,9 @@ typedef virDomainMeta *virDomainMetaPtr;
struct _virDomainEventCallback { int callbackID; + int qemuCallbackID; int eventID; + char *eventName;
Since we don't want to piggy-back main libvirt events, but instead send this directly from qemu, I'm wondering if it's better to have a qemu-specific structure in src/qemu rather than trying to combine it into conf/domain_event.c.
virConnectPtr conn; virDomainMetaPtr dom; virConnectDomainEventGenericCallback cb; @@ -94,6 +96,10 @@ struct _virDomainEvent { char *devAlias; int reason; } diskChange; + struct { + char *eventName; + char *eventArgs; + }qemuUnknownEvent;
Style: space after }.
+/** + * virDomainEventCallbackListAddName: + * @conn: pointer to the connection + * @cbList: the list + * @eventName: the event eventName + * @callback: the callback to add + * @eventID: the specific eventID + * @opaque: opaque data tio pass to callback
s/tio/to/ but again, I'm not sure we want this function in the generic libvirt code; it may fit better in the qemu-specific code.
+ * + * Internal function to add a callback from a virDomainEventCallbackListPtr + */ +int +virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + virDomainEventCallbackPtr event; + int i; + + /* 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_DOMAIN_EVENT_CALLBACK(callback) && + STREQ(cbList->callbacks[i]->eventName, eventName) &&
Is a linear search wise? Or should we be setting up a hashtable of event names to the appropriate struct. After all, we don't know how many event names qemu might support, so the goal of O(1) hash lookup will make it more efficient.
+++ b/src/libvirt-qemu.c @@ -36,6 +36,77 @@ virReportErrorHelper(VIR_FROM_DOM, error, NULL, __FUNCTION__, \ __LINE__, info)
+/* Helper macros to implement VIR_DOMAIN_DEBUG using just C99. This + * assumes you pass fewer than 15 arguments to VIR_DOMAIN_DEBUG, but + * can easily be expanded if needed.
Why are we copying this pre-processor magic? It seems like it should be coming from a common header, and not something repeated in this .c file. If we need to move it into a common location in order to reuse it, then that should be a pre-requisite patch (don't mix code motion and new code in the same patch).
/** * virDomainQemuMonitorCommand: * @domain: a domain object @@ -178,3 +249,121 @@ error: virDispatchError(conn); return NULL; } + +/** + * virConnectDomainQemuEventRegister: + * @conn: pointer to the connection + * @dom: pointer to the domain + * @eventName: the event Name to receive + * @cb: callback to the function handling domain events + * @opaque: opaque data to pass on to the callback + * @freecb: optional function to deallocate opaque when not used anymore + * + * Adds a callback to receive notifications of arbitrary qemu domain events + * occurring on a domain. + * + * If dom is NULL, then events will be monitored for any domain. If dom + * is non-NULL, then only the specific domain will be monitored + * + * Most types of event have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus neccessary to use + * the VIR_DOMAIN_EVENT_CALLBACK() macro to cast the supplied function pointer + * to match the signature of this method.
This paragraph is not needed - we only support one type of event callback (return the entire JSON event details, no additional structure imposed by libvirt-qemu), so no casting is needed.
+++ b/src/libvirt_qemu.syms @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; +LIBVIRT_QEMU_0.9.9 { + global: + virConnectDomainQemuEventRegister; + virConnectDomainQemuEventDeregister; +} LIBVIRT_QEMU_0.9.4;
We'd want this to be LIBVIRT_QEMU_0.9.10. Overall, I think the signature looks reasonable, and I'm half inclined to split this patch into the new API and commit that pre-freeze, then worry about polishing up the implementation even if that is post-freeze (that is, the implementation would always fail, but we'd be committing to the API signature). I'll see what I can come up with, because I'm interested in getting this patch series in. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Thank you, Eric. I want to improve this feature as follow , in order to simplify the code. 1. set up a hashtable of event names to a appropriate struct. when user register an event, this event name will be added to this hashtable. and maybe different connection will add the same event name, so the appropriate struct should include all the connections. 2. when qemuMonitorIOProcess receives qemu Monitor event, I want to to simplify the process the qemu-event, not same with the libvirt event. if the qeme-event name can be found in the hashtable, I think I should sent the event to the connection client by qemu_protocol.x directly, instead of putting it into the libvirt event queue. This is different from the libvirt events. The libvirt events are putted into the libvirt event queue and then libvirtd will run event loop to dispatch these events. if this is OK, then I think the hashtable of event names should be a global variable. Is it appropriate that I make the hashtable as a member of qemu_driver struct? I wonder when will the 0.9.10 be released? On 02/04/2012 07:29 AM, Eric Blake wrote:
On 12/16/2011 09:58 AM, shaohef@linux.vnet.ibm.com wrote:
From: ShaoHe Feng<shaohef@linux.vnet.ibm.com>
Basically, this feature can go along with qemu monitor passthrough. That way, if we use new commands in the monitor that generate new events, we want some way to receive those new events too.
Signed-off-by: ShaoHe Feng<shaohef@linux.vnet.ibm.com> --- include/libvirt/libvirt-qemu.h | 27 ++++ include/libvirt/libvirt.h.in | 2 +- src/conf/domain_event.c | 293 ++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 50 ++++++- src/driver.h | 14 ++ src/libvirt-qemu.c | 189 ++++++++++++++++++++++++++ src/libvirt_private.syms | 6 + src/libvirt_qemu.syms | 5 + 8 files changed, 571 insertions(+), 15 deletions(-)
+/** + * virConnectDomainQemuEventCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @eventName : the name of the unknow or un-implementation event s/unknow/unknown/
except that I think this interface should be usable to get the JSON argument string of _any_ qemu monitor event, even if libvirt is already handling it. That is, in qemu, I think we want to parse every incoming event, and do two things with it:
1. if the event name has been registered with the qemu-specific event registration, send a libvirt-qemu event 2. if the event name is known and has a libvirt callback, then do the libvirt callback (which might send a libvirt event).
So UNKNOWN is really not the right word to use. Rather, it is an arbitrary qemu event, whether or not libvirt knows about it.
+ * @eventArgs: the content of the unknow or un-implementation event + * + * The callback signature to use when registering for an event of type + * VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN with virConnectDomainQemuEventRegister() This sentence would then be:
The callback signature to use when registering for an arbitrary qemu event with virConnectDomainQemuEventRegister().
+ */ +typedef void (*virConnectDomainQemuEventCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *eventName, /* The JSON event name */ + const char *eventArgs, /* The JSON string of args */ + void *opaque); This signature looks reasonable.
+ +int +virConnectDomainQemuEventRegister(virConnectPtr conn, + virDomainPtr dom, /* option to filter */ + const char *eventName, /* JSON event name */ + virConnectDomainQemuEventCallback cb, + void *opaque, + virFreeCallback freecb); As does this API.
+int +virConnectDomainQemuEventDeregister(virConnectPtr conn, + int callbackID); + # ifdef __cplusplus } # endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2480add..9fcb400 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3207,7 +3207,6 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, int type, int status, void *opaque); - /** * virConnectDomainEventDiskChangeReason: * @@ -3263,6 +3262,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB = 8, /* virConnectDomainEventBlockJobCallback */ VIR_DOMAIN_EVENT_ID_DISK_CHANGE = 9, /* virConnectDomainEventDiskChangeCallback */ + VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN = 10, /* virConnectDomainEventDefaultCallback */ No. We do not want to be adding this event type to libvirt.so. Qemu events should be sent on a different channel of the RPC protocol, and never interfere with libvirt events.
/* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 614ab97..0388a66 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -45,7 +45,9 @@ typedef virDomainMeta *virDomainMetaPtr;
struct _virDomainEventCallback { int callbackID; + int qemuCallbackID; int eventID; + char *eventName;
Since we don't want to piggy-back main libvirt events, but instead send this directly from qemu, I'm wondering if it's better to have a qemu-specific structure in src/qemu rather than trying to combine it into conf/domain_event.c.
virConnectPtr conn; virDomainMetaPtr dom; virConnectDomainEventGenericCallback cb; @@ -94,6 +96,10 @@ struct _virDomainEvent { char *devAlias; int reason; } diskChange; + struct { + char *eventName; + char *eventArgs; + }qemuUnknownEvent;
Style: space after }.
+/** + * virDomainEventCallbackListAddName: + * @conn: pointer to the connection + * @cbList: the list + * @eventName: the event eventName + * @callback: the callback to add + * @eventID: the specific eventID + * @opaque: opaque data tio pass to callback s/tio/to/
but again, I'm not sure we want this function in the generic libvirt code; it may fit better in the qemu-specific code.
+ * + * Internal function to add a callback from a virDomainEventCallbackListPtr + */ +int +virDomainEventCallbackListAddName(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + const char* eventName, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + virDomainEventCallbackPtr event; + int i; + + /* 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_DOMAIN_EVENT_CALLBACK(callback)&& + STREQ(cbList->callbacks[i]->eventName, eventName)&& Is a linear search wise? Or should we be setting up a hashtable of event names to the appropriate struct. After all, we don't know how many event names qemu might support, so the goal of O(1) hash lookup will make it more efficient.
+++ b/src/libvirt-qemu.c @@ -36,6 +36,77 @@ virReportErrorHelper(VIR_FROM_DOM, error, NULL, __FUNCTION__, \ __LINE__, info)
+/* Helper macros to implement VIR_DOMAIN_DEBUG using just C99. This + * assumes you pass fewer than 15 arguments to VIR_DOMAIN_DEBUG, but + * can easily be expanded if needed. Why are we copying this pre-processor magic? It seems like it should be coming from a common header, and not something repeated in this .c file. If we need to move it into a common location in order to reuse it, then that should be a pre-requisite patch (don't mix code motion and new code in the same patch).
/** * virDomainQemuMonitorCommand: * @domain: a domain object @@ -178,3 +249,121 @@ error: virDispatchError(conn); return NULL; } + +/** + * virConnectDomainQemuEventRegister: + * @conn: pointer to the connection + * @dom: pointer to the domain + * @eventName: the event Name to receive + * @cb: callback to the function handling domain events + * @opaque: opaque data to pass on to the callback + * @freecb: optional function to deallocate opaque when not used anymore + * + * Adds a callback to receive notifications of arbitrary qemu domain events + * occurring on a domain. + * + * If dom is NULL, then events will be monitored for any domain. If dom + * is non-NULL, then only the specific domain will be monitored + * + * Most types of event have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus neccessary to use + * the VIR_DOMAIN_EVENT_CALLBACK() macro to cast the supplied function pointer + * to match the signature of this method. This paragraph is not needed - we only support one type of event callback (return the entire JSON event details, no additional structure imposed by libvirt-qemu), so no casting is needed.
+++ b/src/libvirt_qemu.syms @@ -19,3 +19,8 @@ LIBVIRT_QEMU_0.9.4 { global: virDomainQemuAttach; } LIBVIRT_QEMU_0.8.3; +LIBVIRT_QEMU_0.9.9 { + global: + virConnectDomainQemuEventRegister; + virConnectDomainQemuEventDeregister; +} LIBVIRT_QEMU_0.9.4; We'd want this to be LIBVIRT_QEMU_0.9.10.
Overall, I think the signature looks reasonable, and I'm half inclined to split this patch into the new API and commit that pre-freeze, then worry about polishing up the implementation even if that is post-freeze (that is, the implementation would always fail, but we'd be committing to the API signature). I'll see what I can come up with, because I'm interested in getting this patch series in.
participants (4)
-
Adam Litke
-
Eric Blake
-
ShaoHe Feng
-
shaohef@linux.vnet.ibm.com