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(a)redhat.com> 0.0.2-1
+- add SNMP trap/notification support
+
* Fri Mar 11 2011 Michal Privoznik <mprivozn(a)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(a)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(a)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(a)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>
+/**
+ * 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
--
|: