
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@redhat.com],[],[http://libvirt.org]) +AC_INIT([libvirt-snmp],[0.0.2],[libvir-list@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@redhat.com> 0.0.2-1 +- add SNMP trap/notification support + * Fri Mar 11 2011 Michal Privoznik <mprivozn@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@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@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@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 :|