diff options
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | openlmi-providers.spec | 1 | ||||
-rw-r--r-- | src/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/account/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/openlmi.c | 160 | ||||
-rw-r--r-- | src/openlmi.h | 35 | ||||
-rw-r--r-- | src/python/lmi/base/BaseConfiguration.py | 40 |
7 files changed, 228 insertions, 22 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bb4dd2..d3184e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,10 @@ else(CMAKE_SIZEOF_VOID_P EQUAL 4) SET(LIB_SUFFIX 64) endif(CMAKE_SIZEOF_VOID_P EQUAL 4) +if(NOT SYSCONF_INSTALL_DIR) + set(SYSCONF_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/etc) +endif(NOT SYSCONF_INSTALL_DIR) + option(WITH-FAN "Build fan provider" ON) option(WITH-POWER "Build power provider" ON) option(WITH-SERVICE "Build service provider" ON) @@ -40,6 +44,8 @@ find_package(PkgConfig) find_package(CMPI REQUIRED) find_package(KonkretCMPI REQUIRED) +pkg_check_modules(GLIB REQUIRED glib-2.0) + add_subdirectory(src) add_subdirectory(mof) add_subdirectory(tools) diff --git a/openlmi-providers.spec b/openlmi-providers.spec index f85faca..e3f1e68 100644 --- a/openlmi-providers.spec +++ b/openlmi-providers.spec @@ -312,6 +312,7 @@ cp -pr src/account/doc/build/html/* $RPM_BUILD_ROOT/%{_docdir}/%{name}/account/a %files %doc README COPYING %dir %{_datadir}/%{name} +%config(noreplace) %{_sysconfdir}/openlmi/openlmi.conf %{_datadir}/%{name}/05_LMI_Qualifiers.mof %{_datadir}/%{name}/30_LMI_Jobs.mof %{_libdir}/libopenlmicommon.so.* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a9dc85d..589c845 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,11 @@ -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMPI_INCLUDE_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMPI_INCLUDE_DIR} ${GLIB_INCLUDE_DIRS}) add_library(openlmicommon SHARED openlmi.c ) +target_link_libraries(openlmicommon ${GLIB_LIBRARIES}) + set(OPENLMICOMMON_VERSION_MAJOR 0) set(OPENLMICOMMON_VERSION_MINOR 0) set(OPENLMICOMMON_VERSION_PATCH 1) @@ -16,6 +18,7 @@ install(TARGETS openlmicommon DESTINATION lib${LIB_SUFFIX}) install(FILES openlmi.h DESTINATION include/openlmi) configure_file(openlmi.pc.in 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) if (WITH-FAN) add_subdirectory(fan) diff --git a/src/account/CMakeLists.txt b/src/account/CMakeLists.txt index a993094..021b5f2 100644 --- a/src/account/CMakeLists.txt +++ b/src/account/CMakeLists.txt @@ -41,8 +41,7 @@ endforeach(CIM_CLASS ${CIM_CLASSES}) set(TARGET_MOF "${CMAKE_BINARY_DIR}/mof/90_LMI_Account_Profile.mof") profile_mof_generate("90_LMI_Account_Profile.mof.skel" "${TARGET_MOF}" "${CIM_PROVIDERS_CLASSES}") -# Require GLib-2.0 and libuser -pkg_check_modules(GLIB REQUIRED glib-2.0) +# Require libuser pkg_check_modules(LIBUSER REQUIRED libuser) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMPI_INCLUDE_DIR} ${GLIB_INCLUDE_DIRS} ${LIBUSER_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/src/indmanager) diff --git a/src/openlmi.c b/src/openlmi.c index 3b46f8d..ac539bd 100644 --- a/src/openlmi.c +++ b/src/openlmi.c @@ -22,17 +22,39 @@ #include <stdlib.h> #include <stdarg.h> +#include <stdbool.h> #include <unistd.h> #include <sys/utsname.h> #include <string.h> #include <netdb.h> #include <stdio.h> #include <cmpimacs.h> +#include <glib.h> static char *_fqdn = NULL; static int _log_level = _LMI_DEBUG_DEBUG; static const CMPIBroker *_cb = NULL; -static char *_log_id = 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; + +static ConfigEntry _toplevel_config_defaults[] = { + { "CIM", "Namespace", "root/cimv2" }, + { "CIM", "SystemClassName", "Linux_ComputerSystem" }, + { "LOG", "Level", "ERROR" }, + { "LOG", "Stderr" , "false" } +}; + +#define TOPLEVEL_CONFIG_FILE "/etc/openlmi/openlmi.conf" +#define PROVIDER_CONFIG_FILE "/etc/openlmi/%s/%s.conf" /** Gets Fully Quantified Domain Name of the system * @return FQDN (must be freed by the caller) @@ -91,16 +113,140 @@ const char *lmi_get_system_name() const char *lmi_get_system_creation_class_name() { - return "Linux_ComputerSystem"; + if (_system_creation_class_name == NULL) { + if (_masterKeyFile == NULL) { + lmi_error("Configuration was not read, using default option"); + return "Linux_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 "Linux_ComputerSystem"; + } + } + return _system_creation_class_name; } -void lmi_init_logging(const char *log_id, const CMPIBroker *cb) +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; + } + return g_key_file_get_string(_masterKeyFile, group, key, NULL); +} + +GKeyFile *parse_config(const char *provider_name, const ConfigEntry *provider_config_defaults) { - if (_log_id != NULL) { - free(_log_id); + 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); + } + + // 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); + return NULL; } - _log_id = strdup(log_id); + 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); + } + + // 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, &error); + for (j = 0; j < keys_len; ++j) { + v = g_key_file_get_value(providerKeyFile, groups[i], keys[j], &error); + 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 + gsize len = 0; + if (provider_config_defaults != NULL) { + len = sizeof(provider_config_defaults) / sizeof(ConfigEntry); + } + for (i = 0; i < len; i++) { + if (!g_key_file_has_key(masterKeyFile, + provider_config_defaults[i].group, + provider_config_defaults[i].key, NULL)) { + + g_key_file_set_value(masterKeyFile, + provider_config_defaults[i].group, + provider_config_defaults[i].key, + provider_config_defaults[i].value); + } + } + + // Top-level configs + len = sizeof(_toplevel_config_defaults)/sizeof(ConfigEntry); + for (i = 0; i < len; i++) { + if (!g_key_file_has_key(masterKeyFile, + _toplevel_config_defaults[i].group, + _toplevel_config_defaults[i].key, NULL)) { + + g_key_file_set_value(masterKeyFile, + _toplevel_config_defaults[i].group, + _toplevel_config_defaults[i].key, + _toplevel_config_defaults[i].value); + } + } + return masterKeyFile; +} + +void lmi_init(const char *provider, const CMPIBroker *cb, const ConfigEntry *provider_config_defaults) +{ + 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); + pthread_mutex_unlock(&_init_mutex); +} + +void lmi_init_logging(const char *log_id, const CMPIBroker *cb) +{ + lmi_init(log_id, cb, NULL); } int lmi_log_level(void) @@ -135,7 +281,7 @@ void _lmi_debug(int level, const char *file, int line, const char *format, ...) rc.rc = CMPI_RC_OK; if (_cb != NULL) { // try to use standard CMPI logging - rc = _cb->eft->trace(_cb, CMPI_LEV_INFO, _log_id, text, NULL); + rc = _cb->eft->trace(_cb, CMPI_LEV_INFO, _provider, text, NULL); } if (_cb == NULL || rc.rc != CMPI_RC_OK) { diff --git a/src/openlmi.h b/src/openlmi.h index 6ab4158..93778ce 100644 --- a/src/openlmi.h +++ b/src/openlmi.h @@ -23,6 +23,12 @@ #include <cmpidt.h> +typedef struct { + const char *group; + const char *key; + const char *value; +} ConfigEntry; + /** * This function returns FQDN (fully qualified domain name) of the machine * @@ -43,6 +49,35 @@ const char *lmi_get_system_name(); const char *lmi_get_system_creation_class_name(); /** + * 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 provider_config_defaults Array of default config values for given provider + * NULL if there is no provider-specific configuration + */ +void lmi_init(const char *provider, const CMPIBroker *cb, + 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); + +/** * To use standard CIMOM logging facility, broker must be assigned. Without * calling this function, logging will go to stderr. * diff --git a/src/python/lmi/base/BaseConfiguration.py b/src/python/lmi/base/BaseConfiguration.py index 8790acf..275fc0f 100644 --- a/src/python/lmi/base/BaseConfiguration.py +++ b/src/python/lmi/base/BaseConfiguration.py @@ -72,9 +72,12 @@ class BaseConfiguration(Singleton): There should be only one instance of this class. """ - CONFIG_DIRECTORY_TEMPLATE = '/etc/openlmi/%(provider_prefix)s/' - CONFIG_FILE_PATH_TEMPLATE = \ - CONFIG_DIRECTORY_TEMPLATE + '%(provider_prefix)s.conf' + CONFIG_DIRECTORY_TEMPLATE_TOPLEVEL = '/etc/openlmi/' + CONFIG_DIRECTORY_TEMPLATE_PROVIDER = '/etc/openlmi/%(provider_prefix)s/' + CONFIG_FILE_PATH_TEMPLATE_TOPLEVEL = \ + CONFIG_DIRECTORY_TEMPLATE_TOPLEVEL + 'openlmi.conf' + CONFIG_FILE_PATH_TEMPLATE_PROVIDER = \ + CONFIG_DIRECTORY_TEMPLATE_PROVIDER + '%(provider_prefix)s.conf' PERSISTENT_PATH_TEMPLATE = '/var/lib/openlmi-%(provider_prefix)s/' SETTINGS_DIR = 'settings/' @@ -106,11 +109,17 @@ class BaseConfiguration(Singleton): return cls.DEFAULT_OPTIONS @classmethod - def config_directory(cls): - """ Base directory with configuration settings. """ - return cls.CONFIG_DIRECTORY_TEMPLATE % { + def config_directory_toplevel(cls): + """ Base directory with toplevel configuration settings. """ + return cls.CONFIG_DIRECTORY_TEMPLATE_TOPLEVEL + + @classmethod + def config_directory_provider(cls): + """ Base directory with provider specific configuration settings. """ + return cls.CONFIG_DIRECTORY_TEMPLATE_PROVIDER % { 'provider_prefix' : cls.provider_prefix() } + @classmethod def persistent_path(cls): """ Base directory with persistent settings. """ @@ -118,11 +127,17 @@ class BaseConfiguration(Singleton): 'provider_prefix': cls.provider_prefix() } @classmethod - def config_file_path(cls): - """ File path of configuration file. """ - return cls.CONFIG_FILE_PATH_TEMPLATE % { + def config_file_path_toplevel(cls): + """ File path of toplevel configuration file. """ + return cls.CONFIG_FILE_PATH_TEMPLATE_TOPLEVEL + + @classmethod + def config_file_path_provider(cls): + """ File path of provider specific configuration file. """ + return cls.CONFIG_FILE_PATH_TEMPLATE_PROVIDER % { 'provider_prefix' : cls.provider_prefix() } + @classmethod def mandatory_sections(cls): """ @@ -163,10 +178,11 @@ class BaseConfiguration(Singleton): def load(self): """ - Load configuration from config file path. - The file does not need to exist. + Load configuration from config files. Provider specific options + overrides toplevel openlmi configuration. + The files do not need to exist. """ - self.config.read(self.config_file_path()) + self.config.read([self.config_file_path_toplevel(), self.config_file_path_provider()]) for section in self.mandatory_sections(): if not self.config.has_section(section): self.config.add_section(section) |