summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Minar <miminar@redhat.com>2014-05-06 13:42:34 +0200
committerMichal Minar <miminar@redhat.com>2014-06-20 14:00:28 +0200
commitcc85c3e2903b28379a4bbdea477ab4afcb8878a2 (patch)
tree51cdb8d6263456eb2f477f807e15a88bb53b9a46
parent2f2de126dd6f21ee552fa7dd06a10269e162e71b (diff)
downloadopenlmi-providers-cc85c3e2903b28379a4bbdea477ab4afcb8878a2.tar.gz
openlmi-providers-cc85c3e2903b28379a4bbdea477ab4afcb8878a2.tar.xz
openlmi-providers-cc85c3e2903b28379a4bbdea477ab4afcb8878a2.zip
added indication sender library
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/libs/indsender/CMakeLists.txt23
-rw-r--r--src/libs/indsender/ind_sender.c729
-rw-r--r--src/libs/indsender/ind_sender.h222
-rw-r--r--src/libs/indsender/openlmiindsender.pc.in11
6 files changed, 990 insertions, 0 deletions
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 <miminar@redhat.com>
+ */
+#include <stdio.h>
+#include <glib.h>
+#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 `<cim_class_name>:<filter_id>`. */
+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 <ORGID>, 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 <miminar@redhat.com>
+ */
+
+#ifndef IND_SENDER_H
+#define IND_SENDER_H
+
+#include <cmpimacs.h>
+#include <glib.h>
+
+#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_<prefix>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_<prefix>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_<prefix>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