/* * Copyright (C) 2013 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); CMLogMessage(b, CMPI_DEV_DEBUG, "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; IMIDiff compare_instances(CMPIInstance *, CMPIInstance *); bool maybe_remove_polled(IMManager *, CMPIObjectPath *, IMError *); /* For threading purposes */ pthread_t _t_manage; pthread_mutex_t _t_mutex; pthread_cond_t _t_cond; /* CMPI rc handler */ CMPIStatus im_rc; /* * Compare 2 object paths * return true if they are same, false otherwise * is using ft->toString() */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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); } // 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 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 (!last) { // there isn't any enumeration yet manager->enums->first = new_ime; } else { last->next = new_ime; } return true; } // Pair enumerations. This enum will become prev enum and new this enum // will be created by new obtained by object path 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 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; } 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 */ 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 */ 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 */ 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. */ 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. */ bool send_indication(CMPIInstance *old, CMPIInstance *new, IMManager *manager) { if (!manager) { return false; } CMPIObjectPath *op = CMNewObjectPath( manager->broker, CMGetCharsPtr(CMGetNameSpace(manager->enums->first->op, 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 void *manage(void *data) { //TODO - make this thread's cancel state and type more specific? // Set the type to PTHREAD_CANCEL_ASYNCHRONOUS? // Switch Enable/Disable state on some places? IMManager *manager = (IMManager *)data; IMError err = IM_ERR_OK; // TODO - How to handle potential errors? DEBUG("manage thread started"); IMPolledDiffs diffs = {NULL}; CMPIInstance *iold = NULL, *inew = NULL; DEBUG("manage thread - attaching thread manager context = %p", manager->ctx_manage); CBAttachThread(manager->broker, manager->ctx_manage); while (1) { DEBUG("manage thread in infinite loop"); // wait until manager is running pthread_mutex_lock(&_t_mutex); while (!manager->running) { DEBUG("manage thread waiting to indication start"); pthread_cond_wait(&_t_cond, &_t_mutex); } if (manager->polling) { if (!first_poll(manager, &err)) { pthread_mutex_unlock(&_t_mutex); return (void *)err; } } pthread_mutex_unlock(&_t_mutex); DEBUG("manage thread calling watcher"); if(!manager->watcher(manager->data)) { continue; } pthread_mutex_lock(&_t_mutex); if (manager->polling) { // poll enumerations if (!_im_poll(manager, &err)) { pthread_mutex_unlock(&_t_mutex); return (void *)err; } // create list of diffs if (!gen_diffs(manager, &diffs, &err)) { pthread_mutex_unlock(&_t_mutex); return (void *)err; } manager->data = &diffs; } DEBUG("manage thread calling gather"); while (manager->gather(&iold, &inew, manager->data)) { send_indication(iold, inew, manager); } pthread_mutex_unlock(&_t_mutex); } } /* * Default gather function using polling * It is going thru all polled instances. If there is some pair (previous enum * and this enum) */ static bool default_gather (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 (!f_checker) { *err = IM_ERR_FILTER_CHECKER; 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->broker = broker; manager->f_checker = f_checker; manager->enums = NULL; manager->ctx_main = NULL; manager->ctx_manage = NULL; DEBUG("Manager created"); if (pthread_mutex_init(&_t_mutex, NULL) || pthread_cond_init(&_t_cond, 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; } pthread_mutex_lock(&_t_mutex); manager->ctx_main = ctx; if (!im_stop_ind(manager, ctx, err)) { pthread_mutex_unlock(&_t_mutex); return false; } if (!remove_all_filters(manager, err)) { pthread_mutex_unlock(&_t_mutex); return false; } if (pthread_cancel(_t_manage)) { *err = IM_ERR_THREAD; pthread_mutex_unlock(&_t_mutex); return false; } pthread_mutex_unlock(&_t_mutex); DEBUG("Destroying manager"); if (pthread_mutex_destroy(&_t_mutex)) { *err = IM_ERR_THREAD; return false; } free(manager); return true; } 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; } if (!filter) { *err = IM_ERR_FILTER; return NULL; } manager->ctx_main = ctx; DEBUG("Verifying filter. Manager = %p, filter checker = %p", manager, manager->f_checker); return manager->f_checker(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 */ 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; } if(!manager->filters) { DEBUG("This is the first filter creation"); IMFilters *filters = malloc(sizeof(IMFilters)); if (!filters) { *err = IM_ERR_MALLOC; free(ses); return false; } if (!(filters->class_name = get_classname(se))) { *err = IM_ERR_MALLOC; 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; free(ses); return false; } next->next = NULL; next->select_exp_string = ses; next->op = CMClone(get_object_path(manager->broker, se), NULL); if (prev) { prev->next = next; } else { manager->filters->first = next; } return true; } bool im_add_filter(IMManager *manager, CMPISelectExp *filter, const CMPIContext *ctx, IMError *err) { pthread_mutex_lock(&_t_mutex); if (!manager) { *err = IM_ERR_MANAGER; pthread_mutex_unlock(&_t_mutex); return false; } if (!ctx) { *err = IM_ERR_CONTEXT; pthread_mutex_unlock(&_t_mutex); return false; } if (!filter) { *err = IM_ERR_FILTER; pthread_mutex_unlock(&_t_mutex); return false; } manager->ctx_main = ctx; DEBUG("Adding filter"); if (!_im_add_filter(manager, filter, err)) { pthread_mutex_unlock(&_t_mutex); return false; } if (manager->polling) { if (!add_enumeration(manager, get_object_path(manager->broker, filter), err)) { pthread_mutex_unlock(&_t_mutex); return false; } } pthread_mutex_unlock(&_t_mutex); 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. */ 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; } CMRelease(current->prev_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(&_t_mutex); if (!manager->filters) { pthread_mutex_unlock(&_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(&_t_mutex); return false; } CMRelease(current->op); free(current->select_exp_string); free(current); pthread_mutex_unlock(&_t_mutex); return true; } prev = current; current = current->next; } *err = IM_ERR_NOT_FOUND; pthread_mutex_unlock(&_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; } manager->ctx_main = ctx; manager->ctx_manage = CBPrepareAttachThread(manager->broker, manager->ctx_main); DEBUG("Creating second thread"); if (pthread_create(&_t_manage, NULL, manage, manager)) { *err = IM_ERR_THREAD; return false; } DEBUG("Starting indications"); manager->running = true; pthread_cond_signal(&_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; return true; }