summaryrefslogtreecommitdiffstats
path: root/src/libs
diff options
context:
space:
mode:
authorJan Synacek <jsynacek@redhat.com>2014-04-29 08:33:13 +0200
committerJan Synacek <jsynacek@redhat.com>2014-04-30 15:54:14 +0200
commit55f63d29f5d2b4e82979d71386df58394e87ef5a (patch)
tree34227f5696df9b3a85c5999d2fd48f50be82681f /src/libs
parentdab89c6afb2a2b339ebadea3d47e841cd749b5ef (diff)
downloadopenlmi-providers-55f63d29f5d2b4e82979d71386df58394e87ef5a.tar.gz
openlmi-providers-55f63d29f5d2b4e82979d71386df58394e87ef5a.tar.xz
openlmi-providers-55f63d29f5d2b4e82979d71386df58394e87ef5a.zip
libopenlmi: reorganize and gather
Make naming consistent. Gather common functionality into one library and try to use it across all providers. Introduce libtool-style versioning for libraries.
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/libopenlmi/CMakeLists.txt24
-rw-r--r--src/libs/libopenlmi/openlmi.c464
-rw-r--r--src/libs/libopenlmi/openlmi.conf20
-rw-r--r--src/libs/libopenlmi/openlmi.h236
-rw-r--r--src/libs/libopenlmi/openlmi.pc.in10
5 files changed, 754 insertions, 0 deletions
diff --git a/src/libs/libopenlmi/CMakeLists.txt b/src/libs/libopenlmi/CMakeLists.txt
new file mode 100644
index 0000000..8540323
--- /dev/null
+++ b/src/libs/libopenlmi/CMakeLists.txt
@@ -0,0 +1,24 @@
+# This library uses libtool versioning. For more information on how to update
+# the version numbers, see libtool manual.
+# https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
+# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+set(OPENLMI_VERSION_CURRENT 1)
+set(OPENLMI_VERSION_REVISION 0)
+set(OPENLMI_VERSION_AGE 2)
+set(OPENLMI_VERSION "${OPENLMI_VERSION_CURRENT}.${OPENLMI_VERSION_REVISION}.${OPENLMI_VERSION_AGE}")
+
+configure_file(openlmi.pc.in ${CMAKE_CURRENT_BINARY_DIR}/openlmi.pc @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/openlmi.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig)
+install(FILES openlmi.conf DESTINATION ${SYSCONF_INSTALL_DIR}/openlmi)
+
+add_library(openlmi SHARED
+ openlmi.c
+)
+
+target_link_libraries(openlmi ${GLIB_LIBRARIES} dl)
+
+set_target_properties(openlmi PROPERTIES VERSION ${OPENLMI_VERSION})
+set_target_properties(openlmi PROPERTIES SOVERSION ${OPENLMI_VERSION_CURRENT})
+
+install(TARGETS openlmi DESTINATION lib${LIB_SUFFIX})
+install(FILES openlmi.h DESTINATION include/openlmi)
diff --git a/src/libs/libopenlmi/openlmi.c b/src/libs/libopenlmi/openlmi.c
new file mode 100644
index 0000000..6257520
--- /dev/null
+++ b/src/libs/libopenlmi/openlmi.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2012-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: Radek Novacek <rnovacek@redhat.com>
+ * Jan Synacek <jsynacek@redhat.com>
+ */
+
+#include "openlmi.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <pthread.h>
+
+static char *_system_name = NULL;
+static CMPIObjectPath *_computer_system = NULL;
+static const CMPIBroker *_cb = NULL;
+static char *_provider = NULL;
+
+// Storage for all keys from configuration files and default configuration option
+// Access needs to be mutexed
+static GKeyFile *_masterKeyFile = NULL;
+
+// Mutex for running lmi_init
+static pthread_mutex_t _init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+// Cache for SystemCreationClassName
+static char *_system_creation_class_name = NULL;
+
+// Cache for log level
+static int _log_level = _LMI_DEBUG_NONE;
+
+// Cache for log stderr
+static bool _log_stderr = false;
+
+static ConfigEntry _toplevel_config_defaults[] = {
+ { "CIM", "Namespace", "root/cimv2" },
+ { "CIM", "SystemClassName", "PG_ComputerSystem" },
+ { "Log", "Level", "ERROR" },
+ { "Log", "Stderr" , "false" },
+ { NULL, NULL, NULL }
+};
+
+#define TOPLEVEL_CONFIG_FILE "/etc/openlmi/openlmi.conf"
+#define PROVIDER_CONFIG_FILE "/etc/openlmi/%s/%s.conf"
+
+/**
+ * Enumerate configured ComputerSystem class in order to obtain
+ * ComputerSystem ObjectPath and Hostname
+ *
+ * @param cb CMPIBroker
+ * @param ctx CMPIContext
+ * @retval true Success
+ * @retval false Failure
+ */
+static bool get_computer_system(const CMPIBroker *cb, const CMPIContext *ctx)
+{
+ free(_system_name);
+ _system_name = NULL;
+ if (_computer_system != NULL) {
+ CMRelease(_computer_system);
+ _computer_system = NULL;
+ }
+
+ const char *class_name = lmi_get_system_creation_class_name();
+ char *namespace = lmi_read_config("CIM", "Namespace");
+ CMPIStatus rc = { 0, NULL };
+ CMPIObjectPath *op = CMNewObjectPath(cb, namespace, class_name, &rc);
+ g_free (namespace);
+ if (rc.rc != CMPI_RC_OK) {
+ lmi_error("Unable to create object path: %s", rc.msg);
+ return false;
+ }
+ CMPIEnumeration *en = CBEnumInstanceNames(cb, ctx, op, &rc);
+ if (rc.rc != CMPI_RC_OK) {
+ lmi_error("Unable to enumerate instance names of class %s", class_name);
+ return false;
+ }
+
+ if (!CMHasNext(en, &rc)) {
+ lmi_error("No instance of class %s exists", class_name);
+ CMRelease(en);
+ return false;
+ }
+ CMPIData data = CMGetNext(en, &rc);
+ if (rc.rc != CMPI_RC_OK) {
+ lmi_error("Unable to get instance name of class %s", class_name);
+ CMRelease(en);
+ return false;
+ }
+ if (data.type != CMPI_ref) {
+ lmi_error("EnumInstanceNames didn't return CMPI_ref type, but %d", data.type);
+ CMRelease(en);
+ return false;
+ }
+ _computer_system = CMClone(data.value.ref, &rc);
+ if (rc.rc != CMPI_RC_OK) {
+ lmi_error("Unable to clone ComputerSystem object path");
+ CMRelease(en);
+ return false;
+ }
+ _system_name = strdup(CMGetCharPtr(CMGetKey(_computer_system, "Name", &rc).value.string));
+ CMRelease(en);
+ return true;
+}
+
+CMPIObjectPath *lmi_get_computer_system(void)
+{
+ return _computer_system;
+}
+
+const char *lmi_get_system_name(void)
+{
+ return _system_name;
+}
+
+const char *lmi_get_system_creation_class_name(void)
+{
+ if (_system_creation_class_name == NULL) {
+ if (_masterKeyFile == NULL) {
+ lmi_error("Configuration was not read, using default option");
+ return "PG_ComputerSystem";
+ }
+ _system_creation_class_name = lmi_read_config("CIM", "SystemClassName");
+ if (_system_creation_class_name == NULL) {
+ // This shouldn't happen, SystemClassName should be at least
+ // in default config keys
+ return "PG_ComputerSystem";
+ }
+ }
+ return _system_creation_class_name;
+}
+
+char *lmi_read_config(const char *group, const char *key)
+{
+ if (_masterKeyFile == NULL) {
+ lmi_warn("Attempted to read config file before calling lmi_init");
+ return NULL;
+ }
+ char *result = g_key_file_get_string(_masterKeyFile, group, key, NULL);
+ if (result == NULL) {
+ lmi_warn("Attempted to read unknown group/key: %s/%s", group, key);
+ }
+ return result;
+}
+
+bool lmi_read_config_boolean(const char *group, const char *key)
+{
+ char *value = lmi_read_config(group, key);
+ if (value == NULL) {
+ return false;
+ }
+ char *true_values[] = { "1", "yes", "true", "on" };
+ size_t len = sizeof(true_values) / sizeof(true_values[0]);
+ for (size_t i = 0; i < len; ++i) {
+ if (strcasecmp(true_values[i], value) == 0) {
+ free(value);
+ return true;
+ }
+ }
+ free(value);
+ return false;
+}
+
+void read_config(GKeyFile *keyFile, const ConfigEntry *config_dict)
+{
+ while (config_dict != NULL && config_dict->group != NULL && config_dict->key != NULL) {
+ if (!g_key_file_has_key(keyFile, config_dict->group,
+ config_dict->key, NULL)) {
+
+ g_key_file_set_value(keyFile, config_dict->group, config_dict->key,
+ config_dict->value);
+ }
+ config_dict++;
+ }
+}
+
+GKeyFile *parse_config(const char *provider_name, const ConfigEntry *provider_config_defaults)
+{
+ GError *error = NULL;
+ char *providerconf;
+
+ // Get all configuration options to masterKeyFile
+ GKeyFile *masterKeyFile = g_key_file_new();
+
+ // Read top-level openlmi configuration
+ if (!g_key_file_load_from_file(masterKeyFile, TOPLEVEL_CONFIG_FILE, G_KEY_FILE_NONE, &error)) {
+ lmi_debug("Can't read openlmi top-level config file %s: %s", TOPLEVEL_CONFIG_FILE, error->message);
+ g_clear_error(&error);
+ }
+
+ // Read provider-specific configuration
+ if (asprintf(&providerconf, PROVIDER_CONFIG_FILE, provider_name, provider_name) <= 0) {
+ lmi_error("Memory allocation failed");
+ g_key_file_free(masterKeyFile);
+ return NULL;
+ }
+ GKeyFile *providerKeyFile;
+ if ((providerKeyFile = g_key_file_new()) == NULL) {
+ lmi_error("Memory allocation failed");
+ g_key_file_free(masterKeyFile);
+ free(providerconf);
+ return NULL;
+ }
+ if (!g_key_file_load_from_file(providerKeyFile, providerconf, G_KEY_FILE_NONE, &error)) {
+ lmi_debug("Can't read provider specific config file %s: %s", providerconf, error->message);
+ g_clear_error(&error);
+ }
+
+ // Merge provider config to _masterKeyFile
+ gsize groups_len, keys_len, i, j;
+ gchar *v;
+ gchar **groups = g_key_file_get_groups(providerKeyFile, &groups_len), **keys;
+ if (groups != NULL) {
+ for (i = 0; i < groups_len; ++i) {
+ keys = g_key_file_get_keys(providerKeyFile, groups[i], &keys_len, NULL);
+ for (j = 0; j < keys_len; ++j) {
+ v = g_key_file_get_value(providerKeyFile, groups[i], keys[j], NULL);
+ g_key_file_set_value(masterKeyFile, groups[i], keys[j], v);
+ free(v);
+ }
+ g_strfreev(keys);
+ }
+ g_strfreev(groups);
+ }
+ g_key_file_free(providerKeyFile);
+
+ // Fill in default values where nothing gets read from config file.
+ // Provider-specific configs first
+ read_config(masterKeyFile, provider_config_defaults);
+ // Top-level configs
+ read_config(masterKeyFile, _toplevel_config_defaults);
+
+ free(providerconf);
+ return masterKeyFile;
+}
+
+void lmi_init(const char *provider, const CMPIBroker *cb,
+ const CMPIContext *ctx,
+ const ConfigEntry *provider_config_defaults)
+{
+ static void *glib_loaded = 0;
+
+ pthread_mutex_lock(&_init_mutex);
+ // Broker can change between threads
+ _cb = cb;
+
+ // Provider should remain the same
+ if (_provider != NULL) {
+ if (strcmp(_provider, provider) != 0) {
+ lmi_error("lmi_init called twice with different provider (%s -> %s), "
+ "this shouldn't happen", _provider, provider);
+ free(_provider);
+ } else {
+ pthread_mutex_unlock(&_init_mutex);
+ return;
+ }
+ }
+ _provider = strdup(provider);
+ if (_masterKeyFile != NULL) {
+ g_key_file_free(_masterKeyFile);
+ }
+ // Read config only on first call of this function
+ _masterKeyFile = parse_config(provider, provider_config_defaults);
+ // We might read different log config, reread it in next _log_debug call
+ _log_level = _LMI_DEBUG_NONE;
+
+ // Read ComputerSystem instance
+ if (_computer_system == NULL || _system_name == NULL) {
+ get_computer_system(cb, ctx);
+ }
+
+ /*
+ * Ugly hack to prevent Pegasus from crashing on cleanup.
+ * Glib2 adds some thread shutdown callback (see man pthread_key_create),
+ * but when cimprovagt destroys threads, this provider is already dlclosed().
+ * So keep the libglib in memory so the callback does not crash.
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1010238
+ */
+ if (!glib_loaded) {
+ glib_loaded = dlopen("libglib-2.0.so.0", RTLD_LAZY);
+ lmi_info("Loaded glib: %p\n", glib_loaded);
+ }
+
+ pthread_mutex_unlock(&_init_mutex);
+}
+
+int lmi_log_level(void)
+{
+ return _log_level;
+}
+
+void lmi_set_log_level(int level)
+{
+ _log_level = level;
+}
+
+void _lmi_debug(int level, const char *file, int line, const char *format, ...)
+{
+ const char *lvl[] = { "NONE", "ERROR", "WARNING", "INFO", "DEBUG" };
+ if (_log_level == _LMI_DEBUG_NONE) {
+ // Read log level from config or default
+ _log_level = _LMI_DEBUG_ERROR; // Default
+ char *level = lmi_read_config("Log", "Level");
+ if (level != NULL) {
+ size_t len = sizeof(lvl) / sizeof(lvl[0]);
+ for (size_t i = 0; i < len; ++i) {
+ if (strcasecmp(level, lvl[i]) == 0) {
+ _log_level = i;
+ break;
+ }
+ }
+ free(level);
+ }
+
+ // Read if log to stderr
+ _log_stderr = lmi_read_config_boolean("Log", "Stderr");
+ }
+ if (level > 4) {
+ level = 4;
+ }
+ if (level < 1) {
+ level = 1;
+ }
+
+ if (level > _log_level) {
+ // Do not log this message
+ return;
+ }
+
+ char *message, *text;
+ va_list args;
+ va_start(args, format);
+ vasprintf(&message, format, args);
+ va_end(args);
+ asprintf(&text, "[%s] %s:%d\t%s", lvl[level], file, line, message);
+ free(message);
+
+ CMPIStatus rc;
+ rc.rc = CMPI_RC_OK;
+ if (_cb != NULL) {
+ // try to use standard CMPI logging
+
+ // CMPI has different severity levels (1=info, 4=fatal)
+ int severity = CMPI_SEV_INFO;
+ switch (level) {
+ case _LMI_DEBUG_DEBUG:
+ severity = CMPI_DEV_DEBUG;
+ break;
+ case _LMI_DEBUG_INFO:
+ severity = CMPI_SEV_INFO;
+ break;
+ case _LMI_DEBUG_WARN:
+ severity = CMPI_SEV_WARNING;
+ break;
+ case _LMI_DEBUG_ERROR:
+ severity = CMPI_SEV_ERROR;
+ break;
+ }
+ rc = CMLogMessage(_cb, severity, _provider, text, NULL);
+ }
+
+ if (_log_stderr || _cb == NULL || rc.rc != CMPI_RC_OK) {
+ // Fallback to stderr
+ fprintf(stderr, "%s\n", text);
+ }
+ free(text);
+}
+
+
+
+CMPIStatus lmi_check_required(
+ const CMPIBroker *b,
+ const CMPIContext *ctx,
+ const CMPIObjectPath *o)
+{
+ CMPIStatus st;
+ CMPIData data;
+ const char *prop;
+
+ /* check computer system creation class name */
+ data = CMGetKey(o, "CSCreationClassName", &st);
+ lmi_check_status(st);
+
+ if (CMIsNullValue(data)) {
+ CMReturnWithChars(b, CMPI_RC_ERR_FAILED, "CSCreationClassName is empty");
+ }
+ prop = lmi_get_string_property_from_objectpath(o, "CSCreationClassName");
+ if (strcmp(prop, lmi_get_system_creation_class_name()) != 0) {
+ CMReturnWithChars(b, CMPI_RC_ERR_FAILED, "Wrong CSCreationClassName");
+ }
+
+ /* check fqdn */
+ data = CMGetKey(o, "CSName", &st);
+ lmi_check_status(st);
+ if (CMIsNullValue(data)) {
+ CMReturnWithChars(b, CMPI_RC_ERR_FAILED, "CSName is empty");
+ }
+ prop = lmi_get_string_property_from_objectpath(o, "CSName");
+ if (strcmp(prop, lmi_get_system_name()) != 0) {
+ CMReturnWithChars(b, CMPI_RC_ERR_FAILED, "Wrong CSName");
+ }
+
+ CMReturn(CMPI_RC_OK);
+}
+
+CMPIStatus lmi_check_assoc_class(
+ const CMPIBroker *cb,
+ const char *namespace,
+ const char *assocClass,
+ const char *class)
+{
+ CMPIObjectPath *o;
+ CMPIStatus st = {.rc = CMPI_RC_OK};
+
+ o = CMNewObjectPath(cb, namespace, class, &st);
+ if (!o) {
+ /* assume that status has been properly set */
+ return st;
+ } else if (st.rc != CMPI_RC_OK) {
+ CMRelease(o);
+ return st;
+ }
+ if (assocClass && !CMClassPathIsA(cb, o, assocClass, &st)) {
+ CMRelease(o);
+ st.rc = LMI_RC_ERR_CLASS_CHECK_FAILED;
+ return st;
+ }
+ CMRelease(o);
+ return st;
+}
+
+const char *lmi_get_string_property_from_objectpath(const CMPIObjectPath *o, const char *prop)
+{
+ CMPIData d;
+ d = CMGetKey(o, prop, NULL);
+ return KChars(d.value.string);
+}
+
+const char *lmi_get_string_property_from_instance(const CMPIInstance *i, const char *prop)
+{
+ CMPIData d;
+ d = CMGetProperty(i, prop, NULL);
+ return KChars(d.value.string);
+}
diff --git a/src/libs/libopenlmi/openlmi.conf b/src/libs/libopenlmi/openlmi.conf
new file mode 100644
index 0000000..b86e3d9
--- /dev/null
+++ b/src/libs/libopenlmi/openlmi.conf
@@ -0,0 +1,20 @@
+# Sample configuration file for OpenLMI providers
+
+[CIM]
+# To override default CIM Namespace, uncomment the line below.
+#Namespace = root/cimv2
+
+# To change the CIM class of ComputerSystem, which is associated to many
+# software classes, uncomment the line below.
+#SystemClassName = PG_ComputerSystem
+
+[Log]
+# These options modify logging configuration of the main process spawned
+# by CIMOM.
+
+# Level can be set to following values:
+# DEBUG, INFO, WARNING, ERROR
+#Level = ERROR
+
+# If logging to stderr is desired, set this option to True.
+#Stderr = False
diff --git a/src/libs/libopenlmi/openlmi.h b/src/libs/libopenlmi/openlmi.h
new file mode 100644
index 0000000..7f151cd
--- /dev/null
+++ b/src/libs/libopenlmi/openlmi.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2012-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: Radek Novacek <rnovacek@redhat.com>
+ * Jan Synacek <jsynacek@redhat.com>
+ */
+
+#ifndef OPENLMI_H
+#define OPENLMI_H
+
+#include <cmpidt.h>
+#include <cmpimacs.h>
+#include <errno.h>
+#include <glib.h>
+#include <konkret/konkret.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#ifndef BUFLEN
+ #define BUFLEN 1024
+#endif
+#ifndef PATH_MAX
+ #define PATH_MAX 4096
+#endif
+
+#define WHITESPACES " \f\n\r\t\v"
+
+#define ARRAY_SIZE(name) (sizeof(name) / sizeof(name[0]))
+
+/* Association sides. */
+#define LMI_GROUP_COMPONENT "GroupComponent"
+#define LMI_PART_COMPONENT "PartComponent"
+#define LMI_SAME_ELEMENT "SameElement"
+#define LMI_SYSTEM_ELEMENT "SystemElement"
+
+/* CMPI_RC_ERR_<error> values end at 200. 0xFF should be safe. */
+#define LMI_RC_ERR_CLASS_CHECK_FAILED 0xFF
+
+/* Organization ID. Used for InstaceIDs. */
+#define LMI_ORGID "LMI"
+
+/* Helper time conversion macros. */
+#define LMI_DAYS_TO_MS(days) ((days) * 86400000000)
+#define LMI_MS_TO_DAYS(ms) ((ms) / 86400000000)
+#define LMI_SECS_TO_MS(s) ((s) * 1000000)
+
+
+typedef struct {
+ const char *group;
+ const char *key;
+ const char *value;
+} ConfigEntry;
+
+/**
+ * This function returns object path of an instance of CIM_ComputerSystem
+ * subclass.
+ *
+ * The instance is obtained by enumerating the configured ComputerSystem
+ * classname.
+ *
+ * @warning Call lmi_init function before calling this function!
+ *
+ * @return CIM_ComputerSystem object path
+ */
+CMPIObjectPath *lmi_get_computer_system(void);
+
+/**
+ * This function returns system name for the computer system
+ *
+ * @note Use this in the SystemName property of all provider created instances.
+ *
+ * @warning Call lmi_init function before calling this function!
+ *
+ * @return The scoping System's Name.
+ */
+const char *lmi_get_system_name(void);
+
+/**
+ * This function returns system creation class name
+ *
+ * @note Use this in the SystemCreationClassName property of all provider
+ * created instances.
+ *
+ * @return The scoping System's Creation class name.
+ */
+const char *lmi_get_system_creation_class_name(void);
+
+
+/**
+ * Initialize usage base openlmi tools like configuration file access,
+ * logging etc.
+ *
+ * @note You must call this function prior to getting any configuration option
+ * or usage of logging. lmi_get_system_creation_class_name requires that this
+ * function will be called first (SystemCreationClassName is read from config).
+ *
+ * This function is reentrant and thread safe, but it should be called always
+ * with same parameters
+ *
+ * @param provider Identification of the CIM provider (must be same as name of the
+ * configuration file)
+ * @param cb CMPIBroker
+ * @param ctx CMPIContext
+ * @param provider_config_defaults Array of default config values for given provider
+ * terminated by empty struct or NULL if there is no
+ * provider-specific configuration
+ */
+void lmi_init(const char *provider, const CMPIBroker *cb,
+ const CMPIContext *ctx,
+ const ConfigEntry *provider_config_defaults);
+
+/**
+ * Reads string key out of configration files or default configration options.
+ *
+ * @param group Configration group
+ * @param key Configration key
+ * @return String value of the key or NULL if group/key is not found
+ */
+char *lmi_read_config(const char *group, const char *key);
+
+/**
+ * Reads a boolean value out of configuration files or default configuration
+ * options.
+ *
+ * Values "1", "yes", "true", and "on" are converted to TRUE, others to FALSE
+ *
+ * @param group Configration group
+ * @param key Configration key
+ * @return Boolean value of the key, false if the key is not in the
+ * configuration files neither in default options.
+ */
+bool lmi_read_config_boolean(const char *group, const char *key);
+
+/**
+ * To use standard CIMOM logging facility, broker must be assigned. Without
+ * calling this function, logging will go to stderr.
+ *
+ * @deprecated Use lmi_init instead
+ *
+ * @param log_id Identification of log messages
+ * @param cb CMPIBroker
+ */
+void lmi_init_logging(const char *log_id, const CMPIBroker *cb);
+
+/**
+ * Get currently set logging level
+ *
+ * @return logging level
+ */
+int lmi_log_level(void);
+
+/**
+ * Set logging level
+ *
+ * @note This method shouldn't be used directly, user setting
+ * from the configuration file should be honored
+ *
+ * @param level new logging level
+ */
+void lmi_set_log_level(int level);
+
+/**
+ * Add an instance \p w to the result \p cr.
+ *
+ * @param cr CMPIResult where should be the instance added
+ * @param w instance to add
+ * @retval true if succeeds
+ * @retval false if addition fails
+ */
+/* TODO: Rename. Looks like a class. */
+#define LMI_ReturnInstance(cr, w) KOkay(__KReturnInstance((cr), &(w).__base))
+
+enum {
+ _LMI_DEBUG_NONE=0, _LMI_DEBUG_ERROR, _LMI_DEBUG_WARN,
+ _LMI_DEBUG_INFO, _LMI_DEBUG_DEBUG
+};
+
+void _lmi_debug(int level, const char *file, int line, const char *format, ...);
+
+#define lmi_debug(...) _lmi_debug(_LMI_DEBUG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
+#define lmi_info(...) _lmi_debug(_LMI_DEBUG_INFO, __FILE__, __LINE__, __VA_ARGS__)
+#define lmi_warn(...) _lmi_debug(_LMI_DEBUG_WARN, __FILE__, __LINE__, __VA_ARGS__)
+#define lmi_error(...) _lmi_debug(_LMI_DEBUG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
+#define lmi_dump_objectpath(o, lvl) \
+ lmi_##lvl("OP: %s", CMGetCharsPtr((o)->ft->toString(o, NULL), NULL))
+
+
+/* Shortcuts for functions and macros from openlmicommon library. */
+#define get_system_creation_class_name lmi_get_system_creation_class_name
+#define get_system_name lmi_get_system_name
+
+/* Status checking helpers. Beware of the returns! */
+#define lmi_check_status(st) \
+ do { \
+ if (st.rc != CMPI_RC_OK) { \
+ return st; \
+ } \
+ } while (0)
+
+#define lmi_check_class_check_status(st) \
+ do { \
+ if (st.rc == LMI_RC_ERR_CLASS_CHECK_FAILED) { \
+ CMReturn(CMPI_RC_OK); \
+ } \
+ lmi_check_status(st); \
+ } while (0)
+
+#define lmi_return_with_status(b, st, code, msg) \
+ do { \
+ KSetStatus2(b, st, code, msg); \
+ return *(st); \
+ } while(0)
+
+
+CMPIStatus lmi_check_required(const CMPIBroker *, const CMPIContext *, const CMPIObjectPath *);
+CMPIStatus lmi_check_assoc_class(const CMPIBroker *, const char *, const char *, const char *);
+
+const char *lmi_get_string_property_from_objectpath(const CMPIObjectPath *, const char *);
+const char *lmi_get_string_property_from_instance(const CMPIInstance *, const char *);
+
+#endif /* OPENLMI_H */
diff --git a/src/libs/libopenlmi/openlmi.pc.in b/src/libs/libopenlmi/openlmi.pc.in
new file mode 100644
index 0000000..5824e73
--- /dev/null
+++ b/src/libs/libopenlmi/openlmi.pc.in
@@ -0,0 +1,10 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+includedir=${prefix}/include
+libdir=${exec_prefix}/lib@LIB_SUFFIX@
+
+Name: openlmi
+Description: OpenLMI provider support
+Version: @OPENLMICOMMON_VERSION@
+Libs: -L${libdir} -lopenlmi
+CFlags: -I${includedir}/openlmi