[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