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

Michal Privoznik mprivozn at redhat.com
Wed Mar 30 11:20:30 UTC 2011


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            |   23 ++-
 src/README.txt             |    2 +
 src/event.c                |  193 ++++++++++++
 src/event.h                |  101 ++++++
 src/event_poll.c           |  724 ++++++++++++++++++++++++++++++++++++++++++++
 src/event_poll.h           |  132 ++++++++
 src/ignore-value.h         |   64 ++++
 src/internal.h             |  265 ++++++++++++++++
 src/libvirtNotifications.c |   16 +-
 src/libvirtNotifications.h |   16 +-
 src/libvirtSnmp.c          |  136 ++++++++-
 src/memory.c               |  313 +++++++++++++++++++
 src/memory.h               |  212 +++++++++++++
 src/threads.c              |  251 +++++++++++++++
 src/threads.h              |  101 ++++++
 src/util.c                 |  105 +++++++
 src/util.h                 |   38 +++
 20 files changed, 2709 insertions(+), 16 deletions(-)
 create mode 100644 src/event.c
 create mode 100644 src/event.h
 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..0e3ae28 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,8 +9,25 @@ AM_CFLAGS =	\
 
 AM_LDFLAGS = 	\
 		$(COVERAGE_LDFLAGS) \
+		$(PTHREAD_LIBS) \
 		$(SNMP_LIBS)
 
+LIBVIRT_OLD_SRCS = \
+		threads.c \
+		event_poll.c \
+		event.c \
+		memory.c \
+		util.c
+
+LIBVIRT_OLD_HDRS = \
+		internal.h \
+		ignore-value.h \
+		threads.h \
+		event_poll.h \
+		event.h \
+		memory.h \
+		util.h
+
 USER_SRCS = 	\
 		libvirtGuestTable_data_get.c \
 		libvirtGuestTable_data_set.c \
@@ -43,10 +60,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.c b/src/event.c
new file mode 100644
index 0000000..27abc54
--- /dev/null
+++ b/src/event.c
@@ -0,0 +1,193 @@
+/*
+ * event.c: event loop for monitoring file handles
+ *
+ * Copyright (C) 2007, 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 "event.h"
+#include "event_poll.h"
+
+#include <stdlib.h>
+
+static virEventAddHandleFunc addHandleImpl = NULL;
+static virEventUpdateHandleFunc updateHandleImpl = NULL;
+static virEventRemoveHandleFunc removeHandleImpl = NULL;
+static virEventAddTimeoutFunc addTimeoutImpl = NULL;
+static virEventUpdateTimeoutFunc updateTimeoutImpl = NULL;
+static virEventRemoveTimeoutFunc removeTimeoutImpl = NULL;
+
+int virEventAddHandle(int fd,
+                      int events,
+                      virEventHandleCallback cb,
+                      void *opaque,
+                      virFreeCallback ff) {
+    if (!addHandleImpl)
+        return -1;
+
+    return addHandleImpl(fd, events, cb, opaque, ff);
+}
+
+void virEventUpdateHandle(int watch, int events) {
+    updateHandleImpl(watch, events);
+}
+
+int virEventRemoveHandle(int watch) {
+    if (!removeHandleImpl)
+        return -1;
+
+    return removeHandleImpl(watch);
+}
+
+int virEventAddTimeout(int timeout,
+                       virEventTimeoutCallback cb,
+                       void *opaque,
+                       virFreeCallback ff) {
+    if (!addTimeoutImpl)
+        return -1;
+
+    return addTimeoutImpl(timeout, cb, opaque, ff);
+}
+
+void virEventUpdateTimeout(int timer, int timeout) {
+    updateTimeoutImpl(timer, timeout);
+}
+
+int virEventRemoveTimeout(int timer) {
+    if (!removeTimeoutImpl)
+        return -1;
+
+    return removeTimeoutImpl(timer);
+}
+
+
+/*****************************************************
+ *
+ * Below this point are 3  *PUBLIC*  APIs for event
+ * loop integration with applications using libvirt.
+ * These API contracts cannot be changed.
+ *
+ *****************************************************/
+
+/**
+ * virEventRegisterImpl:
+ * @addHandle: the callback to add fd handles
+ * @updateHandle: the callback to update fd handles
+ * @removeHandle: the callback to remove fd handles
+ * @addTimeout: the callback to add a timeout
+ * @updateTimeout: the callback to update a timeout
+ * @removeTimeout: the callback to remove a timeout
+ *
+ * Registers an event implementation, to allow integration
+ * with an external event loop. Applications would use this
+ * to integrate with the libglib2 event loop, or libevent
+ * or the QT event loop.
+ *
+ * If an application does not need to integrate with an
+ * existing event loop implementation, then the
+ * virEventRegisterDefaultImpl method can be used to setup
+ * the generic libvirt implementation.
+ */
+void virEventRegisterImpl(virEventAddHandleFunc addHandle,
+                          virEventUpdateHandleFunc updateHandle,
+                          virEventRemoveHandleFunc removeHandle,
+                          virEventAddTimeoutFunc addTimeout,
+                          virEventUpdateTimeoutFunc updateTimeout,
+                          virEventRemoveTimeoutFunc removeTimeout)
+{
+    VIR_DEBUG("addHandle=%p updateHandle=%p removeHandle=%p "
+              "addTimeout=%p updateTimeout=%p removeTimeout=%p",
+              addHandle, updateHandle, removeHandle,
+              addTimeout, updateTimeout, removeTimeout);
+
+    addHandleImpl = addHandle;
+    updateHandleImpl = updateHandle;
+    removeHandleImpl = removeHandle;
+    addTimeoutImpl = addTimeout;
+    updateTimeoutImpl = updateTimeout;
+    removeTimeoutImpl = removeTimeout;
+}
+
+/**
+ * virEventRegisterDefaultImpl:
+ *
+ * Registers a default event implementation based on the
+ * poll() system call. This is a generic implementation
+ * that can be used by any client application which does
+ * not have a need to integrate with an external event
+ * loop impl.
+ *
+ * Once registered, the application can invoke
+ * virEventRunDefaultImpl in a loop to process
+ * events
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int virEventRegisterDefaultImpl(void)
+{
+    VIR_DEBUG0("");
+
+    virResetLastError();
+
+    if (virEventPollInit() < 0) {
+        return -1;
+    }
+
+    virEventRegisterImpl(
+        virEventPollAddHandle,
+        virEventPollUpdateHandle,
+        virEventPollRemoveHandle,
+        virEventPollAddTimeout,
+        virEventPollUpdateTimeout,
+        virEventPollRemoveTimeout
+        );
+
+    return 0;
+}
+
+
+/**
+ * virEventRunDefaultImpl:
+ *
+ * Run one iteration of the event loop. Applications
+ * will generally want to have a thread which invokes
+ * this method in an infinite loop
+ *
+ *  static bool quit = false;
+ *
+ *  while (!quit) {
+ *    if (virEventRunDefaultImpl() < 0)
+ *       ...print error...
+ *  }
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int virEventRunDefaultImpl(void)
+{
+    VIR_DEBUG0("");
+    virResetLastError();
+
+    if (virEventPollRunOnce() < 0) {
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/src/event.h b/src/event.h
new file mode 100644
index 0000000..68b06c6
--- /dev/null
+++ b/src/event.h
@@ -0,0 +1,101 @@
+/*
+ * 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_H__
+# define __VIR_EVENT_H__
+# include "internal.h"
+/**
+ * virEventAddHandle: register a callback for monitoring file handle events
+ *
+ * @fd: file handle to monitor for events
+ * @events: bitset of events to watch from virEventHandleType 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 virEventAddHandle(int fd, int events,
+                      virEventHandleCallback cb,
+                      void *opaque,
+                      virFreeCallback ff);
+
+/**
+ * virEventUpdateHandle: change event set for a monitored file handle
+ *
+ * @watch: watch whose file handle to update
+ * @events: bitset of events to watch from virEventHandleType constants
+ *
+ * Will not fail if fd exists
+ */
+void virEventUpdateHandle(int watch, int events);
+
+/**
+ * virEventRemoveHandle: unregister a callback from a file handle
+ *
+ * @watch: watch whose file handle to remove
+ *
+ * returns -1 if the file handle was not registered, 0 upon success
+ */
+int virEventRemoveHandle(int watch);
+
+/**
+ * virEventAddTimeout: 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 timer cannot be registered, a positive
+ * integer timer id upon success
+ */
+int virEventAddTimeout(int frequency,
+                       virEventTimeoutCallback cb,
+                       void *opaque,
+                       virFreeCallback ff);
+
+/**
+ * virEventUpdateTimeoutImpl: 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 virEventUpdateTimeout(int timer, int frequency);
+
+/**
+ * virEventRemoveTimeout: unregister a callback for a timer
+ *
+ * @timer: the timer id to remove
+ *
+ * returns -1 if the timer was not registered, 0 upon success
+ */
+int virEventRemoveTimeout(int timer);
+
+#endif /* __VIR_EVENT_H__ */
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..fe0b740
--- /dev/null
+++ b/src/internal.h
@@ -0,0 +1,265 @@
+/*
+ * 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>
+
+/* C99 uses __func__.  __FUNCTION__ is legacy. */
+# ifndef __GNUC__
+#  define __FUNCTION__ __func__
+# endif
+
+# ifdef __GNUC__
+
+#  ifndef __GNUC_PREREQ
+#   if defined __GNUC__ && defined __GNUC_MINOR__
+#    define __GNUC_PREREQ(maj, min)                                        \
+    ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#   else
+#    define __GNUC_PREREQ(maj,min) 0
+#   endif
+
+/* Work around broken limits.h on debian etch */
+#   if defined _GCC_LIMITS_H_ && ! defined ULLONG_MAX
+#    define ULLONG_MAX   ULONG_LONG_MAX
+#   endif
+
+#  endif /* __GNUC__ */
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * Macro to flag conciously unused parameters to functions
+ */
+#  ifndef ATTRIBUTE_UNUSED
+#   define ATTRIBUTE_UNUSED __attribute__((__unused__))
+#  endif
+
+/**
+ * ATTRIBUTE_SENTINEL:
+ *
+ * Macro to check for NULL-terminated varargs lists
+ */
+#  ifndef ATTRIBUTE_SENTINEL
+#   if __GNUC_PREREQ (4, 0)
+#    define ATTRIBUTE_SENTINEL __attribute__((__sentinel__))
+#   else
+#    define ATTRIBUTE_SENTINEL
+#   endif
+#  endif
+
+/**
+ * ATTRIBUTE_FMT_PRINTF
+ *
+ * Macro used to check printf like functions, if compiling
+ * with gcc.
+ *
+ * We use gnulib which guarentees we always have GNU style
+ * printf format specifiers even on broken Win32 platforms
+ * hence we have to force 'gnu_printf' for new GCC
+ */
+#  ifndef ATTRIBUTE_FMT_PRINTF
+#   if __GNUC_PREREQ (4, 4)
+#    define ATTRIBUTE_FMT_PRINTF(fmtpos,argpos) __attribute__((__format__ (gnu_printf, fmtpos,argpos)))
+#   else
+#    define ATTRIBUTE_FMT_PRINTF(fmtpos,argpos) __attribute__((__format__ (printf, fmtpos,argpos)))
+#   endif
+#  endif
+
+#  ifndef ATTRIBUTE_RETURN_CHECK
+#   if __GNUC_PREREQ (3, 4)
+#    define ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__))
+#   else
+#    define ATTRIBUTE_RETURN_CHECK
+#   endif
+#  endif
+
+/**
+ * ATTRIBUTE_PACKED
+ *
+ * force a structure to be packed, i.e. not following architecture and
+ * compiler best alignments for its sub components. It's needed for example
+ * for the network filetering code when defining the content of raw
+ * ethernet packets.
+ * Others compiler than gcc may use something different e.g. #pragma pack(1)
+ */
+#  ifndef ATTRIBUTE_PACKED
+#   if __GNUC_PREREQ (3, 3)
+#    define ATTRIBUTE_PACKED __attribute__((packed))
+#   else
+#    error "Need an __attribute__((packed)) equivalent"
+#   endif
+#  endif
+
+#  ifndef ATTRIBUTE_NONNULL
+#   if __GNUC_PREREQ (3, 3)
+#    define ATTRIBUTE_NONNULL(m) __attribute__((__nonnull__(m)))
+#   else
+#    define ATTRIBUTE_NONNULL(m)
+#   endif
+#  endif
+
+# else
+#  ifndef ATTRIBUTE_UNUSED
+#   define ATTRIBUTE_UNUSED
+#  endif
+#  ifndef ATTRIBUTE_FMT_PRINTF
+#   define ATTRIBUTE_FMT_PRINTF(...)
+#  endif
+#  ifndef ATTRIBUTE_RETURN_CHECK
+#   define ATTRIBUTE_RETURN_CHECK
+#  endif
+# endif             /* __GNUC__ */
+
+/*
+ * Events Implementation
+ */
+
+/**
+ * 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);
+
+#define VIR_DEBUG0(fmt) fprintf(stderr, "%s:%d :: " fmt "\n", \
+        __func__, __LINE__)
+#define VIR_DEBUG(fmt, ...) fprintf(stderr, "%s:%d: " fmt "\n", \
+        __func__, __LINE__, __VA_ARGS__)
+#define VIR_WARN(fmt, ...) fprintf(stderr, "%s:%d: " fmt "\n", \
+        __func__, __LINE__, __VA_ARGS__)
+
+#endif
diff --git a/src/libvirtNotifications.c b/src/libvirtNotifications.c
index 60fc44b..52c7ae4 100644
--- a/src/libvirtNotifications.c
+++ b/src/libvirtNotifications.c
@@ -3,9 +3,21 @@
  *
  * Copyright (C) 2011 Red Hat, Inc.
  *
- * See COPYING for the license of this software
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
  *
- * Michal Privoznik <mprivozn at redhat.com>
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Michal Privoznik <mprivozn at redhat.com>
  */
 
 #include <net-snmp/net-snmp-config.h>
diff --git a/src/libvirtNotifications.h b/src/libvirtNotifications.h
index 84eefc3..0be8fdc 100644
--- a/src/libvirtNotifications.h
+++ b/src/libvirtNotifications.h
@@ -3,9 +3,21 @@
  *
  * Copyright (C) 2011 Red Hat, Inc.
  *
- * See COPYING for the license of this software
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
  *
- * Michal Privoznik <mprivozn at redhat.com>
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Michal Privoznik <mprivozn at redhat.com>
  */
 
 #ifndef LIBVIRTNOTIFICATIONS_H
diff --git a/src/libvirtSnmp.c b/src/libvirtSnmp.c
index dd1bd33..48eeb5c 100644
--- a/src/libvirtSnmp.c
+++ b/src/libvirtSnmp.c
@@ -20,14 +20,63 @@
  * Author: Michal Privoznik <mprivozn at redhat.com>
  */
 
+#include <config.h>
+
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <signal.h>
 
 #include "libvirtSnmp.h"
-/* include our MIB structures*/
-#include "libvirtGuestTable.h"
-
+#include "libvirtGuestTable.h"      /* include our MIB structures*/
+#include "libvirtNotifications.h"
+#ifdef LIBVIRT_OLD
+# include "event.h"
+#endif
+
+#define DEBUG0(fmt) if (verbose) printf("%s:%d :: " fmt "\n", \
+        __func__, __LINE__)
+#define DEBUG(fmt, ...) if (verbose) printf("%s:%d: " fmt "\n", \
+        __func__, __LINE__, __VA_ARGS__)
+#define STREQ(a,b) (strcmp(a,b) == 0)
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+#endif
+
+int verbose = 0;
 virConnectPtr conn;
+int callbackRet = -1;
+int run = 1;
+pthread_t poll_thread;
+
+static int
+domainEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                    virDomainPtr dom, int event, int detail,
+                    void *opaque ATTRIBUTE_UNUSED)
+{
+    DEBUG("%s EVENT: Domain %s(%d) %d %d\n", __func__,
+          virDomainGetName(dom), virDomainGetID(dom), event, detail);
+
+    send_libvirtGuestNotif_trap(dom);
+    return 0;
+}
+
+static void
+myFreeFunc(void *opaque)
+{
+    if (opaque)
+        free(opaque);
+}
+
+/* Signal trap function */
+static void
+stop(int sig)
+{
+    run = 0;
+}
 
 static void
 showError(virConnectPtr conn)
@@ -188,10 +237,77 @@ out:
     return ret;
 }
 
+/* Polling thread function */
+void *
+pollingThreadFunc(void *foo) {
+    while (run) {
+        if (virEventRunDefaultImpl() < 0) {
+            showError(conn);
+            pthread_exit(NULL);
+        }
+    }
+    return NULL;
+}
+
+/* Function to register domain lifecycle events collection */
+int
+libvirtRegisterEvents(virConnectPtr conn) {
+    struct sigaction action_stop;
+    pthread_attr_t thread_attr;
+
+    memset(&action_stop, 0, sizeof action_stop);
+
+    action_stop.sa_handler = stop;
+
+    sigaction(SIGTERM, &action_stop, NULL);
+    sigaction(SIGINT, &action_stop, NULL);
+
+    DEBUG0("Registering domain event callback");
+
+    callbackRet = virConnectDomainEventRegisterAny(conn, NULL,
+                                                   VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+                                                   VIR_DOMAIN_EVENT_CALLBACK
+                                                   (domainEventCallback),
+                                                   NULL, myFreeFunc);
+
+    if (callbackRet == -1)
+        return -1;
+
+    /* we need a thread to poll for events */
+    pthread_attr_init(&thread_attr);
+    pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
+
+    if (pthread_create
+        (&poll_thread, &thread_attr, pollingThreadFunc, NULL))
+        return -1;
+
+    pthread_attr_destroy(&thread_attr);
+
+    return 0;
+}
+
+/* Unregister domain events collection */
+int
+libvirtUnregisterEvents(virConnectPtr conn)
+{
+    void *status;
+
+    pthread_join(poll_thread, &status);
+    virConnectDomainEventDeregisterAny(conn, callbackRet);
+    callbackRet = -1;
+    return 0;
+}
+
 int libvirtSnmpInit(void)
 {
-    /* virConnectOpenAuth is called here with all default parameters,
-     * except, possibly, the URI of the hypervisor. */
+    char *verbose_env = getenv("LIBVIRT_SNMP_VERBOSE");
+
+    verbose = verbose_env != NULL;
+
+    /* if we don't already have registered callback */
+    if (callbackRet == -1)
+        virEventRegisterDefaultImpl();
+
     /* TODO: configure the URI */
     /* Use libvirt env variable LIBVIRT_DEFAULT_URI by default*/
     conn = virConnectOpenAuth(NULL, virConnectAuthPtrDefault, 0);
@@ -201,11 +317,21 @@ int libvirtSnmpInit(void)
         showError(conn);
         return -1;
     }
+
+    if ((callbackRet == -1) && libvirtRegisterEvents(conn)) {
+        printf("Unable to register domain events\n");
+        return -1;
+    }
+
     return 0;
 }
 
 void libvirtSnmpShutdown(void)
 {
+    if (libvirtUnregisterEvents(conn)) {
+        printf("Failed to unregister domain events\n");
+    }
+
     if (0 != virConnectClose(conn)) {
         printf("Failed to disconnect from hypervisor\n");
         showError(conn);
diff --git a/src/memory.c b/src/memory.c
new file mode 100644
index 0000000..bfa32a8
--- /dev/null
+++ b/src/memory.c
@@ -0,0 +1,313 @@
+/*
+ * memory.c: safer memory allocation
+ *
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ * Copyright (C) 2008 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
+ *
+ */
+
+#include <config.h>
+#include <stdlib.h>
+
+#include "memory.h"
+#include "ignore-value.h"
+
+
+#if TEST_OOM
+static int testMallocNext = 0;
+static int testMallocFailFirst = 0;
+static int testMallocFailLast = 0;
+static void (*testMallocHook)(int, void*) = NULL;
+static void *testMallocHookData = NULL;
+
+void virAllocTestInit(void)
+{
+    testMallocNext = 1;
+    testMallocFailFirst = 0;
+    testMallocFailLast = 0;
+}
+
+int virAllocTestCount(void)
+{
+    return testMallocNext - 1;
+}
+
+void virAllocTestHook(void (*func)(int, void*), void *data)
+{
+    testMallocHook = func;
+    testMallocHookData = data;
+}
+
+void virAllocTestOOM(int n, int m)
+{
+    testMallocNext = 1;
+    testMallocFailFirst = n;
+    testMallocFailLast = n + m - 1;
+}
+
+static int virAllocTestFail(void)
+{
+    int fail = 0;
+    if (testMallocNext == 0)
+        return 0;
+
+    fail =
+        testMallocNext >= testMallocFailFirst &&
+        testMallocNext <= testMallocFailLast;
+
+    if (fail && testMallocHook)
+        (testMallocHook)(testMallocNext, testMallocHookData);
+
+    testMallocNext++;
+    return fail;
+}
+#endif
+
+
+/**
+ * virAlloc:
+ * @ptrptr: pointer to pointer for address of allocated memory
+ * @size: number of bytes to allocate
+ *
+ * Allocate  'size' bytes of memory. Return the address of the
+ * allocated memory in 'ptrptr'. The newly allocated memory is
+ * filled with zeros.
+ *
+ * Returns -1 on failure to allocate, zero on success
+ */
+int virAlloc(void *ptrptr, size_t size)
+{
+#if TEST_OOM
+    if (virAllocTestFail()) {
+        *(void **)ptrptr = NULL;
+        return -1;
+    }
+#endif
+
+    *(void **)ptrptr = calloc(1, size);
+    if (*(void **)ptrptr == NULL)
+        return -1;
+    return 0;
+}
+
+/**
+ * virAllocN:
+ * @ptrptr: pointer to pointer for address of allocated memory
+ * @size: number of bytes to allocate
+ * @count: number of elements to allocate
+ *
+ * Allocate an array of memory 'count' elements long,
+ * each with 'size' bytes. Return the address of the
+ * allocated memory in 'ptrptr'.  The newly allocated
+ * memory is filled with zeros.
+ *
+ * Returns -1 on failure to allocate, zero on success
+ */
+int virAllocN(void *ptrptr, size_t size, size_t count)
+{
+#if TEST_OOM
+    if (virAllocTestFail()) {
+        *(void **)ptrptr = NULL;
+        return -1;
+    }
+#endif
+
+    *(void**)ptrptr = calloc(count, size);
+    if (*(void**)ptrptr == NULL)
+        return -1;
+    return 0;
+}
+
+/**
+ * virReallocN:
+ * @ptrptr: pointer to pointer for address of allocated memory
+ * @size: number of bytes to allocate
+ * @count: number of elements in array
+ *
+ * Resize the block of memory in 'ptrptr' to be an array of
+ * 'count' elements, each 'size' bytes in length. Update 'ptrptr'
+ * with the address of the newly allocated memory. On failure,
+ * 'ptrptr' is not changed and still points to the original memory
+ * block. Any newly allocated memory in 'ptrptr' is uninitialized.
+ *
+ * Returns -1 on failure to allocate, zero on success
+ */
+int virReallocN(void *ptrptr, size_t size, size_t count)
+{
+    void *tmp;
+#if TEST_OOM
+    if (virAllocTestFail())
+        return -1;
+#endif
+
+    if (xalloc_oversized(count, size)) {
+        errno = ENOMEM;
+        return -1;
+    }
+    tmp = realloc(*(void**)ptrptr, size * count);
+    if (!tmp && (size * count))
+        return -1;
+    *(void**)ptrptr = tmp;
+    return 0;
+}
+
+/**
+ * virExpandN:
+ * @ptrptr: pointer to pointer for address of allocated memory
+ * @size: number of bytes per element
+ * @countptr: pointer to number of elements in array
+ * @add: number of elements to add
+ *
+ * Resize the block of memory in 'ptrptr' to be an array of
+ * '*countptr' + 'add' elements, each 'size' bytes in length.
+ * Update 'ptrptr' and 'countptr'  with the details of the newly
+ * allocated memory. On failure, 'ptrptr' and 'countptr' are not
+ * changed. Any newly allocated memory in 'ptrptr' is zero-filled.
+ *
+ * Returns -1 on failure to allocate, zero on success
+ */
+int virExpandN(void *ptrptr, size_t size, size_t *countptr, size_t add)
+{
+    int ret;
+
+    if (*countptr + add < *countptr) {
+        errno = ENOMEM;
+        return -1;
+    }
+    ret = virReallocN(ptrptr, size, *countptr + add);
+    if (ret == 0) {
+        memset(*(char **)ptrptr + (size * *countptr), 0, size * add);
+        *countptr += add;
+    }
+    return ret;
+}
+
+/**
+ * virResizeN:
+ * @ptrptr: pointer to pointer for address of allocated memory
+ * @size: number of bytes per element
+ * @allocptr: pointer to number of elements allocated in array
+ * @count: number of elements currently used in array
+ * @add: minimum number of additional elements to support in array
+ *
+ * If 'count' + 'add' is larger than '*allocptr', then resize the
+ * block of memory in 'ptrptr' to be an array of at least 'count' +
+ * 'add' elements, each 'size' bytes in length. Update 'ptrptr' and
+ * 'allocptr' with the details of the newly allocated memory. On
+ * failure, 'ptrptr' and 'allocptr' are not changed. Any newly
+ * allocated memory in 'ptrptr' is zero-filled.
+ *
+ * Returns -1 on failure to allocate, zero on success
+ */
+int virResizeN(void *ptrptr, size_t size, size_t *allocptr, size_t count,
+               size_t add)
+{
+    size_t delta;
+
+    if (count + add < count) {
+        errno = ENOMEM;
+        return -1;
+    }
+    if (count + add <= *allocptr)
+        return 0;
+
+    delta = count + add - *allocptr;
+    if (delta < *allocptr / 2)
+        delta = *allocptr / 2;
+    return virExpandN(ptrptr, size, allocptr, delta);
+}
+
+/**
+ * virShrinkN:
+ * @ptrptr: pointer to pointer for address of allocated memory
+ * @size: number of bytes per element
+ * @countptr: pointer to number of elements in array
+ * @toremove: number of elements to remove
+ *
+ * Resize the block of memory in 'ptrptr' to be an array of
+ * '*countptr' - 'toremove' elements, each 'size' bytes in length.
+ * Update 'ptrptr' and 'countptr'  with the details of the newly
+ * allocated memory. If 'toremove' is larger than 'countptr', free
+ * the entire array.
+ */
+void virShrinkN(void *ptrptr, size_t size, size_t *countptr, size_t toremove)
+{
+    if (toremove < *countptr)
+        ignore_value(virReallocN(ptrptr, size, *countptr -= toremove));
+    else {
+        virFree(ptrptr);
+        *countptr = 0;
+    }
+}
+
+
+/**
+ * Vir_Alloc_Var:
+ * @ptrptr: pointer to hold address of allocated memory
+ * @struct_size: size of initial struct
+ * @element_size: size of array elements
+ * @count: number of array elements to allocate
+ *
+ * Allocate struct_size bytes plus an array of 'count' elements, each
+ * of size element_size.  This sort of allocation is useful for
+ * receiving the data of certain ioctls and other APIs which return a
+ * struct in which the last element is an array of undefined length.
+ * The caller of this type of API is expected to know the length of
+ * the array that will be returned and allocate a suitable buffer to
+ * contain the returned data.  C99 refers to these variable length
+ * objects as structs containing flexible array members.
+ *
+ * Returns -1 on failure, 0 on success
+ */
+int virAllocVar(void *ptrptr, size_t struct_size, size_t element_size, size_t count)
+{
+    size_t alloc_size = 0;
+
+#if TEST_OOM
+    if (virAllocTestFail())
+        return -1;
+#endif
+
+    if (VIR_ALLOC_VAR_OVERSIZED(struct_size, count, element_size)) {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    alloc_size = struct_size + (element_size * count);
+    *(void **)ptrptr = calloc(1, alloc_size);
+    if (*(void **)ptrptr == NULL)
+        return -1;
+    return 0;
+}
+
+
+/**
+ * virFree:
+ * @ptrptr: pointer to pointer for address of memory to be freed
+ *
+ * Release the chunk of memory in the pointer pointed to by
+ * the 'ptrptr' variable. After release, 'ptrptr' will be
+ * updated to point to NULL.
+ */
+void virFree(void *ptrptr)
+{
+    int save_errno = errno;
+
+    free(*(void**)ptrptr);
+    *(void**)ptrptr = NULL;
+    errno = save_errno;
+}
diff --git a/src/memory.h b/src/memory.h
new file mode 100644
index 0000000..66b4c42
--- /dev/null
+++ b/src/memory.h
@@ -0,0 +1,212 @@
+/*
+ * memory.c: safer memory allocation
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2008 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
+ *
+ */
+
+
+#ifndef __VIR_MEMORY_H_
+# define __VIR_MEMORY_H_
+
+# include "internal.h"
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+   to size arithmetic overflow.  S must be positive and N must be
+   nonnegative.  This is a macro, not an inline function, so that it
+   works correctly even when SIZE_MAX < N.
+
+   By gnulib convention, SIZE_MAX represents overflow in size
+   calculations, so the conservative dividend to use here is
+   SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+   However, malloc (SIZE_MAX) fails on all known hosts where
+   sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+   exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+   branch when S is known to be 1.  */
+# ifndef xalloc_oversized
+#  define xalloc_oversized(n, s) \
+    ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+# endif
+
+
+
+/* Don't call these directly - use the macros below */
+int virAlloc(void *ptrptr, size_t size) ATTRIBUTE_RETURN_CHECK
+    ATTRIBUTE_NONNULL(1);
+int virAllocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK
+    ATTRIBUTE_NONNULL(1);
+int virReallocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK
+    ATTRIBUTE_NONNULL(1);
+int virExpandN(void *ptrptr, size_t size, size_t *count, size_t add)
+    ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
+int virResizeN(void *ptrptr, size_t size, size_t *alloc, size_t count,
+               size_t desired)
+    ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
+void virShrinkN(void *ptrptr, size_t size, size_t *count, size_t toremove)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
+int virAllocVar(void *ptrptr,
+                size_t struct_size,
+                size_t element_size,
+                size_t count) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1);
+void virFree(void *ptrptr) ATTRIBUTE_NONNULL(1);
+
+/**
+ * VIR_ALLOC:
+ * @ptr: pointer to hold address of allocated memory
+ *
+ * Allocate sizeof(*ptr) bytes of memory and store
+ * the address of allocated memory in 'ptr'. Fill the
+ * newly allocated memory with zeros.
+ *
+ * Returns -1 on failure, 0 on success
+ */
+# define VIR_ALLOC(ptr) virAlloc(&(ptr), sizeof(*(ptr)))
+
+/**
+ * VIR_ALLOC_N:
+ * @ptr: pointer to hold address of allocated memory
+ * @count: number of elements to allocate
+ *
+ * Allocate an array of 'count' elements, each sizeof(*ptr)
+ * bytes long and store the address of allocated memory in
+ * 'ptr'. Fill the newly allocated memory with zeros.
+ *
+ * Returns -1 on failure, 0 on success
+ */
+# define VIR_ALLOC_N(ptr, count) virAllocN(&(ptr), sizeof(*(ptr)), (count))
+
+/**
+ * VIR_REALLOC_N:
+ * @ptr: pointer to hold address of allocated memory
+ * @count: number of elements to allocate
+ *
+ * Re-allocate an array of 'count' elements, each sizeof(*ptr)
+ * bytes long and store the address of allocated memory in
+ * 'ptr'. If 'ptr' grew, the added memory is uninitialized.
+ *
+ * Returns -1 on failure, 0 on success
+ */
+# define VIR_REALLOC_N(ptr, count) virReallocN(&(ptr), sizeof(*(ptr)), (count))
+
+/**
+ * VIR_EXPAND_N:
+ * @ptr: pointer to hold address of allocated memory
+ * @count: variable tracking number of elements currently allocated
+ * @add: number of elements to add
+ *
+ * Re-allocate an array of 'count' elements, each sizeof(*ptr)
+ * bytes long, to be 'count' + 'add' elements long, then store the
+ * address of allocated memory in 'ptr' and the new size in 'count'.
+ * The new elements are filled with zero.
+ *
+ * Returns -1 on failure, 0 on success
+ */
+# define VIR_EXPAND_N(ptr, count, add) \
+    virExpandN(&(ptr), sizeof(*(ptr)), &(count), add)
+
+/**
+ * VIR_RESIZE_N:
+ * @ptr: pointer to hold address of allocated memory
+ * @alloc: variable tracking number of elements currently allocated
+ * @count: number of elements currently in use
+ * @add: minimum number of elements to additionally support
+ *
+ * Blindly using VIR_EXPAND_N(array, alloc, 1) in a loop scales
+ * quadratically, because every iteration must copy contents from
+ * all prior iterations.  But amortized linear scaling can be achieved
+ * by tracking allocation size separately from the number of used
+ * elements, and growing geometrically only as needed.
+ *
+ * If 'count' + 'add' is larger than 'alloc', then geometrically reallocate
+ * the array of 'alloc' elements, each sizeof(*ptr) bytes long, and store
+ * the address of allocated memory in 'ptr' and the new size in 'alloc'.
+ * The new elements are filled with zero.
+ *
+ * Returns -1 on failure, 0 on success
+ */
+# define VIR_RESIZE_N(ptr, alloc, count, add) \
+    virResizeN(&(ptr), sizeof(*(ptr)), &(alloc), count, add)
+
+/**
+ * VIR_SHRINK_N:
+ * @ptr: pointer to hold address of allocated memory
+ * @count: variable tracking number of elements currently allocated
+ * @remove: number of elements to remove
+ *
+ * Re-allocate an array of 'count' elements, each sizeof(*ptr)
+ * bytes long, to be 'count' - 'remove' elements long, then store the
+ * address of allocated memory in 'ptr' and the new size in 'count'.
+ * If 'count' <= 'remove', the entire array is freed.
+ *
+ * No return value.
+ */
+# define VIR_SHRINK_N(ptr, count, remove) \
+    virShrinkN(&(ptr), sizeof(*(ptr)), &(count), remove)
+
+/*
+ * VIR_ALLOC_VAR_OVERSIZED:
+ * @M: size of base structure
+ * @N: number of array elements in trailing array
+ * @S: size of trailing array elements
+ *
+ * Check to make sure that the requested allocation will not cause
+ * arithmetic overflow in the allocation size.  The check is
+ * essentially the same as that in gnulib's xalloc_oversized.
+ */
+# define VIR_ALLOC_VAR_OVERSIZED(M, N, S) ((((size_t)-1) - (M)) / (S) < (N))
+
+/**
+ * VIR_ALLOC_VAR:
+ * @ptr: pointer to hold address of allocated memory
+ * @type: element type of trailing array
+ * @count: number of array elements to allocate
+ *
+ * Allocate sizeof(*ptr) bytes plus an array of 'count' elements, each
+ * sizeof('type').  This sort of allocation is useful for receiving
+ * the data of certain ioctls and other APIs which return a struct in
+ * which the last element is an array of undefined length.  The caller
+ * of this type of API is expected to know the length of the array
+ * that will be returned and allocate a suitable buffer to contain the
+ * returned data.  C99 refers to these variable length objects as
+ * structs containing flexible array members.
+
+ * Returns -1 on failure, 0 on success
+ */
+# define VIR_ALLOC_VAR(ptr, type, count) \
+    virAllocVar(&(ptr), sizeof(*(ptr)), sizeof(type), (count))
+
+/**
+ * VIR_FREE:
+ * @ptr: pointer holding address to be freed
+ *
+ * Free the memory stored in 'ptr' and update to point
+ * to NULL.
+ */
+# define VIR_FREE(ptr) virFree(&(ptr))
+
+
+# if TEST_OOM
+void virAllocTestInit(void);
+int virAllocTestCount(void);
+void virAllocTestOOM(int n, int m);
+void virAllocTestHook(void (*func)(int, void*), void *data);
+# endif
+
+
+
+#endif /* __VIR_MEMORY_H_ */
diff --git a/src/threads.c b/src/threads.c
new file mode 100644
index 0000000..8987173
--- /dev/null
+++ b/src/threads.c
@@ -0,0 +1,251 @@
+/*
+ * threads.c: basic thread synchronization primitives
+ *
+ * Copyright (C) 2009-2010 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
+ *
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <inttypes.h>
+#if HAVE_SYS_SYSCALL_H
+# include <sys/syscall.h>
+#endif
+
+#include "threads.h"
+#include "memory.h"
+
+
+/* Nothing special required for pthreads */
+int virThreadInitialize(void)
+{
+    return 0;
+}
+
+void virThreadOnExit(void)
+{
+}
+
+
+int virMutexInit(virMutexPtr m)
+{
+    int ret;
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+    ret = pthread_mutex_init(&m->lock, &attr);
+    pthread_mutexattr_destroy(&attr);
+    if (ret != 0) {
+        errno = ret;
+        return -1;
+    }
+    return 0;
+}
+
+int virMutexInitRecursive(virMutexPtr m)
+{
+    int ret;
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+    ret = pthread_mutex_init(&m->lock, &attr);
+    pthread_mutexattr_destroy(&attr);
+    if (ret != 0) {
+        errno = ret;
+        return -1;
+    }
+    return 0;
+}
+
+void virMutexDestroy(virMutexPtr m)
+{
+    pthread_mutex_destroy(&m->lock);
+}
+
+void virMutexLock(virMutexPtr m){
+    pthread_mutex_lock(&m->lock);
+}
+
+void virMutexUnlock(virMutexPtr m)
+{
+    pthread_mutex_unlock(&m->lock);
+}
+
+
+int virCondInit(virCondPtr c)
+{
+    int ret;
+    if ((ret = pthread_cond_init(&c->cond, NULL)) != 0) {
+        errno = ret;
+        return -1;
+    }
+    return 0;
+}
+
+int virCondDestroy(virCondPtr c)
+{
+    int ret;
+    if ((ret = pthread_cond_destroy(&c->cond)) != 0) {
+        errno = ret;
+        return -1;
+    }
+    return 0;
+}
+
+int virCondWait(virCondPtr c, virMutexPtr m)
+{
+    int ret;
+    if ((ret = pthread_cond_wait(&c->cond, &m->lock)) != 0) {
+        errno = ret;
+        return -1;
+    }
+    return 0;
+}
+
+int virCondWaitUntil(virCondPtr c, virMutexPtr m, unsigned long long whenms)
+{
+    int ret;
+    struct timespec ts;
+
+    ts.tv_sec = whenms / 1000;
+    ts.tv_nsec = (whenms % 1000) * 1000;
+
+    if ((ret = pthread_cond_timedwait(&c->cond, &m->lock, &ts)) != 0) {
+        errno = ret;
+        return -1;
+    }
+    return 0;
+}
+
+void virCondSignal(virCondPtr c)
+{
+    pthread_cond_signal(&c->cond);
+}
+
+void virCondBroadcast(virCondPtr c)
+{
+    pthread_cond_broadcast(&c->cond);
+}
+
+struct virThreadArgs {
+    virThreadFunc func;
+    void *opaque;
+};
+
+static void *virThreadHelper(void *data)
+{
+    struct virThreadArgs *args = data;
+    args->func(args->opaque);
+    VIR_FREE(args);
+    return NULL;
+}
+
+int virThreadCreate(virThreadPtr thread,
+                    bool joinable,
+                    virThreadFunc func,
+                    void *opaque)
+{
+    struct virThreadArgs *args;
+    pthread_attr_t attr;
+    int ret = -1;
+    int err;
+
+    if ((err = pthread_attr_init(&attr)) != 0)
+        goto cleanup;
+    if (VIR_ALLOC(args) < 0) {
+        err = ENOMEM;
+        goto cleanup;
+    }
+
+    args->func = func;
+    args->opaque = opaque;
+
+    if (!joinable)
+        pthread_attr_setdetachstate(&attr, 1);
+
+    err = pthread_create(&thread->thread, &attr, virThreadHelper, args);
+    if (err != 0) {
+        VIR_FREE(args);
+        goto cleanup;
+    }
+    /* New thread owns 'args' in success case, so don't free */
+
+    ret = 0;
+cleanup:
+    pthread_attr_destroy(&attr);
+    if (ret < 0)
+        errno = err;
+    return ret;
+}
+
+void virThreadSelf(virThreadPtr thread)
+{
+    thread->thread = pthread_self();
+}
+
+bool virThreadIsSelf(virThreadPtr thread)
+{
+    return pthread_equal(pthread_self(), thread->thread) ? true : false;
+}
+
+/* For debugging use only; this result is not guaranteed unique on BSD
+ * systems when pthread_t is a 64-bit pointer.  */
+int virThreadSelfID(void)
+{
+#if defined(HAVE_SYS_SYSCALL_H) && defined(SYS_gettid)
+    pid_t tid;
+    tid = syscall(SYS_gettid);
+    return (int)tid;
+#else
+    return (int)pthread_self();
+#endif
+}
+
+/* For debugging use only; this result is not guaranteed unique on BSD
+ * systems when pthread_t is a 64-bit pointer, nor does it match the
+ * thread id of virThreadSelfID on Linux.  */
+int virThreadID(virThreadPtr thread)
+{
+    return (int)(uintptr_t)thread->thread;
+}
+
+void virThreadJoin(virThreadPtr thread)
+{
+    pthread_join(thread->thread, NULL);
+}
+
+int virThreadLocalInit(virThreadLocalPtr l,
+                       virThreadLocalCleanup c)
+{
+    int ret;
+    if ((ret = pthread_key_create(&l->key, c)) != 0) {
+        errno = ret;
+        return -1;
+    }
+    return 0;
+}
+
+void *virThreadLocalGet(virThreadLocalPtr l)
+{
+    return pthread_getspecific(l->key);
+}
+
+void virThreadLocalSet(virThreadLocalPtr l, void *val)
+{
+    pthread_setspecific(l->key, val);
+}
diff --git a/src/threads.h b/src/threads.h
new file mode 100644
index 0000000..ae81273
--- /dev/null
+++ b/src/threads.h
@@ -0,0 +1,101 @@
+/*
+ * threads.h: basic thread synchronization primitives
+ *
+ * Copyright (C) 2009-2010 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
+ *
+ */
+
+#ifndef __THREADS_H_
+# define __THREADS_H_
+
+# include "internal.h"
+# include <pthread.h>
+
+typedef struct virMutex virMutex;
+typedef virMutex *virMutexPtr;
+
+typedef struct virCond virCond;
+typedef virCond *virCondPtr;
+
+typedef struct virThreadLocal virThreadLocal;
+typedef virThreadLocal *virThreadLocalPtr;
+
+typedef struct virThread virThread;
+typedef virThread *virThreadPtr;
+
+
+int virThreadInitialize(void) ATTRIBUTE_RETURN_CHECK;
+void virThreadOnExit(void);
+
+typedef void (*virThreadFunc)(void *opaque);
+
+int virThreadCreate(virThreadPtr thread,
+                    bool joinable,
+                    virThreadFunc func,
+                    void *opaque) ATTRIBUTE_RETURN_CHECK;
+void virThreadSelf(virThreadPtr thread);
+bool virThreadIsSelf(virThreadPtr thread);
+void virThreadJoin(virThreadPtr thread);
+
+/* These next two functions are for debugging only, since they are not
+ * guaranteed to give unique values for distinct threads on all
+ * architectures, nor are the two functions guaranteed to give the same
+ * value for the same thread. */
+int virThreadSelfID(void);
+int virThreadID(virThreadPtr thread);
+
+int virMutexInit(virMutexPtr m) ATTRIBUTE_RETURN_CHECK;
+int virMutexInitRecursive(virMutexPtr m) ATTRIBUTE_RETURN_CHECK;
+void virMutexDestroy(virMutexPtr m);
+
+void virMutexLock(virMutexPtr m);
+void virMutexUnlock(virMutexPtr m);
+
+
+
+int virCondInit(virCondPtr c) ATTRIBUTE_RETURN_CHECK;
+int virCondDestroy(virCondPtr c) ATTRIBUTE_RETURN_CHECK;
+
+int virCondWait(virCondPtr c, virMutexPtr m) ATTRIBUTE_RETURN_CHECK;
+int virCondWaitUntil(virCondPtr c, virMutexPtr m, unsigned long long whenms) ATTRIBUTE_RETURN_CHECK;
+void virCondSignal(virCondPtr c);
+void virCondBroadcast(virCondPtr c);
+
+
+typedef void (*virThreadLocalCleanup)(void *);
+int virThreadLocalInit(virThreadLocalPtr l,
+                       virThreadLocalCleanup c) ATTRIBUTE_RETURN_CHECK;
+void *virThreadLocalGet(virThreadLocalPtr l);
+void virThreadLocalSet(virThreadLocalPtr l, void*);
+
+struct virMutex {
+    pthread_mutex_t lock;
+};
+
+struct virCond {
+    pthread_cond_t cond;
+};
+
+struct virThread {
+    pthread_t thread;
+};
+
+struct virThreadLocal {
+    pthread_key_t key;
+};
+
+#endif
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..30d4e47
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,105 @@
+/*
+ * utils.c: common, generic utility functions
+ *
+ * Copyright (C) 2006-2011 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ * Copyright (C) 2006, 2007 Binary Karma
+ * Copyright (C) 2006 Shuveb Hussain
+ *
+ * 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>
+ * File created Jul 18, 2007 - Shuveb Hussain <shuveb at binarykarma.com>
+ */
+
+#include "util.h"
+#include <unistd.h>
+#include <fcntl.h>
+
+/* Like read(), but restarts after EINTR */
+ssize_t
+saferead(int fd, void *buf, size_t count)
+{
+    size_t nread = 0;
+    while (count > 0) {
+        ssize_t r = read(fd, buf, count);
+        if (r < 0 && errno == EINTR)
+            continue;
+        if (r < 0)
+            return r;
+        if (r == 0)
+            return nread;
+        buf = (char *)buf + r;
+        count -= r;
+        nread += r;
+    }
+    return nread;
+}
+
+/* Like write(), but restarts after EINTR */
+ssize_t
+safewrite(int fd, const void *buf, size_t count)
+{
+    size_t nwritten = 0;
+    while (count > 0) {
+        ssize_t r = write(fd, buf, count);
+
+        if (r < 0 && errno == EINTR)
+            continue;
+        if (r < 0)
+            return r;
+        if (r == 0)
+            return nwritten;
+        buf = (const char *)buf + r;
+        count -= r;
+        nwritten += r;
+    }
+    return nwritten;
+}
+
+int virSetBlocking(int fd, bool blocking) {
+    int flags;
+    if ((flags = fcntl(fd, F_GETFL)) < 0)
+        return -1;
+    if (blocking)
+        flags &= ~O_NONBLOCK;
+    else
+        flags |= O_NONBLOCK;
+    if ((fcntl(fd, F_SETFL, flags)) < 0)
+        return -1;
+    return 0;
+}
+
+int virSetNonBlock(int fd) {
+    return virSetBlocking(fd, false);
+}
+
+int virSetCloseExec(int fd)
+{
+    return virSetInherit(fd, false);
+}
+
+int virSetInherit(int fd, bool inherit) {
+    int flags;
+    if ((flags = fcntl(fd, F_GETFD)) < 0)
+        return -1;
+    if (inherit)
+        flags &= ~FD_CLOEXEC;
+    else
+        flags |= FD_CLOEXEC;
+    if ((fcntl(fd, F_SETFD, flags)) < 0)
+        return -1;
+    return 0;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..a3d289e
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,38 @@
+/*
+ * utils.h: common, generic utility functions
+ *
+ * Copyright (C) 2010-2011 Red Hat, Inc.
+ * Copyright (C) 2006, 2007 Binary Karma
+ * Copyright (C) 2006 Shuveb Hussain
+ *
+ * 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
+ *
+ * File created Jul 18, 2007 - Shuveb Hussain <shuveb at binarykarma.com>
+ */
+
+#ifndef __VIR_UTIL_H__
+#define __VIR_UTIL_H__
+
+#include "internal.h"
+
+int virSetBlocking(int fd, bool blocking) ATTRIBUTE_RETURN_CHECK;
+int virSetNonBlock(int fd) ATTRIBUTE_RETURN_CHECK;
+int virSetInherit(int fd, bool inherit) ATTRIBUTE_RETURN_CHECK;
+int virSetCloseExec(int fd) ATTRIBUTE_RETURN_CHECK;
+
+ssize_t saferead(int fd, void *buf, size_t count) ATTRIBUTE_RETURN_CHECK;
+ssize_t safewrite(int fd, const void *buf, size_t count)  ATTRIBUTE_RETURN_CHECK;
+
+#endif
-- 
1.7.4




More information about the libvir-list mailing list