summaryrefslogtreecommitdiffstats
path: root/common/elapi/elapi_sink.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/elapi/elapi_sink.c')
-rw-r--r--common/elapi/elapi_sink.c500
1 files changed, 500 insertions, 0 deletions
diff --git a/common/elapi/elapi_sink.c b/common/elapi/elapi_sink.c
new file mode 100644
index 000000000..3908c3adf
--- /dev/null
+++ b/common/elapi/elapi_sink.c
@@ -0,0 +1,500 @@
+/*
+ ELAPI
+
+ Module that contains functions that manipulate ELAPI sinks.
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ This program 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 General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <sys/types.h> /* for stat() */
+#include <errno.h> /* for errors */
+#include <string.h> /* for memset() and other */
+#include <stdlib.h> /* for free() */
+#include <stdarg.h> /* for va_arg */
+#include <dlfcn.h> /* for dlopen() */
+
+#include "elapi_priv.h"
+#include "ini_config.h"
+#include "file_provider.h"
+#include "trace.h"
+#include "config.h"
+
+/* NOTE: Add new provider names here */
+const char *providers[] = { ELAPI_EMB_PRVDR_FILE,
+ ELAPI_EMB_PRVDR_STDERR,
+ ELAPI_EMB_PRVDR_SYSLOG,
+ NULL };
+
+
+/* This is a traverse callback for sink list */
+int elapi_sink_cb(const char *sink,
+ int sink_len,
+ int type,
+ void *data,
+ int length,
+ void *passed_data,
+ int *stop)
+{
+ TRACE_FLOW_STRING("elapi_sink_cb", "Entry.");
+
+ /* FIXME THIS IS A PLACEHOLDER FUNCTION FOR NOW */
+
+ /* Skip header */
+ if (type == COL_TYPE_COLLECTION) {
+ TRACE_FLOW_STRING("elapi_sink_cb - skip header", "Exit.");
+ return EOK;
+ }
+
+ printf("Sink: %s\n", sink);
+
+ TRACE_FLOW_STRING("elapi_sink_cb", "Exit.");
+ return EOK;
+}
+
+/* Destroy sink */
+void elapi_sink_destroy(struct elapi_sink_ctx *context)
+{
+ TRACE_FLOW_STRING("elapi_sink_destroy", "Entry.");
+
+#ifdef ELAPI_UTEST
+ /* FIXME: Can be removeed when the interface is stable */
+ /* For testing purposes print the context we are trying to free */
+ elapi_print_sink_ctx(context);
+#endif
+
+ if (context) {
+ TRACE_INFO_STRING("Context is not null.", "Destroying sink.");
+ /* FIXME: Do something about pending data if any */
+ /* Assume for now that we do not care about pending data */
+
+ /* If the private data has been allocated and close callback is there
+ * call a callback to clean the data and free it.
+ */
+ if (context->sink_cfg.priv_ctx) {
+ TRACE_INFO_STRING("Calling provider's close function.", "");
+ /* Call close function of the provider */
+ context->sink_cfg.cpb_cb.close_cb(&(context->sink_cfg.priv_ctx));
+ }
+
+ /* Now if the handle of the provider is set, offload the library instance */
+ if (context->sink_cfg.libhandle) {
+ TRACE_INFO_STRING("Offloading shared library.", "");
+ dlclose(context->sink_cfg.libhandle);
+ context->sink_cfg.libhandle = NULL;
+ }
+
+ if (context->sink_cfg.provider) {
+ TRACE_INFO_STRING("Cleaning provider.", "");
+ free(context->sink_cfg.provider);
+ context->sink_cfg.provider = NULL;
+ }
+
+ TRACE_INFO_STRING("Freeing context", "");
+ free(context);
+ }
+
+ TRACE_FLOW_STRING("elapi_sink_destroy", "Exit.");
+}
+
+/* Internal sink cleanup function */
+int elapi_sink_free_cb(const char *sink,
+ int sink_len,
+ int type,
+ void *data,
+ int length,
+ void *passed_data,
+ int *stop)
+{
+ TRACE_FLOW_STRING("elapi_sink_free_cb", "Entry.");
+
+ /* Skip header */
+ if (type == COL_TYPE_COLLECTION) {
+ TRACE_FLOW_STRING("elapi_sink_free_cb - skip header", "Exit.");
+ return EOK;
+ }
+
+ TRACE_INFO_STRING("Cleaning Sink:", sink);
+
+ elapi_sink_destroy(*((struct elapi_sink_ctx **)(data)));
+
+ TRACE_FLOW_STRING("elapi_sink_free_cb", "Exit.");
+ return EOK;
+}
+
+/* Function to read sink common configuration */
+static int elapi_read_sink_cfg(struct elapi_sink_cfg *sink_cfg,
+ char *name,
+ struct collection_item *ini_config)
+{
+ int error = EOK;
+ struct collection_item *cfg_item = NULL;
+ const char *provider;
+
+ TRACE_FLOW_STRING("elapi_read_sink_cfg", "Entry point");
+
+ /*********** Provider *************/
+
+ /* First check if this sink is properly configured and get its provider */
+ error = get_config_item(name,
+ ELAPI_SINK_PROVIDER,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read \"provider\" attribute returned error", error);
+ return error;
+ }
+
+ /* Do we have provider? */
+ if (cfg_item == NULL) {
+ /* There is no provider - return error */
+ TRACE_ERROR_STRING("Required key is missing in the configuration.", "Fatal Error!");
+ return ENOENT;
+ }
+
+ /* Get provider value */
+ provider = get_const_string_config_value(cfg_item, &error);
+ if ((error) || (!provider)) {
+ TRACE_ERROR_STRING("Invalid \"provider\" value", "Fatal Error!");
+ return EINVAL;
+ }
+
+ /* Save provider inside configuration data */
+ sink_cfg->provider = strdup(provider);
+ if (sink_cfg->provider == NULL) {
+ /* Failed to save the provider value */
+ TRACE_ERROR_STRING("Failed to save \"provider\" value.", "Fatal Error!");
+ return ENOMEM;
+ }
+
+ /*********** Required *************/
+ /* Next is "required" field */
+ cfg_item = NULL;
+ error = get_config_item(name,
+ ELAPI_SINK_REQUIRED,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read \"required\" attribute returned error", error);
+ return error;
+ }
+
+ /* Do we have "required"? */
+ if (cfg_item == NULL) {
+ /* There is no attribute - assume default */
+ TRACE_INFO_STRING("No \"required\" attribute.", "Assume optional");
+ sink_cfg->required = 0;
+ }
+ else {
+ sink_cfg->required = (uint32_t) get_bool_config_value(cfg_item, '\0', &error);
+ if (error) {
+ TRACE_ERROR_STRING("Invalid \"required\" value", "Fatal Error!");
+ return EINVAL;
+ }
+ }
+
+ /*********** Onerror *************/
+ /* Next is "onerror" field */
+ cfg_item = NULL;
+ error = get_config_item(name,
+ ELAPI_SINK_ONERROR,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read \"onerror\" attribute returned error", error);
+ return error;
+ }
+
+ /* Do we have "required"? */
+ if (cfg_item == NULL) {
+ /* There is no attribute - assume default */
+ TRACE_INFO_STRING("No \"onerror\" attribute.", "Assume retry (0)");
+ sink_cfg->onerror = 0;
+ }
+ else {
+ sink_cfg->onerror = (uint32_t) get_unsigned_config_value(cfg_item, 1, 0, &error);
+ if (error) {
+ TRACE_ERROR_STRING("Invalid \"onerror\" value", "Fatal Error!");
+ return EINVAL;
+ }
+ }
+
+ /*********** Timeout *************/
+ /* Next is "timeout" field */
+ cfg_item = NULL;
+ error = get_config_item(name,
+ ELAPI_SINK_TIMEOUT,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read \"timeout\" attribute returned error", error);
+ return error;
+ }
+
+ /* Do we have "required"? */
+ if (cfg_item == NULL) {
+ /* There is no attribute - assume default */
+ TRACE_INFO_STRING("No \"timeout\" attribute.", "Assume default timeout");
+ sink_cfg->timeout = ELAPI_SINK_DEFAULT_TIMEOUT;
+ }
+ else {
+ sink_cfg->timeout = (uint32_t) get_unsigned_config_value(cfg_item,
+ 1,
+ ELAPI_SINK_DEFAULT_TIMEOUT,
+ &error);
+ if (error) {
+ TRACE_ERROR_STRING("Invalid \"timeout\" value", "Fatal Error!");
+ return EINVAL;
+ }
+ }
+
+ /*********** Synch *************/
+ /* Next is "synch" field */
+ cfg_item = NULL;
+ error = get_config_item(name,
+ ELAPI_SINK_SYNCH,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read \"synch\" attribute returned error", error);
+ return error;
+ }
+
+ /* Do we have "required"? */
+ if (cfg_item == NULL) {
+ /* There is no attribute - assume default */
+ TRACE_INFO_STRING("No \"synch\" attribute.", "Assume retry (0)");
+ sink_cfg->synch = 0;
+ }
+ else {
+ sink_cfg->synch = (uint32_t) get_bool_config_value(cfg_item, '\0', &error);
+ if (error) {
+ TRACE_ERROR_STRING("Invalid \"synch\" value", "Fatal Error!");
+ return EINVAL;
+ }
+ }
+
+ TRACE_FLOW_STRING("elapi_read_sink_cfg", "Exit");
+ return error;
+}
+
+/* Function to load external sink library */
+static int elapi_load_lib(void **libhandle, sink_cpb_fn *sink_fn, char *name)
+{
+ char sink_lib_name[SINK_LIB_NAME_SIZE];
+ sink_cpb_fn sink_symbol = NULL;
+ void *handle = NULL;
+ char *lib_error = NULL;
+
+ TRACE_FLOW_STRING("elapi_load_lib", "Entry point");
+
+ if ((strlen(name) + sizeof(SINK_NAME_TEMPLATE)) >= SINK_LIB_NAME_SIZE) {
+ TRACE_ERROR_STRING("Provider string is too long:", name);
+ return EINVAL;
+ }
+
+ sprintf(sink_lib_name, SINK_NAME_TEMPLATE, name);
+ TRACE_INFO_STRING("Name of the library to try to load:", sink_lib_name);
+
+ /* Load library */
+ handle = dlopen(sink_lib_name, RTLD_LAZY);
+ if (!handle) {
+ TRACE_ERROR_STRING("Dlopen returned error", dlerror());
+ return ELIBACC;
+ }
+
+ /* Clear any existing error */
+ dlerror();
+ /* Get addres to the main entry point */
+ sink_symbol = (sink_cpb_fn)(dlsym(handle, SINK_ENTRY_POINT));
+ if ((lib_error = dlerror()) != NULL) {
+ TRACE_ERROR_STRING("Dlsym returned error", lib_error);
+ dlclose(handle);
+ return ELIBACC;
+ }
+
+ *libhandle = handle;
+ *sink_fn = sink_symbol;
+
+ TRACE_FLOW_STRING("elapi_load_lib", "Exit");
+ return EOK;
+}
+
+/* Function to load sink provider */
+int elapi_sink_loader(struct elapi_sink_cfg *sink_cfg)
+{
+ int error = EOK;
+ int num = 0;
+
+ TRACE_FLOW_STRING("elapi_sink_loader", "Entry point");
+
+ while (providers[num]) {
+ TRACE_INFO_STRING("Checking provider:", providers[num]);
+ if (strcasecmp(providers[num], sink_cfg->provider) == 0) break;
+ num++;
+ }
+
+ TRACE_INFO_NUMBER("Provider number:", num);
+
+ /* NOTE: Add provider handler into the switch below */
+ switch (num) {
+ case ELAPI_EMB_PRVDR_FILENUM:
+ TRACE_INFO_STRING("Using \"file\" provider:", "");
+ sink_cfg->ability = file_ability;
+ break;
+/* FIXME: Not implemented yet
+ case ELAPI_EMB_PRVDR_STDERRNUM:
+ TRACE_INFO_STRING("Using \"stderr\" provider:", "");
+ sink_cfg->ability = stderr_ability;
+ break;
+ case ELAPI_EMB_PRVDR_SYSLOGNUM:
+ TRACE_INFO_STRING("Using \"syslog\" provider:", "");
+ sink_cfg->ability = syslog_ability;
+ break;
+*/
+ default:
+ /* It is an external provider */
+ error = elapi_load_lib(&(sink_cfg->libhandle), &(sink_cfg->ability), sink_cfg->provider);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to load library", error);
+ return error;
+ }
+ break;
+ }
+
+ TRACE_FLOW_STRING("elapi_sink_loader", "Exit");
+ return error;
+}
+
+
+/* Function to load sink provider */
+int elapi_load_sink(struct elapi_sink_cfg *sink_cfg,
+ char *name,
+ struct collection_item *ini_config)
+{
+ int error = EOK;
+ TRACE_FLOW_STRING("elapi_load_sink", "Entry point");
+
+ /* Use sink loading wrapper */
+ error = elapi_sink_loader(sink_cfg);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to load sink", error);
+ return error;
+ }
+
+ /* Call ability function to fill in the pointers */
+ sink_cfg->ability(&(sink_cfg->cpb_cb));
+
+ /* Make sure the callbacks are initialized */
+ if ((sink_cfg->cpb_cb.init_cb == NULL) ||
+ (sink_cfg->cpb_cb.submit_cb == NULL) ||
+ (sink_cfg->cpb_cb.close_cb == NULL)) {
+ TRACE_ERROR_NUMBER("One of the callbacks is missing",
+ "Bad provider!");
+ return EINVAL;
+ }
+
+
+ /* Call init entry point */
+ /* NOTE: it is the responsibility of the provider
+ * to enforce singleton in case provider can't
+ * be loaded more than once like syslog for example.
+ */
+ error = sink_cfg->cpb_cb.init_cb(&(sink_cfg->priv_ctx),
+ name,
+ ini_config);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to initalize sink", error);
+ return error;
+ }
+
+ TRACE_FLOW_STRING("elapi_load_sink", "Exit");
+ return error;
+
+}
+
+/* Function to create a sink */
+int elapi_sink_create(struct elapi_sink_ctx **sink_ctx,
+ char *name,
+ struct collection_item *ini_config)
+{
+ int error = EOK;
+ uint32_t required;
+ struct elapi_sink_ctx *sink_context = NULL;
+
+ TRACE_FLOW_STRING("elapi_sink_create", "Entry point");
+
+ /* Allocate context */
+ sink_context = (struct elapi_sink_ctx *)malloc(sizeof(struct elapi_sink_ctx));
+ if (sink_context == NULL) {
+ TRACE_ERROR_NUMBER("Memory allocation failed. Error", ENOMEM);
+ return ENOMEM;
+ }
+
+ /* Initialize the allocatable items so that we can call destroy function
+ * in case of error.
+ * FIXME - add initialization here for other elements as they are added.
+ */
+
+ sink_context->async_mode = 0;
+ sink_context->in_queue = NULL;
+ sink_context->pending = NULL;
+ sink_context->sink_cfg.provider = NULL;
+ sink_context->sink_cfg.priv_ctx = NULL;
+ sink_context->sink_cfg.libhandle = NULL;
+ sink_context->sink_cfg.ability = NULL;
+ sink_context->sink_cfg.cpb_cb.init_cb = NULL;
+ sink_context->sink_cfg.cpb_cb.submit_cb = NULL;
+ sink_context->sink_cfg.cpb_cb.close_cb = NULL;
+
+ /* Read common fields */
+ error = elapi_read_sink_cfg(&(sink_context->sink_cfg),
+ name, ini_config);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to read sink configuration", error);
+ elapi_sink_destroy(sink_context);
+ return error;
+ }
+
+ TRACE_INFO_NUMBER("Address of init function",
+ sink_context->sink_cfg.cpb_cb.init_cb);
+ TRACE_INFO_NUMBER("Address of submit function",
+ sink_context->sink_cfg.cpb_cb.submit_cb);
+ TRACE_INFO_NUMBER("Address of close function",
+ sink_context->sink_cfg.cpb_cb.close_cb);
+
+ /* Load sink */
+ error = elapi_load_sink(&(sink_context->sink_cfg),
+ name, ini_config);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to load sink", error);
+ required = sink_context->sink_cfg.required;
+ elapi_sink_destroy(sink_context);
+ if (required) {
+ TRACE_ERROR_NUMBER("Sink is required so returning error", error);
+ return error;
+ }
+ else {
+ *sink_ctx = NULL;
+ TRACE_FLOW_STRING("Sink is not required so OK", "Exit");
+ return EOK;
+ }
+ }
+
+ /* We are done so return the context to the caller */
+ *sink_ctx = sink_context;
+
+ TRACE_FLOW_STRING("elapi_sink_create", "Exit");
+ return error;
+}