From c6cbafa06ccee8d12163b5eeac97fc2adc542668 Mon Sep 17 00:00:00 2001 From: Jan Synacek Date: Tue, 6 May 2014 11:09:17 +0200 Subject: indmanager: move to libs --- src/CMakeLists.txt | 2 +- src/account/CMakeLists.txt | 2 +- src/indmanager/CMakeLists.txt | 19 - src/indmanager/README | 142 --- src/indmanager/ind_manager.c | 1286 --------------------------- src/indmanager/ind_manager.h | 218 ----- src/indmanager/openlmiindmanager.pc.in | 10 - src/journald/CMakeLists.txt | 2 +- src/libs/indmanager/CMakeLists.txt | 19 + src/libs/indmanager/README | 142 +++ src/libs/indmanager/ind_manager.c | 1286 +++++++++++++++++++++++++++ src/libs/indmanager/ind_manager.h | 218 +++++ src/libs/indmanager/openlmiindmanager.pc.in | 10 + src/service-dbus/CMakeLists.txt | 2 +- 14 files changed, 1679 insertions(+), 1679 deletions(-) delete mode 100644 src/indmanager/CMakeLists.txt delete mode 100644 src/indmanager/README delete mode 100644 src/indmanager/ind_manager.c delete mode 100644 src/indmanager/ind_manager.h delete mode 100644 src/indmanager/openlmiindmanager.pc.in create mode 100644 src/libs/indmanager/CMakeLists.txt create mode 100644 src/libs/indmanager/README create mode 100644 src/libs/indmanager/ind_manager.c create mode 100644 src/libs/indmanager/ind_manager.h create mode 100644 src/libs/indmanager/openlmiindmanager.pc.in diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 79c316c..75e655c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,7 +39,7 @@ if (WITH-REALMD) endif (WITH-REALMD) if (WITH-INDMANAGER) - add_subdirectory(indmanager) + add_subdirectory(libs/indmanager) endif (WITH-INDMANAGER) if (WITH-SOFTWARE) diff --git a/src/account/CMakeLists.txt b/src/account/CMakeLists.txt index 83ca474..6390ae2 100644 --- a/src/account/CMakeLists.txt +++ b/src/account/CMakeLists.txt @@ -44,7 +44,7 @@ profile_mof_generate("90_LMI_Account_Profile.mof.skel" "${TARGET_MOF}" "${CIM_PR # Require libuser pkg_check_modules(LIBUSER REQUIRED libuser>=0.60) -include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMPI_INCLUDE_DIR} ${GLIB_INCLUDE_DIRS} ${LIBUSER_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src/indmanager) +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMPI_INCLUDE_DIR} ${GLIB_INCLUDE_DIRS} ${LIBUSER_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src/libs/indmanager) target_link_libraries(${LIBRARY_NAME} openlmicommon ${KONKRETCMPI_LIBRARIES} ${GLIB_LIBRARIES} ${LIBUSER_LIBRARIES} openlmiindmanager) diff --git a/src/indmanager/CMakeLists.txt b/src/indmanager/CMakeLists.txt deleted file mode 100644 index 3a265c9..0000000 --- a/src/indmanager/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -add_library(openlmiindmanager SHARED - ind_manager.c -) - -set(OPENLMIINDMANAGER_VERSION_MAJOR 1) -set(OPENLMIINDMANAGER_VERSION_MINOR 0) -set(OPENLMIINDMANAGER_VERSION_PATCH 0) -set(OPENLMIINDMANAGER_VERSION "${OPENLMIINDMANAGER_VERSION_MAJOR}.${OPENLMIINDMANAGER_VERSION_MINOR}.${OPENLMIINDMANAGER_VERSION_PATCH}") - -set_target_properties(openlmiindmanager PROPERTIES VERSION ${OPENLMIINDMANAGER_VERSION}) -set_target_properties(openlmiindmanager PROPERTIES SOVERSION ${OPENLMIINDMANAGER_VERSION_MAJOR}) - -target_link_libraries(openlmiindmanager pthread) - -install(TARGETS openlmiindmanager DESTINATION lib${LIB_SUFFIX}) -install(FILES ind_manager.h DESTINATION include/openlmi) - -configure_file(openlmiindmanager.pc.in ${CMAKE_CURRENT_BINARY_DIR}/openlmiindmanager.pc @ONLY) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/openlmiindmanager.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) diff --git a/src/indmanager/README b/src/indmanager/README deleted file mode 100644 index 4f9654d..0000000 --- a/src/indmanager/README +++ /dev/null @@ -1,142 +0,0 @@ -###################### -# Indication manager # -###################### - in short IM - -Purpose -~~~~~~~ -Indication manager (in short IM) should help in writing indication provider -using CMPI. Main concept is to lessen work you need to do to only define -few callback functions. - -Usage -~~~~~ -Mainly the user of IM have to write callback functions: IMFilterChecker, -IMEventWatcher. -IM can be used to poll instance. This has to be set upon initialization -and cannot be changed. When not using polling the user have to write also -IMInstGather callback function. -For user are provided the following functions: -im_create_manager, im_destroy_manager, im_verify_filter, im_add_filter, -im_remove_filter, im_start_ind and im_stop_ind. -Following are documentation for functions and callback definitions, -in the following form: -* name of function or callback -C definition -Documentation. - -* im_create_manager -IMManager* im_create_manager(IMInstGather gather, IMFilterChecker f_checker, - bool polling, IMEventWatcher watcher, - IMIndType type, CMPIBroker *broker, IMError *err); -Return newly created reference to IMManager. User should use this reference -only as parameter for functions. User should provide callback functions (see -below), should choose if want to use polling, choose type indications and -provide broker reference. On any error occurance the NULL is returned and -err is set. - -* im_destroy_manager -bool im_destroy_manager(IMManager *manager, const CMPIContext *ctx, - IMError *err); -Destroy the given manager. User should provide context. Returns true when -everything was correctly done, or false and err is set. - -* im_verify_filter -bool im_verify_filter(IMManager *manager, const CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err); -Ask manager to verify the given filter. User should provide context. In turn, -the manager calls IMFilterChecker (see below). If filter is accepted true -is returned, otherwise false is returned and err is set. - -* im_add_filter -bool im_add_filter(IMManager *manager, CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err); -Adds the given filter to the manager. It is logical error to add filter, which -is not verified before. User should provide context. Return true when everything -is correctly done, false otherwise and err is set. - -* im_remove_filter -bool im_remove_filter(IMManager *manager, const CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err); -Removes the given filter from the manager. Returns true when correctly done, -or false and err is set. - -* im_start_ind -bool im_start_ind(IMManager *manager, const CMPIContext *ctx, IMError *err); -Informs the manager to start sending indications. It is recursively calling -IMEventWatcher and IMInstGather (if using polling, the default gather is used). -Returns true if everything started correctly, false otherwise and err is set. -NOTE: Callbacks are called within a thread that can be forcefully interrupted -by the im_stop_ind() call without any chance for cleanup. For that reason, any -dynamically allocated memory will get leaked. Please keep that in mind and use -as much stack memory as possible. If needed, please perform cleanup at the same -place the im_stop_ind() is called. Only one thread is executed within each -indication manager instance, no need for extra thread safety. - -* im_stop_ind -bool im_stop_ind(IMManager *manager, const CMPIContext *ctx, IMError *err); -Informs the manager to stop sending indications. Internally this call cancels -the thread function, see the note above. Returns true when everything is done -correctly, false otherwise and err is set. - -* IMFilterChecker -typedef bool (*IMFilterChecker) (const CMPISelectExp *filter); -User should define this callback, which is called whenever manager is asked -to verify filter. Function should return true when given filter is allowed, -false otherwise. Filter is passed from im_verify_filter function - -* IMEventWatcher -typedef bool (*IMEventWatcher) (void **data); -User should define this callback, which should be blocking. The purpose of this -called is to inform manager that event occured. When the callback returns with -true value the manager is informed that event has occured and will send -indication right after. When the callback returns with false value the manager -is informed that event hasn't occured and will not send indication and will -retart the loop - the callback will be executed again soon. -User can use data to transfer any information from this callback -to IMInstGather. -This is called within a thread and is advised that any blocking call should be -a pthread cancellation point (see pthreads(7)) for easy interruption (see the -note at im_start_ind()). - -* IMInstGather -typedef bool (*IMInstGather) (const IMManager *manager, CMPIInstance **old, - CMPIInstance **new, void *data); -This callback has to be defined only when not using default polling mechanism. -data are passed from IMEventWatcher and are supposed to help creating instances. -User should create and set instance and/or instances. When type of indications -is IM_IND_CREATION the new instance has to be set. When IM_IND_DELETION the old -instance has to be set. Both when IM_IND_MODIFICATION is used. When the callback -returns true the manager will send the indication and call the callback again. -In other words, the return value means "There is another indication -with instances to be send". When false is returned the manager will send -the last indication and starts another round in the loop. - -Cancellation, indications enabling and disabling -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Indications should be enabled or disabled in relation with EnableIndications() -and DisableIndications() CIM class method calls. These are called typically when -subscription is added or removed. Note that enabling or disabling indications -multiple times has no effect. - -Polling -~~~~~~~ -IM supports basic polling mechanism. When enabled the IM remembers instances -after every IMEventWatcher call and default IMInstGather callback is used. -Which instances are to be polled are determined by the filters, where -the IM looks for the first occurence of ISA predicate in WHERE clause of select -expression. For example we have filter with expression: -SELECT * FROM LMI_SomeIndication WHERE SourceInstance ISA LMI_Account -the manager will be polling instances from LMI_Account class (from the same -namespace as LMI_SomeIndication). Polling is possible on multiple classes. - -Known limitations -~~~~~~~~~~~~~~~~~ -IM supports only instance indications of the following types: -Creation, Deletion, Modification -and supports only one type per provider. -Settings cannot be changed. - -Example -~~~~~~~ -Is not provided yet. diff --git a/src/indmanager/ind_manager.c b/src/indmanager/ind_manager.c deleted file mode 100644 index 7009e3b..0000000 --- a/src/indmanager/ind_manager.c +++ /dev/null @@ -1,1286 +0,0 @@ -/* - * Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Roman Rakus - */ - -#include "ind_manager.h" - -#include -#include -#include - -#include -#include -#include -#include - -/* Debugging macros */ -#define DEBUG(...) \ - _im_debug(manager->broker, __FILE__, __func__, __LINE__, __VA_ARGS__) -#include - -static inline void _im_debug(const CMPIBroker *b, const char *file, const char *func, - int line, const char *format, ...) -{ - char text[2048] = ""; - snprintf(text, 2047, "%s:%d(%s):", file, line, func); - va_list args; - va_start(args, format); - vsnprintf(text+strlen(text), 2047-strlen(text), format, args); - va_end(args); - CMTraceMessage(b, CMPI_LEV_VERBOSE, "LMI indication manager", text, NULL); -} - -// Get char * representation of name space from object path -#define NS_STR_FROM_OP(op) CMGetCharPtr(CMGetNameSpace((op), NULL)) - -// Get char * representation of class name from object path -#define CN_STR_FROM_OP(op) CMGetCharPtr(CMGetClassName((op), NULL)) - -typedef struct _IMPolledDiff { - CMPIInstance *iold; - CMPIInstance *inew; - struct _IMPolledDiff *next; -} IMPolledDiff; - -typedef struct _IMPolledDiffs { - IMPolledDiff *first; -} IMPolledDiffs; - -typedef enum _IMIDiff { - IM_I_SAME, // Instances are the same - IM_I_DIFFERENT, // Instances are different in key properties or value types - IM_I_CHANGE // Instances are different in non-key properties -} IMIDiff; - -static IMIDiff compare_instances(CMPIInstance *, CMPIInstance *); - -/* CMPI rc handler */ -CMPIStatus im_rc; - -/* - * Compare 2 object paths - * return true if they are same, false otherwise - * is using ft->toString() - */ -static bool compare_objectpaths(CMPIObjectPath *op1, CMPIObjectPath *op2) -{ - return (strcmp(CMGetCharsPtr(CMObjectPathToString(op1, NULL), NULL), - CMGetCharsPtr(CMObjectPathToString(op2, NULL), NULL)) == 0); -} - -/* - * Compare 2 cmpi data - * return true if they are the same, false otherwise - * note: inspired by value.c:sfcb_comp_CMPIValue() from sblim-sfcb - */ -static bool compare_values(CMPIValue *v1, CMPIValue *v2, CMPIType type) -{ -// Checks for NULL, but it is incorrect -#define CHECK_PTRS \ - if (!v1->array && !v2->array) {\ - return true;\ - }\ - if (!v1->array || !v2->array) {\ - return false;\ - }\ - - if (type & CMPI_ARRAY) { - CHECK_PTRS; - CMPICount c = CMGetArrayCount(v1->array, NULL); - if (c != CMGetArrayCount(v2->array, NULL)) { - return false; - } - for (; c > 0; c--) { - CMPIValue val1 = CMGetArrayElementAt(v1->array, c-1, NULL).value; - CMPIValue val2 = CMGetArrayElementAt(v2->array, c-1, NULL).value; - if (!compare_values(&val1, &val2, type & ~CMPI_ARRAY)) { - return false; - } - } - return true; - } - switch (type) { - case CMPI_boolean: - case CMPI_uint8: - case CMPI_sint8: - return (v1->Byte == v2->Byte); - case CMPI_char16: - case CMPI_uint16: - case CMPI_sint16: - return (v1->Short == v2->Short); - case CMPI_uint32: - case CMPI_sint32: - return (v1->Int == v2->Int); - case CMPI_real32: - return (v1->Float == v2->Float); - case CMPI_uint64: - case CMPI_sint64: - return (v1->Long == v2->Long); - case CMPI_real64: - return(v1->Double == v2->Double); - case CMPI_instance: - CHECK_PTRS; - return (compare_instances(v1->inst, v2->inst) == IM_I_SAME); - case CMPI_ref: - CHECK_PTRS; - return (compare_objectpaths(v1->ref, v2->ref)); - case CMPI_string: - CHECK_PTRS; - return (strcmp(CMGetCharsPtr(v1->string, NULL), - CMGetCharsPtr(v2->string, NULL)) == 0); - case CMPI_dateTime: - CHECK_PTRS; - return (strcmp(CMGetCharsPtr( - CMGetStringFormat(v1->dateTime, NULL), NULL), - CMGetCharsPtr( - CMGetStringFormat(v2->dateTime, NULL), NULL)) == 0); - default: - return true; - } - return true; -} - -/* - * Compare 2 instances. - * They can be same, different in key properties or different in non-key props - */ -static IMIDiff compare_instances(CMPIInstance *i1, CMPIInstance *i2) -{ - CMPIString *prop_name = NULL; - // At first compare key properties. They will differ in most cases - // Then compare non-key properties - CMPIObjectPath *op1 = CMGetObjectPath(i1, NULL); - CMPIObjectPath *op2 = CMGetObjectPath(i2, NULL); - int pcount1 = CMGetKeyCount(op1, NULL); - int pcount2 = CMGetKeyCount(op2, NULL); - - if (pcount1 != pcount2) { - // They differ in count of key properties -// DEBUG("compare instances: Different count of properties"); - return IM_I_DIFFERENT; - } - int i; - CMPIData data1, data2; - // Check key properties one by one - for (i = 0; i < pcount1; i++) { - data1 = CMGetKeyAt(op1, i, &prop_name, NULL); - data2 = CMGetKey(op2, CMGetCharsPtr(prop_name, NULL), NULL); - // XXX - is state really CMPI_badValue? - if (data2.state == CMPI_badValue || data1.type != data2.type || - !compare_values(&data1.value, &data2.value, data1.type)) { - return IM_I_DIFFERENT; - } - } - // Check all properties one by one - pcount1 = CMGetPropertyCount(i1, NULL); - pcount2 = CMGetPropertyCount(i2, NULL); - if (pcount1 != pcount2) { - return IM_I_DIFFERENT; - } - for (i = 0; i < pcount1; i++) { - data1 = CMGetPropertyAt(i1, i, &prop_name, NULL); - data2 = CMGetProperty(i2, CMGetCharsPtr(prop_name, NULL), NULL); - // XXX - is state really CMPI_badValue? - if (data2.state == CMPI_badValue || data1.type != data2.type) { - return IM_I_DIFFERENT; - } else if (!compare_values(&data1.value, &data2.value, data1.type)) { - return IM_I_CHANGE; - } - } - return IM_I_SAME; -} - - -/* - * Remove (free()) the first polled diff from diff list - * returns nothing - * Does not perform any checks - */ -/* Called with lock held */ -static void remove_diff(IMPolledDiffs *d) -{ - IMPolledDiff *first = d->first; - if (first) { - d->first = first->next; - free (first); - } -} - -/* - * add instance pair to the list of diffs - * First argument is diffs list. - * Returns true when ok, false if not and set IMError - */ -/* Called with lock held */ -static bool add_diff(IMPolledDiffs *d, CMPIInstance *newi, CMPIInstance *oldi, - IMError *err) -{ - IMPolledDiff *nd = malloc(sizeof(IMPolledDiff)); - if (!nd) { - *err = IM_ERR_MALLOC; - return false; - } - nd->inew = newi; - nd->iold = oldi; - nd->next = NULL; - if (!d->first) { - d->first = nd; - } else { - IMPolledDiff *last = d->first; - while (last->next) last = last->next; - last->next = nd; - } - return true; -} - -/* - * Generate diffs based on polled enumerations. - * Generated diff will contain: - * - pair => there is a change - * - old NULL and new value => new instance created - * - old value and new NULL => instance deletion - * Return true when ok, false if not and set IMError - */ -/* Called with lock held */ -static bool gen_diffs(IMManager *manager, IMPolledDiffs *d, IMError *err) -{ - DEBUG("gen diffs called"); - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - if (!manager->enums) { - return false; - } - IMEnumerationPair *pair = manager->enums->first; - while (pair) { -// DEBUG("gen diffs - next pair"); - CMPIArray *penum = pair->prev_enum; - CMPIArray *tenum = pair->this_enum; - if (penum && tenum) { - int pcount = CMGetArrayCount(penum, NULL); - int tcount = CMGetArrayCount(tenum, NULL); - bool p_used[pcount], t_used[tcount]; - bzero(p_used, pcount); - bzero(t_used, tcount); - int pi = 0, ti = 0; - CMPIInstance *newi = NULL; - CMPIInstance *oldi = NULL; - bool do_compare = true; - for (pi = 0; pi < pcount; pi++) { - oldi = CMGetArrayElementAt(penum, pi, NULL).value.inst; - do_compare = true; - for (ti = 0; ti < tcount && do_compare; ti++) { - if (t_used[ti] == true) continue; - newi = CMGetArrayElementAt(tenum, ti, NULL).value.inst; // XXX are them really in same order, on the same index? - switch (compare_instances(oldi, newi)) { - case IM_I_SAME: - DEBUG("gen diffs - same instances"); - do_compare = false; - t_used[ti] = true; - p_used[pi] = true; - break; - case IM_I_CHANGE: - DEBUG("gen diffs - change in instances"); - if (manager->type == IM_IND_MODIFICATION) { - if (!add_diff(d, newi, oldi, err)) { - return false; - } - } - do_compare = false; - t_used[ti] = true; - p_used[pi] = true; - break; - case IM_I_DIFFERENT: - DEBUG("gen diffs - different instances"); - default: break; - } - } - } - if (manager->type == IM_IND_DELETION) { - for (pi = 0; pi < pcount; pi++) { - if (p_used[pi] == false) { - DEBUG("gen diffs - deleted instance"); - if (!add_diff(d, NULL, - CMGetArrayElementAt(penum, pi, NULL).value.inst, - err)) { - return false; - } - } - } - } - if (manager->type == IM_IND_CREATION) { - for (ti = 0; ti < tcount; ti++) { - if (t_used[ti] == false) { - DEBUG("gen diffs - created instance"); - if (!add_diff(d, - CMGetArrayElementAt(tenum, ti, NULL).value.inst, - NULL, err)) { - return false; - } - } - } - } - - } - pair = pair->next; - } - DEBUG("gen diffs returning true"); - return true; -} - - -/* helper functions */ - -/* - * Extract object path from select expression - * returns match for the first occurence of ISA operator - */ -static CMPIObjectPath *get_object_path(const CMPIBroker *broker, CMPISelectExp *se) -{ - CMPISelectCond *sc = CMGetDoc(se, NULL); - CMPICount scount = CMGetSubCondCountAndType(sc,NULL,NULL); - unsigned int i, j; - for (i = 0; i < scount; i++) { - CMPISubCond *subcond = CMGetSubCondAt(sc,i,NULL); - CMPICount pcount = CMGetPredicateCount(subcond,NULL); - for (j = 0; j < pcount; j++) { - CMPIPredicate *pred = CMGetPredicateAt(subcond,j,NULL); - CMPIType type; - CMPIPredOp op; - CMPIString *lhs=NULL; - CMPIString *rhs=NULL; - CMGetPredicateData(pred,&type,&op,&lhs,&rhs); - if (op == CMPI_PredOp_Isa) { - return CMNewObjectPath(broker, "root/cimv2", - CMGetCharsPtr(rhs, NULL), NULL); - } - } - } - return NULL; -} - -// compare 2 object paths, return true when equal, false if not -// equality is set by comparing class names and namespaces -static bool equal_ops(CMPIObjectPath *op1, CMPIObjectPath *op2) -{ - return (strcmp(NS_STR_FROM_OP(op1), NS_STR_FROM_OP(op2)) == 0 && - strcmp(CN_STR_FROM_OP(op1), CN_STR_FROM_OP(op2)) == 0); -} - -// Pair enumerations. This enum will become prev enum and new this enum -// will be created by new obtained by object path -/* Called with lock held */ -static bool pair_enumeration(IMManager *manager, IMEnumerationPair *epair) -{ - CMPIEnumeration *aux_e = CBEnumInstances(manager->broker, - manager->ctx_manage, epair->op, NULL, NULL); - if (!aux_e) { - return false; - } - CMPIArray *new_e = CMClone(CMToArray(aux_e, NULL), NULL); - if (!epair->prev_enum && !epair->this_enum) { - epair->prev_enum = CMClone(new_e, NULL); - epair->this_enum = new_e; - return true; - } - if (epair->prev_enum) { - CMRelease(epair->prev_enum); - } - epair->prev_enum = epair->this_enum; - epair->this_enum = new_e; - return true; -} - -// When the filter is added there is no enum pairs created -// This function will generate pairs where they are missing -/* Called with lock held */ -static bool first_poll(IMManager *manager, IMError *err) -{ - DEBUG("IM first poll called"); - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - if (!manager->enums) { - // Nothing to poll - return true; - } - IMEnumerationPair *epair = NULL; - for (epair = manager->enums->first; epair; epair = epair->next) { - if (epair->this_enum == NULL && epair->prev_enum == NULL) { - if (!pair_enumeration(manager, epair)) { - return false; - } - } - } - return true; -} - -// Try to find enumeration with given object path -// if found increase reference count -// if not found add it to the end of list with ref count = 1 -/* Called with lock held */ -static bool add_enumeration(IMManager *manager, CMPIObjectPath *op, IMError *err) -{ - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - if (!op) { - *err = IM_ERR_OP; - return false; - } - if (!manager->enums) { - if (!(manager->enums = malloc(sizeof(IMEnumerations)))) { - *err = IM_ERR_MALLOC; - return false; - } - manager->enums->first = NULL; - } - IMEnumerationPair *e = manager->enums->first; - IMEnumerationPair *last = NULL; - while (e) { // find last enumeration - if (compare_objectpaths(op, e->op)) { - e->ref_count++; - return true; - } - last = e; - e = e->next; - } - IMEnumerationPair *new_ime = malloc(sizeof(IMEnumerationPair)); - if (!new_ime) { - *err = IM_ERR_MALLOC; - return false; - } - new_ime->next = NULL; - new_ime->op = CMClone(op, NULL); - new_ime->ref_count = 1; - new_ime->prev_enum = NULL; - new_ime->this_enum = NULL; - if (manager->ctx_manage) { - /* Fill the enumeration with values valid at the time the filter was registered and - * only if we're adding new filter when indications have been started already */ - pair_enumeration(manager, new_ime); - } - if (!last) { // there isn't any enumeration yet - manager->enums->first = new_ime; - } else { - last->next = new_ime; - } - return true; -} - -/* Called with lock held */ -static bool _im_poll(IMManager *manager, IMError *err) -{ - DEBUG("IM poll called"); - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - if (!manager->enums) { - // Nothing to poll - DEBUG("IM poll nothing to poll"); - return true; - } - IMEnumerationPair *epair = NULL; - // go thru all enumerations' object path - // Use object path to enumerate instances - DEBUG("IM poll will go thru all filters"); - for (epair = manager->enums->first; epair; epair = epair->next) { - DEBUG("IM poll next enumeration"); - pair_enumeration(manager, epair); - } - DEBUG("IM poll returning true"); - return true; -} - -/* - * Fill ind_inst with properties and send it - */ -static bool send_creation_indication(CMPIInstance *ind_inst, CMPIInstance *new_inst, - IMManager *manager) -{ - DEBUG("Instance creation indication to be send"); - if (!manager || !ind_inst || !new_inst) { - return false; - } - CMPIObjectPath *ind_op = CMGetObjectPath(ind_inst, NULL); - const char *ns = CMGetCharsPtr(CMGetNameSpace(ind_op, NULL), NULL); - CMSetProperty(ind_inst, "SourceInstance", &new_inst, CMPI_instance); - CBDeliverIndication(manager->broker, manager->ctx_manage, ns, ind_inst); - return true; -} - -/* - * Fill ind_inst with properties and send it - */ -static bool send_deletion_indication(CMPIInstance *ind_inst, CMPIInstance *old_inst, - IMManager *manager) -{ - DEBUG("Instance deletion indication to be send"); - if (!manager || !ind_inst || !old_inst) { - return false; - } - CMPIObjectPath *ind_op = CMGetObjectPath(ind_inst, NULL); - const char *ns = CMGetCharsPtr(CMGetNameSpace(ind_op, NULL), NULL); - CMSetProperty(ind_inst, "SourceInstance", &old_inst, CMPI_instance); - CBDeliverIndication(manager->broker, manager->ctx_manage, ns, ind_inst); - return true; -} - -/* - * Fill ind_inst with properties and send it - */ -static bool send_modification_indication(CMPIInstance *ind_inst, CMPIInstance *new, - CMPIInstance *old, IMManager *manager) -{ - DEBUG("Instance modification indication to be send"); - if (!manager || !ind_inst || !new || !old) { - return false; - } - CMPIObjectPath *ind_op = CMGetObjectPath(ind_inst, NULL); - const char *ns = CMGetCharsPtr(CMGetNameSpace(ind_op, NULL), NULL); - CMSetProperty(ind_inst, "SourceInstance", &new, CMPI_instance); - CMSetProperty(ind_inst, "PreviousInstance", &old, CMPI_instance); - CBDeliverIndication(manager->broker, manager->ctx_manage, ns, ind_inst); - return true; -} - -/* - * Will extract class name from select expression - * example: SELECT * FROM LMI_AccountInstCreationIndication WHERE ... - * will return LMI_AccountInstCreationIndication - * returns NULL when not found or on error - * Caller has to free memory. - */ -static char *get_classname(CMPISelectExp *se) -{ - if (!se) { - return NULL; - } -// TODO remove when working -#if 0 - char *select = (char*)CMGetCharsPtr(CMGetSelExpString(se, NULL), NULL); - char *after_from = strcasestr(select, "from") + strlen("from"); - char *start = after_from + strspn(after_from, " "); - char *end = index(start,' '); - char *cname = malloc(end-start+1); - bzero(cname, end-start+1); - return memcpy(cname, start, end-start); -#else - char *select = (char*)CMGetCharsPtr(CMGetSelExpString(se, NULL), NULL); - char *rest = NULL, *token = NULL, *ptr = select; - if (!ptr) { - return NULL; - } - for (; ptr || strcasecmp(token, "from") != 0; ptr = NULL) { - token = strtok_r(ptr, " ", &rest); - if (!token) { - break; - } - } - if (token) { - token = strtok_r(ptr, " ", &rest); - return strdup(token); - } - return NULL; -#endif -} - -/* - * Main send indication function. Will decide by type of indication what - * to send. - * Returns true when everything is OK. False otherwise. - */ -/* Called with lock held */ -static bool send_indication(CMPIInstance *old, CMPIInstance *new, IMManager *manager) -{ - if (!manager) { - return false; - } - - CMPIObjectPath *op = CMNewObjectPath( - manager->broker, - CMGetCharsPtr(CMGetNameSpace(manager->polling ? manager->enums->first->op : CMGetObjectPath(new ? new : old, &im_rc), NULL), NULL), - manager->filters->class_name, - NULL); - DEBUG("Will send indication with this OP: %s", - CMGetCharsPtr(CDToString(manager->broker, op, NULL), NULL)); - CMPIInstance *ind_inst = CMNewInstance(manager->broker, op, NULL); - DEBUG("Will send indication with this instance): %s", - CMGetCharsPtr(CDToString(manager->broker, ind_inst, NULL), NULL), NULL); - DEBUG("Old instance: %p - %s", old, - CMGetCharsPtr(CDToString(manager->broker, old, NULL), NULL), NULL); - DEBUG("New instance: %p - %s", new, - CMGetCharsPtr(CDToString(manager->broker, new, NULL), NULL), NULL); - - CMPIDateTime *date = CMNewDateTimeFromBinary(manager->broker, - time(NULL) * 1000000, - 0, NULL); - CMSetProperty(ind_inst, "IndicationTime", &date, CMPI_dateTime); - - switch (manager->type) { - case IM_IND_CREATION: - if (!new) { - return false; - } - send_creation_indication(ind_inst, new, manager); - break; - case IM_IND_DELETION: - if (!old) { - return false; - } - send_deletion_indication(ind_inst, old, manager); - break; - case IM_IND_MODIFICATION: - if (!old || !new) { - return false; - } - send_modification_indication(ind_inst, new, old, manager); - break; - } - CMRelease(date); - return true; -} - -/* - * Run in separate thread - */ -static IMError manage(IMManager *manager) -{ - IMError err = IM_ERR_OK; // TODO - How to handle potential errors? - IMPolledDiffs diffs = {NULL}; - CMPIInstance *iold = NULL, *inew = NULL; - - while (1) { - DEBUG("manage thread in infinite loop"); - // wait until manager is running - pthread_mutex_lock(&manager->_t_mutex); - if (manager->cancelled) { - pthread_mutex_unlock(&manager->_t_mutex); - return IM_ERR_CANCELLED; - } - while (!manager->running) { - DEBUG("manage thread waiting to indication start"); - pthread_cond_wait(&manager->_t_cond, &manager->_t_mutex); - } - - if (manager->polling) { - if (!first_poll(manager, &err)) { - pthread_mutex_unlock(&manager->_t_mutex); - return err; - } - } - pthread_mutex_unlock(&manager->_t_mutex); - - DEBUG("manage thread calling watcher"); - if(!manager->watcher(&manager->data)) { - continue; - } - pthread_mutex_lock(&manager->_t_mutex); - if (manager->cancelled) { - pthread_mutex_unlock(&manager->_t_mutex); - return IM_ERR_CANCELLED; - } - if (manager->polling) { - // poll enumerations - if (!_im_poll(manager, &err)) { - pthread_mutex_unlock(&manager->_t_mutex); - return err; - } - // create list of diffs - if (!gen_diffs(manager, &diffs, &err)) { - while (diffs.first) - remove_diff(&diffs); - pthread_mutex_unlock(&manager->_t_mutex); - return err; - } - manager->data = &diffs; - } - DEBUG("manage thread calling gather"); - while (manager->gather(manager, &iold, &inew, manager->data)) { - send_indication(iold, inew, manager); - } - pthread_mutex_unlock(&manager->_t_mutex); - } -} - -static void manage_cleanup(void *data) -{ - IMManager *manager = (IMManager *)data; - - DEBUG("manage thread - detaching thread manager context = %p", manager->ctx_manage); - CBDetachThread(manager->broker, manager->ctx_manage); -} - -/* Wrapper thread function, to isolate code control flow due to sensitive pthread_cleanup_ macros */ -static void *manage_wrapper(void *data) -{ - // TODO: Switch Enable/Disable state on some places? - IMManager *manager = (IMManager *)data; - IMError err; - - DEBUG("manage thread started"); - - /* Allow thread cancellation */ - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); - - DEBUG("manage thread - attaching thread manager context = %p", manager->ctx_manage); - CBAttachThread(manager->broker, manager->ctx_manage); - - /* Main execution block closed in a cleanup handler */ - pthread_cleanup_push(manage_cleanup, data); - err = manage(manager); - pthread_cleanup_pop(1); - - DEBUG("manage thread stopped"); - - return (void *)err; -} - -/* - * Default gather function using polling - * It is going thru all polled instances. If there is some pair (previous enum - * and this enum) - */ -/* Called with lock held */ -static bool default_gather (const IMManager *manager, CMPIInstance **old_inst, - CMPIInstance **new_inst, void* data) -{ - // passed data is a list of diffs - // It is going to be called in loop - // return true if now we are going to create indication - // return false if there will be no indication and loop will break - IMPolledDiffs *diffs = (IMPolledDiffs *) data; - if (diffs->first) { - *new_inst = diffs->first->inew; - *old_inst = diffs->first->iold; - remove_diff(diffs); - return true; - } - return false; -} - -/* - * Remove every polled enums - * true if ok, false if not - */ -static bool remove_all_enums(IMManager *manager, IMError *err) -{ - if (!manager->enums) { - return true; - } - IMEnumerationPair *current = manager->enums->first; - IMEnumerationPair *next = NULL; - while (current) { - next = current->next; - CMRelease(current->prev_enum); - CMRelease(current->this_enum); - CMRelease(current->op); - free(current); - current = next; - } - free(manager->enums); - manager->enums = NULL; - return true; -} - -/* - * true if ok - * false if not - */ -static bool remove_all_filters(IMManager *manager, IMError *err) -{ - if (!manager->filters) { - return true; - } - IMFilter *current = manager->filters->first; - IMFilter *next = NULL; - while (current) { - next = current->next; - free(current->select_exp_string); - CMRelease(current->op); - free(current); - current = next; - } - free(manager->filters->class_name); - free(manager->filters); - manager->filters = NULL; - - if (!remove_all_enums(manager, err)) { - return false; - } - - return true; -} - -IMManager* im_create_manager(IMInstGather gather, IMFilterChecker f_checker, - bool polling, IMEventWatcher watcher, - IMIndType type, const CMPIBroker *broker, - IMError *err) -{ - if ((!polling && !gather) || (polling && gather) ) { - *err = IM_ERR_GATHER; - return NULL; - } - if (!watcher) { - *err = IM_ERR_WATCHER; - return NULL; - } - if (!broker) { - *err = IM_ERR_BROKER; - return NULL; - } - IMManager* manager = malloc(sizeof(IMManager)); - if (!manager) { - *err = IM_ERR_MALLOC; - return NULL; - } - manager->type = type; - manager->gather = gather ? gather : default_gather; - manager->watcher = watcher; - manager->filters = NULL; - manager->running = false; - manager->polling = polling; - manager->cancelled = false; - manager->broker = broker; - manager->f_checker = f_checker; - manager->enums = NULL; - manager->ctx_main = NULL; - manager->ctx_manage = NULL; - manager->data = NULL; - DEBUG("Manager created"); - - if (pthread_cond_init(&manager->_t_cond, NULL) || - pthread_mutex_init(&manager->_t_mutex, NULL)) { - *err = IM_ERR_THREAD; - free(manager); - return NULL; - } - - return manager; -} - -bool im_destroy_manager(IMManager *manager, const CMPIContext *ctx, - IMError *err) -{ - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - if (!ctx) { - *err = IM_ERR_CONTEXT; - return false; - } - if (manager->running) - im_stop_ind(manager, ctx, err); - /* No need for locking as all running threads should be stopped at this point */ - if (!remove_all_filters(manager, err)) - return false; - DEBUG("Destroying manager"); - if (pthread_mutex_destroy(&manager->_t_mutex) || - pthread_cond_destroy(&manager->_t_cond)) { - *err = IM_ERR_THREAD; - return false; - } - free(manager); - return true; -} - -static bool default_ind_filter_cb(IMManager *manager, const CMPISelectExp *filter) -{ - /* Looks for a class to be subscribed and matches it against a list of - * allowed classes. Filter query may contain more conditions, only those - * with a ISA operator are looked for. - */ - CMPIStatus st; - unsigned int i; - - CMPISelectCond *sec = CMGetDoc(filter, &st); - if (!sec) - return false; - CMPICount count = CMGetSubCondCountAndType(sec, NULL, &st); - if (count != 1) - return false; - CMPISubCond *sub = CMGetSubCondAt(sec, 0, &st); - if (!sub) - return false; - count = CMGetPredicateCount(sub, &st); - if (count == 0) - return false; - - for (i = 0; i < count; i++) { - CMPIType type; - CMPIPredOp op; - CMPIString *lhs = NULL; - CMPIString *rhs = NULL; - CMPIPredicate *pred = CMGetPredicateAt(sub, i, &st); - if (!pred) - return false; - st = CMGetPredicateData(pred, &type, &op, &lhs, &rhs); - if (st.rc != CMPI_RC_OK || op != CMPI_PredOp_Isa) - continue; - const char *rhs_str = CMGetCharsPtr(rhs, &st); - if (!rhs_str) - continue; - while (manager->f_allowed_classes[i]) { - if (strcasecmp(rhs_str, manager->f_allowed_classes[i++]) == 0) - return true; - } - } - return false; -} - -bool im_verify_filter(IMManager *manager, const CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err) -{ - if (!manager) { - *err = IM_ERR_MANAGER; - return NULL; - } - if (!ctx) { - *err = IM_ERR_CONTEXT; - return NULL; - } - if (!filter) { - *err = IM_ERR_FILTER; - return NULL; - } - if (!manager->f_checker && !manager->f_allowed_classes) { - *err = IM_ERR_FILTER_CHECKER; - return NULL; - } - manager->ctx_main = ctx; - if (manager->f_checker) { - DEBUG("Verifying filter. Manager = %p, filter checker = %p", manager, manager->f_checker); - return manager->f_checker(filter); - } else { - DEBUG("Verifying filter. Manager = %p, using default filter checker", manager); - return default_ind_filter_cb(manager, filter); - } -} - -/* - * Because Pegasus cannot easily clone nor copy select expression - * we need to dig internals and store them directly. - * Return true when ok, or NULL and error set - * appropiately - */ -/* Called with lock held */ -static bool _im_add_filter(IMManager *manager, CMPISelectExp *se, IMError *err) -{ - if (!se) { - *err = IM_ERR_SELECT_EXP; - return false; - } - - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - - CMPIString *str = CMGetSelExpString(se, &im_rc); - if (!str) { - *err = IM_ERR_CMPI_RC; - return false; - } - - const char *query = CMGetCharsPtr(str, &im_rc); - if (!query) { - *err = IM_ERR_CMPI_RC; - return false; - } - char *ses = NULL; - if (!(ses = strdup(query))) { - *err = IM_ERR_MALLOC; - return false; - } - CMPIObjectPath *op = get_object_path(manager->broker, se); - if (!op) { - *err = IM_ERR_SELECT_EXP; - free(ses); - return false; - } - - if(!manager->filters) { - DEBUG("This is the first filter creation"); - IMFilters *filters = malloc(sizeof(IMFilters)); - if (!filters) { - *err = IM_ERR_MALLOC; - CMRelease(op); - free(ses); - return false; - } - if (!(filters->class_name = get_classname(se))) { - *err = IM_ERR_MALLOC; - CMRelease(op); - free(ses); - free(filters); - return false; - } - filters->first = NULL; - manager->filters = filters; - } - - DEBUG("Adding filter to the list"); - IMFilter *current = manager->filters->first; - IMFilter *prev = NULL; - while (current) { - prev = current; - current = current->next; - } - IMFilter *next = malloc(sizeof(IMFilter)); - if (!next) { - *err = IM_ERR_MALLOC; - CMRelease(op); - free(ses); - return false; - } - next->next = NULL; - next->select_exp_string = ses; - next->op = CMClone(op, NULL); - if (prev) { - prev->next = next; - } else { - manager->filters->first = next; - } - CMRelease(op); - - return true; -} - -bool im_add_filter(IMManager *manager, CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err) -{ - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - pthread_mutex_lock(&manager->_t_mutex); - if (!ctx) { - *err = IM_ERR_CONTEXT; - pthread_mutex_unlock(&manager->_t_mutex); - return false; - } - if (!filter) { - *err = IM_ERR_FILTER; - pthread_mutex_unlock(&manager->_t_mutex); - return false; - } - manager->ctx_main = ctx; - DEBUG("Adding filter"); - if (!_im_add_filter(manager, filter, err)) { - pthread_mutex_unlock(&manager->_t_mutex); - return false; - } - if (manager->polling) { - if (!add_enumeration(manager, - get_object_path(manager->broker, filter), - err)) { - pthread_mutex_unlock(&manager->_t_mutex); - return false; - } - } - pthread_mutex_unlock(&manager->_t_mutex); - return true; -} - -bool im_register_filter_classes(IMManager *manager, - const char** allowed_classes, IMError *err) -{ - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - manager->f_allowed_classes = allowed_classes; - return true; -} - -/* - * Decrease reference count for polled enumerations. If count is 0, remove - * the whole enumerations. - * Returns true/false on success/fail and set err. - */ -/* Called with lock held */ -static bool maybe_remove_polled(IMManager *manager, CMPIObjectPath *op, IMError *err) -{ - if (!manager->enums || !manager->enums->first) { - *err = IM_ERR_NOT_FOUND; - return false; - } - IMEnumerationPair *current = manager->enums->first; - IMEnumerationPair *prev = NULL; - while (current) { - if (compare_objectpaths(op, current->op)) { - if (--current->ref_count == 0) { - if (prev) { - prev->next = current->next; - } else { - manager->enums->first = current->next; - } - if (current->prev_enum) - CMRelease(current->prev_enum); - if (current->this_enum) - CMRelease(current->this_enum); - CMRelease(current->op); - } - return true; - } - prev = current; - current = current->next; - } - *err = IM_ERR_NOT_FOUND; - return false; -} - -bool im_remove_filter(IMManager *manager, const CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err) -{ - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - if (!ctx) { - *err = IM_ERR_CONTEXT; - return false; - } - if (!filter) { - *err = IM_ERR_FILTER; - return false; - } - pthread_mutex_lock(&manager->_t_mutex); - if (!manager->filters) { - pthread_mutex_unlock(&manager->_t_mutex); - *err = IM_ERR_NOT_FOUND; - return false; - } - manager->ctx_main = ctx; - DEBUG("Removing filter"); - IMFilter *current = manager->filters->first; - IMFilter *prev = NULL; - while (current) { - if (strcmp(current->select_exp_string, - CMGetCharsPtr(CMGetSelExpString(filter, NULL), NULL)) == 0) { - if (prev) { - prev->next = current->next; - } else { - manager->filters->first = current->next; - } - if (manager->polling && !maybe_remove_polled(manager, current->op, - err)) { - pthread_mutex_unlock(&manager->_t_mutex); - return false; - } - CMRelease(current->op); - free(current->select_exp_string); - free(current); - pthread_mutex_unlock(&manager->_t_mutex); - return true; - } - prev = current; - current = current->next; - } - *err = IM_ERR_NOT_FOUND; - pthread_mutex_unlock(&manager->_t_mutex); - return false; -} - -// start indications -bool im_start_ind(IMManager *manager, const CMPIContext *ctx, IMError *err) -{ - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - if (!ctx) { - *err = IM_ERR_CONTEXT; - return false; - } - if (manager->running) { - DEBUG("WARNING: thread already exists"); - *err = IM_ERR_THREAD; - return false; - } - manager->cancelled = false; - manager->ctx_main = ctx; - manager->ctx_manage = CBPrepareAttachThread(manager->broker, - manager->ctx_main); - DEBUG("Creating second thread"); - if (pthread_create(&manager->_t_manage, NULL, manage_wrapper, manager)) { - *err = IM_ERR_THREAD; - return false; - } - DEBUG("Starting indications"); - manager->running = true; - - pthread_cond_signal(&manager->_t_cond); - return true; -} - -// stop indications -bool im_stop_ind(IMManager *manager, const CMPIContext *ctx, IMError *err) -{ - if (!manager) { - *err = IM_ERR_MANAGER; - return false; - } - if (!ctx) { - *err = IM_ERR_CONTEXT; - return false; - } - manager->ctx_main = ctx; - DEBUG("Stopping indications"); - manager->running = false; - - /* First lock the mutex so we are sure the thread does not hold it. */ - pthread_mutex_lock(&manager->_t_mutex); - /* Then cancel the thread in deferred mode and set a private flag. - * The thread may be doing unlocked stuff that will cancel just fine - * or may be waiting for mutex acquisition where it will cancel and - * unlock right after that using our private flag. - */ - manager->cancelled = true; - /* Note that mutex functions ARE NOT cancellation points! */ - if (pthread_cancel(manager->_t_manage)) { - *err = IM_ERR_THREAD; - pthread_mutex_unlock(&manager->_t_mutex); - return false; - } - - /* Unlock the mutex and give the thread chance to cancel properly. */ - pthread_mutex_unlock(&manager->_t_mutex); - - /* Wait until thread cancellation is finished. */ - if (pthread_join(manager->_t_manage, NULL)) { - *err = IM_ERR_THREAD; - return false; - } - - /* Cleanup */ - remove_all_enums(manager, err); - manager->data = NULL; - manager->ctx_manage = NULL; - manager->cancelled = false; - - return true; -} - diff --git a/src/indmanager/ind_manager.h b/src/indmanager/ind_manager.h deleted file mode 100644 index 4ad2ea1..0000000 --- a/src/indmanager/ind_manager.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: Roman Rakus - */ - -#ifndef __IND_MANAGER_H__ -#define __IND_MANAGER_H__ - -/* - * Limitations: - * * Queries * - * - supports only DMTF:CQL query language - * - select expression has to have ISA predicatea - * - * * Indication types * - * - supports only CIM_InstCreation, CIM_InstDeletion, CIM_InstModification - */ - -#include -#include -#include - -#include - -/* - * Usage: - * 1) Define the following callback functions: - * IMInstGather - * Used only when not using polling - * In this function you should set old and/or new pointers to CMPIInstance. - * These instances will be send as part of indication. - * The table bellow show which pointers have to be set and are used - * indication type old instance new instance - * -------------------------------------------------------- - * IM_IND_CREATION NULL instance w/ new values - * IM_IND_DELETION instance w/ old values NULL - * IM_IND_MODIFICATION instance w/ old values instance w/ new values - * - * You can use data (see below) pointer to pass any data - * - * This function is reentrant. When you return true, the function is called - * again. Return false, when you don't want to be called again. - * - * IMFilterChecker - * In this function implement checking of filter. Passed filter should be - * checked. Return true when filter is allowed, false otherwise. - * - * IMEventWatcher - * This is supposed to be blocking function. Returning the true will inform - * the indication manager that the event has occured. You can set data for - * further processing in IMInstGather. - * - * 2) Create manager object using im_create_manager() during provider init - * 3) Call other functions as necessary - * im_destroy_manager() in provider cleanup - * im_verify_filter() in provider's authorize filter (also in activate filter) - * im_add_filter() in provider's activate filter - * im_remove_filter() in provider's deactivate filter - * im_start_ind() in provider's enable indications - * im_stop_ind() in provider's disable indications - */ - -// forward definitions -typedef struct _IMManager IMManager; - -// callback functions -typedef bool (*IMInstGather) (const IMManager *manager, CMPIInstance **old, CMPIInstance **new, void *data); -typedef bool (*IMFilterChecker) (const CMPISelectExp *filter); -typedef bool (*IMEventWatcher) (void **data); - -// Indication types -// Only one type supported per instance of manager -typedef enum { - // for instance indications - IM_IND_CREATION, // instance creation - IM_IND_DELETION, // instance deletion - IM_IND_MODIFICATION, // instance modification -} IMIndType; - - -/* - * Definition of IMFilter - * Linked list of all filters - */ -typedef struct _IMFilter { - struct _IMFilter *next; - char *select_exp_string; - CMPIObjectPath *op; -} IMFilter; - -typedef struct _IMFilters { - IMFilter *first; - char *class_name; -} IMFilters; - -/* - * Linked list of polled enumeration pairs of instances - * enumerations are converted to arrays - */ -typedef struct _IMEnumerationPair { - struct _IMEnumerationPair *next; - CMPIObjectPath *op; - CMPIArray *prev_enum; - CMPIArray *this_enum; - unsigned ref_count; -} IMEnumerationPair; - -typedef struct _IMEnumerations { - IMEnumerationPair *first; -} IMEnumerations; - -struct _IMManager { - // callback functions - IMEventWatcher watcher; - IMInstGather gather; - IMFilterChecker f_checker; - // filters container - IMFilters *filters; - const char** f_allowed_classes; - // others - IMIndType type; - bool running; - bool polling; - bool cancelled; - const CMPIBroker *broker; - const CMPIContext *ctx_main; /* main thread */ - CMPIContext *ctx_manage; /* manage thread */ - IMEnumerations *enums; - // threading - pthread_t _t_manage; - pthread_mutex_t _t_mutex; - pthread_cond_t _t_cond; - // passed data, used for communication between gather/watcher/etc. - void *data; -}; - -typedef enum { - IM_ERR_OK, - IM_ERR_GATHER, // bad or null gather callback - IM_ERR_FILTER_CHECKER, // bad or null filter checker callback - IM_ERR_WATCHER, // bad or null watcher callback - IM_ERR_MALLOC, // memory allocation error - IM_ERR_FILTER, // bad or null filter - IM_ERR_MANAGER, // bad or null manager - IM_ERR_BROKER, // bad or null broker - IM_ERR_CONTEXT, // bad or null context - IM_ERR_SELECT_EXP, // bad or null select expression - IM_ERR_NOT_FOUND, // specified data were not found - IM_ERR_THREAD, // some error on threading - IM_ERR_CMPI_RC, // CMPI status not OK - IM_ERR_OP, // bad or null object path - IM_ERR_CANCELLED, // job has been cancelled -} IMError; - -// Create manager with given properties and callbacks. -// gather may not be specified if you want to use polling -// Return newly created IMManager or NULL and appropiate IMError is set -IMManager* im_create_manager(IMInstGather gather, IMFilterChecker f_checker, - bool polling, IMEventWatcher watcher, - IMIndType type, const CMPIBroker *broker, - IMError *err); - -// Destroy given manager. -// Return true when ok or false and IMError is set -bool im_destroy_manager(IMManager *manager, const CMPIContext *ctx, - IMError *err); - -// Call verification callback on given manager and filter. -// Return true if filter is verified. False if not. -// IMError will be set to to other value than IM_ERR_OK -// if verification failed -bool im_verify_filter(IMManager *manager, const CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err); - -// add given filter to manager. -// Return true when all is ok, otherwise false and IMError -// is as appropriate. -bool im_add_filter(IMManager *manager, CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err); - -// Remove given filter from manager. -// Return true when removed, false if not and appropriate IMError is set. -bool im_remove_filter(IMManager *manager, const CMPISelectExp *filter, - const CMPIContext *ctx, IMError *err); - -// Register list of classes to be used with a default filter checker -// when callback not given during im_create_manager() -// The allowed_classes array should be kept accessible all the time -bool im_register_filter_classes(IMManager *manager, - const char** allowed_classes, IMError *err); - -// Start indications. -// Return true when correctly started, false if not and -// appropriate IMError is set, -bool im_start_ind(IMManager *manager, const CMPIContext *ctx, IMError *err); - -// Stop indications. -// Return true when correctly stopped, false if not and -// appropriate IMError is set, -bool im_stop_ind(IMManager *manager, const CMPIContext *ctx, IMError *err); - - -#endif /* __IND_MANAGER_H__ */ diff --git a/src/indmanager/openlmiindmanager.pc.in b/src/indmanager/openlmiindmanager.pc.in deleted file mode 100644 index b4d76b5..0000000 --- a/src/indmanager/openlmiindmanager.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/lib@LIB_SUFFIX@ - -Name: openlmiindmanager -Description: OpenLMI indication manager support -Version: @OPENLMIINDMANAGER_VERSION@ -Libs: -L${libdir} -lopenlmiindmanager -CFlags: -I${includedir}/openlmi diff --git a/src/journald/CMakeLists.txt b/src/journald/CMakeLists.txt index 1e6f193..7e956c0 100644 --- a/src/journald/CMakeLists.txt +++ b/src/journald/CMakeLists.txt @@ -30,7 +30,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMPI_INCLUDE_DIR} ${GLIB2_INCLUDE_DIRS} ${SYSTEMD-JOURNAL_INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/src/indmanager + ${CMAKE_SOURCE_DIR}/src/libs/indmanager ) target_link_libraries(${LIBRARY_NAME} diff --git a/src/libs/indmanager/CMakeLists.txt b/src/libs/indmanager/CMakeLists.txt new file mode 100644 index 0000000..3a265c9 --- /dev/null +++ b/src/libs/indmanager/CMakeLists.txt @@ -0,0 +1,19 @@ +add_library(openlmiindmanager SHARED + ind_manager.c +) + +set(OPENLMIINDMANAGER_VERSION_MAJOR 1) +set(OPENLMIINDMANAGER_VERSION_MINOR 0) +set(OPENLMIINDMANAGER_VERSION_PATCH 0) +set(OPENLMIINDMANAGER_VERSION "${OPENLMIINDMANAGER_VERSION_MAJOR}.${OPENLMIINDMANAGER_VERSION_MINOR}.${OPENLMIINDMANAGER_VERSION_PATCH}") + +set_target_properties(openlmiindmanager PROPERTIES VERSION ${OPENLMIINDMANAGER_VERSION}) +set_target_properties(openlmiindmanager PROPERTIES SOVERSION ${OPENLMIINDMANAGER_VERSION_MAJOR}) + +target_link_libraries(openlmiindmanager pthread) + +install(TARGETS openlmiindmanager DESTINATION lib${LIB_SUFFIX}) +install(FILES ind_manager.h DESTINATION include/openlmi) + +configure_file(openlmiindmanager.pc.in ${CMAKE_CURRENT_BINARY_DIR}/openlmiindmanager.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/openlmiindmanager.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) diff --git a/src/libs/indmanager/README b/src/libs/indmanager/README new file mode 100644 index 0000000..4f9654d --- /dev/null +++ b/src/libs/indmanager/README @@ -0,0 +1,142 @@ +###################### +# Indication manager # +###################### + in short IM + +Purpose +~~~~~~~ +Indication manager (in short IM) should help in writing indication provider +using CMPI. Main concept is to lessen work you need to do to only define +few callback functions. + +Usage +~~~~~ +Mainly the user of IM have to write callback functions: IMFilterChecker, +IMEventWatcher. +IM can be used to poll instance. This has to be set upon initialization +and cannot be changed. When not using polling the user have to write also +IMInstGather callback function. +For user are provided the following functions: +im_create_manager, im_destroy_manager, im_verify_filter, im_add_filter, +im_remove_filter, im_start_ind and im_stop_ind. +Following are documentation for functions and callback definitions, +in the following form: +* name of function or callback +C definition +Documentation. + +* im_create_manager +IMManager* im_create_manager(IMInstGather gather, IMFilterChecker f_checker, + bool polling, IMEventWatcher watcher, + IMIndType type, CMPIBroker *broker, IMError *err); +Return newly created reference to IMManager. User should use this reference +only as parameter for functions. User should provide callback functions (see +below), should choose if want to use polling, choose type indications and +provide broker reference. On any error occurance the NULL is returned and +err is set. + +* im_destroy_manager +bool im_destroy_manager(IMManager *manager, const CMPIContext *ctx, + IMError *err); +Destroy the given manager. User should provide context. Returns true when +everything was correctly done, or false and err is set. + +* im_verify_filter +bool im_verify_filter(IMManager *manager, const CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err); +Ask manager to verify the given filter. User should provide context. In turn, +the manager calls IMFilterChecker (see below). If filter is accepted true +is returned, otherwise false is returned and err is set. + +* im_add_filter +bool im_add_filter(IMManager *manager, CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err); +Adds the given filter to the manager. It is logical error to add filter, which +is not verified before. User should provide context. Return true when everything +is correctly done, false otherwise and err is set. + +* im_remove_filter +bool im_remove_filter(IMManager *manager, const CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err); +Removes the given filter from the manager. Returns true when correctly done, +or false and err is set. + +* im_start_ind +bool im_start_ind(IMManager *manager, const CMPIContext *ctx, IMError *err); +Informs the manager to start sending indications. It is recursively calling +IMEventWatcher and IMInstGather (if using polling, the default gather is used). +Returns true if everything started correctly, false otherwise and err is set. +NOTE: Callbacks are called within a thread that can be forcefully interrupted +by the im_stop_ind() call without any chance for cleanup. For that reason, any +dynamically allocated memory will get leaked. Please keep that in mind and use +as much stack memory as possible. If needed, please perform cleanup at the same +place the im_stop_ind() is called. Only one thread is executed within each +indication manager instance, no need for extra thread safety. + +* im_stop_ind +bool im_stop_ind(IMManager *manager, const CMPIContext *ctx, IMError *err); +Informs the manager to stop sending indications. Internally this call cancels +the thread function, see the note above. Returns true when everything is done +correctly, false otherwise and err is set. + +* IMFilterChecker +typedef bool (*IMFilterChecker) (const CMPISelectExp *filter); +User should define this callback, which is called whenever manager is asked +to verify filter. Function should return true when given filter is allowed, +false otherwise. Filter is passed from im_verify_filter function + +* IMEventWatcher +typedef bool (*IMEventWatcher) (void **data); +User should define this callback, which should be blocking. The purpose of this +called is to inform manager that event occured. When the callback returns with +true value the manager is informed that event has occured and will send +indication right after. When the callback returns with false value the manager +is informed that event hasn't occured and will not send indication and will +retart the loop - the callback will be executed again soon. +User can use data to transfer any information from this callback +to IMInstGather. +This is called within a thread and is advised that any blocking call should be +a pthread cancellation point (see pthreads(7)) for easy interruption (see the +note at im_start_ind()). + +* IMInstGather +typedef bool (*IMInstGather) (const IMManager *manager, CMPIInstance **old, + CMPIInstance **new, void *data); +This callback has to be defined only when not using default polling mechanism. +data are passed from IMEventWatcher and are supposed to help creating instances. +User should create and set instance and/or instances. When type of indications +is IM_IND_CREATION the new instance has to be set. When IM_IND_DELETION the old +instance has to be set. Both when IM_IND_MODIFICATION is used. When the callback +returns true the manager will send the indication and call the callback again. +In other words, the return value means "There is another indication +with instances to be send". When false is returned the manager will send +the last indication and starts another round in the loop. + +Cancellation, indications enabling and disabling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Indications should be enabled or disabled in relation with EnableIndications() +and DisableIndications() CIM class method calls. These are called typically when +subscription is added or removed. Note that enabling or disabling indications +multiple times has no effect. + +Polling +~~~~~~~ +IM supports basic polling mechanism. When enabled the IM remembers instances +after every IMEventWatcher call and default IMInstGather callback is used. +Which instances are to be polled are determined by the filters, where +the IM looks for the first occurence of ISA predicate in WHERE clause of select +expression. For example we have filter with expression: +SELECT * FROM LMI_SomeIndication WHERE SourceInstance ISA LMI_Account +the manager will be polling instances from LMI_Account class (from the same +namespace as LMI_SomeIndication). Polling is possible on multiple classes. + +Known limitations +~~~~~~~~~~~~~~~~~ +IM supports only instance indications of the following types: +Creation, Deletion, Modification +and supports only one type per provider. +Settings cannot be changed. + +Example +~~~~~~~ +Is not provided yet. diff --git a/src/libs/indmanager/ind_manager.c b/src/libs/indmanager/ind_manager.c new file mode 100644 index 0000000..7009e3b --- /dev/null +++ b/src/libs/indmanager/ind_manager.c @@ -0,0 +1,1286 @@ +/* + * Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Roman Rakus + */ + +#include "ind_manager.h" + +#include +#include +#include + +#include +#include +#include +#include + +/* Debugging macros */ +#define DEBUG(...) \ + _im_debug(manager->broker, __FILE__, __func__, __LINE__, __VA_ARGS__) +#include + +static inline void _im_debug(const CMPIBroker *b, const char *file, const char *func, + int line, const char *format, ...) +{ + char text[2048] = ""; + snprintf(text, 2047, "%s:%d(%s):", file, line, func); + va_list args; + va_start(args, format); + vsnprintf(text+strlen(text), 2047-strlen(text), format, args); + va_end(args); + CMTraceMessage(b, CMPI_LEV_VERBOSE, "LMI indication manager", text, NULL); +} + +// Get char * representation of name space from object path +#define NS_STR_FROM_OP(op) CMGetCharPtr(CMGetNameSpace((op), NULL)) + +// Get char * representation of class name from object path +#define CN_STR_FROM_OP(op) CMGetCharPtr(CMGetClassName((op), NULL)) + +typedef struct _IMPolledDiff { + CMPIInstance *iold; + CMPIInstance *inew; + struct _IMPolledDiff *next; +} IMPolledDiff; + +typedef struct _IMPolledDiffs { + IMPolledDiff *first; +} IMPolledDiffs; + +typedef enum _IMIDiff { + IM_I_SAME, // Instances are the same + IM_I_DIFFERENT, // Instances are different in key properties or value types + IM_I_CHANGE // Instances are different in non-key properties +} IMIDiff; + +static IMIDiff compare_instances(CMPIInstance *, CMPIInstance *); + +/* CMPI rc handler */ +CMPIStatus im_rc; + +/* + * Compare 2 object paths + * return true if they are same, false otherwise + * is using ft->toString() + */ +static bool compare_objectpaths(CMPIObjectPath *op1, CMPIObjectPath *op2) +{ + return (strcmp(CMGetCharsPtr(CMObjectPathToString(op1, NULL), NULL), + CMGetCharsPtr(CMObjectPathToString(op2, NULL), NULL)) == 0); +} + +/* + * Compare 2 cmpi data + * return true if they are the same, false otherwise + * note: inspired by value.c:sfcb_comp_CMPIValue() from sblim-sfcb + */ +static bool compare_values(CMPIValue *v1, CMPIValue *v2, CMPIType type) +{ +// Checks for NULL, but it is incorrect +#define CHECK_PTRS \ + if (!v1->array && !v2->array) {\ + return true;\ + }\ + if (!v1->array || !v2->array) {\ + return false;\ + }\ + + if (type & CMPI_ARRAY) { + CHECK_PTRS; + CMPICount c = CMGetArrayCount(v1->array, NULL); + if (c != CMGetArrayCount(v2->array, NULL)) { + return false; + } + for (; c > 0; c--) { + CMPIValue val1 = CMGetArrayElementAt(v1->array, c-1, NULL).value; + CMPIValue val2 = CMGetArrayElementAt(v2->array, c-1, NULL).value; + if (!compare_values(&val1, &val2, type & ~CMPI_ARRAY)) { + return false; + } + } + return true; + } + switch (type) { + case CMPI_boolean: + case CMPI_uint8: + case CMPI_sint8: + return (v1->Byte == v2->Byte); + case CMPI_char16: + case CMPI_uint16: + case CMPI_sint16: + return (v1->Short == v2->Short); + case CMPI_uint32: + case CMPI_sint32: + return (v1->Int == v2->Int); + case CMPI_real32: + return (v1->Float == v2->Float); + case CMPI_uint64: + case CMPI_sint64: + return (v1->Long == v2->Long); + case CMPI_real64: + return(v1->Double == v2->Double); + case CMPI_instance: + CHECK_PTRS; + return (compare_instances(v1->inst, v2->inst) == IM_I_SAME); + case CMPI_ref: + CHECK_PTRS; + return (compare_objectpaths(v1->ref, v2->ref)); + case CMPI_string: + CHECK_PTRS; + return (strcmp(CMGetCharsPtr(v1->string, NULL), + CMGetCharsPtr(v2->string, NULL)) == 0); + case CMPI_dateTime: + CHECK_PTRS; + return (strcmp(CMGetCharsPtr( + CMGetStringFormat(v1->dateTime, NULL), NULL), + CMGetCharsPtr( + CMGetStringFormat(v2->dateTime, NULL), NULL)) == 0); + default: + return true; + } + return true; +} + +/* + * Compare 2 instances. + * They can be same, different in key properties or different in non-key props + */ +static IMIDiff compare_instances(CMPIInstance *i1, CMPIInstance *i2) +{ + CMPIString *prop_name = NULL; + // At first compare key properties. They will differ in most cases + // Then compare non-key properties + CMPIObjectPath *op1 = CMGetObjectPath(i1, NULL); + CMPIObjectPath *op2 = CMGetObjectPath(i2, NULL); + int pcount1 = CMGetKeyCount(op1, NULL); + int pcount2 = CMGetKeyCount(op2, NULL); + + if (pcount1 != pcount2) { + // They differ in count of key properties +// DEBUG("compare instances: Different count of properties"); + return IM_I_DIFFERENT; + } + int i; + CMPIData data1, data2; + // Check key properties one by one + for (i = 0; i < pcount1; i++) { + data1 = CMGetKeyAt(op1, i, &prop_name, NULL); + data2 = CMGetKey(op2, CMGetCharsPtr(prop_name, NULL), NULL); + // XXX - is state really CMPI_badValue? + if (data2.state == CMPI_badValue || data1.type != data2.type || + !compare_values(&data1.value, &data2.value, data1.type)) { + return IM_I_DIFFERENT; + } + } + // Check all properties one by one + pcount1 = CMGetPropertyCount(i1, NULL); + pcount2 = CMGetPropertyCount(i2, NULL); + if (pcount1 != pcount2) { + return IM_I_DIFFERENT; + } + for (i = 0; i < pcount1; i++) { + data1 = CMGetPropertyAt(i1, i, &prop_name, NULL); + data2 = CMGetProperty(i2, CMGetCharsPtr(prop_name, NULL), NULL); + // XXX - is state really CMPI_badValue? + if (data2.state == CMPI_badValue || data1.type != data2.type) { + return IM_I_DIFFERENT; + } else if (!compare_values(&data1.value, &data2.value, data1.type)) { + return IM_I_CHANGE; + } + } + return IM_I_SAME; +} + + +/* + * Remove (free()) the first polled diff from diff list + * returns nothing + * Does not perform any checks + */ +/* Called with lock held */ +static void remove_diff(IMPolledDiffs *d) +{ + IMPolledDiff *first = d->first; + if (first) { + d->first = first->next; + free (first); + } +} + +/* + * add instance pair to the list of diffs + * First argument is diffs list. + * Returns true when ok, false if not and set IMError + */ +/* Called with lock held */ +static bool add_diff(IMPolledDiffs *d, CMPIInstance *newi, CMPIInstance *oldi, + IMError *err) +{ + IMPolledDiff *nd = malloc(sizeof(IMPolledDiff)); + if (!nd) { + *err = IM_ERR_MALLOC; + return false; + } + nd->inew = newi; + nd->iold = oldi; + nd->next = NULL; + if (!d->first) { + d->first = nd; + } else { + IMPolledDiff *last = d->first; + while (last->next) last = last->next; + last->next = nd; + } + return true; +} + +/* + * Generate diffs based on polled enumerations. + * Generated diff will contain: + * - pair => there is a change + * - old NULL and new value => new instance created + * - old value and new NULL => instance deletion + * Return true when ok, false if not and set IMError + */ +/* Called with lock held */ +static bool gen_diffs(IMManager *manager, IMPolledDiffs *d, IMError *err) +{ + DEBUG("gen diffs called"); + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + if (!manager->enums) { + return false; + } + IMEnumerationPair *pair = manager->enums->first; + while (pair) { +// DEBUG("gen diffs - next pair"); + CMPIArray *penum = pair->prev_enum; + CMPIArray *tenum = pair->this_enum; + if (penum && tenum) { + int pcount = CMGetArrayCount(penum, NULL); + int tcount = CMGetArrayCount(tenum, NULL); + bool p_used[pcount], t_used[tcount]; + bzero(p_used, pcount); + bzero(t_used, tcount); + int pi = 0, ti = 0; + CMPIInstance *newi = NULL; + CMPIInstance *oldi = NULL; + bool do_compare = true; + for (pi = 0; pi < pcount; pi++) { + oldi = CMGetArrayElementAt(penum, pi, NULL).value.inst; + do_compare = true; + for (ti = 0; ti < tcount && do_compare; ti++) { + if (t_used[ti] == true) continue; + newi = CMGetArrayElementAt(tenum, ti, NULL).value.inst; // XXX are them really in same order, on the same index? + switch (compare_instances(oldi, newi)) { + case IM_I_SAME: + DEBUG("gen diffs - same instances"); + do_compare = false; + t_used[ti] = true; + p_used[pi] = true; + break; + case IM_I_CHANGE: + DEBUG("gen diffs - change in instances"); + if (manager->type == IM_IND_MODIFICATION) { + if (!add_diff(d, newi, oldi, err)) { + return false; + } + } + do_compare = false; + t_used[ti] = true; + p_used[pi] = true; + break; + case IM_I_DIFFERENT: + DEBUG("gen diffs - different instances"); + default: break; + } + } + } + if (manager->type == IM_IND_DELETION) { + for (pi = 0; pi < pcount; pi++) { + if (p_used[pi] == false) { + DEBUG("gen diffs - deleted instance"); + if (!add_diff(d, NULL, + CMGetArrayElementAt(penum, pi, NULL).value.inst, + err)) { + return false; + } + } + } + } + if (manager->type == IM_IND_CREATION) { + for (ti = 0; ti < tcount; ti++) { + if (t_used[ti] == false) { + DEBUG("gen diffs - created instance"); + if (!add_diff(d, + CMGetArrayElementAt(tenum, ti, NULL).value.inst, + NULL, err)) { + return false; + } + } + } + } + + } + pair = pair->next; + } + DEBUG("gen diffs returning true"); + return true; +} + + +/* helper functions */ + +/* + * Extract object path from select expression + * returns match for the first occurence of ISA operator + */ +static CMPIObjectPath *get_object_path(const CMPIBroker *broker, CMPISelectExp *se) +{ + CMPISelectCond *sc = CMGetDoc(se, NULL); + CMPICount scount = CMGetSubCondCountAndType(sc,NULL,NULL); + unsigned int i, j; + for (i = 0; i < scount; i++) { + CMPISubCond *subcond = CMGetSubCondAt(sc,i,NULL); + CMPICount pcount = CMGetPredicateCount(subcond,NULL); + for (j = 0; j < pcount; j++) { + CMPIPredicate *pred = CMGetPredicateAt(subcond,j,NULL); + CMPIType type; + CMPIPredOp op; + CMPIString *lhs=NULL; + CMPIString *rhs=NULL; + CMGetPredicateData(pred,&type,&op,&lhs,&rhs); + if (op == CMPI_PredOp_Isa) { + return CMNewObjectPath(broker, "root/cimv2", + CMGetCharsPtr(rhs, NULL), NULL); + } + } + } + return NULL; +} + +// compare 2 object paths, return true when equal, false if not +// equality is set by comparing class names and namespaces +static bool equal_ops(CMPIObjectPath *op1, CMPIObjectPath *op2) +{ + return (strcmp(NS_STR_FROM_OP(op1), NS_STR_FROM_OP(op2)) == 0 && + strcmp(CN_STR_FROM_OP(op1), CN_STR_FROM_OP(op2)) == 0); +} + +// Pair enumerations. This enum will become prev enum and new this enum +// will be created by new obtained by object path +/* Called with lock held */ +static bool pair_enumeration(IMManager *manager, IMEnumerationPair *epair) +{ + CMPIEnumeration *aux_e = CBEnumInstances(manager->broker, + manager->ctx_manage, epair->op, NULL, NULL); + if (!aux_e) { + return false; + } + CMPIArray *new_e = CMClone(CMToArray(aux_e, NULL), NULL); + if (!epair->prev_enum && !epair->this_enum) { + epair->prev_enum = CMClone(new_e, NULL); + epair->this_enum = new_e; + return true; + } + if (epair->prev_enum) { + CMRelease(epair->prev_enum); + } + epair->prev_enum = epair->this_enum; + epair->this_enum = new_e; + return true; +} + +// When the filter is added there is no enum pairs created +// This function will generate pairs where they are missing +/* Called with lock held */ +static bool first_poll(IMManager *manager, IMError *err) +{ + DEBUG("IM first poll called"); + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + if (!manager->enums) { + // Nothing to poll + return true; + } + IMEnumerationPair *epair = NULL; + for (epair = manager->enums->first; epair; epair = epair->next) { + if (epair->this_enum == NULL && epair->prev_enum == NULL) { + if (!pair_enumeration(manager, epair)) { + return false; + } + } + } + return true; +} + +// Try to find enumeration with given object path +// if found increase reference count +// if not found add it to the end of list with ref count = 1 +/* Called with lock held */ +static bool add_enumeration(IMManager *manager, CMPIObjectPath *op, IMError *err) +{ + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + if (!op) { + *err = IM_ERR_OP; + return false; + } + if (!manager->enums) { + if (!(manager->enums = malloc(sizeof(IMEnumerations)))) { + *err = IM_ERR_MALLOC; + return false; + } + manager->enums->first = NULL; + } + IMEnumerationPair *e = manager->enums->first; + IMEnumerationPair *last = NULL; + while (e) { // find last enumeration + if (compare_objectpaths(op, e->op)) { + e->ref_count++; + return true; + } + last = e; + e = e->next; + } + IMEnumerationPair *new_ime = malloc(sizeof(IMEnumerationPair)); + if (!new_ime) { + *err = IM_ERR_MALLOC; + return false; + } + new_ime->next = NULL; + new_ime->op = CMClone(op, NULL); + new_ime->ref_count = 1; + new_ime->prev_enum = NULL; + new_ime->this_enum = NULL; + if (manager->ctx_manage) { + /* Fill the enumeration with values valid at the time the filter was registered and + * only if we're adding new filter when indications have been started already */ + pair_enumeration(manager, new_ime); + } + if (!last) { // there isn't any enumeration yet + manager->enums->first = new_ime; + } else { + last->next = new_ime; + } + return true; +} + +/* Called with lock held */ +static bool _im_poll(IMManager *manager, IMError *err) +{ + DEBUG("IM poll called"); + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + if (!manager->enums) { + // Nothing to poll + DEBUG("IM poll nothing to poll"); + return true; + } + IMEnumerationPair *epair = NULL; + // go thru all enumerations' object path + // Use object path to enumerate instances + DEBUG("IM poll will go thru all filters"); + for (epair = manager->enums->first; epair; epair = epair->next) { + DEBUG("IM poll next enumeration"); + pair_enumeration(manager, epair); + } + DEBUG("IM poll returning true"); + return true; +} + +/* + * Fill ind_inst with properties and send it + */ +static bool send_creation_indication(CMPIInstance *ind_inst, CMPIInstance *new_inst, + IMManager *manager) +{ + DEBUG("Instance creation indication to be send"); + if (!manager || !ind_inst || !new_inst) { + return false; + } + CMPIObjectPath *ind_op = CMGetObjectPath(ind_inst, NULL); + const char *ns = CMGetCharsPtr(CMGetNameSpace(ind_op, NULL), NULL); + CMSetProperty(ind_inst, "SourceInstance", &new_inst, CMPI_instance); + CBDeliverIndication(manager->broker, manager->ctx_manage, ns, ind_inst); + return true; +} + +/* + * Fill ind_inst with properties and send it + */ +static bool send_deletion_indication(CMPIInstance *ind_inst, CMPIInstance *old_inst, + IMManager *manager) +{ + DEBUG("Instance deletion indication to be send"); + if (!manager || !ind_inst || !old_inst) { + return false; + } + CMPIObjectPath *ind_op = CMGetObjectPath(ind_inst, NULL); + const char *ns = CMGetCharsPtr(CMGetNameSpace(ind_op, NULL), NULL); + CMSetProperty(ind_inst, "SourceInstance", &old_inst, CMPI_instance); + CBDeliverIndication(manager->broker, manager->ctx_manage, ns, ind_inst); + return true; +} + +/* + * Fill ind_inst with properties and send it + */ +static bool send_modification_indication(CMPIInstance *ind_inst, CMPIInstance *new, + CMPIInstance *old, IMManager *manager) +{ + DEBUG("Instance modification indication to be send"); + if (!manager || !ind_inst || !new || !old) { + return false; + } + CMPIObjectPath *ind_op = CMGetObjectPath(ind_inst, NULL); + const char *ns = CMGetCharsPtr(CMGetNameSpace(ind_op, NULL), NULL); + CMSetProperty(ind_inst, "SourceInstance", &new, CMPI_instance); + CMSetProperty(ind_inst, "PreviousInstance", &old, CMPI_instance); + CBDeliverIndication(manager->broker, manager->ctx_manage, ns, ind_inst); + return true; +} + +/* + * Will extract class name from select expression + * example: SELECT * FROM LMI_AccountInstCreationIndication WHERE ... + * will return LMI_AccountInstCreationIndication + * returns NULL when not found or on error + * Caller has to free memory. + */ +static char *get_classname(CMPISelectExp *se) +{ + if (!se) { + return NULL; + } +// TODO remove when working +#if 0 + char *select = (char*)CMGetCharsPtr(CMGetSelExpString(se, NULL), NULL); + char *after_from = strcasestr(select, "from") + strlen("from"); + char *start = after_from + strspn(after_from, " "); + char *end = index(start,' '); + char *cname = malloc(end-start+1); + bzero(cname, end-start+1); + return memcpy(cname, start, end-start); +#else + char *select = (char*)CMGetCharsPtr(CMGetSelExpString(se, NULL), NULL); + char *rest = NULL, *token = NULL, *ptr = select; + if (!ptr) { + return NULL; + } + for (; ptr || strcasecmp(token, "from") != 0; ptr = NULL) { + token = strtok_r(ptr, " ", &rest); + if (!token) { + break; + } + } + if (token) { + token = strtok_r(ptr, " ", &rest); + return strdup(token); + } + return NULL; +#endif +} + +/* + * Main send indication function. Will decide by type of indication what + * to send. + * Returns true when everything is OK. False otherwise. + */ +/* Called with lock held */ +static bool send_indication(CMPIInstance *old, CMPIInstance *new, IMManager *manager) +{ + if (!manager) { + return false; + } + + CMPIObjectPath *op = CMNewObjectPath( + manager->broker, + CMGetCharsPtr(CMGetNameSpace(manager->polling ? manager->enums->first->op : CMGetObjectPath(new ? new : old, &im_rc), NULL), NULL), + manager->filters->class_name, + NULL); + DEBUG("Will send indication with this OP: %s", + CMGetCharsPtr(CDToString(manager->broker, op, NULL), NULL)); + CMPIInstance *ind_inst = CMNewInstance(manager->broker, op, NULL); + DEBUG("Will send indication with this instance): %s", + CMGetCharsPtr(CDToString(manager->broker, ind_inst, NULL), NULL), NULL); + DEBUG("Old instance: %p - %s", old, + CMGetCharsPtr(CDToString(manager->broker, old, NULL), NULL), NULL); + DEBUG("New instance: %p - %s", new, + CMGetCharsPtr(CDToString(manager->broker, new, NULL), NULL), NULL); + + CMPIDateTime *date = CMNewDateTimeFromBinary(manager->broker, + time(NULL) * 1000000, + 0, NULL); + CMSetProperty(ind_inst, "IndicationTime", &date, CMPI_dateTime); + + switch (manager->type) { + case IM_IND_CREATION: + if (!new) { + return false; + } + send_creation_indication(ind_inst, new, manager); + break; + case IM_IND_DELETION: + if (!old) { + return false; + } + send_deletion_indication(ind_inst, old, manager); + break; + case IM_IND_MODIFICATION: + if (!old || !new) { + return false; + } + send_modification_indication(ind_inst, new, old, manager); + break; + } + CMRelease(date); + return true; +} + +/* + * Run in separate thread + */ +static IMError manage(IMManager *manager) +{ + IMError err = IM_ERR_OK; // TODO - How to handle potential errors? + IMPolledDiffs diffs = {NULL}; + CMPIInstance *iold = NULL, *inew = NULL; + + while (1) { + DEBUG("manage thread in infinite loop"); + // wait until manager is running + pthread_mutex_lock(&manager->_t_mutex); + if (manager->cancelled) { + pthread_mutex_unlock(&manager->_t_mutex); + return IM_ERR_CANCELLED; + } + while (!manager->running) { + DEBUG("manage thread waiting to indication start"); + pthread_cond_wait(&manager->_t_cond, &manager->_t_mutex); + } + + if (manager->polling) { + if (!first_poll(manager, &err)) { + pthread_mutex_unlock(&manager->_t_mutex); + return err; + } + } + pthread_mutex_unlock(&manager->_t_mutex); + + DEBUG("manage thread calling watcher"); + if(!manager->watcher(&manager->data)) { + continue; + } + pthread_mutex_lock(&manager->_t_mutex); + if (manager->cancelled) { + pthread_mutex_unlock(&manager->_t_mutex); + return IM_ERR_CANCELLED; + } + if (manager->polling) { + // poll enumerations + if (!_im_poll(manager, &err)) { + pthread_mutex_unlock(&manager->_t_mutex); + return err; + } + // create list of diffs + if (!gen_diffs(manager, &diffs, &err)) { + while (diffs.first) + remove_diff(&diffs); + pthread_mutex_unlock(&manager->_t_mutex); + return err; + } + manager->data = &diffs; + } + DEBUG("manage thread calling gather"); + while (manager->gather(manager, &iold, &inew, manager->data)) { + send_indication(iold, inew, manager); + } + pthread_mutex_unlock(&manager->_t_mutex); + } +} + +static void manage_cleanup(void *data) +{ + IMManager *manager = (IMManager *)data; + + DEBUG("manage thread - detaching thread manager context = %p", manager->ctx_manage); + CBDetachThread(manager->broker, manager->ctx_manage); +} + +/* Wrapper thread function, to isolate code control flow due to sensitive pthread_cleanup_ macros */ +static void *manage_wrapper(void *data) +{ + // TODO: Switch Enable/Disable state on some places? + IMManager *manager = (IMManager *)data; + IMError err; + + DEBUG("manage thread started"); + + /* Allow thread cancellation */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + DEBUG("manage thread - attaching thread manager context = %p", manager->ctx_manage); + CBAttachThread(manager->broker, manager->ctx_manage); + + /* Main execution block closed in a cleanup handler */ + pthread_cleanup_push(manage_cleanup, data); + err = manage(manager); + pthread_cleanup_pop(1); + + DEBUG("manage thread stopped"); + + return (void *)err; +} + +/* + * Default gather function using polling + * It is going thru all polled instances. If there is some pair (previous enum + * and this enum) + */ +/* Called with lock held */ +static bool default_gather (const IMManager *manager, CMPIInstance **old_inst, + CMPIInstance **new_inst, void* data) +{ + // passed data is a list of diffs + // It is going to be called in loop + // return true if now we are going to create indication + // return false if there will be no indication and loop will break + IMPolledDiffs *diffs = (IMPolledDiffs *) data; + if (diffs->first) { + *new_inst = diffs->first->inew; + *old_inst = diffs->first->iold; + remove_diff(diffs); + return true; + } + return false; +} + +/* + * Remove every polled enums + * true if ok, false if not + */ +static bool remove_all_enums(IMManager *manager, IMError *err) +{ + if (!manager->enums) { + return true; + } + IMEnumerationPair *current = manager->enums->first; + IMEnumerationPair *next = NULL; + while (current) { + next = current->next; + CMRelease(current->prev_enum); + CMRelease(current->this_enum); + CMRelease(current->op); + free(current); + current = next; + } + free(manager->enums); + manager->enums = NULL; + return true; +} + +/* + * true if ok + * false if not + */ +static bool remove_all_filters(IMManager *manager, IMError *err) +{ + if (!manager->filters) { + return true; + } + IMFilter *current = manager->filters->first; + IMFilter *next = NULL; + while (current) { + next = current->next; + free(current->select_exp_string); + CMRelease(current->op); + free(current); + current = next; + } + free(manager->filters->class_name); + free(manager->filters); + manager->filters = NULL; + + if (!remove_all_enums(manager, err)) { + return false; + } + + return true; +} + +IMManager* im_create_manager(IMInstGather gather, IMFilterChecker f_checker, + bool polling, IMEventWatcher watcher, + IMIndType type, const CMPIBroker *broker, + IMError *err) +{ + if ((!polling && !gather) || (polling && gather) ) { + *err = IM_ERR_GATHER; + return NULL; + } + if (!watcher) { + *err = IM_ERR_WATCHER; + return NULL; + } + if (!broker) { + *err = IM_ERR_BROKER; + return NULL; + } + IMManager* manager = malloc(sizeof(IMManager)); + if (!manager) { + *err = IM_ERR_MALLOC; + return NULL; + } + manager->type = type; + manager->gather = gather ? gather : default_gather; + manager->watcher = watcher; + manager->filters = NULL; + manager->running = false; + manager->polling = polling; + manager->cancelled = false; + manager->broker = broker; + manager->f_checker = f_checker; + manager->enums = NULL; + manager->ctx_main = NULL; + manager->ctx_manage = NULL; + manager->data = NULL; + DEBUG("Manager created"); + + if (pthread_cond_init(&manager->_t_cond, NULL) || + pthread_mutex_init(&manager->_t_mutex, NULL)) { + *err = IM_ERR_THREAD; + free(manager); + return NULL; + } + + return manager; +} + +bool im_destroy_manager(IMManager *manager, const CMPIContext *ctx, + IMError *err) +{ + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + if (!ctx) { + *err = IM_ERR_CONTEXT; + return false; + } + if (manager->running) + im_stop_ind(manager, ctx, err); + /* No need for locking as all running threads should be stopped at this point */ + if (!remove_all_filters(manager, err)) + return false; + DEBUG("Destroying manager"); + if (pthread_mutex_destroy(&manager->_t_mutex) || + pthread_cond_destroy(&manager->_t_cond)) { + *err = IM_ERR_THREAD; + return false; + } + free(manager); + return true; +} + +static bool default_ind_filter_cb(IMManager *manager, const CMPISelectExp *filter) +{ + /* Looks for a class to be subscribed and matches it against a list of + * allowed classes. Filter query may contain more conditions, only those + * with a ISA operator are looked for. + */ + CMPIStatus st; + unsigned int i; + + CMPISelectCond *sec = CMGetDoc(filter, &st); + if (!sec) + return false; + CMPICount count = CMGetSubCondCountAndType(sec, NULL, &st); + if (count != 1) + return false; + CMPISubCond *sub = CMGetSubCondAt(sec, 0, &st); + if (!sub) + return false; + count = CMGetPredicateCount(sub, &st); + if (count == 0) + return false; + + for (i = 0; i < count; i++) { + CMPIType type; + CMPIPredOp op; + CMPIString *lhs = NULL; + CMPIString *rhs = NULL; + CMPIPredicate *pred = CMGetPredicateAt(sub, i, &st); + if (!pred) + return false; + st = CMGetPredicateData(pred, &type, &op, &lhs, &rhs); + if (st.rc != CMPI_RC_OK || op != CMPI_PredOp_Isa) + continue; + const char *rhs_str = CMGetCharsPtr(rhs, &st); + if (!rhs_str) + continue; + while (manager->f_allowed_classes[i]) { + if (strcasecmp(rhs_str, manager->f_allowed_classes[i++]) == 0) + return true; + } + } + return false; +} + +bool im_verify_filter(IMManager *manager, const CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err) +{ + if (!manager) { + *err = IM_ERR_MANAGER; + return NULL; + } + if (!ctx) { + *err = IM_ERR_CONTEXT; + return NULL; + } + if (!filter) { + *err = IM_ERR_FILTER; + return NULL; + } + if (!manager->f_checker && !manager->f_allowed_classes) { + *err = IM_ERR_FILTER_CHECKER; + return NULL; + } + manager->ctx_main = ctx; + if (manager->f_checker) { + DEBUG("Verifying filter. Manager = %p, filter checker = %p", manager, manager->f_checker); + return manager->f_checker(filter); + } else { + DEBUG("Verifying filter. Manager = %p, using default filter checker", manager); + return default_ind_filter_cb(manager, filter); + } +} + +/* + * Because Pegasus cannot easily clone nor copy select expression + * we need to dig internals and store them directly. + * Return true when ok, or NULL and error set + * appropiately + */ +/* Called with lock held */ +static bool _im_add_filter(IMManager *manager, CMPISelectExp *se, IMError *err) +{ + if (!se) { + *err = IM_ERR_SELECT_EXP; + return false; + } + + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + + CMPIString *str = CMGetSelExpString(se, &im_rc); + if (!str) { + *err = IM_ERR_CMPI_RC; + return false; + } + + const char *query = CMGetCharsPtr(str, &im_rc); + if (!query) { + *err = IM_ERR_CMPI_RC; + return false; + } + char *ses = NULL; + if (!(ses = strdup(query))) { + *err = IM_ERR_MALLOC; + return false; + } + CMPIObjectPath *op = get_object_path(manager->broker, se); + if (!op) { + *err = IM_ERR_SELECT_EXP; + free(ses); + return false; + } + + if(!manager->filters) { + DEBUG("This is the first filter creation"); + IMFilters *filters = malloc(sizeof(IMFilters)); + if (!filters) { + *err = IM_ERR_MALLOC; + CMRelease(op); + free(ses); + return false; + } + if (!(filters->class_name = get_classname(se))) { + *err = IM_ERR_MALLOC; + CMRelease(op); + free(ses); + free(filters); + return false; + } + filters->first = NULL; + manager->filters = filters; + } + + DEBUG("Adding filter to the list"); + IMFilter *current = manager->filters->first; + IMFilter *prev = NULL; + while (current) { + prev = current; + current = current->next; + } + IMFilter *next = malloc(sizeof(IMFilter)); + if (!next) { + *err = IM_ERR_MALLOC; + CMRelease(op); + free(ses); + return false; + } + next->next = NULL; + next->select_exp_string = ses; + next->op = CMClone(op, NULL); + if (prev) { + prev->next = next; + } else { + manager->filters->first = next; + } + CMRelease(op); + + return true; +} + +bool im_add_filter(IMManager *manager, CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err) +{ + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + pthread_mutex_lock(&manager->_t_mutex); + if (!ctx) { + *err = IM_ERR_CONTEXT; + pthread_mutex_unlock(&manager->_t_mutex); + return false; + } + if (!filter) { + *err = IM_ERR_FILTER; + pthread_mutex_unlock(&manager->_t_mutex); + return false; + } + manager->ctx_main = ctx; + DEBUG("Adding filter"); + if (!_im_add_filter(manager, filter, err)) { + pthread_mutex_unlock(&manager->_t_mutex); + return false; + } + if (manager->polling) { + if (!add_enumeration(manager, + get_object_path(manager->broker, filter), + err)) { + pthread_mutex_unlock(&manager->_t_mutex); + return false; + } + } + pthread_mutex_unlock(&manager->_t_mutex); + return true; +} + +bool im_register_filter_classes(IMManager *manager, + const char** allowed_classes, IMError *err) +{ + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + manager->f_allowed_classes = allowed_classes; + return true; +} + +/* + * Decrease reference count for polled enumerations. If count is 0, remove + * the whole enumerations. + * Returns true/false on success/fail and set err. + */ +/* Called with lock held */ +static bool maybe_remove_polled(IMManager *manager, CMPIObjectPath *op, IMError *err) +{ + if (!manager->enums || !manager->enums->first) { + *err = IM_ERR_NOT_FOUND; + return false; + } + IMEnumerationPair *current = manager->enums->first; + IMEnumerationPair *prev = NULL; + while (current) { + if (compare_objectpaths(op, current->op)) { + if (--current->ref_count == 0) { + if (prev) { + prev->next = current->next; + } else { + manager->enums->first = current->next; + } + if (current->prev_enum) + CMRelease(current->prev_enum); + if (current->this_enum) + CMRelease(current->this_enum); + CMRelease(current->op); + } + return true; + } + prev = current; + current = current->next; + } + *err = IM_ERR_NOT_FOUND; + return false; +} + +bool im_remove_filter(IMManager *manager, const CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err) +{ + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + if (!ctx) { + *err = IM_ERR_CONTEXT; + return false; + } + if (!filter) { + *err = IM_ERR_FILTER; + return false; + } + pthread_mutex_lock(&manager->_t_mutex); + if (!manager->filters) { + pthread_mutex_unlock(&manager->_t_mutex); + *err = IM_ERR_NOT_FOUND; + return false; + } + manager->ctx_main = ctx; + DEBUG("Removing filter"); + IMFilter *current = manager->filters->first; + IMFilter *prev = NULL; + while (current) { + if (strcmp(current->select_exp_string, + CMGetCharsPtr(CMGetSelExpString(filter, NULL), NULL)) == 0) { + if (prev) { + prev->next = current->next; + } else { + manager->filters->first = current->next; + } + if (manager->polling && !maybe_remove_polled(manager, current->op, + err)) { + pthread_mutex_unlock(&manager->_t_mutex); + return false; + } + CMRelease(current->op); + free(current->select_exp_string); + free(current); + pthread_mutex_unlock(&manager->_t_mutex); + return true; + } + prev = current; + current = current->next; + } + *err = IM_ERR_NOT_FOUND; + pthread_mutex_unlock(&manager->_t_mutex); + return false; +} + +// start indications +bool im_start_ind(IMManager *manager, const CMPIContext *ctx, IMError *err) +{ + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + if (!ctx) { + *err = IM_ERR_CONTEXT; + return false; + } + if (manager->running) { + DEBUG("WARNING: thread already exists"); + *err = IM_ERR_THREAD; + return false; + } + manager->cancelled = false; + manager->ctx_main = ctx; + manager->ctx_manage = CBPrepareAttachThread(manager->broker, + manager->ctx_main); + DEBUG("Creating second thread"); + if (pthread_create(&manager->_t_manage, NULL, manage_wrapper, manager)) { + *err = IM_ERR_THREAD; + return false; + } + DEBUG("Starting indications"); + manager->running = true; + + pthread_cond_signal(&manager->_t_cond); + return true; +} + +// stop indications +bool im_stop_ind(IMManager *manager, const CMPIContext *ctx, IMError *err) +{ + if (!manager) { + *err = IM_ERR_MANAGER; + return false; + } + if (!ctx) { + *err = IM_ERR_CONTEXT; + return false; + } + manager->ctx_main = ctx; + DEBUG("Stopping indications"); + manager->running = false; + + /* First lock the mutex so we are sure the thread does not hold it. */ + pthread_mutex_lock(&manager->_t_mutex); + /* Then cancel the thread in deferred mode and set a private flag. + * The thread may be doing unlocked stuff that will cancel just fine + * or may be waiting for mutex acquisition where it will cancel and + * unlock right after that using our private flag. + */ + manager->cancelled = true; + /* Note that mutex functions ARE NOT cancellation points! */ + if (pthread_cancel(manager->_t_manage)) { + *err = IM_ERR_THREAD; + pthread_mutex_unlock(&manager->_t_mutex); + return false; + } + + /* Unlock the mutex and give the thread chance to cancel properly. */ + pthread_mutex_unlock(&manager->_t_mutex); + + /* Wait until thread cancellation is finished. */ + if (pthread_join(manager->_t_manage, NULL)) { + *err = IM_ERR_THREAD; + return false; + } + + /* Cleanup */ + remove_all_enums(manager, err); + manager->data = NULL; + manager->ctx_manage = NULL; + manager->cancelled = false; + + return true; +} + diff --git a/src/libs/indmanager/ind_manager.h b/src/libs/indmanager/ind_manager.h new file mode 100644 index 0000000..4ad2ea1 --- /dev/null +++ b/src/libs/indmanager/ind_manager.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: Roman Rakus + */ + +#ifndef __IND_MANAGER_H__ +#define __IND_MANAGER_H__ + +/* + * Limitations: + * * Queries * + * - supports only DMTF:CQL query language + * - select expression has to have ISA predicatea + * + * * Indication types * + * - supports only CIM_InstCreation, CIM_InstDeletion, CIM_InstModification + */ + +#include +#include +#include + +#include + +/* + * Usage: + * 1) Define the following callback functions: + * IMInstGather + * Used only when not using polling + * In this function you should set old and/or new pointers to CMPIInstance. + * These instances will be send as part of indication. + * The table bellow show which pointers have to be set and are used + * indication type old instance new instance + * -------------------------------------------------------- + * IM_IND_CREATION NULL instance w/ new values + * IM_IND_DELETION instance w/ old values NULL + * IM_IND_MODIFICATION instance w/ old values instance w/ new values + * + * You can use data (see below) pointer to pass any data + * + * This function is reentrant. When you return true, the function is called + * again. Return false, when you don't want to be called again. + * + * IMFilterChecker + * In this function implement checking of filter. Passed filter should be + * checked. Return true when filter is allowed, false otherwise. + * + * IMEventWatcher + * This is supposed to be blocking function. Returning the true will inform + * the indication manager that the event has occured. You can set data for + * further processing in IMInstGather. + * + * 2) Create manager object using im_create_manager() during provider init + * 3) Call other functions as necessary + * im_destroy_manager() in provider cleanup + * im_verify_filter() in provider's authorize filter (also in activate filter) + * im_add_filter() in provider's activate filter + * im_remove_filter() in provider's deactivate filter + * im_start_ind() in provider's enable indications + * im_stop_ind() in provider's disable indications + */ + +// forward definitions +typedef struct _IMManager IMManager; + +// callback functions +typedef bool (*IMInstGather) (const IMManager *manager, CMPIInstance **old, CMPIInstance **new, void *data); +typedef bool (*IMFilterChecker) (const CMPISelectExp *filter); +typedef bool (*IMEventWatcher) (void **data); + +// Indication types +// Only one type supported per instance of manager +typedef enum { + // for instance indications + IM_IND_CREATION, // instance creation + IM_IND_DELETION, // instance deletion + IM_IND_MODIFICATION, // instance modification +} IMIndType; + + +/* + * Definition of IMFilter + * Linked list of all filters + */ +typedef struct _IMFilter { + struct _IMFilter *next; + char *select_exp_string; + CMPIObjectPath *op; +} IMFilter; + +typedef struct _IMFilters { + IMFilter *first; + char *class_name; +} IMFilters; + +/* + * Linked list of polled enumeration pairs of instances + * enumerations are converted to arrays + */ +typedef struct _IMEnumerationPair { + struct _IMEnumerationPair *next; + CMPIObjectPath *op; + CMPIArray *prev_enum; + CMPIArray *this_enum; + unsigned ref_count; +} IMEnumerationPair; + +typedef struct _IMEnumerations { + IMEnumerationPair *first; +} IMEnumerations; + +struct _IMManager { + // callback functions + IMEventWatcher watcher; + IMInstGather gather; + IMFilterChecker f_checker; + // filters container + IMFilters *filters; + const char** f_allowed_classes; + // others + IMIndType type; + bool running; + bool polling; + bool cancelled; + const CMPIBroker *broker; + const CMPIContext *ctx_main; /* main thread */ + CMPIContext *ctx_manage; /* manage thread */ + IMEnumerations *enums; + // threading + pthread_t _t_manage; + pthread_mutex_t _t_mutex; + pthread_cond_t _t_cond; + // passed data, used for communication between gather/watcher/etc. + void *data; +}; + +typedef enum { + IM_ERR_OK, + IM_ERR_GATHER, // bad or null gather callback + IM_ERR_FILTER_CHECKER, // bad or null filter checker callback + IM_ERR_WATCHER, // bad or null watcher callback + IM_ERR_MALLOC, // memory allocation error + IM_ERR_FILTER, // bad or null filter + IM_ERR_MANAGER, // bad or null manager + IM_ERR_BROKER, // bad or null broker + IM_ERR_CONTEXT, // bad or null context + IM_ERR_SELECT_EXP, // bad or null select expression + IM_ERR_NOT_FOUND, // specified data were not found + IM_ERR_THREAD, // some error on threading + IM_ERR_CMPI_RC, // CMPI status not OK + IM_ERR_OP, // bad or null object path + IM_ERR_CANCELLED, // job has been cancelled +} IMError; + +// Create manager with given properties and callbacks. +// gather may not be specified if you want to use polling +// Return newly created IMManager or NULL and appropiate IMError is set +IMManager* im_create_manager(IMInstGather gather, IMFilterChecker f_checker, + bool polling, IMEventWatcher watcher, + IMIndType type, const CMPIBroker *broker, + IMError *err); + +// Destroy given manager. +// Return true when ok or false and IMError is set +bool im_destroy_manager(IMManager *manager, const CMPIContext *ctx, + IMError *err); + +// Call verification callback on given manager and filter. +// Return true if filter is verified. False if not. +// IMError will be set to to other value than IM_ERR_OK +// if verification failed +bool im_verify_filter(IMManager *manager, const CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err); + +// add given filter to manager. +// Return true when all is ok, otherwise false and IMError +// is as appropriate. +bool im_add_filter(IMManager *manager, CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err); + +// Remove given filter from manager. +// Return true when removed, false if not and appropriate IMError is set. +bool im_remove_filter(IMManager *manager, const CMPISelectExp *filter, + const CMPIContext *ctx, IMError *err); + +// Register list of classes to be used with a default filter checker +// when callback not given during im_create_manager() +// The allowed_classes array should be kept accessible all the time +bool im_register_filter_classes(IMManager *manager, + const char** allowed_classes, IMError *err); + +// Start indications. +// Return true when correctly started, false if not and +// appropriate IMError is set, +bool im_start_ind(IMManager *manager, const CMPIContext *ctx, IMError *err); + +// Stop indications. +// Return true when correctly stopped, false if not and +// appropriate IMError is set, +bool im_stop_ind(IMManager *manager, const CMPIContext *ctx, IMError *err); + + +#endif /* __IND_MANAGER_H__ */ diff --git a/src/libs/indmanager/openlmiindmanager.pc.in b/src/libs/indmanager/openlmiindmanager.pc.in new file mode 100644 index 0000000..b4d76b5 --- /dev/null +++ b/src/libs/indmanager/openlmiindmanager.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib@LIB_SUFFIX@ + +Name: openlmiindmanager +Description: OpenLMI indication manager support +Version: @OPENLMIINDMANAGER_VERSION@ +Libs: -L${libdir} -lopenlmiindmanager +CFlags: -I${includedir}/openlmi diff --git a/src/service-dbus/CMakeLists.txt b/src/service-dbus/CMakeLists.txt index 4ba3231..60a3d8a 100644 --- a/src/service-dbus/CMakeLists.txt +++ b/src/service-dbus/CMakeLists.txt @@ -26,7 +26,7 @@ pkg_check_modules(GIO REQUIRED gio-2.0) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMPI_INCLUDE_DIR} ${GIO_INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/src/indmanager) + ${CMAKE_SOURCE_DIR}/src/libs/indmanager) target_link_libraries(${LIBRARY_NAME} openlmicommon openlmiindmanager -- cgit