hooklift/node-libvirt

libvirt events and virEventRegisterImpl()

atoy40 opened this issue · 8 comments

Hello,

When you try :

c.registerDomainEvent({evtype: c.VIR_DOMAIN_EVENT_ID_LIFECYCLE, callback: function() {
  console.log("lifecycle event");
}});

you get a libvirt error : "could not initialize domain event timer"

In order to use libvirt events, an event "manager" has to be defined using virEventRegisterImpl. This function must provide function pointers for various task (http://libvirt.org/html/libvirt-libvirt.html#virEventRegisterImpl).
For example virEventAddHandleFunc receive a FD, so it can be integrated with libuv (fd poll).

Anthony.

I see. Unfortunately, I don't currently have extra bandwidth to work on that. Contributions are very welcome.

I started working on the implementation, and it seems to work, but i've a problem, and may be you can help me to solve this one.

I registered a callback for a lifecycle event, and, if i started a vm, libvirt call the "C" handler you specified for this kind of event : domain_event_lifecycle_callback
But there is a problem with the opaque pointer you used to pass the node object containing the hypervisor and the JS callback : the opaque pointer address is the "same" (if i print the address of the opaque pointer where you define it, and if i print it in the domain_event_lifecycle_callback function, i got the same value) but it generate a segfault as soon as you used it :
Local hyp = obj->Get(domain_event_hypervisor_symbol)->ToObject();

it seems the Object has been freed between the callback registering and the callback call (when a lifecycle event occurs).

I tried to pass a simple integer pointer (allocated on the heap with a c++ new) instead of the Persistent, and i got it with the correct value in the domain_event_lifecycle_callback function.

Any idea ? i'm not very familar with node API and variable scopes...

I see, you should be able to call hypervisor->Ref() before calling the JS callback and once it comes back you call hypervisor->Unref(). I'm doing a similar thing in https://github.com/c4milo/node-inotify/blob/master/src/bindings.cc#L154 and https://github.com/c4milo/node-inotify/blob/master/src/bindings.cc#L302

it will increase the reference count and will avoid v8 GC to release that object.

The released object is not the Hypervisor, but the Persistent v8 Object you set-up as "opaque" (the void* libvirt give you back in handler) :

Persistent<Object> opaque = Persistent<Object>::New(Object::New());
opaque->Set(domain_event_hypervisor_symbol, args.This());
opaque->Set(domain_event_callback_symbol, jscallback);

Hypervisor *hypervisor = ObjectWrap::Unwrap<Hypervisor>(args.This());
int ret = virConnectDomainEventRegisterAny( hypervisor->conn_,
                                                domain != NULL ? domain->domain() : NULL,
                                                evtype, callback,
                                                &opaque, domain_event_free);

I found the fix for this issue. It comes from the way you pass the opaque object.
You use &opaque, which means "the address" of the persitent handle "opaque". But this variable is allocated on the stack, so only available in the function it is defined.
What you want to pass to the virConnectDomainEventRegisterAny is not the v8::Persistent address, but the v8::Object address. Handle (so Persistent) overload the * operator to get the embedded v8::Value pointer.
This is a bit strange at the syntax level, but you need to use *opaque (instead of &opaque) which return the address of the Object encapsulated in the Persistent.
There is nothing to change is the callback itself .. it works right.

I've some improvements to make, and i'll propose you a pull request for libvirt event management.

Anthony.

hey @atoy40, great that you made it work! Thanks a lot for your help and apologies for my lack of timely and accurate responses. The truth is, It will take me a bit of time to wrap my head around this project again since I'm currently working on very different things.

No problem :)