[libvirt] [libvirt-snmp][PATCH v5 4/4] Add SNMP trap/notification support.

Daniel P. Berrange berrange at redhat.com
Wed Apr 20 10:24:11 UTC 2011


On Thu, Apr 14, 2011 at 02:30:53PM +0200, Michal Privoznik wrote:
> This patch adds support for domain lifecycle notification support
> over SNMP traps. SNMP subagent monitors any domain events and when
> something interesting happens, it sends a trap.
> 
> Monitoring is done in a joinable thread using polling (used
> domain-events example from libvirt) so we won't block the agent itself.
> 
> Some debug info can be printed out by setting LIBVIRT_SNMP_VERBOSE
> environment variable.
> ---
>  INSTALL.1st                |    9 +-
>  configure.ac               |   17 +-
>  libvirt-snmp.spec.in       |    7 +-
>  src/Makefile.am            |   21 ++-
>  src/README.txt             |    2 +
>  src/event_poll.c           |  724 ++++++++++++++++++++++++++++++++++++++++++++
>  src/event_poll.h           |  132 ++++++++
>  src/ignore-value.h         |   64 ++++
>  src/internal.h             |  267 ++++++++++++++++
>  src/libvirtNotifications.c |   16 +-
>  src/libvirtNotifications.h |   16 +-
>  src/libvirtSnmp.c          |  154 +++++++++-
>  src/memory.c               |  313 +++++++++++++++++++
>  src/memory.h               |  212 +++++++++++++
>  src/threads.c              |  251 +++++++++++++++
>  src/threads.h              |  101 ++++++
>  src/util.c                 |  105 +++++++
>  src/util.h                 |   38 +++
>  18 files changed, 2433 insertions(+), 16 deletions(-)
>  create mode 100644 src/event_poll.c
>  create mode 100644 src/event_poll.h
>  create mode 100644 src/ignore-value.h
>  create mode 100644 src/internal.h
>  create mode 100644 src/memory.c
>  create mode 100644 src/memory.h
>  create mode 100644 src/threads.c
>  create mode 100644 src/threads.h
>  create mode 100644 src/util.c
>  create mode 100644 src/util.h
> 
> diff --git a/INSTALL.1st b/INSTALL.1st
> index 31345d8..c439bf3 100644
> --- a/INSTALL.1st
> +++ b/INSTALL.1st
> @@ -15,14 +15,17 @@ Now it's time for make:
>      make
>      su -c "make install"
>  
> -This compile all sources producing runable SNMP subagent
> -libvirtMib_subagent, which is installed right after.
> +This compiles all source producing a runnable SNMP subagent,
> +libvirtMib_subagent, which is installed afterward.
>  But before we run it, we need to edit /etc/snmp/snmpd.conf
> -so it contains this two lines:
> +so it contains these four lines:
>  
>  rwcommunity public
>  master agentx
>  
> +trap2sink  localhost
> +trapcommunity public
> +
>  and then restart snmpd:
>      /etc/init.d/snmpd restart
>  
> diff --git a/configure.ac b/configure.ac
> index dcab0ae..d12f540 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1,4 +1,4 @@
> -AC_INIT([libvirt-snmp],[0.0.1],[libvir-list at redhat.com],[],[http://libvirt.org])
> +AC_INIT([libvirt-snmp],[0.0.2],[libvir-list at redhat.com],[],[http://libvirt.org])
>  AM_INIT_AUTOMAKE([-Wall -Werror])
>  AC_CONFIG_HEADERS([config.h])
>  
> @@ -32,6 +32,14 @@ fi
>  AC_SUBST([LIBVIRT_CFLAGS])
>  AC_SUBST([LIBVIRT_LIBS])
>  
> +dnl do we have old libvirt?
> +AC_CHECK_LIB([virt], [virEventRunDefaultImpl], [old=0], [old=1])
> +if test $old = 1 ; then
> +    AC_DEFINE_UNQUOTED([LIBVIRT_OLD], ["$old"], [we are using old libvirt
> +                        which does not have new event api])
> +fi
> +AM_CONDITIONAL([LIBVIRT_OLD], [test $old = 1])
> +
>  SNMP_CONFIG="net-snmp-config"
>  SNMP_CFLAGS=""
>  SNMP_LIBS=""
> @@ -86,5 +94,12 @@ fi
>  
>  AC_SUBST([MIB_DIR])
>  
> +dnl pthread
> +PTHREAD_LIBS=
> +AC_CHECK_HEADERS(pthread.h, [], [AC_MSG_ERROR([pthread.h required])])
> +AC_CHECK_LIB(pthread, pthread_create, [PTHREAD_LIBS="-lpthread"])
> +
> +AC_SUBST([PTHREAD_LIBS])
> +
>  AC_OUTPUT(Makefile src/Makefile docs/Makefile libvirt-snmp.spec)
>  
> diff --git a/libvirt-snmp.spec.in b/libvirt-snmp.spec.in
> index bbc5602..293c375 100644
> --- a/libvirt-snmp.spec.in
> +++ b/libvirt-snmp.spec.in
> @@ -1,6 +1,6 @@
>  Name:		libvirt-snmp
>  Version:	@VERSION@
> -Release:	3%{?dist}%{?extra_release}
> +Release:	1%{?dist}%{?extra_release}
>  Summary:	SNMP functionality for libvirt
>  
>  Group:		Development/Libraries
> @@ -36,8 +36,11 @@ make install DESTDIR=$RPM_BUILD_ROOT
>  
>  
>  %changelog
> +* Wed Mar 23 2011 Michal Privoznik <mprivozn at redhat.com> 0.0.2-1
> +- add SNMP trap/notification support
> +
>  * Fri Mar 11 2011 Michal Privoznik <mprivozn at redhat.com> 0.0.1-3
> -- remove LIBVIRT-MIB.txt from %doc
> +- remove LIBVIRT-MIB.txt from doc
>  
>  * Wed Mar  9 2011 Michal Privoznik <mprivozn at redhat.com> 0.0.1-2
>  - resolve licensing conflicts
> diff --git a/src/Makefile.am b/src/Makefile.am
> index dcd463a..1a60c91 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -9,8 +9,23 @@ AM_CFLAGS =	\
>  
>  AM_LDFLAGS = 	\
>  		$(COVERAGE_LDFLAGS) \
> +		$(PTHREAD_LIBS) \
>  		$(SNMP_LIBS)
>  
> +LIBVIRT_OLD_SRCS = \
> +		threads.c \
> +		event_poll.c \
> +		memory.c \
> +		util.c
> +
> +LIBVIRT_OLD_HDRS = \
> +		internal.h \
> +		ignore-value.h \
> +		threads.h \
> +		event_poll.h \
> +		memory.h \
> +		util.h
> +
>  USER_SRCS = 	\
>  		libvirtGuestTable_data_get.c \
>  		libvirtGuestTable_data_set.c \
> @@ -43,10 +58,14 @@ HDRS = \
>  libvirtMib_subagent_SOURCES=${SRCS} ${HDRS}
>  libvirtMib_subagent_LDFLAGS=${AM_LDFLAGS}
>  
> +if LIBVIRT_OLD
> +libvirtMib_subagent_SOURCES+=${LIBVIRT_OLD_SRCS} ${LIBVIRT_OLD_HDRS}
> +endif
> +
>  EXTRA_DIST = LIBVIRT-MIB.txt
>  
>  install-data-local:
> -	$(MKDIR_P) "$(DESTDIR)$(MIB_DIR)"
> +	test -z "$(DESTDIR)$(MIB_DIR)" || @mkdir_p@ "$(DESTDIR)$(MIB_DIR)"
>  	$(INSTALL_DATA) "$(srcdir)/LIBVIRT-MIB.txt" \
>  		"$(DESTDIR)$(MIB_DIR)/LIBVIRT-MIB.txt"
>  	
> diff --git a/src/README.txt b/src/README.txt
> index 6d010f6..5e9823a 100644
> --- a/src/README.txt
> +++ b/src/README.txt
> @@ -47,6 +47,8 @@ $ make
>  2. use following /etc/snmp/snmpd.conf:
>  rwcommunity public
>  master agentx
> +trap2sink  localhost
> +trapcommunity public
>  
>  3. service snmpd start
>  
> diff --git a/src/event_poll.c b/src/event_poll.c
> new file mode 100644
> index 0000000..f8c4a8b
> --- /dev/null
> +++ b/src/event_poll.c
> @@ -0,0 +1,724 @@
> +/*
> + * event.c: event loop for monitoring file handles
> + *
> + * Copyright (C) 2007, 2010-2011 Red Hat, Inc.
> + * Copyright (C) 2007 Daniel P. Berrange
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#include <config.h>
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include <poll.h>
> +#include <sys/time.h>
> +#include <errno.h>
> +#include <unistd.h>
> +
> +#include "threads.h"
> +#include "event_poll.h"
> +#include "memory.h"
> +#include "util.h"
> +#include "ignore-value.h"
> +
> +#define EVENT_DEBUG(fmt, ...) VIR_DEBUG(fmt, __VA_ARGS__)
> +
> +static int virEventPollInterruptLocked(void);
> +
> +/* State for a single file handle being monitored */
> +struct virEventPollHandle {
> +    int watch;
> +    int fd;
> +    int events;
> +    virEventHandleCallback cb;
> +    virFreeCallback ff;
> +    void *opaque;
> +    int deleted;
> +};
> +
> +/* State for a single timer being generated */
> +struct virEventPollTimeout {
> +    int timer;
> +    int frequency;
> +    unsigned long long expiresAt;
> +    virEventTimeoutCallback cb;
> +    virFreeCallback ff;
> +    void *opaque;
> +    int deleted;
> +};
> +
> +/* Allocate extra slots for virEventPollHandle/virEventPollTimeout
> +   records in this multiple */
> +#define EVENT_ALLOC_EXTENT 10
> +
> +/* State for the main event loop */
> +struct virEventPollLoop {
> +    virMutex lock;
> +    int running;
> +    virThread leader;
> +    int wakeupfd[2];
> +    size_t handlesCount;
> +    size_t handlesAlloc;
> +    struct virEventPollHandle *handles;
> +    size_t timeoutsCount;
> +    size_t timeoutsAlloc;
> +    struct virEventPollTimeout *timeouts;
> +};
> +
> +/* Only have one event loop */
> +static struct virEventPollLoop eventLoop;
> +
> +/* Unique ID for the next FD watch to be registered */
> +static int nextWatch = 1;
> +
> +/* Unique ID for the next timer to be registered */
> +static int nextTimer = 1;
> +
> +/*
> + * Register a callback for monitoring file handle events.
> + * NB, it *must* be safe to call this from within a callback
> + * For this reason we only ever append to existing list.
> + */
> +int virEventPollAddHandle(int fd, int events,
> +                          virEventHandleCallback cb,
> +                          void *opaque,
> +                          virFreeCallback ff) {
> +    int watch;
> +    EVENT_DEBUG("Add handle fd=%d events=%d cb=%p opaque=%p", fd, events, cb, opaque);
> +    virMutexLock(&eventLoop.lock);
> +    if (eventLoop.handlesCount == eventLoop.handlesAlloc) {
> +        EVENT_DEBUG("Used %zu handle slots, adding at least %d more",
> +                    eventLoop.handlesAlloc, EVENT_ALLOC_EXTENT);
> +        if (VIR_RESIZE_N(eventLoop.handles, eventLoop.handlesAlloc,
> +                         eventLoop.handlesCount, EVENT_ALLOC_EXTENT) < 0) {
> +            virMutexUnlock(&eventLoop.lock);
> +            return -1;
> +        }
> +    }
> +
> +    watch = nextWatch++;
> +
> +    eventLoop.handles[eventLoop.handlesCount].watch = watch;
> +    eventLoop.handles[eventLoop.handlesCount].fd = fd;
> +    eventLoop.handles[eventLoop.handlesCount].events =
> +                                         virEventPollToNativeEvents(events);
> +    eventLoop.handles[eventLoop.handlesCount].cb = cb;
> +    eventLoop.handles[eventLoop.handlesCount].ff = ff;
> +    eventLoop.handles[eventLoop.handlesCount].opaque = opaque;
> +    eventLoop.handles[eventLoop.handlesCount].deleted = 0;
> +
> +    eventLoop.handlesCount++;
> +
> +    virEventPollInterruptLocked();
> +    virMutexUnlock(&eventLoop.lock);
> +
> +    return watch;
> +}
> +
> +void virEventPollUpdateHandle(int watch, int events) {
> +    int i;
> +    EVENT_DEBUG("Update handle w=%d e=%d", watch, events);
> +
> +    if (watch <= 0) {
> +        VIR_WARN("Ignoring invalid update watch %d", watch);
> +        return;
> +    }
> +
> +    virMutexLock(&eventLoop.lock);
> +    for (i = 0 ; i < eventLoop.handlesCount ; i++) {
> +        if (eventLoop.handles[i].watch == watch) {
> +            eventLoop.handles[i].events =
> +                    virEventPollToNativeEvents(events);
> +            virEventPollInterruptLocked();
> +            break;
> +        }
> +    }
> +    virMutexUnlock(&eventLoop.lock);
> +}
> +
> +/*
> + * Unregister a callback from a file handle
> + * NB, it *must* be safe to call this from within a callback
> + * For this reason we only ever set a flag in the existing list.
> + * Actual deletion will be done out-of-band
> + */
> +int virEventPollRemoveHandle(int watch) {
> +    int i;
> +    EVENT_DEBUG("Remove handle w=%d", watch);
> +
> +    if (watch <= 0) {
> +        VIR_WARN("Ignoring invalid remove watch %d", watch);
> +        return -1;
> +    }
> +
> +    virMutexLock(&eventLoop.lock);
> +    for (i = 0 ; i < eventLoop.handlesCount ; i++) {
> +        if (eventLoop.handles[i].deleted)
> +            continue;
> +
> +        if (eventLoop.handles[i].watch == watch) {
> +            EVENT_DEBUG("mark delete %d %d", i, eventLoop.handles[i].fd);
> +            eventLoop.handles[i].deleted = 1;
> +            virEventPollInterruptLocked();
> +            virMutexUnlock(&eventLoop.lock);
> +            return 0;
> +        }
> +    }
> +    virMutexUnlock(&eventLoop.lock);
> +    return -1;
> +}
> +
> +
> +/*
> + * Register a callback for a timer event
> + * NB, it *must* be safe to call this from within a callback
> + * For this reason we only ever append to existing list.
> + */
> +int virEventPollAddTimeout(int frequency,
> +                           virEventTimeoutCallback cb,
> +                           void *opaque,
> +                           virFreeCallback ff) {
> +    struct timeval now;
> +    int ret;
> +    EVENT_DEBUG("Adding timer %d with %d ms freq", nextTimer, frequency);
> +    if (gettimeofday(&now, NULL) < 0) {
> +        return -1;
> +    }
> +
> +    virMutexLock(&eventLoop.lock);
> +    if (eventLoop.timeoutsCount == eventLoop.timeoutsAlloc) {
> +        EVENT_DEBUG("Used %zu timeout slots, adding at least %d more",
> +                    eventLoop.timeoutsAlloc, EVENT_ALLOC_EXTENT);
> +        if (VIR_RESIZE_N(eventLoop.timeouts, eventLoop.timeoutsAlloc,
> +                         eventLoop.timeoutsCount, EVENT_ALLOC_EXTENT) < 0) {
> +            virMutexUnlock(&eventLoop.lock);
> +            return -1;
> +        }
> +    }
> +
> +    eventLoop.timeouts[eventLoop.timeoutsCount].timer = nextTimer++;
> +    eventLoop.timeouts[eventLoop.timeoutsCount].frequency = frequency;
> +    eventLoop.timeouts[eventLoop.timeoutsCount].cb = cb;
> +    eventLoop.timeouts[eventLoop.timeoutsCount].ff = ff;
> +    eventLoop.timeouts[eventLoop.timeoutsCount].opaque = opaque;
> +    eventLoop.timeouts[eventLoop.timeoutsCount].deleted = 0;
> +    eventLoop.timeouts[eventLoop.timeoutsCount].expiresAt =
> +        frequency >= 0 ? frequency +
> +        (((unsigned long long)now.tv_sec)*1000) +
> +        (((unsigned long long)now.tv_usec)/1000) : 0;
> +
> +    eventLoop.timeoutsCount++;
> +    ret = nextTimer-1;
> +    virEventPollInterruptLocked();
> +    virMutexUnlock(&eventLoop.lock);
> +    return ret;
> +}
> +
> +void virEventPollUpdateTimeout(int timer, int frequency) {
> +    struct timeval tv;
> +    int i;
> +    EVENT_DEBUG("Updating timer %d timeout with %d ms freq", timer, frequency);
> +
> +    if (timer <= 0) {
> +        VIR_WARN("Ignoring invalid update timer %d", timer);
> +        return;
> +    }
> +
> +    if (gettimeofday(&tv, NULL) < 0) {
> +        return;
> +    }
> +
> +    virMutexLock(&eventLoop.lock);
> +    for (i = 0 ; i < eventLoop.timeoutsCount ; i++) {
> +        if (eventLoop.timeouts[i].timer == timer) {
> +            eventLoop.timeouts[i].frequency = frequency;
> +            eventLoop.timeouts[i].expiresAt =
> +                frequency >= 0 ? frequency +
> +                (((unsigned long long)tv.tv_sec)*1000) +
> +                (((unsigned long long)tv.tv_usec)/1000) : 0;
> +            virEventPollInterruptLocked();
> +            break;
> +        }
> +    }
> +    virMutexUnlock(&eventLoop.lock);
> +}
> +
> +/*
> + * Unregister a callback for a timer
> + * NB, it *must* be safe to call this from within a callback
> + * For this reason we only ever set a flag in the existing list.
> + * Actual deletion will be done out-of-band
> + */
> +int virEventPollRemoveTimeout(int timer) {
> +    int i;
> +    EVENT_DEBUG("Remove timer %d", timer);
> +
> +    if (timer <= 0) {
> +        VIR_WARN("Ignoring invalid remove timer %d", timer);
> +        return -1;
> +    }
> +
> +    virMutexLock(&eventLoop.lock);
> +    for (i = 0 ; i < eventLoop.timeoutsCount ; i++) {
> +        if (eventLoop.timeouts[i].deleted)
> +            continue;
> +
> +        if (eventLoop.timeouts[i].timer == timer) {
> +            eventLoop.timeouts[i].deleted = 1;
> +            virEventPollInterruptLocked();
> +            virMutexUnlock(&eventLoop.lock);
> +            return 0;
> +        }
> +    }
> +    virMutexUnlock(&eventLoop.lock);
> +    return -1;
> +}
> +
> +/* Iterates over all registered timeouts and determine which
> + * will be the first to expire.
> + * @timeout: filled with expiry time of soonest timer, or -1 if
> + *           no timeout is pending
> + * returns: 0 on success, -1 on error
> + */
> +static int virEventPollCalculateTimeout(int *timeout) {
> +    unsigned long long then = 0;
> +    int i;
> +    EVENT_DEBUG("Calculate expiry of %zu timers", eventLoop.timeoutsCount);
> +    /* Figure out if we need a timeout */
> +    for (i = 0 ; i < eventLoop.timeoutsCount ; i++) {
> +        if (eventLoop.timeouts[i].frequency < 0)
> +            continue;
> +
> +        EVENT_DEBUG("Got a timeout scheduled for %llu", eventLoop.timeouts[i].expiresAt);
> +        if (then == 0 ||
> +            eventLoop.timeouts[i].expiresAt < then)
> +            then = eventLoop.timeouts[i].expiresAt;
> +    }
> +
> +    /* Calculate how long we should wait for a timeout if needed */
> +    if (then > 0) {
> +        struct timeval tv;
> +
> +        if (gettimeofday(&tv, NULL) < 0) {
> +            perror("Unable to get current time");
> +            return -1;
> +        }
> +
> +        *timeout = then -
> +            ((((unsigned long long)tv.tv_sec)*1000) +
> +             (((unsigned long long)tv.tv_usec)/1000));
> +
> +        if (*timeout < 0)
> +            *timeout = 0;
> +    } else {
> +        *timeout = -1;
> +    }
> +
> +    EVENT_DEBUG("Timeout at %llu due in %d ms", then, *timeout);
> +
> +    return 0;
> +}
> +
> +/*
> + * Allocate a pollfd array containing data for all registered
> + * file handles. The caller must free the returned data struct
> + * returns: the pollfd array, or NULL on error
> + */
> +static struct pollfd *virEventPollMakePollFDs(int *nfds) {
> +    struct pollfd *fds;
> +    int i;
> +
> +    *nfds = 0;
> +    for (i = 0 ; i < eventLoop.handlesCount ; i++) {
> +        if (eventLoop.handles[i].events && !eventLoop.handles[i].deleted)
> +            (*nfds)++;
> +    }
> +
> +    /* Setup the poll file handle data structs */
> +    if (VIR_ALLOC_N(fds, *nfds) < 0) {
> +        perror("unable to allocate memory");
> +        return NULL;
> +    }
> +
> +    *nfds = 0;
> +    for (i = 0 ; i < eventLoop.handlesCount ; i++) {
> +        EVENT_DEBUG("Prepare n=%d w=%d, f=%d e=%d d=%d", i,
> +                    eventLoop.handles[i].watch,
> +                    eventLoop.handles[i].fd,
> +                    eventLoop.handles[i].events,
> +                    eventLoop.handles[i].deleted);
> +        if (!eventLoop.handles[i].events || eventLoop.handles[i].deleted)
> +            continue;
> +        fds[*nfds].fd = eventLoop.handles[i].fd;
> +        fds[*nfds].events = eventLoop.handles[i].events;
> +        fds[*nfds].revents = 0;
> +        (*nfds)++;
> +        //EVENT_DEBUG("Wait for %d %d", eventLoop.handles[i].fd, eventLoop.handles[i].events);
> +    }
> +
> +    return fds;
> +}
> +
> +
> +/*
> + * Iterate over all timers and determine if any have expired.
> + * Invoke the user supplied callback for each timer whose
> + * expiry time is met, and schedule the next timeout. Does
> + * not try to 'catch up' on time if the actual expiry time
> + * was later than the requested time.
> + *
> + * This method must cope with new timers being registered
> + * by a callback, and must skip any timers marked as deleted.
> + *
> + * Returns 0 upon success, -1 if an error occurred
> + */
> +static int virEventPollDispatchTimeouts(void) {
> +    struct timeval tv;
> +    unsigned long long now;
> +    int i;
> +    /* Save this now - it may be changed during dispatch */
> +    int ntimeouts = eventLoop.timeoutsCount;
> +    VIR_DEBUG("Dispatch %d", ntimeouts);
> +
> +    if (gettimeofday(&tv, NULL) < 0) {
> +        perror("Unable to get current time");
> +        return -1;
> +    }
> +    now = (((unsigned long long)tv.tv_sec)*1000) +
> +        (((unsigned long long)tv.tv_usec)/1000);
> +
> +    for (i = 0 ; i < ntimeouts ; i++) {
> +        if (eventLoop.timeouts[i].deleted || eventLoop.timeouts[i].frequency < 0)
> +            continue;
> +
> +        /* Add 20ms fuzz so we don't pointlessly spin doing
> +         * <10ms sleeps, particularly on kernels with low HZ
> +         * it is fine that a timer expires 20ms earlier than
> +         * requested
> +         */
> +        if (eventLoop.timeouts[i].expiresAt <= (now+20)) {
> +            virEventTimeoutCallback cb = eventLoop.timeouts[i].cb;
> +            int timer = eventLoop.timeouts[i].timer;
> +            void *opaque = eventLoop.timeouts[i].opaque;
> +            eventLoop.timeouts[i].expiresAt =
> +                now + eventLoop.timeouts[i].frequency;
> +
> +            virMutexUnlock(&eventLoop.lock);
> +            (cb)(timer, opaque);
> +            virMutexLock(&eventLoop.lock);
> +        }
> +    }
> +    return 0;
> +}
> +
> +
> +/* Iterate over all file handles and dispatch any which
> + * have pending events listed in the poll() data. Invoke
> + * the user supplied callback for each handle which has
> + * pending events
> + *
> + * This method must cope with new handles being registered
> + * by a callback, and must skip any handles marked as deleted.
> + *
> + * Returns 0 upon success, -1 if an error occurred
> + */
> +static int virEventPollDispatchHandles(int nfds, struct pollfd *fds) {
> +    int i, n;
> +    VIR_DEBUG("Dispatch %d", nfds);
> +
> +    /* NB, use nfds not eventLoop.handlesCount, because new
> +     * fds might be added on end of list, and they're not
> +     * in the fds array we've got */
> +    for (i = 0, n = 0 ; n < nfds && i < eventLoop.handlesCount ; n++) {
> +        while ((eventLoop.handles[i].fd != fds[n].fd ||
> +                eventLoop.handles[i].events == 0) &&
> +               i < eventLoop.handlesCount) {
> +            i++;
> +        }
> +        if (i == eventLoop.handlesCount)
> +            break;
> +
> +        VIR_DEBUG("i=%d w=%d", i, eventLoop.handles[i].watch);
> +        if (eventLoop.handles[i].deleted) {
> +            EVENT_DEBUG("Skip deleted n=%d w=%d f=%d", i,
> +                        eventLoop.handles[i].watch, eventLoop.handles[i].fd);
> +            continue;
> +        }
> +
> +        if (fds[n].revents) {
> +            virEventHandleCallback cb = eventLoop.handles[i].cb;
> +            int watch = eventLoop.handles[i].watch;
> +            void *opaque = eventLoop.handles[i].opaque;
> +            int hEvents = virEventPollFromNativeEvents(fds[n].revents);
> +            EVENT_DEBUG("Dispatch n=%d f=%d w=%d e=%d %p", i,
> +                        fds[n].fd, watch, fds[n].revents, opaque);
> +            virMutexUnlock(&eventLoop.lock);
> +            (cb)(watch, fds[n].fd, hEvents, opaque);
> +            virMutexLock(&eventLoop.lock);
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +
> +/* Used post dispatch to actually remove any timers that
> + * were previously marked as deleted. This asynchronous
> + * cleanup is needed to make dispatch re-entrant safe.
> + */
> +static void virEventPollCleanupTimeouts(void) {
> +    int i;
> +    size_t gap;
> +    VIR_DEBUG("Cleanup %zu", eventLoop.timeoutsCount);
> +
> +    /* Remove deleted entries, shuffling down remaining
> +     * entries as needed to form contiguous series
> +     */
> +    for (i = 0 ; i < eventLoop.timeoutsCount ; ) {
> +        if (!eventLoop.timeouts[i].deleted) {
> +            i++;
> +            continue;
> +        }
> +
> +        EVENT_DEBUG("Purging timeout %d with id %d", i,
> +                    eventLoop.timeouts[i].timer);
> +        if (eventLoop.timeouts[i].ff) {
> +            virFreeCallback ff = eventLoop.timeouts[i].ff;
> +            void *opaque = eventLoop.timeouts[i].opaque;
> +            virMutexUnlock(&eventLoop.lock);
> +            ff(opaque);
> +            virMutexLock(&eventLoop.lock);
> +        }
> +
> +        if ((i+1) < eventLoop.timeoutsCount) {
> +            memmove(eventLoop.timeouts+i,
> +                    eventLoop.timeouts+i+1,
> +                    sizeof(struct virEventPollTimeout)*(eventLoop.timeoutsCount
> +                                                    -(i+1)));
> +        }
> +        eventLoop.timeoutsCount--;
> +    }
> +
> +    /* Release some memory if we've got a big chunk free */
> +    gap = eventLoop.timeoutsAlloc - eventLoop.timeoutsCount;
> +    if (eventLoop.timeoutsCount == 0 ||
> +        (gap > eventLoop.timeoutsCount && gap > EVENT_ALLOC_EXTENT)) {
> +        EVENT_DEBUG("Found %zu out of %zu timeout slots used, releasing %zu",
> +                    eventLoop.timeoutsCount, eventLoop.timeoutsAlloc, gap);
> +        VIR_SHRINK_N(eventLoop.timeouts, eventLoop.timeoutsAlloc, gap);
> +    }
> +}
> +
> +/* Used post dispatch to actually remove any handles that
> + * were previously marked as deleted. This asynchronous
> + * cleanup is needed to make dispatch re-entrant safe.
> + */
> +static void virEventPollCleanupHandles(void) {
> +    int i;
> +    size_t gap;
> +    VIR_DEBUG("Cleanup %zu", eventLoop.handlesCount);
> +
> +    /* Remove deleted entries, shuffling down remaining
> +     * entries as needed to form contiguous series
> +     */
> +    for (i = 0 ; i < eventLoop.handlesCount ; ) {
> +        if (!eventLoop.handles[i].deleted) {
> +            i++;
> +            continue;
> +        }
> +
> +        if (eventLoop.handles[i].ff) {
> +            virFreeCallback ff = eventLoop.handles[i].ff;
> +            void *opaque = eventLoop.handles[i].opaque;
> +            virMutexUnlock(&eventLoop.lock);
> +            ff(opaque);
> +            virMutexLock(&eventLoop.lock);
> +        }
> +
> +        if ((i+1) < eventLoop.handlesCount) {
> +            memmove(eventLoop.handles+i,
> +                    eventLoop.handles+i+1,
> +                    sizeof(struct virEventPollHandle)*(eventLoop.handlesCount
> +                                                   -(i+1)));
> +        }
> +        eventLoop.handlesCount--;
> +    }
> +
> +    /* Release some memory if we've got a big chunk free */
> +    gap = eventLoop.handlesAlloc - eventLoop.handlesCount;
> +    if (eventLoop.handlesCount == 0 ||
> +        (gap > eventLoop.handlesCount && gap > EVENT_ALLOC_EXTENT)) {
> +        EVENT_DEBUG("Found %zu out of %zu handles slots used, releasing %zu",
> +                    eventLoop.handlesCount, eventLoop.handlesAlloc, gap);
> +        VIR_SHRINK_N(eventLoop.handles, eventLoop.handlesAlloc, gap);
> +    }
> +}
> +
> +/*
> + * Run a single iteration of the event loop, blocking until
> + * at least one file handle has an event, or a timer expires
> + */
> +int virEventPollRunOnce(void) {
> +    struct pollfd *fds = NULL;
> +    int ret, timeout, nfds;
> +
> +    virMutexLock(&eventLoop.lock);
> +    eventLoop.running = 1;
> +    virThreadSelf(&eventLoop.leader);
> +
> +    virEventPollCleanupTimeouts();
> +    virEventPollCleanupHandles();
> +
> +    if (!(fds = virEventPollMakePollFDs(&nfds)) ||
> +        virEventPollCalculateTimeout(&timeout) < 0)
> +        goto error;
> +
> +    virMutexUnlock(&eventLoop.lock);
> +
> + retry:
> +    EVENT_DEBUG("Poll on %d handles %p timeout %d", nfds, fds, timeout);
> +    ret = poll(fds, nfds, timeout);
> +    if (ret < 0) {
> +        EVENT_DEBUG("Poll got error event %d", errno);
> +        if (errno == EINTR) {
> +            goto retry;
> +        }
> +        perror("Unable to poll on file handles");
> +        goto error_unlocked;
> +    }
> +    EVENT_DEBUG("Poll got %d event(s)", ret);
> +
> +    virMutexLock(&eventLoop.lock);
> +    if (virEventPollDispatchTimeouts() < 0)
> +        goto error;
> +
> +    if (ret > 0 &&
> +        virEventPollDispatchHandles(nfds, fds) < 0)
> +        goto error;
> +
> +    virEventPollCleanupTimeouts();
> +    virEventPollCleanupHandles();
> +
> +    eventLoop.running = 0;
> +    virMutexUnlock(&eventLoop.lock);
> +    VIR_FREE(fds);
> +    return 0;
> +
> +error:
> +    virMutexUnlock(&eventLoop.lock);
> +error_unlocked:
> +    VIR_FREE(fds);
> +    return -1;
> +}
> +
> +
> +static void virEventPollHandleWakeup(int watch ATTRIBUTE_UNUSED,
> +                                     int fd,
> +                                     int events ATTRIBUTE_UNUSED,
> +                                     void *opaque ATTRIBUTE_UNUSED)
> +{
> +    char c;
> +    virMutexLock(&eventLoop.lock);
> +    ignore_value(saferead(fd, &c, sizeof(c)));
> +    virMutexUnlock(&eventLoop.lock);
> +}
> +
> +int virEventPollInit(void)
> +{
> +    if (virMutexInit(&eventLoop.lock) < 0) {
> +        perror("Unable to initialize mutex");
> +        return -1;
> +    }
> +
> +    if (pipe(eventLoop.wakeupfd) < 0 ||
> +        virSetNonBlock(eventLoop.wakeupfd[0]) < 0 ||
> +        virSetNonBlock(eventLoop.wakeupfd[1]) < 0 ||
> +        virSetCloseExec(eventLoop.wakeupfd[0]) < 0 ||
> +        virSetCloseExec(eventLoop.wakeupfd[1]) < 0) {
> +        perror("Unable to setup wakeup pipe");
> +        return -1;
> +    }
> +
> +    if (virEventPollAddHandle(eventLoop.wakeupfd[0],
> +                              VIR_EVENT_HANDLE_READABLE,
> +                              virEventPollHandleWakeup, NULL, NULL) < 0) {
> +            fprintf(stderr, "Unable to add handle %d to event loop",
> +                    eventLoop.wakeupfd[0]);
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int virEventPollInterruptLocked(void)
> +{
> +    char c = '\0';
> +
> +    if (!eventLoop.running ||
> +        virThreadIsSelf(&eventLoop.leader)) {
> +        VIR_DEBUG("Skip interrupt, %d %d", eventLoop.running,
> +                  virThreadID(&eventLoop.leader));
> +        return 0;
> +    }
> +
> +    VIR_DEBUG0("Interrupting");
> +    if (safewrite(eventLoop.wakeupfd[1], &c, sizeof(c)) != sizeof(c))
> +        return -1;
> +    return 0;
> +}
> +
> +int virEventPollInterrupt(void)
> +{
> +    int ret;
> +    virMutexLock(&eventLoop.lock);
> +    ret = virEventPollInterruptLocked();
> +    virMutexUnlock(&eventLoop.lock);
> +    return ret;
> +}
> +
> +int
> +virEventPollToNativeEvents(int events)
> +{
> +    int ret = 0;
> +    if(events & VIR_EVENT_HANDLE_READABLE)
> +        ret |= POLLIN;
> +    if(events & VIR_EVENT_HANDLE_WRITABLE)
> +        ret |= POLLOUT;
> +    if(events & VIR_EVENT_HANDLE_ERROR)
> +        ret |= POLLERR;
> +    if(events & VIR_EVENT_HANDLE_HANGUP)
> +        ret |= POLLHUP;
> +    return ret;
> +}
> +
> +int
> +virEventPollFromNativeEvents(int events)
> +{
> +    int ret = 0;
> +    if(events & POLLIN)
> +        ret |= VIR_EVENT_HANDLE_READABLE;
> +    if(events & POLLOUT)
> +        ret |= VIR_EVENT_HANDLE_WRITABLE;
> +    if(events & POLLERR)
> +        ret |= VIR_EVENT_HANDLE_ERROR;
> +    if(events & POLLNVAL) /* Treat NVAL as error, since libvirt doesn't distinguish */
> +        ret |= VIR_EVENT_HANDLE_ERROR;
> +    if(events & POLLHUP)
> +        ret |= VIR_EVENT_HANDLE_HANGUP;
> +    return ret;
> +}
> diff --git a/src/event_poll.h b/src/event_poll.h
> new file mode 100644
> index 0000000..4ab3789
> --- /dev/null
> +++ b/src/event_poll.h
> @@ -0,0 +1,132 @@
> +/*
> + * event.h: event loop for monitoring file handles
> + *
> + * Copyright (C) 2007 Daniel P. Berrange
> + * Copyright (C) 2007 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
> + *
> + * Author: Daniel P. Berrange <berrange at redhat.com>
> + */
> +
> +#ifndef __VIR_EVENT_POLL_H__
> +# define __VIR_EVENT_POLL_H__
> +
> +# include "internal.h"
> +
> +/**
> + * virEventPollAddHandle: register a callback for monitoring file handle events
> + *
> + * @fd: file handle to monitor for events
> + * @events: bitset of events to watch from POLLnnn constants
> + * @cb: callback to invoke when an event occurs
> + * @opaque: user data to pass to callback
> + *
> + * returns -1 if the file handle cannot be registered, 0 upon success
> + */
> +int virEventPollAddHandle(int fd, int events,
> +                            virEventHandleCallback cb,
> +                            void *opaque,
> +                            virFreeCallback ff);
> +
> +/**
> + * virEventPollUpdateHandle: change event set for a monitored file handle
> + *
> + * @watch: watch whose handle to update
> + * @events: bitset of events to watch from POLLnnn constants
> + *
> + * Will not fail if fd exists
> + */
> +void virEventPollUpdateHandle(int watch, int events);
> +
> +/**
> + * virEventPollRemoveHandle: unregister a callback from a file handle
> + *
> + * @watch: watch whose handle to remove
> + *
> + * returns -1 if the file handle was not registered, 0 upon success
> + */
> +int virEventPollRemoveHandle(int watch);
> +
> +/**
> + * virEventPollAddTimeout: register a callback for a timer event
> + *
> + * @frequency: time between events in milliseconds
> + * @cb: callback to invoke when an event occurs
> + * @opaque: user data to pass to callback
> + *
> + * Setting frequency to -1 will disable the timer. Setting the frequency
> + * to zero will cause it to fire on every event loop iteration.
> + *
> + * returns -1 if the file handle cannot be registered, a positive
> + * integer timer id upon success
> + */
> +int virEventPollAddTimeout(int frequency,
> +                             virEventTimeoutCallback cb,
> +                             void *opaque,
> +                             virFreeCallback ff);
> +
> +/**
> + * virEventPollUpdateTimeout: change frequency for a timer
> + *
> + * @timer: timer id to change
> + * @frequency: time between events in milliseconds
> + *
> + * Setting frequency to -1 will disable the timer. Setting the frequency
> + * to zero will cause it to fire on every event loop iteration.
> + *
> + * Will not fail if timer exists
> + */
> +void virEventPollUpdateTimeout(int timer, int frequency);
> +
> +/**
> + * virEventPollRemoveTimeout: unregister a callback for a timer
> + *
> + * @timer: the timer id to remove
> + *
> + * returns -1 if the timer was not registered, 0 upon success
> + */
> +int virEventPollRemoveTimeout(int timer);
> +
> +/**
> + * virEventPollInit: Initialize the event loop
> + *
> + * returns -1 if initialization failed
> + */
> +int virEventPollInit(void);
> +
> +/**
> + * virEventPollRunOnce: run a single iteration of the event loop.
> + *
> + * Blocks the caller until at least one file handle has an
> + * event or the first timer expires.
> + *
> + * returns -1 if the event monitoring failed
> + */
> +int virEventPollRunOnce(void);
> +
> +int virEventPollFromNativeEvents(int events);
> +int virEventPollToNativeEvents(int events);
> +
> +
> +/**
> + * virEventPollInterrupt: wakeup any thread waiting in poll()
> + *
> + * return -1 if wakup failed
> + */
> +int virEventPollInterrupt(void);
> +
> +
> +#endif /* __VIRTD_EVENT_H__ */
> diff --git a/src/ignore-value.h b/src/ignore-value.h
> new file mode 100644
> index 0000000..0df1c01
> --- /dev/null
> +++ b/src/ignore-value.h
> @@ -0,0 +1,64 @@
> +/* -*- buffer-read-only: t -*- vi: set ro: */
> +/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
> +/* ignore a function return without a compiler warning
> +
> +   Copyright (C) 2008-2011 Free Software Foundation, Inc.
> +
> +   This program is free software: you can redistribute it and/or modify
> +   it under the terms of the GNU Lesser General Public License as published by
> +   the Free Software Foundation; either version 2.1 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +/* Written by Jim Meyering, Eric Blake and Pádraig Brady.  */
> +
> +/* Use "ignore_value" to avoid a warning when using a function declared with
> +   gcc's warn_unused_result attribute, but for which you really do want to
> +   ignore the result.  Traditionally, people have used a "(void)" cast to
> +   indicate that a function's return value is deliberately unused.  However,
> +   if the function is declared with __attribute__((warn_unused_result)),
> +   gcc issues a warning even with the cast.
> +
> +   Caution: most of the time, you really should heed gcc's warning, and
> +   check the return value.  However, in those exceptional cases in which
> +   you're sure you know what you're doing, use this function.
> +
> +   For the record, here's one of the ignorable warnings:
> +   "copy.c:233: warning: ignoring return value of 'fchown',
> +   declared with attribute warn_unused_result".  */
> +
> +#ifndef _GL_IGNORE_VALUE_H
> +# define _GL_IGNORE_VALUE_H
> +
> +# ifndef _GL_ATTRIBUTE_DEPRECATED
> +/* The __attribute__((__deprecated__)) feature
> +   is available in gcc versions 3.1 and newer.  */
> +#  if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 1)
> +#   define _GL_ATTRIBUTE_DEPRECATED /* empty */
> +#  else
> +#   define _GL_ATTRIBUTE_DEPRECATED __attribute__ ((__deprecated__))
> +#  endif
> +# endif
> +
> +/* The __attribute__((__warn_unused_result__)) feature
> +   is available in gcc versions 3.4 and newer,
> +   while the typeof feature has been available since 2.7 at least.  */
> +# if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
> +#  define ignore_value(x) ((void) (x))
> +# else
> +#  define ignore_value(x) (({ __typeof__ (x) __x = (x); (void) __x; }))
> +# endif
> +
> +/* ignore_value works for scalars, pointers and aggregates;
> +   deprecate ignore_ptr.  */
> +static inline void _GL_ATTRIBUTE_DEPRECATED
> +ignore_ptr (void *p) { (void) p; } /* deprecated: use ignore_value */
> +
> +#endif
> diff --git a/src/internal.h b/src/internal.h
> new file mode 100644
> index 0000000..eaa6d70
> --- /dev/null
> +++ b/src/internal.h
> @@ -0,0 +1,267 @@
> +/*
> + * internal.h: internal definitions just used by code from the library
> + *
> + * Copy:  Copyright (C) 2005-2006, 2010-2011 Red Hat, Inc.
> + *
> + * See libvirt's COPYING.LIB for the License of this software
> + *
> + */
> +
> +#ifndef __INTERNAL_H__
> +# define __INTERNAL_H__
> +
> +# include <stdio.h>
> +# include <stdlib.h>
> +# include <stdbool.h>
> +# include <stddef.h>
> +# include <errno.h>
> +# include <string.h>
> +# include <libvirt/libvirt.h>
> +# include <libvirt/virterror.h>

Since you have included libvirt.h

> +/**
> + * virEventHandleCallback:
> + *
> + * @watch: watch on which the event occurred
> + * @fd: file handle on which the event occurred
> + * @events: bitset of events from virEventHandleType constants
> + * @opaque: user data registered with handle
> + *
> + * Callback for receiving file handle events. The callback will
> + * be invoked once for each event which is pending.
> + */
> +typedef void (*virEventHandleCallback)(int watch, int fd, int events, void *opaque);
> +
> +/**
> + * virEventAddHandleFunc:
> + * @fd: file descriptor to listen on
> + * @event: bitset of events on which to fire the callback
> + * @cb: the callback to be called when an event occurrs
> + * @opaque: user data to pass to the callback
> + * @ff: the callback invoked to free opaque data blob
> + *
> + * Part of the EventImpl, this callback Adds a file handle callback to
> + * listen for specific events. The same file handle can be registered
> + * multiple times provided the requested event sets are non-overlapping
> + *
> + * If the opaque user data requires free'ing when the handle
> + * is unregistered, then a 2nd callback can be supplied for
> + * this purpose.
> + *
> + * Returns a handle watch number to be used for updating
> + * and unregistering for events
> + */
> +typedef int (*virEventAddHandleFunc)(int fd, int event,
> +                                     virEventHandleCallback cb,
> +                                     void *opaque,
> +                                     virFreeCallback ff);
> +
> +/**
> + * virEventUpdateHandleFunc:
> + * @watch: file descriptor watch to modify
> + * @event: new events to listen on
> + *
> + * Part of the EventImpl, this user-provided callback is notified when
> + * events to listen on change
> + */
> +typedef void (*virEventUpdateHandleFunc)(int watch, int event);
> +
> +/**
> + * virEventRemoveHandleFunc:
> + * @watch: file descriptor watch to stop listening on
> + *
> + * Part of the EventImpl, this user-provided callback is notified when
> + * an fd is no longer being listened on.
> + *
> + * If a virEventHandleFreeFunc was supplied when the handle was
> + * registered, it will be invoked some time during, or after this
> + * function call, when it is safe to release the user data.
> + */
> +typedef int (*virEventRemoveHandleFunc)(int watch);
> +
> +/**
> + * virEventTimeoutCallback:
> + *
> + * @timer: timer id emitting the event
> + * @opaque: user data registered with handle
> + *
> + * callback for receiving timer events
> + */
> +typedef void (*virEventTimeoutCallback)(int timer, void *opaque);
> +
> +/**
> + * virEventAddTimeoutFunc:
> + * @timeout: The timeout to monitor
> + * @cb: the callback to call when timeout has expired
> + * @opaque: user data to pass to the callback
> + * @ff: the callback invoked to free opaque data blob
> + *
> + * Part of the EventImpl, this user-defined callback handles adding an
> + * event timeout.
> + *
> + * If the opaque user data requires free'ing when the handle
> + * is unregistered, then a 2nd callback can be supplied for
> + * this purpose.
> + *
> + * Returns a timer value
> + */
> +typedef int (*virEventAddTimeoutFunc)(int timeout,
> +                                      virEventTimeoutCallback cb,
> +                                      void *opaque,
> +                                      virFreeCallback ff);
> +
> +/**
> + * virEventUpdateTimeoutFunc:
> + * @timer: the timer to modify
> + * @timeout: the new timeout value
> + *
> + * Part of the EventImpl, this user-defined callback updates an
> + * event timeout.
> + */
> +typedef void (*virEventUpdateTimeoutFunc)(int timer, int timeout);
> +
> +/**
> + * virEventRemoveTimeoutFunc:
> + * @timer: the timer to remove
> + *
> + * Part of the EventImpl, this user-defined callback removes a timer
> + *
> + * If a virEventTimeoutFreeFunc was supplied when the handle was
> + * registered, it will be invoked some time during, or after this
> + * function call, when it is safe to release the user data.
> + *
> + * Returns 0 on success, -1 on failure
> + */
> +typedef int (*virEventRemoveTimeoutFunc)(int timer);
> +
> +void virEventRegisterImpl(virEventAddHandleFunc addHandle,
> +                          virEventUpdateHandleFunc updateHandle,
> +                          virEventRemoveHandleFunc removeHandle,
> +                          virEventAddTimeoutFunc addTimeout,
> +                          virEventUpdateTimeoutFunc updateTimeout,
> +                          virEventRemoveTimeoutFunc removeTimeout);
> +
> +int virEventRegisterDefaultImpl(void);
> +int virEventRunDefaultImpl(void);

You don't need any of these functions or typedefs.


ACK if those are removed.

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list