From cc85c3e2903b28379a4bbdea477ab4afcb8878a2 Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Tue, 6 May 2014 13:42:34 +0200 Subject: added indication sender library --- CMakeLists.txt | 1 + src/CMakeLists.txt | 4 + src/libs/indsender/CMakeLists.txt | 23 + src/libs/indsender/ind_sender.c | 729 ++++++++++++++++++++++++++++++ src/libs/indsender/ind_sender.h | 222 +++++++++ src/libs/indsender/openlmiindsender.pc.in | 11 + 6 files changed, 990 insertions(+) create mode 100644 src/libs/indsender/CMakeLists.txt create mode 100644 src/libs/indsender/ind_sender.c create mode 100644 src/libs/indsender/ind_sender.h create mode 100644 src/libs/indsender/openlmiindsender.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fe6853..37eae82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ option(WITH-LOGICALFILE "Build logical file provider" ON) option(WITH-REALMD "Build RealmD provider" ON) option(WITH-PCP "Build PCP provider" ON) option(WITH-INDMANAGER "Build indication manager" ON) +option(WITH-INDSENDER "Build indication sender" ON) option(WITH-SOFTWARE "Build software provider" ON) option(WITH-SOFTWARE-DBUS "Use experimental dbus implementation of software provider" OFF) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b0b41f..5157c3d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,6 +46,10 @@ if (WITH-INDMANAGER) add_subdirectory(libs/indmanager) endif (WITH-INDMANAGER) +if (WITH-INDSENDER) + add_subdirectory(libs/indsender) +endif (WITH-INDSENDER) + if (WITH-SOFTWARE) if (WITH-SOFTWARE-DBUS) add_subdirectory(software-dbus) diff --git a/src/libs/indsender/CMakeLists.txt b/src/libs/indsender/CMakeLists.txt new file mode 100644 index 0000000..cc50be4 --- /dev/null +++ b/src/libs/indsender/CMakeLists.txt @@ -0,0 +1,23 @@ +add_library(openlmiindsender SHARED + ind_sender.c +) + +set(OPENLMIINDSENDER_VERSION_MAJOR 0) +set(OPENLMIINDSENDER_VERSION_MINOR 0) +set(OPENLMIINDSENDER_VERSION_PATCH 1) +set(OPENLMIINDSENDER_VERSION "${OPENLMIINDSENDER_VERSION_MAJOR}.${OPENLMIINDSENDER_VERSION_MINOR}.${OPENLMIINDSENDER_VERSION_PATCH}") + +set_target_properties(openlmiindsender PROPERTIES VERSION ${OPENLMIINDSENDER_VERSION}) +set_target_properties(openlmiindsender PROPERTIES SOVERSION ${OPENLMIINDSENDER_VERSION_MAJOR}) + + +target_link_libraries(openlmiindsender + openlmicommon + ${GLIB_LIBRARIES} +) + +install(TARGETS openlmiindsender DESTINATION lib${LIB_SUFFIX}) +install(FILES ind_sender.h DESTINATION include/openlmi) + +configure_file(openlmiindsender.pc.in ${CMAKE_CURRENT_BINARY_DIR}/openlmiindsender.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/openlmiindsender.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) diff --git a/src/libs/indsender/ind_sender.c b/src/libs/indsender/ind_sender.c new file mode 100644 index 0000000..7a2f945 --- /dev/null +++ b/src/libs/indsender/ind_sender.c @@ -0,0 +1,729 @@ +/* + * Copyright (C) 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: Michal Minar + */ +#include +#include +#include "openlmi.h" +#include "openlmi-utils.h" +#include "ind_sender.h" + +/* Prefix used in indications's class name creation. */ +static gchar *_name_prefix; +/** + * Number of calls to ind_sender_init() minus calls to ind_sender_cleanup(). + * This is to ensure that static variables are initialised upon initialization + * of the first provider that needs us and cleaned up upon clean up of last + * provider using us. */ +static gint _init_count = 0; +/* Manipulated by CIMOM. */ +static gint _indications_enabled = 0; +/* Maps keys to filter query strings. + * Key is a string composed of `:`. */ +static GHashTable *_filter_table = NULL; +/* Set of filter keys that are activated. It's a subset of keys in + * _filter_table. */ +static GTree *_subscribed_filters = NULL; +/* Mutex for accessing static variables. */ +static pthread_mutex_t _filter_mutex = PTHREAD_MUTEX_INITIALIZER; + +const gchar *ind_sender_static_filter_names[] = { + "Changed", + "Created", + "Deleted", + "Failed", + "PercentUpdated", + "Succeeded", +}; + +/** + * Treat keys of _subscribed_filters in case-insensitive way. + */ +static gint keys_compare(gconstpointer a, + gconstpointer b, + gpointer unused) +{ + return g_ascii_strcasecmp((const gchar *) a, (const gchar *) b); +} + +/** + * Create a key identifying static filter. + * + * @return number of bytes written without the terminating '\0' + */ +static gint make_filter_key(gchar *buf, + guint buflen, + const gchar *class_name, + const gchar *filter_id) +{ + return g_snprintf(buf, buflen, "%s:%s", class_name, filter_id); +} + +/** + * Create filter name used to fill IndicationFilterName property of created + * CIM_Indication. + * + * @return number of bytes written without the terminating '\0' + */ +static gint make_filter_name(gchar *buf, + guint buflen, + const gchar *class_name, + const gchar *filter_id) +{ + gint res = g_snprintf(buf, buflen, LMI_ORGID ":"); + res += make_filter_key(buf + res, buflen - res, class_name, filter_id); + return res; +} + +/** + * Find registered static filter with matching query. + * + * @note caller must ensure that _filter_mutex is locked during execution + * of this function. + * + * @param query value checked for exact match against registered filters. + * @param filter_key if not NULL, will be filled with filter's corresponding + * key in _filter_table. + * @param class_name if not NULL, will be filled with class name this filter + * belongs to. + * @param filter_id if not NULL, will be filled with corresponding filter_id. + * @returns status with CMPI_RC_OK code if found. Code CMPI_RC_ERR_NOT_FOUND + * will be set if not found. + */ +static CMPIStatus get_matching_filter(const gchar *query, + gchar **filter_key, + gchar **class_name, + gchar **filter_id) +{ + CMPIStatus status = {CMPI_RC_ERR_NOT_FOUND, NULL}; + GHashTableIter iter; + const gchar *value; + const gchar *key; + const gchar *cls_name_end; + + g_assert(_filter_table); + + g_hash_table_iter_init(&iter, _filter_table); + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &value)) { + if (g_ascii_strcasecmp(query, value)) + continue; + cls_name_end = index(key, ':'); + if ( (filter_key && (*filter_key = g_strdup(key)) == NULL) + || ( class_name + && (*class_name = g_strndup(key, cls_name_end - key)) == NULL) + || ( filter_id + && (*filter_id = g_strdup(cls_name_end + 1)) == NULL)) + { + if (class_name && *class_name) { + g_free((gchar *) *class_name); + *class_name = NULL; + } + lmi_error("Memory allocation failed"); + CMSetStatus(&status, CMPI_RC_ERR_FAILED); + }else { + CMSetStatus(&status, CMPI_RC_OK); + } + break; + } + + return status; +} + +/** + * Checks whether static filter is subscribed and indications are enabled. + * + * @return TRUE if indication can be sent. + */ +static gboolean is_subscribed(const gchar *class_name, const gchar *filter_id) +{ + gboolean found = FALSE; + gchar filter_key[BUFLEN]; + + if (_indications_enabled) + { + make_filter_key(filter_key, BUFLEN, class_name, filter_id); + if (g_tree_lookup_extended( + _subscribed_filters, filter_key, NULL, NULL)) { + found = TRUE; + } + } + + return found; +} + +/** + * Similar to is_subscribed(). This function obtains class_name directly from + * instance. + * + * @param inst subject of indication (source instance). + * @return TRUE if indication can be sent. + */ +static gboolean is_subscribed_inst(CMPIInstance *inst, const gchar *filter_id) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIObjectPath *iop; + CMPIString *clsname; + iop = CMGetObjectPath(inst, &status); + if (status.rc || !iop) + return FALSE; + clsname = CMGetClassName(iop, &status); + if (status.rc || !clsname) + return FALSE; + return is_subscribed(CMGetCharPtr(clsname), filter_id); +} + +/** + * Deliver indication to a broker. Indication is released in any case. + */ +static CMPIStatus do_send(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *indication) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIObjectPath *iop; + CMPIString *tmp; + const gchar *namespace; + const gchar *opstr; + + if ((iop = CMGetObjectPath(indication, &status)) == NULL) { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_FAILED, + "Failed to get indication's object path!"); + } else if (!CMClassPathIsA(cb, iop, "CIM_Indication", &status) || status.rc) + { + if (!status.rc) + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "Can not set indication which does not inherit from" + " CIM_Indication!"); + } else if ((tmp = CMGetNameSpace(iop, &status)) == NULL + || status.rc + || (namespace = CMGetCharsPtr(tmp, &status)) == NULL + || status.rc) + { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_FAILED, + "Failed to get indication's namespace!"); + } else { + if ( (tmp = CMObjectPathToString(iop, NULL)) + && (opstr = CMGetCharsPtr(tmp, NULL))) + { + lmi_info("Delivering indication \"%s\".", opstr); + CMRelease(tmp); + } else { + lmi_info("Delivering indication."); + } + status = CBDeliverIndication(cb, ctx, namespace, indication); + } + CMRelease(indication); + if (status.rc) + lmi_error("Failed to deliver indication: %s", + status.msg ? CMGetCharsPtr(status.msg, NULL) + : "unknown reason"); + return status; +} + +/** + * Make indication class name. This class name must be registered + * with broker. It's composed of , name prefix and suffix. + * Choose suffix from "InstCreation", "InstModification" and + * "InstDeletion". + * + * @return number of characters printed to *cls_name_buf*. + */ +static gint make_indication_class_name( + gchar *cls_name_buf, + guint buflen, + const gchar *suffix) +{ + return g_snprintf(cls_name_buf, buflen, LMI_ORGID "_%s%s", + _name_prefix, suffix); +} + +/** + * Create indication instance. Just a minimal set of properties will + * be filled. + * + * @param class_name_suffix is an argument for make_indication_class_name(). + * @param source_instance will be copied and assigned to SourceInstance + * property. + * @param indication points to a place where pointer to newly allocated + * indication instance will be stored. + */ +static CMPIStatus make_indication_inst(const CMPIBroker *cb, + const gchar *class_name_suffix, + CMPIInstance *source_instance, + const gchar *filter_id, + CMPIInstance **indication) +{ + g_assert(indication); + CMPIStatus status; + gchar filter_name[BUFLEN]; + const gchar *namespace; + gchar clsname[BUFLEN]; + CMPIObjectPath *src_op; + CMPIString *src_clsname; + CMPIString *tmp; + CMPIObjectPath *iop; + CMPIValue value; + + if ((iop = CMGetObjectPath(source_instance, &status)) == NULL) { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "Failed to get indication's object path!"); + } else if ( (tmp = CMGetNameSpace(iop, &status)) == NULL || status.rc + || (namespace = CMGetCharsPtr(tmp, &status)) == NULL || status.rc) + { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "Failed to get indication's namespace!"); + } else { + make_indication_class_name(clsname, BUFLEN, class_name_suffix); + if ( (iop = CMNewObjectPath(cb, namespace, clsname, &status)) == NULL + || status.rc) + goto err; + if ((*indication = CMNewInstance(cb, iop, &status)) == NULL || status.rc) + goto op_err; + if ( (value.inst = CMClone(source_instance, &status)) == NULL + || status.rc) + goto indication_err; + if (CMSetProperty(*indication, "SourceInstance", + &value, CMPI_instance).rc) + goto source_instance_err; + if ((value.string = CMNewString(cb, lmi_get_system_name(), + &status)) == NULL || status.rc) + goto indication_err; + if (CMSetProperty(*indication, "SourceInstanceHost", + &value, CMPI_string).rc) + goto string_err; + if ((src_op = CMGetObjectPath(source_instance, &status)) == NULL || + status.rc) + goto indication_err; + if ((value.string = CMObjectPathToString(src_op, &status)) == NULL || + status.rc) + goto indication_err; + if (CMSetProperty(*indication, "SourceInstanceModelPath", + &value, CMPI_string).rc) + goto string_err; + if ((src_clsname = CMGetClassName(src_op, &status)) == NULL) + goto indication_err; + make_filter_name(filter_name, BUFLEN, + CMGetCharsPtr(src_clsname, NULL), filter_id); + if ( (value.string = CMNewString(cb, filter_name, &status)) == NULL + || status.rc) + goto indication_err; + if (CMSetProperty(*indication, "IndicationFilterName", + &value, CMPI_string).rc) + goto string_err; + value.uint16 = 2; + if (CMSetProperty(*indication, "PerceivedSeverity", &value, CMPI_uint16).rc) + goto indication_err; + } + + return status; + +source_instance_err: + CMRelease(value.inst); + goto indication_err; +string_err: + CMRelease(value.string); +indication_err: + CMRelease(*indication); +op_err: + CMRelease(iop); +err: + lmi_error("Memory allocation failed"); + CMSetStatus(&status, CMPI_RC_ERR_FAILED); + return status; +} + +static CMPIStatus send_indication(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *indication, + const gchar *filter_id) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + gboolean send_it = (filter_id == NULL); + CMPIData tmp; + + if (filter_id) { + tmp = CMGetProperty(indication, "SourceInstance", &status); + if (status.rc || tmp.type != CMPI_instance) { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "Expected indication instance with" + " \"SourceInstance\" property."); + } else { + send_it = is_subscribed_inst(tmp.value.inst, filter_id); + } + } + if (!status.rc && send_it) { + status = do_send(cb, ctx, indication); + } else { + CMRelease(indication); + } + return status; +} + +CMPIStatus ind_sender_init(const gchar *name_prefix) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + + pthread_mutex_lock(&_filter_mutex); + g_assert(_init_count >= 0); + g_assert(_init_count > 0 || _filter_table == NULL); + + if (_init_count == 0) { + if ((_name_prefix = g_strdup(name_prefix)) == NULL) + goto err; + + if ( ((_filter_table = g_hash_table_new_full( + lmi_str_lcase_hash_func, lmi_str_icase_equal, + g_free, g_free)) == NULL) + || ((_subscribed_filters = g_tree_new_full( + keys_compare, NULL, g_free, NULL)) == NULL)) + goto name_alloced_err; + + lmi_debug("Indication sender initialized for %s", name_prefix); + } + ++_init_count; + + pthread_mutex_unlock(&_filter_mutex); + return status; + +name_alloced_err: + if (_filter_table) { + g_hash_table_unref(_filter_table); + _filter_table = NULL; + } + g_free(_name_prefix); +err: + pthread_mutex_unlock(&_filter_mutex); + lmi_error("Memory allocation failed"); + CMSetStatus(&status, CMPI_RC_ERR_FAILED); + return status; +} + +CMPIStatus ind_sender_cleanup() +{ + pthread_mutex_lock(&_filter_mutex); + g_assert(_init_count > 0); + g_assert(_filter_table); + + if (_init_count == 1) { + g_hash_table_destroy(_filter_table); + g_tree_destroy(_subscribed_filters); + _indications_enabled = FALSE; + } + --_init_count; + + pthread_mutex_unlock(&_filter_mutex); + lmi_debug("Cleanup of indication sender finished."); + CMReturn(CMPI_RC_OK); +} + +CMPIStatus ind_sender_add_static_filters(const gchar *class_name, + const StaticFilter *filters, + gint n_filters) +{ + g_assert(filters); + g_assert(n_filters > 0); + + gchar buf[BUFLEN]; + gchar *key; + gchar *query; + CMPIStatus status = {CMPI_RC_OK, NULL}; + + lmi_debug("Adding static filters for class %s.", class_name); + pthread_mutex_lock(&_filter_mutex); + g_assert(_filter_table); + + for (gint i=0; i < n_filters; ++i) { + make_filter_key(buf, BUFLEN, class_name, filters[i].id); + if (g_hash_table_lookup(_filter_table, buf) != NULL) { + lmi_error("Trying to add already registered filter \"%s\".", buf); + CMSetStatus(&status, CMPI_RC_ERR_ALREADY_EXISTS); + break; + } + if ((query = g_strdup(filters[i].query)) == NULL) { + lmi_error("Memory allocation failed"); + CMSetStatus(&status, CMPI_RC_ERR_FAILED); + break; + } + + if ((key = g_strdup(buf)) == NULL) + goto err; + g_hash_table_insert(_filter_table, key, query); + } + pthread_mutex_unlock(&_filter_mutex); + + lmi_debug("Added static filters for class %s.", class_name); + + return status; + +err: + pthread_mutex_unlock(&_filter_mutex); + lmi_error("Memory allocation failed"); + CMSetStatus(&status, CMPI_RC_ERR_FAILED); + return status; +} + +gboolean ind_sender_authorize_filter(const CMPISelectExp *filter, + const gchar *class_name, + const CMPIObjectPath *class_op, + const gchar *owner) +{ + CMPIString *str = CMGetSelExpString(filter, NULL); + const gchar *query = CMGetCharPtr(str); + GHashTableIter iter; + const gchar *value; + const gchar *cls_name_end; + gboolean found = FALSE; + + pthread_mutex_lock(&_filter_mutex); + g_assert(_filter_table); + + g_hash_table_iter_init(&iter, _filter_table); + while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &value)) { + if (g_ascii_strcasecmp(query, value)) + continue; + cls_name_end = index(value, ':'); + if (g_ascii_strncasecmp(class_name, value, cls_name_end - value)) + continue; + found = TRUE; + break; + } + + pthread_mutex_unlock(&_filter_mutex); + + if (found) { + lmi_info("Authorized static filter \"%s\" for class \"%s\".", + cls_name_end + 1, class_name); + }else { + lmi_info("Refused to authorize filter \"%s\".", query); + } + return found; +} + +CMPIStatus ind_sender_activate_filter(const CMPISelectExp *filter, + const gchar *class_name, + const CMPIObjectPath *class_op, + gboolean first_activation) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIString *str = CMGetSelExpString(filter, NULL); + const gchar *query = CMGetCharPtr(str); + gchar *fltr_key; + gchar *filter_id; + + pthread_mutex_lock(&_filter_mutex); + g_assert(_filter_table); + + status = get_matching_filter(query, &fltr_key, NULL, &filter_id); + if (status.rc != CMPI_RC_OK) { + if (status.rc == CMPI_RC_ERR_NOT_FOUND) + lmi_warn("Can't activate not registered filter \"%s\"!", query); + } else if (is_subscribed(class_name, filter_id)) { + lmi_info("Static filter \"%s\" for class \"%s\" is already active.", + filter_id, class_name); + } else { + g_tree_insert(_subscribed_filters, fltr_key, NULL); + lmi_info("Activated static filter \"%s\" for class \"%s\".", + filter_id, class_name); + } + + pthread_mutex_unlock(&_filter_mutex); + return status; +} + +CMPIStatus ind_sender_deactivate_filter(const CMPISelectExp *filter, + const gchar *class_name, + const CMPIObjectPath *class_op, + gboolean last_activation) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIString *str = CMGetSelExpString(filter, NULL); + const gchar *query = CMGetCharPtr(str); + gchar *fltr_key; + gchar *filter_id; + + pthread_mutex_lock(&_filter_mutex); + g_assert(_filter_table); + + status = get_matching_filter(query, &fltr_key, NULL, &filter_id); + if (status.rc != CMPI_RC_OK) { + if (status.rc == CMPI_RC_ERR_NOT_FOUND) + lmi_warn("Can't deactivate not registered filter \"%s\"!", query); + }else if (!g_tree_remove(_subscribed_filters, fltr_key)) { + lmi_warn("Can't deactivate inactive filter \"%s\" for class \"%s\"!", + filter_id, class_name); + CMSetStatus(&status, CMPI_RC_ERR_NOT_FOUND); + }else { + lmi_info("Deactivated static filter \"%s\" for class \"%s\".", + filter_id, class_name); + } + + pthread_mutex_unlock(&_filter_mutex); + return status; +} + +void enable_indications() { + pthread_mutex_lock(&_filter_mutex); + if (_indications_enabled <= 0) { + _indications_enabled = 1; + lmi_info("Indication sending enabled."); + } else { + ++_indications_enabled; + } + pthread_mutex_unlock(&_filter_mutex); +} + +void disable_indications() { + pthread_mutex_lock(&_filter_mutex); + if (_indications_enabled <= 1) { + _indications_enabled = 0; + lmi_info("Indication sending disabled."); + } else { + --_indications_enabled; + } + pthread_mutex_unlock(&_filter_mutex); +} + +CMPIStatus ind_sender_send_indication(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *indication, + const gchar *filter_id) +{ + CMPIStatus status = {CMPI_RC_OK, NULL}; + + pthread_mutex_lock(&_filter_mutex); + status = send_indication(cb, ctx, indication, filter_id); + pthread_mutex_unlock(&_filter_mutex); + return status; +} + +CMPIStatus ind_sender_send_instcreation(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *source_instance, + const gchar *filter_id) +{ + CMPIObjectPath *iop; + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIInstance *indication; + gchar *clsname; + + iop = CMGetObjectPath(source_instance, &status); + if (status.rc || !iop) { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "Missing object path in source instance!"); + } else if ( (clsname = CMGetCharPtr(CMGetClassName(iop, &status))) == NULL + || status.rc) + { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "Missing class name in source instance name!"); + } else { + pthread_mutex_lock(&_filter_mutex); + if (is_subscribed(clsname, filter_id)) { + if (!(status = make_indication_inst(cb, "InstCreation", source_instance, + filter_id, &indication)).rc) + status = send_indication(cb, ctx, indication, filter_id); + } else { + lmi_debug("Ignoring indication matching inactive filter %s:%s", + clsname, filter_id); + } + pthread_mutex_unlock(&_filter_mutex); + } + return status; +} + +CMPIStatus ind_sender_send_instmodification(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *old_instance, + CMPIInstance *new_instance, + const gchar *filter_id) +{ + CMPIObjectPath *iop; + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIInstance *indication; + gchar *clsname; + CMPIValue value; + + iop = CMGetObjectPath(new_instance, &status); + if (status.rc || !iop) { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "MIssing object path in new instance!"); + } else if ( (clsname = CMGetCharPtr(CMGetClassName(iop, &status))) == NULL + || status.rc) + { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "Missing class name in new instance name!"); + } else { + pthread_mutex_lock(&_filter_mutex); + if (is_subscribed(clsname, filter_id)) { + if (!(status = make_indication_inst( + cb, "InstModification", new_instance, + filter_id, &indication)).rc) + { + if ((value.inst = CMClone(old_instance, &status)) == NULL) { + lmi_error("Memory allocation failed"); + CMSetStatus(&status, CMPI_RC_ERR_FAILED); + } else if (CMSetProperty(indication, "PreviousInstance", + &value, CMPI_instance).rc) + { + lmi_error("Memory allocation failed"); + CMSetStatus(&status, CMPI_RC_ERR_FAILED); + } else { + status = send_indication(cb, ctx, indication, filter_id); + } + } + } else { + lmi_debug("Ignoring indication matching inactive filter %s:%s", + clsname, filter_id); + } + pthread_mutex_unlock(&_filter_mutex); + } + return status; +} + +CMPIStatus ind_sender_send_instdeletion(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *source_instance, + const gchar *filter_id) +{ + CMPIObjectPath *iop; + CMPIStatus status = {CMPI_RC_OK, NULL}; + CMPIInstance *indication; + gchar *clsname; + + iop = CMGetObjectPath(source_instance, &status); + if (status.rc || !iop) { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "MIssing object path in source instance!"); + } else if ( (clsname = CMGetCharPtr(CMGetClassName(iop, &status))) == NULL + || status.rc) + { + CMSetStatusWithChars(cb, &status, CMPI_RC_ERR_INVALID_PARAMETER, + "Missing class name in source instance name!"); + } else { + pthread_mutex_lock(&_filter_mutex); + if (is_subscribed(clsname, filter_id)) { + if (!(status = make_indication_inst(cb, "InstDeletion", source_instance, + filter_id, &indication)).rc) + status = send_indication(cb, ctx, indication, filter_id); + } else { + lmi_debug("Ignoring indication matching inactive filter %s:%s", + clsname, filter_id); + } + pthread_mutex_unlock(&_filter_mutex); + } + return status; +} diff --git a/src/libs/indsender/ind_sender.h b/src/libs/indsender/ind_sender.h new file mode 100644 index 0000000..b3c4fcc --- /dev/null +++ b/src/libs/indsender/ind_sender.h @@ -0,0 +1,222 @@ +/* + * Copyright (C) 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: Michal Minar + */ + +#ifndef IND_SENDER_H +#define IND_SENDER_H + +#include +#include + +#define STATIC_FILTER_ID_CHANGED \ + ind_sender_static_filter_names[IND_SENDER_STATIC_FILTER_ENUM_CHANGED] +#define STATIC_FILTER_ID_CREATED \ + ind_sender_static_filter_names[IND_SENDER_STATIC_FILTER_ENUM_CREATED] +#define STATIC_FILTER_ID_DELETED \ + ind_sender_static_filter_names[IND_SENDER_STATIC_FILTER_ENUM_DELETED] +#define STATIC_FILTER_ID_FAILED \ + ind_sender_static_filter_names[IND_SENDER_STATIC_FILTER_ENUM_FAILED] +#define STATIC_FILTER_ID_PERCENT_UPDATED \ + ind_sender_static_filter_names[\ + IND_SENDER_STATIC_FILTER_ENUM_PERCENT_UPDATED] +#define STATIC_FILTER_ID_SUCCEEDED \ + ind_sender_static_filter_names[IND_SENDER_STATIC_FILTER_ENUM_SUCCEEDED] + +typedef enum { + IND_SENDER_STATIC_FILTER_ENUM_CHANGED, + IND_SENDER_STATIC_FILTER_ENUM_CREATED, + IND_SENDER_STATIC_FILTER_ENUM_DELETED, + IND_SENDER_STATIC_FILTER_ENUM_FAILED, + IND_SENDER_STATIC_FILTER_ENUM_PERCENT_UPDATED, + IND_SENDER_STATIC_FILTER_ENUM_SUCCEEDED, + IND_SENDER_STATIC_FILTER_ENUM_LAST, +}IndSenderStaticFilterEnum; + +extern const gchar *ind_sender_static_filter_names[]; + +/** + * Container of static filter's data used for registration with indication + * sender. + */ +typedef struct _StaticFilter { + const gchar *id; + const gchar *query; +} StaticFilter; + +/** + * Intitialize indication sender's data structures. + * + * @note call this function on your provider's startup if you want to send + * indications. Call it after lmi_init(). + * + * @param name_prefix defines part of indication class name. It usually matches + * name of implemented profile. For example LMI_SoftwareInstCreation class + * name results from name_prefix="Software". + */ +CMPIStatus ind_sender_init(const gchar *name_prefix); +/** + * Call this function in cleanup handler of your provider if ind_sender_init() + * was used in your init function. + */ +CMPIStatus ind_sender_cleanup(); + +/** + * Register static filters with indication handler. No indications will be + * sent unless matching static filter is registered, authorized and activated. + * This needs to be called on provider's startup to register any static filters + * for particular CIM class. Static filters are identified with a pair + * (class_name, filter_id). + * + * @param class_name CIM class name residing in a FORM clause of filter's + * query. + * @param filters an array of static filters to register with particular CIM + * class. + * @param n_filters number of filters in *filters* parameter. + */ +CMPIStatus ind_sender_add_static_filters(const gchar *class_name, + const StaticFilter *filters, + gint n_filters); + +/** + * Callback for CIMOM for use in your provider. It asks us to verify client + * shall be allowed to subscribe to particular query. Query must exactly match + * one static query belonging to registered filter. + * + * @param filter query to authorize. + * @param class_name CIM class name matching given query. It must match + * associated class name of static filter used at registration time. + * @param class_op object path indentifying given class. + * @param owner destination owner. + * @return TRUE if static filter is registered. + */ +gboolean ind_sender_authorize_filter(const CMPISelectExp *filter, + const gchar *class_name, + const CMPIObjectPath *class_op, + const gchar *owner); + +/** + * Callback for CIMOM for use in your provider. It asks us to send + * indications matching given static filter generated from now on. + * + * @param filter static query that needs to be registered. + * @param class_name CIM class name matching given query. It must match + * associated class name of static filter used at registration time. + * @param class_op object path indentifying given class. + * @param first_activation shall be set to TRUE if this is the first filter for + * class_name. + */ +CMPIStatus ind_sender_activate_filter(const CMPISelectExp *filter, + const gchar *class_name, + const CMPIObjectPath *class_op, + gboolean first_activation); + +/** + * Callback for CIMOM for use in your provider. It asks us to ignore + * indications matching given static filter generated from now on. + * + * @param filter static query that needs to be registered. + * @param class_name CIM class name matching given query. It must match + * associated class name of static filter used at registration time. + * @param class_op object path indentifying given class. + * @param last_activation shall be set to TRUE if this is the last filter for + class_name. + */ +CMPIStatus ind_sender_deactivate_filter(const CMPISelectExp *filter, + const gchar *class_name, + const CMPIObjectPath *class_op, + gboolean last_activation); + +/** + * Callback for CIMOM for use in your provider. Tells us that indications can + * now be generated. The MB is now prepared to process indications. The + * function is normally called by the MB after having done its intialization + * and processing of persistent subscription requests. + */ +void enable_indications(); + +/* + * Callback for CIMOM for use in your provider. Tells us that indications can + * Tells us that we should stop generating indications. MB will not accept + * any indications until enabled again. The function is normally called + * when the MB is shutting down indication services either temporarily or + * permanently. + */ +void disable_indications(); + +/** + * Send indication if the corresponding static filter is active and indications + * enabled. Matching filter is identified by a class name of indication's + * source instance and given filter_id. + * + * @note indication will be released in any case. + * + * @param indication subject to sent. It must inherit from CIM_Instance. + * @param filter_id identifies static filter associated with particular class. + */ +CMPIStatus ind_sender_send_indication(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *indication, + const gchar *filter_id); + +/** + * Create an instance of LMI_InstCreation and send it if the corresponding + * static filter is active and indications enabled. Matching filter is + * identified by a class name of source instance and given filter_id. + * + * @param source_instance value of indication's SourceInstance property. + * You can safely release this instance later on. + * @param filter_id identifies static filter associated with particular class. + */ +CMPIStatus ind_sender_send_instcreation(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *source_instance, + const gchar *filter_id); + +/** + * Create an instance of LMI_InstModification and send it if the + * corresponding static filter is active and indications enabled. Matching + * filter is identified by a class name of source instance and given filter_id. + * + * @param old_instance value of indication's SourceInstance property. + * You can safely release this instance later on. + * @param new_instance value of indication's PreviousInstance property. + * You can safely release this instance later on. + * @param filter_id identifies static filter associated with particular class. + */ +CMPIStatus ind_sender_send_instmodification(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *old_instance, + CMPIInstance *new_instance, + const gchar *filter_id); + +/** + * Create an instance of LMI_InstDeletion and send it if the + * corresponding static filter is active and indications enabled. Matching + * filter is identified by a class name of source instance and given filter_id. + * + * @param source_instance value of indication's SourceInstance property. + * You can safely release this instance later on. + * @param filter_id identifies static filter associated with particular class. + */ +CMPIStatus ind_sender_send_instdeletion(const CMPIBroker *cb, + const CMPIContext *ctx, + CMPIInstance *source_instance, + const gchar *filter_id); + +#endif /* end of include guard: IND_SENDER_H */ diff --git a/src/libs/indsender/openlmiindsender.pc.in b/src/libs/indsender/openlmiindsender.pc.in new file mode 100644 index 0000000..883bca5 --- /dev/null +++ b/src/libs/indsender/openlmiindsender.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib@LIB_SUFFIX@ + +Name: openlmiindsender +Description: OpenLMI indication sender support +Version: @OPENLMIINDSENDER_VERSION@ +Requires: openlmicommon +Libs: -L${libdir} -lopenlmiindsender +CFlags: -I${includedir}/openlmi -- cgit