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

ShaoHe Feng shaohef at linux.vnet.ibm.com
Tue Dec 13 06:13:36 UTC 2011


there are are some thing wrong with the formate of my last mail, so I
resent it again.

and In this patch, I add the the register callback
_dispatchDomainEventUnknown Callback function as an attribute of the
virConnect class in libvirt.py, not in the libvirt_qemu.py.

should I make it as an attribute of an new subclass that inherits from
virConnect class in libvirt_qemu.py?
 
now, there will come an  "RESUME" events when qemu domains are started.

The attached 0002-for-testing-the-new-qemu-specific-regiater-API.patch
is used to start a qemu domain and to catch this event.
# ./tools/virsh -c qemu:///system
virsh# register-event RESUME
virsh# start domain

the attached python test case, is used to catch RESUME events, in order
to test the python register API.


, shaohef at linux.vnet.ibm.com wrote:
> From: Shao He Feng <shaohef at 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.
> In order to test this patch, see the attached python test case.  When domains are started, it will be able to catch RESUME events.
>
> Signed-off-by: Shao He Feng <shaohef at linux.vnet.ibm.com>
> ---
>  daemon/libvirtd.h                     |   12 +-
>  daemon/remote.c                       |  195 ++++++++++++++++++-
>  include/libvirt/libvirt-qemu.h        |   11 +
>  include/libvirt/libvirt.h.in          |   17 ++-
>  python/generator.py                   |   26 ++-
>  python/libvirt-override-virConnect.py |   14 ++-
>  python/libvirt-qemu-override.c        |  140 +++++++++++++
>  python/libvirt-qemu-override.py       |   41 ++++
>  src/conf/domain_event.c               |  347 +++++++++++++++++++++++++++++++--
>  src/conf/domain_event.h               |   49 +++++-
>  src/driver.h                          |   14 ++
>  src/libvirt-qemu.c                    |  190 ++++++++++++++++++
>  src/libvirt_private.syms              |    6 +
>  src/libvirt_qemu.syms                 |    5 +
>  src/qemu/qemu_driver.c                |   42 ++++
>  src/qemu/qemu_monitor.c               |   10 +
>  src/qemu/qemu_monitor.h               |    8 +
>  src/qemu/qemu_monitor_json.c          |   36 ++++
>  src/qemu/qemu_process.c               |   24 +++
>  src/remote/qemu_protocol.x            |   33 +++-
>  src/remote/remote_driver.c            |  153 ++++++++++++++-
>  src/remote/remote_protocol.x          |    9 +-
>  src/remote_protocol-structs           |    5 +
>  23 files changed, 1353 insertions(+), 34 deletions(-)
>  mode change 100644 => 100755 python/libvirt-override-virConnect.py
>  mode change 100644 => 100755 python/libvirt-override-virStream.py
>  mode change 100644 => 100755 python/libvirt-override.py
>  create mode 100644 python/libvirt-qemu-override.py
>
> diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h
> index c8d3ca2..e3e1c40 100644
> --- a/daemon/libvirtd.h
> +++ b/daemon/libvirtd.h
> @@ -38,6 +38,16 @@
>  # 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,7 +59,7 @@ struct daemonClientPrivate {
>      virMutex lock;
>
>      int domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LAST];
> -
> +    domainEventNameCallBackStatus domainEventNameCallBack;
>  # if HAVE_SASL
>      virNetSASLSessionPtr sasl;
>  # endif
> diff --git a/daemon/remote.c b/daemon/remote.c
> index e1d208c..bc8455d 100644
> --- a/daemon/remote.c
> +++ b/daemon/remote.c
> @@ -421,6 +421,54 @@ 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;
> +    remote_domain_event_default_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, remoteProgram,
> +                                  REMOTE_PROC_QEMU_DOMAIN_EVENT_UNKNOWN_EVENT,
> +                                  (xdrproc_t)xdr_remote_domain_event_default_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 +557,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 +575,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 +632,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 +3055,133 @@ 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
> +remoteDispatchQemuDomainEventUnknownEvent(virNetServerPtr server,
> +                                          virNetServerClientPtr client,
> +                                          virNetMessagePtr msg,
> +                                          virNetMessageErrorPtr rerr)
> +{
> +    int rv = -1;
> +    return rv;
> +}
> +
> +static int
> +qemuDispatchDomainEventsUnknownEvent(virNetServerPtr server,
> +                                     virNetServerClientPtr client,
> +                                     virNetMessagePtr msg,
> +                                     virNetMessageErrorPtr rerr,
> +                                     qemu_domain_events_unknown_event_args *args,
> +                                     qemu_domain_events_unknown_event_ret *ret)
> +{
> +    int rv = -1;
> +    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->cb_deregistered = 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/include/libvirt/libvirt-qemu.h b/include/libvirt/libvirt-qemu.h
> index 7f12e4f..74efe79 100644
> --- a/include/libvirt/libvirt-qemu.h
> +++ b/include/libvirt/libvirt-qemu.h
> @@ -32,6 +32,17 @@ virDomainPtr virDomainQemuAttach(virConnectPtr domain,
>                                   unsigned int pid,
>                                   unsigned int flags);
>
> +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..99e1c5d 100644
> --- a/include/libvirt/libvirt.h.in
> +++ b/include/libvirt/libvirt.h.in
> @@ -3207,7 +3207,21 @@ typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn,
>                                                        int type,
>                                                        int status,
>                                                        void *opaque);
> -
> +/**
> + * 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);
>  /**
>   * virConnectDomainEventDiskChangeReason:
>   *
> @@ -3263,6 +3277,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/python/generator.py b/python/generator.py
> index 88c52b9..3d13622 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",
> @@ -476,6 +477,8 @@ skip_function = (
>
>  qemu_skip_function = (
>      #"virDomainQemuAttach",
> +    'virConnectDomainQemuEventRegister',   # overridden in libvirt_qemu_override.py
> +    'virConnectDomainQemuEventDeregister', # overridden in libvirt_qemu_override.py
>  )
>
>  # Generate C code, but skip python impl
> @@ -1656,17 +1659,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
>      #
> diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py
> old mode 100644
> new mode 100755
> index b908b32..ce34bcf
> --- a/python/libvirt-override-virConnect.py
> +++ b/python/libvirt-override-virConnect.py
> @@ -134,8 +134,20 @@
>          cb(self, virDomain(self, _obj=dom), oldSrcPath, newSrcPath, devAlias, reason, opaque)
>          return 0;
>
> +    def _dispatchDomainEventUnknownCallback(self, dom, eventName, eventArgs, cbData):
> +        """Dispatches events to python user unknown event callbacks
> +        """
> +        try:
> +            cb = cbData["cb"]
> +            opaque = cbData["opaque"]
> +
> +            cb(self, virDomain(self, _obj=dom), eventName, eventArgs, opaque)
> +            return 0
> +        except AttributeError:
> +            pass
> +
>      def domainEventDeregisterAny(self, callbackID):
> -        """Removes a Domain Event Callback. De-registering for a
> +        """Removes a QEMU Domain Event Callback. De-registering for a
>             domain callback will disable delivery of this event type """
>          try:
>              ret = libvirtmod.virConnectDomainEventDeregisterAny(self._o, callbackID)
> diff --git a/python/libvirt-override-virStream.py b/python/libvirt-override-virStream.py
> old mode 100644
> new mode 100755
> diff --git a/python/libvirt-override.py b/python/libvirt-override.py
> old mode 100644
> new mode 100755
> 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..0f5694c
> --- /dev/null
> +++ b/python/libvirt-qemu-override.py
> @@ -0,0 +1,41 @@
> +#
> +# 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 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 = {}
> +    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/src/conf/domain_event.c b/src/conf/domain_event.c
> index 614ab97..b6a763a 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,8 @@ virDomainEventCallbackListFree(virDomainEventCallbackListPtr list)
>
>      for (i=0; i<list->count; i++) {
>          virFreeCallback freecb = list->callbacks[i]->freecb;
> +        if (list->callbacks[i]->eventName)
> +            VIR_FREE(list->callbacks[i]->eventName);
>          if (freecb)
>              (*freecb)(list->callbacks[i]->opaque);
>          VIR_FREE(list->callbacks[i]);
> @@ -143,7 +151,6 @@ virDomainEventCallbackListRemove(virConnectPtr conn,
>                  (*freecb)(cbList->callbacks[i]->opaque);
>              virUnrefConnect(cbList->callbacks[i]->conn);
>              VIR_FREE(cbList->callbacks[i]);
> -
>              if (i < (cbList->count - 1))
>                  memmove(cbList->callbacks + i,
>                          cbList->callbacks + i + 1,
> @@ -187,8 +194,11 @@ 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) {
> +                if (cbList->callbacks[i]->eventName)
> +                    VIR_FREE(cbList->callbacks[i]->eventName);
> +            }
>              VIR_FREE(cbList->callbacks[i]);
> -
>              if (i < (cbList->count - 1))
>                  memmove(cbList->callbacks + i,
>                          cbList->callbacks + i + 1,
> @@ -212,6 +222,52 @@ virDomainEventCallbackListRemoveID(virConnectPtr conn,
>
>
>  /**
> + * virDomainQemuEventCallbackListRemoveID:
> + * @conn: pointer to the connection
> + * @cbList: the list
> + * @callback: the callback to remove
> + *
> + * Internal function to remove a callback from a virDomainEventCallbackListPtr
> + */
> +int
> +virDomainQemuEventCallbackListRemoveID(virConnectPtr conn,
> +                                       virDomainEventCallbackListPtr cbList,
> +                                       int callbackID)
> +{
> +    int i;
> +    for (i = 0 ; i < cbList->count ; i++) {
> +        if (cbList->callbacks[i]->callbackID == callbackID &&
> +            cbList->callbacks[i]->conn == conn &&
> +            cbList->callbacks[i]->eventID == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN) {
> +            virFreeCallback freecb = cbList->callbacks[i]->freecb;
> +            if (freecb)
> +                (*freecb)(cbList->callbacks[i]->opaque);
> +            VIR_FREE(cbList->callbacks[i]->eventName);
> +            virUnrefConnect(cbList->callbacks[i]->conn);
> +            VIR_FREE(cbList->callbacks[i]);
> +
> +            if (i < (cbList->count - 1))
> +                memmove(cbList->callbacks + i,
> +                        cbList->callbacks + i + 1,
> +                        sizeof(*(cbList->callbacks)) *
> +                                (cbList->count - (i + 1)));
> +
> +            if (VIR_REALLOC_N(cbList->callbacks,
> +                              cbList->count - 1) < 0) {
> +                ; /* Failure to reduce memory allocation isn't fatal */
> +            }
> +            cbList->count--;
> +            return 0;
> +        }
> +    }
> +
> +    eventReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                     _("could not find event callback for removal"));
> +    return -1;
> +}
> +
> +
> +/**
>   * virDomainEventCallbackListRemoveConn:
>   * @conn: pointer to the connection
>   * @cbList: the list
> @@ -231,8 +287,11 @@ 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) {
> +                if (cbList->callbacks[i]->eventName)
> +                    VIR_FREE(cbList->callbacks[i]->eventName);
> +            }
>              VIR_FREE(cbList->callbacks[i]);
> -
>              if (i < (cbList->count - 1))
>                  memmove(cbList->callbacks + i,
>                          cbList->callbacks + i + 1,
> @@ -299,8 +358,11 @@ 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) {
> +                if (cbList->callbacks[i]->eventName)
> +                    VIR_FREE(cbList->callbacks[i]->eventName);
> +            }
>              VIR_FREE(cbList->callbacks[i]);
> -
>              if (i < (cbList->count - 1))
>                  memmove(cbList->callbacks + i,
>                          cbList->callbacks + i + 1,
> @@ -404,7 +466,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) &&
> +            !strcmp(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: %d",
> +                         _("not suport this kind of eventID"), 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 +569,41 @@ no_memory:
>          if (event->dom)
>              VIR_FREE(event->dom->name);
>          VIR_FREE(event->dom);
> +	if (event->eventName)
> +            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 +625,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 (!strcmp(cbList->callbacks[i]->eventName,eventName) &&
> +            cbList->callbacks[i]->conn == conn)
> +            count++;
> +    }
> +
> +    return count;
> +}
> +
> +
>  int virDomainEventCallbackListEventID(virConnectPtr conn,
>                                        virDomainEventCallbackListPtr cbList,
>                                        int callbackID)
> @@ -461,6 +665,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 +763,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 +1203,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 +1387,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 +1473,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 +1489,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 +1499,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 ||
> +           strcmp(cb->eventName, event->data.qemuUnknownEvent.eventName) != 0)
> +            return 0;
> +    }
>      if (cb->dom) {
>          /* Deliberately ignoring 'id' for matching, since that
>           * will cause problems when a domain switches between
> @@ -1341,3 +1647,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 = virDomainQemuEventCallbackListRemoveID(conn,
> +                                                     state->callbacks, callbackID);
> +    virDomainEventStateUnlock(state);
> +    return ret;
> +}
> diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h
> index 3ba418e..c0edfac 100644
> --- a/src/conf/domain_event.h
> +++ b/src/conf/domain_event.h
> @@ -26,7 +26,6 @@
>  # define __DOMAIN_EVENT_H__
>
>  # include "domain_conf.h"
> -
>  typedef struct _virDomainEventCallback virDomainEventCallback;
>  typedef virDomainEventCallback *virDomainEventCallbackPtr;
>
> @@ -83,6 +82,15 @@ 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,
> @@ -92,6 +100,10 @@ int virDomainEventCallbackListRemoveID(virConnectPtr conn,
>                                         virDomainEventCallbackListPtr cbList,
>                                         int callbackID)
>      ATTRIBUTE_NONNULL(1);
> +int virDomainQemuEventCallbackListRemoveID(virConnectPtr conn,
> +                                           virDomainEventCallbackListPtr cbList,
> +                                           int callbackID)
> +    ATTRIBUTE_NONNULL(1);
>  int virDomainEventCallbackListRemoveConn(virConnectPtr conn,
>                                           virDomainEventCallbackListPtr cbList)
>      ATTRIBUTE_NONNULL(1);
> @@ -106,9 +118,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 +136,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 +222,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 +285,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..c84375c 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,122 @@ 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__);
> +        virLibConnError(conn, VIR_ERR_INVALID_CONN, "my_test_fuction");
> +        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 a81c230..b92ce71 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -470,11 +470,16 @@ virDomainWatchdogModelTypeToString;
>  # domain_event.h
>  virDomainEventBlockJobNewFromObj;
>  virDomainEventBlockJobNewFromDom;
> +virDomainEventUnknownNewFromObj;
> +virDomainEventunknownNewFromDom;
>  virDomainEventCallbackListAdd;
>  virDomainEventCallbackListAddID;
> +virDomainEventCallbackListAddName;
>  virDomainEventCallbackListCount;
>  virDomainEventCallbackListCountID;
> +virDomainEventCallbackListCountName;
>  virDomainEventCallbackListEventID;
> +virDomainEventCallbackListEventName;
>  virDomainEventCallbackListFree;
>  virDomainEventCallbackListMarkDelete;
>  virDomainEventCallbackListMarkDeleteID;
> @@ -511,6 +516,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;
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index ceb9f47..31fe966 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -8217,6 +8217,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
>   *******************************************************************/
> @@ -11564,6 +11604,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 4141fb7..e6cd39c 100644
> --- a/src/qemu/qemu_monitor.c
> +++ b/src/qemu/qemu_monitor.c
> @@ -959,6 +959,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..a5fc1e6 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,24 @@ 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
> diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x
> index 39f9adf..5f4ed61 100644
> --- a/src/remote/qemu_protocol.x
> +++ b/src/remote/qemu_protocol.x
> @@ -47,6 +47,34 @@ 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 cb_deregistered;
> +};
> +
> +struct qemu_domain_events_unknown_event_args {
> +    remote_nonnull_domain dom;
> +    remote_nonnull_string eventName;
> +    remote_nonnull_string eventArgs;
> +};
> +
> +struct qemu_domain_events_unknown_event_ret {
> +    int event_ret;
> +};
> +
> +
>  /* Define the program number, protocol version and procedure numbers here. */
>  const QEMU_PROGRAM = 0x20008087;
>  const QEMU_PROTOCOL_VERSION = 1;
> @@ -61,5 +89,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 priority:low */
>  };
> diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
> index ff2d4b4..1f7b25d 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,
> @@ -280,6 +284,10 @@ static virNetClientProgramEvent remoteDomainEvents[] = {
>        remoteDomainBuildEventDiskChange,
>        sizeof(remote_domain_event_disk_change_msg),
>        (xdrproc_t)xdr_remote_domain_event_disk_change_msg },
> +    { REMOTE_PROC_QEMU_DOMAIN_EVENT_UNKNOWN_EVENT,
> +      remoteQemuDomainBuildEventDefaultEvent,
> +      sizeof(remote_domain_event_default_event_msg),
> +      (xdrproc_t)xdr_remote_domain_event_default_event_msg },
>  };
>
>  enum virDrvOpenRemoteFlags {
> @@ -3345,6 +3353,28 @@ 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;
> +    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 +3830,6 @@ done:
>      return rv;
>  }
>
> -
>  static int remoteDomainEventDeregisterAny(virConnectPtr conn,
>                                            int callbackID)
>  {
> @@ -3843,6 +3872,126 @@ 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.cb_deregistered = -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.cb_deregistered == -1 ) {
> +            remoteError(VIR_ERR_RPC, _("remote sever deregeiter remote callbackID:%d, and the client callbackID%d"),
> +                        args.callbackID,
> +                        callbackID);
> +            goto done;
> +        }
> +        rv = ret.cb_deregistered;
> +    }
> +
> +
> +done:
> +    remoteDriverUnlock(priv);
> +    if (eventName != NULL)
> +        VIR_FREE(eventName);
> +    return rv;
> +}
>
>  /*----------------------------------------------------------------------*/
>
> @@ -4627,6 +4776,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..4239db8 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;
> @@ -2605,7 +2611,8 @@ enum remote_procedure {
>
>      REMOTE_PROC_DOMAIN_BLOCK_RESIZE = 251, /* autogen autogen */
>      REMOTE_PROC_DOMAIN_SET_BLOCK_IO_TUNE = 252, /* autogen autogen */
> -    REMOTE_PROC_DOMAIN_GET_BLOCK_IO_TUNE = 253 /* skipgen skipgen */
> +    REMOTE_PROC_DOMAIN_GET_BLOCK_IO_TUNE = 253, /* skipgen skipgen */
> +    REMOTE_PROC_QEMU_DOMAIN_EVENT_UNKNOWN_EVENT = 254 /* skipgen skipgen */
>
>      /*
>       * Notice how the entries are grouped in sets of 10 ?
> 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,
>  };

-------------- next part --------------
A non-text attachment was scrubbed...
Name: unknown-event-test.py
Type: text/x-python
Size: 2314 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20111213/fd90a92e/attachment-0001.py>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0002-for-testing-the-new-qemu-specific-regiater-API.patch
Type: text/x-patch
Size: 4745 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20111213/fd90a92e/attachment-0001.bin>


More information about the libvir-list mailing list