[PATCH 0 of 3] [RFC] Handles events generated when the qemu process crashes

1. Look at the init_events function and see how it's called when you cancel the execution of the indication_tester.py 2. Notice that when you kill the qemu process, the event_thread invokes the callback, but the event received (look at the invoke_callback function) is a read, instead of a hangup

# HG changeset patch # User Richard Maciel <rmaciel@linux.vnet.ibm.com> # Date 1248549583 10800 # Node ID 23b104fd6d9cad1416d89c60750c603eef7d3381 # Parent 2e80fd8fdbc575decc0b8a1623f8b9ad9122dccf Creates and register provider class GuestCrashAlertIndication Signed-off-by: Richard Maciel <rmaciel@linux.vnet.ibm.com> diff -r 2e80fd8fdbc5 -r 23b104fd6d9c Makefile.am --- a/Makefile.am Fri Dec 11 18:04:28 2009 -0800 +++ b/Makefile.am Sat Jul 25 16:19:43 2009 -0300 @@ -56,7 +56,8 @@ schema/InputPool.mof \ schema/HostedAccessPoint.mof \ schema/ServiceAccessBySAP.mof \ - schema/SAPAvailableForElement.mof + schema/SAPAvailableForElement.mof \ + schema/GuestCrashAlertIndication.mof INTEROP_MOFS = \ schema/ComputerSystem.mof \ @@ -136,7 +137,8 @@ schema/InputPool.registration \ schema/HostedAccessPoint.registration \ schema/ServiceAccessBySAP.registration \ - schema/SAPAvailableForElement.registration + schema/SAPAvailableForElement.registration \ + schema/GuestCrashAlertIndication.registration INTEROP_REGS = \ schema/RegisteredProfile.registration \ diff -r 2e80fd8fdbc5 -r 23b104fd6d9c schema/GuestCrashAlertIndication.mof --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/schema/GuestCrashAlertIndication.mof Sat Jul 25 16:19:43 2009 -0300 @@ -0,0 +1,17 @@ +// Copyright IBM Corp. 2007 + +[Description ("Xen guest crash alert"), + Provider("cmpi::Virt_GuestCrashAlertIndication") +] +class Xen_GuestCrashAlertIndication : CIM_AlertIndication +{ + uint32 RaiseIndication([IN] CIM_InstCreation REF TheIndication); +}; + +[Description ("KVM guest crash alert"), + Provider("cmpi::Virt_GuestCrashAlertIndication") +] +class KVM_GuestCrashAlertIndication : CIM_AlertIndication +{ + uint32 RaiseIndication([IN] CIM_InstCreation REF TheIndication); +}; diff -r 2e80fd8fdbc5 -r 23b104fd6d9c schema/GuestCrashAlertIndication.registration --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/schema/GuestCrashAlertIndication.registration Sat Jul 25 16:19:43 2009 -0300 @@ -0,0 +1,4 @@ +# Copyright IBM Corp. 2007 +# Classname Namespace ProviderName ProviderModule ProviderTypes +Xen_GuestCrashAlertIndication root/virt Virt_GuestCrashAlertIndicationProvider Virt_GuestCrashAlertIndication indication method +KVM_GuestCrashAlertIndication root/virt Virt_GuestCrashAlertIndicationProvider Virt_GuestCrashAlertIndication indication method

# HG changeset patch # User Richard Maciel <rmaciel@linux.vnet.ibm.com> # Date 1248549583 10800 # Node ID 22540c8901bd5a58a6631c6b449c92a3d0c5fd5b # Parent 23b104fd6d9cad1416d89c60750c603eef7d3381 Raises GuestCrashAlertIndication when QEMU crashes Signed-off-by: Richard Maciel <rmaciel@linux.vnet.ibm.com> diff -r 23b104fd6d9c -r 22540c8901bd src/Makefile.am --- a/src/Makefile.am Sat Jul 25 16:19:43 2009 -0300 +++ b/src/Makefile.am Sat Jul 25 16:19:43 2009 -0300 @@ -76,7 +76,8 @@ libVirt_ServiceAffectsElement.la \ libVirt_HostedAccessPoint.la \ libVirt_ServiceAccessBySAP.la \ - libVirt_SAPAvailableForElement.la + libVirt_SAPAvailableForElement.la \ + libVirt_GuestCrashAlertIndication.la libVirt_ComputerSystem_la_SOURCES = Virt_ComputerSystem.c libVirt_ComputerSystem_la_DEPENDENCIES = libVirt_VirtualSystemSnapshotService.la @@ -238,3 +239,7 @@ libVirt_SAPAvailableForElement_la_SOURCES = Virt_SAPAvailableForElement.c libVirt_SAPAvailableForElement_la_LIBADD = -lVirt_ComputerSystem -lVirt_KVMRedirectionSAP +libVirt_GuestCrashAlertIndication_la_DEPENDENCIES = libVirt_ComputerSystem.la +libVirt_GuestCrashAlertIndication_la_SOURCES = Virt_GuestCrashAlertIndication.c +libVirt_GuestCrashAlertIndication_la_LIBADD = -lVirt_ComputerSystem + diff -r 23b104fd6d9c -r 22540c8901bd src/Virt_GuestCrashAlertIndication.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Virt_GuestCrashAlertIndication.c Sat Jul 25 16:19:43 2009 -0300 @@ -0,0 +1,524 @@ +/* + * Copyright IBM Corp. 2009 + * + * Authors: + * Richard Maciel <rmaciel@linux.vnet.ibm.com> + * + * 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 <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <pthread.h> + +#include <cmpidt.h> +#include <cmpift.h> +#include <cmpimacs.h> + +#include <libvirt/libvirt.h> + +#include <libcmpiutil/libcmpiutil.h> +#include <misc_util.h> +#include <libcmpiutil/std_indication.h> +#include <cs_util.h> + +#include "config.h" +#include "infostore.h" + +typedef struct cb_info { + const CMPIContext *context; + const CMPIObjectPath *obj_path; +} *cb_info_ptr; + +static const CMPIBroker *_BROKER; + +#define CAI_NUM_PLATFORMS 3 +enum CAI_PLATFORMS {CAI_XEN, + CAI_KVM, + CAI_LXC, +}; +static int active_filters[CAI_NUM_PLATFORMS]; + +static pthread_mutex_t cb_reg_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_alert_ind_props(const CMPIBroker *broker, + const char *classname, + const CMPIContext *ctx, + const CMPIInstance *ind) +{ + CMPIStatus s; + CMPIString *str = NULL; + CMPIUint16 val; + CMPIArray *array = NULL; + CMPIDateTime *time = NULL; + + + //CMSetProperty(ind, "Description", &str, CMPI_string); + + + // val 1 + val = 1; + CMSetProperty(ind, "AlertType", &val, CMPI_uint16); + + str = CMNewString(broker, "Virtual guest crash", &s); + CMSetProperty(ind, "OtherAlertType", &str, CMPI_string); + + val = 7; + CMSetProperty(ind, "PerceivedSeverity", &val, CMPI_uint16); + + // val 48 + val = 48; + CMSetProperty(ind, "ProbableCause", &val, CMPI_uint16); + + //CMSetProperty(ind, "ProbableCauseDescription", &str, CMPI_string); + + // val 1 + val = 1; + CMSetProperty(ind, "Trending", &val, CMPI_uint16); + + // This is an array + // CMSetProperty(ind, "RecommendedActions", &str, CMPI_string); + + time = CMNewDateTime(broker, &s); + CMSetProperty(ind, "EventTime", &time, CMPI_dateTime); + + // Kaitlin, do I need to fill this one? + //CMSetProperty(ind, "SystemCreationClassName", &str, CMPI_string); + + // What about this? + // CMSetProperty(ind, "SystemName", &str, CMPI_string); + + str = CMNewString(broker, classname, &s); + CMSetProperty(ind, "ProviderName", &str, CMPI_string); + + str = CMNewString(broker, "DTMF", &s); + CMSetProperty(ind, "OwningEntity", &str, CMPI_string); + + str = CMNewString(broker, "322", &s); + CMSetProperty(ind, "MessageID", &str, CMPI_string); + + //CMSetProperty(ind, "Message", &str, CMPI_string); + + // This is an string array + array = CMNewArray(broker, 1, CMPI_stringA, &s); + str = CMNewString(broker, + "Virtual machine execution ended " + "unexpectly", + &s); + s = CMSetArrayElementAt(array, 0, &str, CMPI_string); + CMSetProperty(ind, "MessageArguments", &array, CMPI_string); +} + +static CMPIStatus create_and_deliver_ind(const CMPIBroker *broker, + const CMPIContext *ctx, + struct ind_args *args, + const CMPIInstance *ind) +{ + CMPIStatus s; + CMPIObjectPath *ind_op = NULL; + + set_alert_ind_props(broker, args->classname, ctx, ind); + + ind_op = CMGetObjectPath(ind, NULL); + CU_DEBUG("Delivering Indication: %s", + CMGetCharPtr(CMObjectPathToString(ind_op, NULL))); + + s = stdi_deliver(broker, ctx, args, (CMPIInstance *)ind); + if (s.rc == CMPI_RC_OK) { + CU_DEBUG("Indication delivered"); + } else { + CU_DEBUG("Not delivered: %s", CMGetCharPtr(s.msg)); + } + + return s; +} + +DECLARE_FILTER(xen_crashed, "Xen_GuestCrashAlertIndication"); +DECLARE_FILTER(kvm_crashed, "KVM_GuestCrashAlertIndication"); + +static struct std_ind_filter *filters[] = { + &xen_crashed, + &kvm_crashed, + NULL, +}; + +static CMPIStatus raise_indication(const CMPIBroker *broker, + const CMPIContext *ctx, + const CMPIInstance *ind) +{ + CMPIStatus s = {CMPI_RC_OK, NULL}; + CMPIObjectPath *ref = NULL; + struct std_indication_ctx *_ctx = NULL; + struct ind_args *args = NULL; + + CU_DEBUG("Guest Crash Raise indication"); + + ref = CMGetObjectPath(ind, &s); + if (s.rc != CMPI_RC_OK) { + cu_statusf(broker, &s, + CMPI_RC_ERR_FAILED, + "Unable to get a reference to the guest"); + goto out; + } + + /* FIXME: This is a Pegasus work around. Pegsus loses the namespace + when an ObjectPath is pulled from an instance */ + if (STREQ(NAMESPACE(ref), "")) + CMSetNameSpace(ref, "root/virt"); + + /*s = get_domain_by_ref(broker, ref, &src_inst); + if (s.rc != CMPI_RC_OK || CMIsNullObject(src_inst)) + goto out;*/ + + _ctx = malloc(sizeof(struct std_indication_ctx)); + if (_ctx == NULL) { + cu_statusf(broker, &s, + CMPI_RC_ERR_FAILED, + "Unable to allocate indication context"); + goto out; + } + + _ctx->brkr = broker; + _ctx->handler = NULL; + _ctx->filters = filters; + _ctx->enabled = true; + + args = malloc(sizeof(struct ind_args)); + if (args == NULL) { + cu_statusf(broker, &s, + CMPI_RC_ERR_FAILED, + "Unable to allocate ind_args"); + goto out; + } + + args->ns = strdup(NAMESPACE(ref)); + args->classname = strdup(CLASSNAME(ref)); + args->_ctx = _ctx; + + + s = create_and_deliver_ind(broker, ctx, args, ind); + + if (s.rc != CMPI_RC_OK) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Unable to generate indication"); + } + + out: + if (args != NULL) + stdi_free_ind_args(&args); + + if (_ctx != NULL) + free(_ctx); + + return s; +} + + +static void free_cb(void *opaque) +{ + cb_info_ptr cbinfo = NULL; + + CU_DEBUG("Releasing memory from guest crash callback"); + + cbinfo = (cb_info_ptr)opaque; + free(cbinfo); +} + +static char * inc_counter(virDomainPtr dom) +{ + struct infostore_ctx *store = NULL; + char *str = NULL; + uint64_t counter = 0; + bool ret = false; + + pthread_mutex_lock(&cb_reg_mutex); + + store = infostore_open(dom); + + if (store == NULL) { + CU_DEBUG("Could not open infostore"); + goto out; + } + + counter = infostore_get_u64(store, "EventID"); + + if (counter == 0) + CU_DEBUG("No EventID available. Creating..."); + + counter++; + + if (asprintf(&str, "%llu", counter) < 0) { + CU_DEBUG("Could not alloc memory for counter"); + goto out; + } + + ret = infostore_set_u64(store, "EventID", counter); + if (!ret) { + CU_DEBUG("Could not save EventID to infostore"); + free(str); + } + + out: + if (!ret) + str = NULL; + + infostore_close(store); + + pthread_mutex_unlock(&cb_reg_mutex); + + return str; +} + +static void preset_alert_ind_props(CMPIInstance *ind, virDomainPtr dom) +{ + CMPIStatus s; + CMPIString *str = NULL; + char *dom_name = (char *)virDomainGetName(dom); + char *counter = NULL; + + if (dom_name == NULL) { + CU_DEBUG("Could not retrieve name of guest responsible for " + "crash"); + dom_name = "Guest name not available"; + } + + str = CMNewString(_BROKER, dom_name, &s); + CMSetProperty(ind, "AlertingManagedElement", &str, CMPI_string); + + // This property must be unique, retrieve a counter from infostore + counter = inc_counter(dom); + if (counter == NULL) { + CU_DEBUG("Could not retrieve counter"); + } else { + str = CMNewString(_BROKER, counter, &s); + CMSetProperty(ind, "EventID", &str, CMPI_string); + free(counter); + } +} + +static int guest_crashed_cb(virConnectPtr conn, + virDomainPtr dom, + int event, + int detail, + void *opaque) +{ + char *type = NULL; + CMPIStatus s = {CMPI_RC_OK, NULL}; + const char *ind_name = "GuestCrashAlertIndication"; + CMPIInstance *ind = NULL; + cb_info_ptr cbinfo = NULL; + const CMPIObjectPath *op = NULL; + const CMPIContext *context = NULL; + + CU_DEBUG("Preparing %s", ind_name); + + if (event != VIR_DOMAIN_EVENT_STOPPED_FAILED) { + CU_DEBUG("Event not monitored. Ignoring..."); + return 0; + } + + cbinfo = (cb_info_ptr)opaque; + op = cbinfo->obj_path; + context = cbinfo->context; + + ind = get_typed_instance(_BROKER, + CLASSNAME(op), + ind_name, + NAMESPACE(op)); + + preset_alert_ind_props(ind, dom); + + if (ind == NULL) { + CU_DEBUG("Failed to create ind '%s'", ind_name); + goto out; + } + + type = get_typed_class(CLASSNAME(op), ind_name); + + s = stdi_raise_indication(_BROKER, + context, + type, + NAMESPACE(op), + ind); + + out: + free(type); + + return s.rc != CMPI_RC_OK; +} + +static int platform_from_class(const char *cn) +{ + if (STARTS_WITH(cn, "Xen")) + return CAI_XEN; + else if (STARTS_WITH(cn, "KVM")) + return CAI_KVM; + else if (STARTS_WITH(cn, "LXC")) + return CAI_LXC; + else + return -1; +} + +static CMPIStatus ActivateFilter(CMPIIndicationMI* mi, + const CMPIContext* ctx, + const CMPISelectExp* se, + const char *ns, + const CMPIObjectPath* op, + CMPIBoolean first) +{ + virConnectPtr conn = NULL; + CMPIStatus s = {CMPI_RC_OK, NULL}; + cb_info_ptr cbinfo = NULL; + int platform; + int ret = 0; + + cbinfo = (cb_info_ptr)malloc(sizeof(struct cb_info)); + cbinfo->context = ctx; + cbinfo->obj_path = op; + + CU_DEBUG("ActivateFilter for %s", CLASSNAME(op)); + + if (CMIsNullObject(op)) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "No ObjectPath given"); + goto out; + } + + platform = platform_from_class(CLASSNAME(op)); + if (platform < 0) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Unknown platform"); + goto out; + } + + pthread_mutex_lock(&cb_reg_mutex); + + active_filters[platform]++; + + if (active_filters[platform] == 1) { + conn = connect_by_classname(_BROKER, CLASSNAME(op), &s); + + CU_DEBUG("Registering callback function"); + ret = virConnectDomainEventRegister(conn, + guest_crashed_cb, + (void *)cbinfo, + free_cb); + CU_DEBUG("ret val: %d\n", ret); + } + + pthread_mutex_unlock(&cb_reg_mutex); + + out: + return s; +} + +static CMPIStatus DeActivateFilter(CMPIIndicationMI* mi, + const CMPIContext* ctx, + const CMPISelectExp* se, + const char *ns, + const CMPIObjectPath* op, + CMPIBoolean last) +{ + virConnectPtr conn = NULL; + int platform; + int ret = 0; + + CMPIStatus s = {CMPI_RC_OK, NULL}; + + pthread_mutex_lock(&cb_reg_mutex); + + platform = platform_from_class(CLASSNAME(op)); + if (platform < 0) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Unknown platform"); + goto out; + } + + if (active_filters[platform] == 1) { + // Unregister callback + conn = connect_by_classname(_BROKER, CLASSNAME(op), &s); + + CU_DEBUG("De-registering callback function"); + ret = virConnectDomainEventDeregister(conn, + guest_crashed_cb); + CU_DEBUG("ret val: %d\n", ret); + + } + + active_filters[platform]--; + + pthread_mutex_unlock(&cb_reg_mutex); + + CU_DEBUG("DeActivateFilter for %s", CLASSNAME(op)); + + out: + return s; +} + +static _EI_RTYPE EnableIndications(CMPIIndicationMI* mi, + const CMPIContext *ctx) +{ + CU_DEBUG("EnableIndications"); + + _EI_RET(); + +} + +static _EI_RTYPE DisableIndications(CMPIIndicationMI* mi, + const CMPIContext *ctx) +{ + CU_DEBUG("DisableIndications"); + + _EI_RET(); +} + + + +static struct std_indication_handler csi = { + .raise_fn = raise_indication, + .trigger_fn = NULL, + .activate_fn = ActivateFilter, + .deactivate_fn = DeActivateFilter, + .enable_fn = EnableIndications, + .disable_fn = DisableIndications, +}; + +DEFAULT_IND_CLEANUP(); +DEFAULT_AF(); +DEFAULT_MP(); + +STDI_IndicationMIStub(, + Virt_GuestCrashAlertIndicationProvider, + _BROKER, + libvirt_cim_init(), + &csi, + filters); + +/* + * Local Variables: + * mode: C + * c-set-style: "K&R" + * tab-width: 8 + * c-basic-offset: 8 + * indent-tabs-mode: nil + * End: + */

# HG changeset patch # User Hollis Blanchard <hollisb@us.ibm.com> # Date 1250181138 25200 # Node ID 9c4cb3443e88714c2029cff3c37d59e3ab8a5a01 # Parent 22540c8901bd5a58a6631c6b449c92a3d0c5fd5b [RFC] Implement libvirt event callback management. Libvirt requires that users implement their own event monitoring and callback-management infrastructure. Actually, two similar lists are needed: one for file descriptors to monitor, and one for pending timers. This patch starts exactly one event-monitoring thread per libvirt-cim instance. Multiple provider threads in the same process will share this thread. Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com> --- This builds but isn't tested, and may very well contain embarrassing list manipulation or locking bugs. It really needs a consumer to test, i.e. for someone to register a domain event callback. Comments welcome. Please CC me on replies. diff -r 22540c8901bd -r 9c4cb3443e88 libxkutil/Makefile.am --- a/libxkutil/Makefile.am Sat Jul 25 16:19:43 2009 -0300 +++ b/libxkutil/Makefile.am Thu Aug 13 09:32:18 2009 -0700 @@ -5,14 +5,14 @@ CFLAGS += $(CFLAGS_STRICT) noinst_HEADERS = cs_util.h misc_util.h device_parsing.h xmlgen.h infostore.h \ - pool_parsing.h + pool_parsing.h event.h lib_LTLIBRARIES = libxkutil.la AM_LDFLAGS = -lvirt -luuid libxkutil_la_SOURCES = cs_util_instance.c misc_util.c device_parsing.c \ - xmlgen.c infostore.c pool_parsing.c + xmlgen.c infostore.c pool_parsing.c event.c noinst_PROGRAMS = xml_parse_test diff -r 22540c8901bd -r 9c4cb3443e88 libxkutil/event.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libxkutil/event.c Thu Aug 13 09:32:18 2009 -0700 @@ -0,0 +1,418 @@ +/* + * Copyright IBM Corp. 2009 + * + * Authors: + * Hollis Blanchard <hollisb@us.ibm.com> + * + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> +#include <unistd.h> +#include <pthread.h> +#include <errno.h> +#include <poll.h> +#include <values.h> + +#include <libvirt/libvirt.h> +#include <libvirt/virterror.h> + +#include "event.h" + +struct timer { + int id; + int timeout; + void *opaque; + virFreeCallback ff; + struct timer *next; + bool deleted; +}; + +struct watch { + int id; + int fd; + int events; + virEventHandleCallback cb; + void *opaque; + virFreeCallback ff; + struct watch *next; + bool deleted; +}; + +static pthread_t watch_thread_id; + +static int next_watch_id; +static struct watch *watch_list; +static int watch_count; +static pthread_mutex_t watch_list_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int next_timer_id; +static struct timer *timer_list; +static pthread_mutex_t timer_list_mutex = PTHREAD_MUTEX_INITIALIZER; + + +static int eventAddHandle(int fd, int events, virEventHandleCallback cb, + void *opaque, virFreeCallback ff) +{ + struct watch *watch; + + CU_DEBUG("%s", __func__); + + watch = malloc(sizeof(struct watch)); + if (!watch) + return -ENOMEM; + + CU_DEBUG("watch->fd = %d", fd); + + watch->fd = fd; + watch->events = events; + watch->cb = cb; + watch->opaque = opaque; + watch->ff = ff; + watch->deleted = 0; + + pthread_mutex_lock(&watch_list_mutex); + + watch->id = next_watch_id++; + CU_DEBUG("watch->id = %d", watch->id); + watch->next = watch_list; + watch_list = watch; + watch_count++; + + pthread_mutex_unlock(&watch_list_mutex); + + return watch->id; +} + +static void eventUpdateHandle(int id, int events) +{ + struct watch *cur; + + CU_DEBUG("%s %d", __func__, id); + + pthread_mutex_lock(&watch_list_mutex); + + for (cur = watch_list; cur != NULL; cur = cur->next) { + if (cur->id == id) { + cur->events = events; + CU_DEBUG("id: %d, events: %d", cur->id, cur->events); + break; + } + } + + pthread_mutex_unlock(&watch_list_mutex); +} + +/* To avoid locking problems, watches are just flagged here, and the memory is + * freed later. */ +static int eventRemoveHandle(int id) +{ + struct watch *cur; + + CU_DEBUG("%s %d", __func__, id); + + pthread_mutex_lock(&watch_list_mutex); + + for (cur = watch_list; cur != NULL; cur = cur->next) { + if (cur->id == id) { + cur->deleted = 1; + break; + } + } + + pthread_mutex_unlock(&watch_list_mutex); + + return 0; +} + +/* Delete all watches marked for deletion. */ +static void event_watch_free_deleted(void) +{ + struct watch *cur; + struct watch **link; + + CU_DEBUG("%s", __func__); + + pthread_mutex_lock(&watch_list_mutex); + + cur = watch_list; + link = &watch_list; + while (cur != NULL) { + struct watch *next = cur->next; + + if (cur->deleted) { + CU_DEBUG("Deleting id: %d", cur->id); + *link = next; + + cur->ff(cur->opaque); + free(cur); + watch_count--; + } else + link = &cur->next; + + cur = next; + } + + pthread_mutex_unlock(&watch_list_mutex); +} + +static int eventAddTimeout(int timeout, virEventTimeoutCallback cb, + void *opaque, virFreeCallback ff) +{ + struct timer *timer; + + CU_DEBUG("%s", __func__); + + timer = malloc(sizeof(struct timer)); + if (!timer) + return -ENOMEM; + + CU_DEBUG("timeout: %d", timeout); + + timer->timeout = timeout; + timer->opaque = opaque; + timer->ff = ff; + timer->deleted = 0; + + pthread_mutex_lock(&timer_list_mutex); + + timer->id = next_timer_id++; + timer->next = timer_list; + timer_list = timer; + + pthread_mutex_unlock(&timer_list_mutex); + + return timer->id; +} + +static void eventUpdateTimeout(int id, int timeout) +{ + struct timer *cur; + + CU_DEBUG("%s %d", __func__, id); + + pthread_mutex_lock(&timer_list_mutex); + + for (cur = timer_list; cur != NULL; cur = cur->next) { + if (cur->id == id) { + cur->timeout = timeout; + break; + } + } + + pthread_mutex_unlock(&timer_list_mutex); +} + +static int eventRemoveTimeout(int id) +{ + struct timer *cur; + + CU_DEBUG("%s %d", __func__, id); + + pthread_mutex_lock(&timer_list_mutex); + + for (cur = timer_list; cur != NULL; cur = cur->next) { + if (cur->id == id) { + cur->deleted = 1; + break; + } + } + + pthread_mutex_unlock(&timer_list_mutex); + + return 0; +} + +/* Delete all timers marked for deletion. */ +static void event_timer_free_deleted(void) +{ + struct timer *cur; + struct timer **link; + + CU_DEBUG("%s", __func__); + + pthread_mutex_lock(&timer_list_mutex); + + cur = timer_list; + link = &timer_list; + while (cur != NULL) { + struct timer *next = cur->next; + + if (cur->deleted) { + CU_DEBUG("deleted timer"); + *link = next; + + cur->ff(cur->opaque); + free(cur); + } else + link = &cur->next; + + cur = next; + } + + pthread_mutex_unlock(&timer_list_mutex); +} + + + +static int poll_to_libvirt_events(int pevents) +{ + int vevents = 0; + + if (pevents & POLLIN) + vevents |= VIR_EVENT_HANDLE_READABLE; + + if (pevents & POLLOUT) + vevents |= VIR_EVENT_HANDLE_WRITABLE; + + if (pevents & POLLERR) + vevents |= VIR_EVENT_HANDLE_ERROR; + + if (pevents & POLLHUP) + vevents |= VIR_EVENT_HANDLE_HANGUP; + + return vevents; +} + +static int libvirt_to_poll_events(int vevents) +{ + int pevents = 0; + + if (vevents & VIR_EVENT_HANDLE_READABLE) + pevents |= POLLIN; + + if (vevents & VIR_EVENT_HANDLE_WRITABLE) + pevents |= POLLOUT; + + if (vevents & VIR_EVENT_HANDLE_ERROR) + pevents |= POLLERR; + + if (vevents & VIR_EVENT_HANDLE_HANGUP) + pevents |= POLLHUP; + + return pevents; +} + +static void invoke_callback(struct watch *watch, struct pollfd *pollfd) +{ + int vevents = poll_to_libvirt_events(watch->events); + + CU_DEBUG("invoke_callback. events: %d", vevents); + + watch->cb(watch->id, watch->fd, vevents, watch->opaque); +} + +static int event_next_timeout(void) +{ + struct timer *cur; + int closest = 10000; + + for (cur = timer_list; cur != NULL; cur = cur->next) { + CU_DEBUG("cur->timeout: %d", cur->timeout); + if (cur->timeout > 0 && cur->timeout < closest) + closest = cur->timeout; + } + + return closest; +} + +/* One thread to watch all fds for all events for all libvirt threads. */ +static void *event_thread(void *ptr) +{ + while (1) { + struct watch *cur; + struct pollfd *pollfds; + struct pollfd *pollfd; + int timeout; + int i; + int pollrv; + + CU_DEBUG("event_thread"); + + //pthread_mutex_lock(&watch_list_mutex); + + pollfds = malloc(sizeof(struct pollfd) * watch_count); + + /* fill in pollfds array from our watch list */ + for (pollfd = &pollfds[0], cur = watch_list; + cur != NULL; + pollfd++, cur = cur->next) { + CU_DEBUG("pollfd = %d", *pollfd); + pollfd->fd = cur->fd; + pollfd->events = libvirt_to_poll_events(cur->events); + } + + //CU_DEBUG("event_next_timeout"); + + timeout = event_next_timeout(); + + //CU_DEBUG("poll timeout: %d", timeout); + + pollrv = poll(pollfds, watch_count, timeout); + + //CU_DEBUG("pool"); + + /* invoke callbacks in case of event */ + if (pollrv > 0) { + CU_DEBUG("Got movement"); + for (i = 0; i < watch_count; i++) { + for (cur = watch_list; cur != NULL; cur = cur->next) + if (cur->fd == pollfds[i].fd + && !cur->deleted) { + CU_DEBUG("Invoke call back fds: %d", pollfds[i]); + invoke_callback(cur, &pollfds[i]); + break; + } + } + } else { + CU_DEBUG("All clear: %d", pollrv); + } + + //pthread_mutex_unlock(&watch_list_mutex); + + free(pollfds); + + event_watch_free_deleted(); + event_timer_free_deleted(); + } + + return NULL; +} + +void init_events(void) +{ + static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; + + CU_DEBUG("%s", __func__); + + pthread_mutex_lock(&thread_mutex); + + if (!watch_thread_id) { + virEventRegisterImpl(eventAddHandle, + eventUpdateHandle, + eventRemoveHandle, + eventAddTimeout, + eventUpdateTimeout, + eventRemoveTimeout); + + pthread_create(&watch_thread_id, NULL, event_thread, NULL); + } + + pthread_mutex_unlock(&thread_mutex); +} diff -r 22540c8901bd -r 9c4cb3443e88 libxkutil/event.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libxkutil/event.h Thu Aug 13 09:32:18 2009 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright IBM Corp. 2009 + * + * Authors: + * Hollis Blanchard <hollisb@us.ibm.com> + * + * 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 __EVENT_H +#define __EVENT_H + +#include <libcmpiutil.h> + +void init_events(void); + +#endif /* __EVENT_H */ diff -r 22540c8901bd -r 9c4cb3443e88 libxkutil/misc_util.c --- a/libxkutil/misc_util.c Sat Jul 25 16:19:43 2009 -0300 +++ b/libxkutil/misc_util.c Thu Aug 13 09:32:18 2009 -0700 @@ -38,6 +38,7 @@ #include "misc_util.h" #include "cs_util.h" +#include "event.h" #include <config.h> @@ -489,6 +490,7 @@ bool libvirt_cim_init(void) { + init_events(); return virInitialize() == 0; }
participants (1)
-
Richard Maciel