/* * 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 /* Debugging macros */ #define DEBUG(text) \ CMLogMessage(manager->broker, 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 _IMPolledDiffs { CMPIInstance *iold; CMPIInstance *inew; struct _IMPolledDiffs *next; } 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 *); /* * 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) { if (!v1->array && !v2->array) { return true; } if (!v1->array || !v2->array) { return false; } if (type & CMPI_ARRAY) { CMPICount c = CMGetArrayCount(v1->array, NULL); if (c != CMGetArrayCount(v2->array, NULL)) { return false; } while (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: return (compare_instances(v1->inst, v2->inst) == IM_I_SAME); case CMPI_ref: return (compare_objectpaths(v1->ref, v2->ref)); case CMPI_string: return (strcmp(CMGetCharsPtr(v1->string, NULL), CMGetCharsPtr(v2->string, NULL)) == 0); case CMPI_dateTime: return (strcmp(CMGetCharsPtr( CMGetStringFormat(v1->dateTime, NULL), NULL), CMGetCharsPtr( CMGetStringFormat(v2->dateTime, NULL), NULL))); default: return true; } return true; } /* * Comape 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 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(i1, 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 polled diff * return next diff from list or NULL * Does not perform any checks */ IMPolledDiffs *remove_diff(IMPolledDiffs *t) { IMPolledDiffs *next = t->next; free(t); return next; } /* * add instance pair to the list of diffs * First argument is last diff in list. NULL if list is empty * Returns new last diff created (malloc()ed) */ IMPolledDiffs *add_diff(IMPolledDiffs *last, CMPIInstance *newi, CMPIInstance *oldi) { IMPolledDiffs *nd = malloc(sizeof(IMPolledDiffs)); nd->inew = newi; nd->iold = oldi; nd->next = NULL; if (last) { last->next = nd; } return nd; } /* * 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 */ IMPolledDiffs *gen_diffs(IMManager *manager) { if (!manager || !manager->enums) { return NULL; } IMPolledDiffs *first = NULL; IMPolledDiffs *last = first; IMEnumerationPair *pair = manager->enums->first; while (pair) { CMPIArray *penum = pair->prev_enum; CMPIArray *tenum = pair->this_enum; if (penum && tenum) { int pcount = CMGetArrayCount(penum, NULL); int tcount = CMGetArrayCount(tenum, NULL); 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++) { newi = CMGetArrayElementAt(tenum, ti, NULL).value.inst; switch (compare_instances(oldi, newi)) { case IM_I_SAME: do_compare = false; break; case IM_I_CHANGE: last = add_diff(last, newi, oldi); if (!first) { first = last; } break; case IM_I_DIFFERENT: default: break; } } } } pair = pair->next; } return first; } /* helper functions */ /* * Extract object path from select expression * returns match for the first occurence of ISA operator */ CMPIObjectPath *get_object_path(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 object paths 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); } // Create new IMEnumerationPair filled with instances from given object path IMEnumerationPair *new_IMEnumerationPair(IMManager *manager, CMPIObjectPath *op) { CMPIEnumeration *aux_e = CBEnumInstances(manager->broker, manager->context, op, NULL, NULL); if (!aux_e) { return NULL; } CMPIArray *new_e = CMClone(CMToArray(aux_e, NULL), NULL); IMEnumerationPair *new_ime = malloc(sizeof(IMEnumerationPair)); new_ime->this_enum = new_e; new_ime->prev_enum = NULL; new_ime->next = NULL; new_ime->op = op; return new_ime; } // retrieve enumeration on given object path and add it to the end of list bool add_enumeration(IMManager *manager, CMPIObjectPath *op) { if (!manager || !manager->enums || !op) { return false; } IMEnumerationPair *e = manager->enums->first; IMEnumerationPair *last = NULL; while (e) { // find last enumeration last = e; e = e->next; } IMEnumerationPair *new_ime = new_IMEnumerationPair(manager, op); if (!new_ime) { return false; } 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 *found, CMPIObjectPath *op) { CMPIEnumeration *aux_e = CBEnumInstances(manager->broker, manager->context, op, NULL, NULL); if (!aux_e) { return false; } CMPIArray *new_e = CMClone(CMToArray(aux_e, NULL), NULL); if (found->prev_enum) { CMRelease(found->prev_enum); } found->prev_enum = found->this_enum; found->this_enum = new_e; return true; } // return the first enumeration pair that has the same namespace and class name // in object path. Return NULL if there isn't any. IMEnumerationPair *find_enum_by_op(IMEnumerations *enums, CMPIObjectPath *op) { if (!enums) { return NULL; } IMEnumerationPair *ime = enums->first; while (ime) { if (equal_ops(op, ime->op)) { return ime; } ime = ime->next; } return NULL; } bool poll(IMManager *manager, IMError *err) { // TODO threading if (!manager) { *err = IM_ERR_MANAGER; return false; } if (!manager->filters) { // Nothing to poll return true; } IMFilter *filter = NULL; // go thru all filters and (try to) get object path from each one // Use object path to enumerate instances // Filter out duplicates for (filter = manager->filters->first; filter; filter = filter->next) { CMPIObjectPath *op = get_object_path(manager->broker, filter->select_exp); IMEnumerationPair *found = find_enum_by_op(manager->enums, op); if (!found) { add_enumeration(manager, op); } else { // TODO replace_enumeration(found, op); } } return true; } /* * Run in separate thread */ static void *manage(void *data) { //TODO threading IMManager *manager = (IMManager *)data; IMError err = IM_ERR_OK; // TODO - How to handle potential errors? if (manager->polling) { if(!poll(manager, &err)) { // make the first poll return (void *)err; } } IMPolledDiffs *diffs = NULL; while (1) { // TODO Wait for spawner if (manager->polling) { // poll enumerations if (!poll(manager, &err)) { return (void *)err; } // create list of diffs diffs = gen_diffs(manager); } CMPIInstance *iold = NULL; CMPIInstance *inew = NULL; while (diffs) { while (manager->gather(iold, inew, manager)) { // TODO - send indication instance } diffs = remove_diff(diffs); } } } /* * 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) { // TODO - compare enumeration // go thru all polled pairs // 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 return true; } /* * 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) { // TODO - threading if (!manager->filters) { return true; } IMFilter *current = manager->filters->first; IMFilter *next = NULL; while (current) { next = current->next; CMRelease(current->select_exp); free(current); current = next; } free(manager->filters); manager->filters = NULL; return true; } IMManager* im_create_manager(IMInstGather gather, IMFilterChecker f_checker, bool polling, IMEventSpawner spawner, IMIndType type, CMPIBroker *broker, CMPIContext *ctx, IMError *err) { if ((!polling && !gather) || (polling && gather) ) { *err = IM_ERR_GATHER; return NULL; } if (!f_checker) { *err = IM_ERR_FILTER_CHECKER; return NULL; } if (!spawner) { *err = IM_ERR_SPAWNER; return NULL; } if (!broker) { *err = IM_ERR_BROKER; return NULL; } if (!ctx) { *err = IM_ERR_CONTEXT; 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->spawner = spawner; manager->filters = NULL; manager->running = false; manager->polling = polling; manager->broker = broker; manager->context = ctx; manager->f_checker = f_checker; manager->enums = NULL; DEBUG("Manager created"); return manager; } bool im_destroy_manager(IMManager *manager, IMError *err) { // TODO - threading if (!manager) { *err = IM_ERR_MANAGER; return false; } if (!im_stop_ind(manager, err)) { return false; } if (!remove_all_filters(manager, err)) { return false; } if (!remove_all_enums(manager, err)) { return false; } DEBUG("Destroying manager"); free(manager); return true; } bool im_verify_filter(const IMManager *manager, const CMPISelectExp *filter, IMError *err) { if (!manager) { *err = IM_ERR_MANAGER; return NULL; } if (!filter) { *err = IM_ERR_FILTER; return NULL; } char msg[256]; snprintf(msg, 255, "Verifyin filter. Manager = %p, filter checker = %p", manager, manager->f_checker); DEBUG(msg); return manager->f_checker(filter); } bool im_add_filter(IMManager *manager, CMPISelectExp *filter, IMError *err) { // TODO - threading if (!manager) { *err = IM_ERR_MANAGER; return false; } if (!filter) { *err = IM_ERR_FILTER; return false; } DEBUG("Adding filter"); if (manager->filters) { DEBUG("There are some filters, adding to the list"); IMFilter *current = manager->filters->first; while (current->next) { current = current->next; } IMFilter *next = malloc(sizeof(IMFilter)); if (!next) { *err = IM_ERR_MALLOC; return false; } next->next = NULL; next->select_exp = filter; current->next = next; } else { DEBUG("There isn't any filter, creating new list"); IMFilters *filters = malloc(sizeof(IMFilters)); if (!filters) { *err = IM_ERR_MALLOC; return false; } IMFilter *next = malloc(sizeof(IMFilter)); if (!next) { *err = IM_ERR_MALLOC; return false; } filters->first = next; next->next = NULL; next->select_exp = filter; manager->filters = filters; } return true; } bool im_remove_filter(IMManager *manager, const CMPISelectExp *filter, IMError *err) { // TODO - threading // TODO - maybe remove polled instances (?) if (!manager) { *err = IM_ERR_MANAGER; return false; } if (!filter) { *err = IM_ERR_FILTER; return false; } if (!manager->filters) { *err = IM_ERR_NOT_FOUND; return false; } DEBUG("Removing filter"); IMFilter *current = manager->filters->first; IMFilter *prev = NULL; while (current) { if (strcmp(CMGetCharsPtr(CMGetSelExpString(current->select_exp, NULL), NULL), CMGetCharsPtr(CMGetSelExpString(filter, NULL), NULL)) == 0) { // This one we want to remove CMRelease(current->select_exp); if (prev) { prev->next = current->next; } else { manager->filters->first = current->next; } free(current); return true; } prev = current; current = current->next; } *err = IM_ERR_NOT_FOUND; return false; } // start indications bool im_start_ind(IMManager *manager, IMError *err) { if (!manager) { *err = IM_ERR_MANAGER; return false; } DEBUG("Starting indications"); manager->running = true; // TODO - create thread for catching events // TODO - create thread for consuming events return true; } // stop indications bool im_stop_ind(IMManager *manager, IMError *err) { if (!manager) { *err = IM_ERR_MANAGER; return false; } DEBUG("Stopping indications"); manager->running = false; return true; }