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@xxxxxxxxxx],[],[http://libvirt.org]) +AC_INIT([libvirt-snmp],[0.0.2],[libvir-list@xxxxxxxxxx],[],[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@xxxxxxxxxx> 0.0.2-1 +- add SNMP trap/notification support + * Fri Mar 11 2011 Michal Privoznik <mprivozn@xxxxxxxxxx> 0.0.1-3 -- remove LIBVIRT-MIB.txt from %doc +- remove LIBVIRT-MIB.txt from doc * Wed Mar 9 2011 Michal Privoznik <mprivozn@xxxxxxxxxx> 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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + * 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@xxxxxxxxxx> */ #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@xxxxxxxxxx> + * 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@xxxxxxxxxx> */ #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@xxxxxxxxxx> */ +#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@xxxxxxxxxx> + * File created Jul 18, 2007 - Shuveb Hussain <shuveb@xxxxxxxxxxxxxxx> + */ + +#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@xxxxxxxxxxxxxxx> + */ + +#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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list