summaryrefslogtreecommitdiffstats
path: root/common/elapi/elapi_log.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/elapi/elapi_log.c')
-rw-r--r--common/elapi/elapi_log.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/common/elapi/elapi_log.c b/common/elapi/elapi_log.c
new file mode 100644
index 000000000..7c0d72abc
--- /dev/null
+++ b/common/elapi/elapi_log.c
@@ -0,0 +1,631 @@
+/*
+ ELAPI
+
+ Implementation of the ELAPI logging interface.
+
+ 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 <sys/stat.h> /* for stat() */
+#include <unistd.h> /* for stat() */
+#include <errno.h> /* for errors */
+#include <string.h> /* for memset() and other */
+#include <stdarg.h> /* for va_arg() */
+#include <stdlib.h> /* for free() */
+
+
+#include "elapi_priv.h"
+#include "elapi_event.h"
+#include "elapi_log.h"
+#include "ini_config.h"
+#include "trace.h"
+#include "config.h"
+
+
+/* Pointer to default global dispatcher */
+struct elapi_dispatcher *global_dispatcher = NULL;
+
+/* Deafult sink names */
+char remote_sink[] = "remote";
+char altremote_sink[] = "altremote";
+char syslog_sink[] = "syslog";
+char db_sink[] = "db";
+char file_sink[] = "file";
+char failover_sink[] = "failover";
+char stderr_sink[] = "stderr";
+
+/* Deafult sink list */
+char *default_sinks[] = { remote_sink,
+ altremote_sink,
+ syslog_sink,
+ db_sink,
+ file_sink,
+ failover_sink,
+ stderr_sink,
+ NULL };
+
+/* Per review I was told to hard cord this name. So be it... */
+#define ELAPI_CONFIG_FILE_NAME "elapi.conf"
+
+/* Default config file */
+static char default_config_file[] = ELAPI_DEFAULT_CONFIG_DIR "/" ELAPI_CONFIG_FILE_NAME;
+/* Default config dir */
+static char default_config_dir[] = ELAPI_DEFAULT_CONFIG_APP_DIR;
+
+
+/* Was a cleanup callback registered ? */
+static int elapi_close_registered = 0;
+
+
+/* Internal function to log message using args */
+static int elapi_dsp_msg_with_vargs(struct elapi_dispatcher *dispatcher,
+ struct collection_item *template,
+ va_list args)
+{
+ int error = EOK;
+ struct collection_item *event;
+
+ TRACE_FLOW_STRING("elapi_dsp_msg_with_vargs", "Entry");
+
+ if (!dispatcher) {
+ TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+ return EINVAL;
+ }
+
+ /* Create event */
+ error = elapi_create_event_with_vargs(&event,
+ template,
+ NULL,
+ 0,
+ args);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to create event", error);
+ return error;
+ }
+
+ /* Now log event */
+ error = elapi_dsp_log(dispatcher, event);
+
+ /* Destroy event */
+ elapi_destroy_event(event);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to log event", error);
+ return error;
+ }
+
+ TRACE_FLOW_STRING("elapi_dsp_msg_with_vargs", "Exit");
+ return error;
+}
+
+
+/********** Main functions of the interface **********/
+
+/* Function to create a dispatcher */
+int elapi_create_dispatcher_adv(struct elapi_dispatcher **dispatcher,
+ const char *appname,
+ const char *config_path,
+ elapi_add_fd add_fd_add_fn,
+ elapi_rem_fd add_fd_rem_fn,
+ elapi_add_timer add_timer_fn,
+ void *callers_data)
+{
+ struct elapi_dispatcher *handle = NULL;
+ struct collection_item *error_set = NULL;
+ int error = EOK;
+ struct collection_item *item = NULL;
+ const char *config_file = NULL;
+ const char *config_dir = NULL;
+ struct stat stat_data;
+ int prm_cnt = 0;
+
+ TRACE_FLOW_STRING("elapi_create_dispatcher_adv", "Entry point");
+
+ /* Make sure the memory for handle is passed in */
+ if (dispatcher == NULL) {
+ TRACE_ERROR_STRING("elapi_create_dispatcher_adv", "Invalid parameter.");
+ return EINVAL;
+ }
+
+ /* Make sure we got the right constant */
+ TRACE_INFO_NUMBER("ELAPI_DEFAULT_APP_NAME_SIZE = ", ELAPI_DEFAULT_APP_NAME_SIZE);
+
+ if ((appname != NULL) && (strlen(appname) > ELAPI_DEFAULT_APP_NAME_SIZE)) {
+ TRACE_ERROR_STRING("elapi_create_dispatcher", "Application name is too long.");
+ return EINVAL;
+ }
+
+ /* Check that all the async data is present */
+ if (!add_fd_add_fn) prm_cnt++;
+ if (!add_fd_rem_fn) prm_cnt++;
+ if (!add_timer_fn) prm_cnt++;
+ if (!callers_data) prm_cnt++;
+
+ if ((prm_cnt > 0) && (prm_cnt < 4)) {
+ /* We got a mixture of NULLs and not NULLs.
+ * This is bad since all should be either provided
+ * or all should be NULL.
+ */
+ TRACE_ERROR_STRING("Invalid sync parameters.", "At least one is NULL while others are not.");
+ return EINVAL;
+ }
+
+ /* Check what is passed in the config_path */
+ if (config_path) {
+ /* What is it ? */
+ if(stat(config_path, &stat_data)) {
+ error = errno;
+ TRACE_ERROR_NUMBER("Invalid path assume defaults. Error", error);
+ config_file = default_config_file;
+ config_dir = default_config_dir;
+ }
+ else {
+ if (S_ISREG(stat_data.st_mode)) {
+ config_file = config_path;
+ config_dir = NULL;
+ TRACE_INFO_STRING("Will use config file", config_file);
+ }
+ else if (S_ISDIR(stat_data.st_mode)) {
+ config_file = NULL;
+ config_dir = config_path;
+ TRACE_INFO_STRING("Will use directory", config_dir);
+ }
+ else {
+ config_file = default_config_file;
+ config_dir = default_config_dir;
+ }
+ }
+ }
+ else {
+ config_file = default_config_file;
+ config_dir = default_config_dir;
+ }
+
+ TRACE_INFO_STRING("FILE:", config_file);
+ TRACE_INFO_STRING("DIR:", config_dir);
+
+ /* Allocate memory */
+ handle = (struct elapi_dispatcher *) malloc(sizeof(struct elapi_dispatcher));
+ if (handle == NULL) {
+ error = errno;
+ TRACE_ERROR_NUMBER("Memory allocation failed. Error", error);
+ return error;
+ }
+
+ /* Clean memory - we need it to be able to destroy the dispatcher at any moment */
+ /* FIXME - eventually remove the memset from here when the structure finalizes */
+ /* Valgrind requires explicit initialization of the structure member, otherwise
+ * it complains about jump or move based on the uninitialized variable.
+ */
+ memset(handle, 0, sizeof(struct elapi_dispatcher *));
+ handle->ini_config = NULL;
+ handle->sink_list = NULL;
+ handle->sinks = NULL;
+ handle->default_template = NULL;
+ handle->need_to_free = 0;
+
+ /* Save application name in the handle */
+ if (appname != NULL) handle->appname = strdup(appname);
+ else handle->appname = strdup(ELAPI_DEFAULT_APP_NAME);
+
+ TRACE_FLOW_STRING("Application name:", handle->appname);
+
+ /* Check error */
+ if (handle->appname == NULL) {
+ error = errno;
+ TRACE_ERROR_NUMBER("Memory allocation failed. Error", error);
+ elapi_destroy_dispatcher(handle);
+ return error;
+ }
+
+ /* Read the ELAPI configuration and store it in the dispatcher handle */
+ error = config_for_app(handle->appname,
+ config_file,
+ config_dir,
+ &(handle->ini_config),
+ INI_STOP_ON_ANY,
+ &error_set);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read configuration returned error", error);
+ elapi_destroy_dispatcher(handle);
+ if (error_set) {
+ elapi_internal_dump_errors_to_file(error_set);
+ free_ini_config_errors(error_set);
+ }
+ return error;
+ }
+ /* Have to clean error set anyways */
+ free_ini_config_errors(error_set);
+
+ /* Get sink list from configuration */
+ error = get_config_item(ELAPI_DISPATCHER,
+ ELAPI_SINKS,
+ handle->ini_config,
+ &item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read configuration returned error", error);
+ elapi_destroy_dispatcher(handle);
+ return error;
+ }
+
+ if (!item) {
+ /* There is no list of sinks - use default list */
+ handle->sinks = default_sinks;
+ }
+ else {
+ /* Get one from config but make sure we free it later */
+ handle->sinks = get_string_config_array(item, NULL, NULL, NULL);
+ handle->need_to_free = 1;
+ }
+
+ /* Populate async processing data if any */
+ if (prm_cnt) {
+ TRACE_INFO_STRING("Async data is present", "");
+ handle->add_fd_add_fn = add_fd_add_fn;
+ handle->add_fd_rem_fn = add_fd_rem_fn;
+ handle->add_timer_fn = add_timer_fn;
+ handle->callers_data = callers_data;
+ handle->async_mode = 1;
+ }
+
+ /* Create the list of sinks */
+ error = elapi_internal_construct_sink_list(handle);
+ if (error != EOK) {
+ TRACE_ERROR_NUMBER("Failed to create sink list. Error", error);
+ elapi_destroy_dispatcher(handle);
+ return error;
+ }
+
+ *dispatcher = handle;
+
+ TRACE_FLOW_STRING("elapi_create_dispatcher_adv", "Returning Success.");
+ return EOK;
+
+}
+
+/* Simple dispatcher */
+int elapi_create_dispatcher(struct elapi_dispatcher **dispatcher,
+ const char *appname,
+ const char *config_path)
+{
+ int error = EOK;
+
+ TRACE_FLOW_STRING("elapi_create_dispatcher", "Entry.");
+
+ /* Will have more parmeters in future */
+ error = elapi_create_dispatcher_adv(dispatcher,
+ appname,
+ config_path,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ TRACE_FLOW_STRING("elapi_create_dispatcher", "Exit.");
+ return error;
+
+}
+
+/* Function to clean memory associated with the dispatcher */
+void elapi_destroy_dispatcher(struct elapi_dispatcher *dispatcher)
+{
+ TRACE_FLOW_STRING("elapi_destroy_dispatcher", "Entry.");
+
+ if (dispatcher) {
+ TRACE_INFO_STRING("Deleting template if any...", "");
+ col_destroy_collection(dispatcher->default_template);
+ TRACE_INFO_STRING("Closing sink handler.", "");
+ (void)col_traverse_collection(dispatcher->sink_list,
+ COL_TRAVERSE_ONELEVEL,
+ elapi_internal_sink_cleanup_handler,
+ NULL);
+ TRACE_INFO_STRING("Deleting sink list.", "");
+ col_destroy_collection(dispatcher->sink_list);
+ TRACE_INFO_STRING("Freeing application name.", "");
+ free(dispatcher->appname);
+ TRACE_INFO_STRING("Freeing config.", "");
+ free_ini_config(dispatcher->ini_config);
+ TRACE_INFO_STRING("Deleting sink name array.", "");
+ if (dispatcher->need_to_free) free_string_config_array(dispatcher->sinks);
+ TRACE_INFO_STRING("Freeing dispatcher.", "");
+ free(dispatcher);
+ }
+
+ TRACE_FLOW_STRING("elapi_destroy_dispatcher", "Exit.");
+}
+
+/* Function to log an event */
+int elapi_dsp_log(struct elapi_dispatcher *dispatcher, struct collection_item *event)
+{
+ int error = EOK;
+ struct elapi_sink_context sink_env;
+
+ TRACE_FLOW_STRING("elapi_dsp_log", "Entry");
+
+ if ((dispatcher == NULL) ||
+ (event == NULL)) {
+ TRACE_ERROR_STRING("elapi_dsp_log", "ERROR Invalid argument");
+ return EINVAL;
+ }
+
+ sink_env.handle = dispatcher;
+ sink_env.event = event;
+
+ /* Logging an event is just iterating through the sinks and calling the sink_handler */
+ error = col_traverse_collection(dispatcher->sink_list,
+ COL_TRAVERSE_ONELEVEL,
+ elapi_internal_sink_handler,
+ (void *)(&sink_env));
+
+ TRACE_FLOW_NUMBER("elapi_dsp_log Exit. Returning", error);
+ return error;
+}
+
+/* Initializes default internal template */
+int elapi_set_default_template(unsigned base, ...)
+{
+ int error = EOK;
+ struct collection_item *tpl = NULL;
+ va_list args;
+
+ TRACE_FLOW_STRING("elapi_set_default_template", "Entry");
+
+ if (global_dispatcher == NULL) elapi_init(NULL, NULL);
+
+ /* Clean previous instance of the default template */
+ elapi_destroy_event_template(global_dispatcher->default_template);
+ global_dispatcher->default_template = NULL;
+
+ /* Process varible arguments */
+ va_start(args, base);
+
+ /* Create template out of base and args */
+ error = elapi_create_event_template_with_vargs(&tpl,
+ base,
+ args);
+ va_end(args);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to create template. Error", error);
+ return error;
+ }
+
+ global_dispatcher->default_template = tpl;
+
+ TRACE_FLOW_STRING("elapi_set_default_template", "Exit");
+ return error;
+}
+
+/* There is one default template associated with the dispatcher */
+int elapi_get_default_template(struct collection_item **template)
+{
+ int error = EOK;
+
+ TRACE_FLOW_STRING("elapi_get_default_template", "Entry");
+
+ if ((global_dispatcher == NULL) ||
+ (global_dispatcher->default_template == NULL)) {
+ TRACE_INFO_STRING("Default template does not exit", "");
+
+ error = elapi_set_default_template(E_BASE_DEFV1);
+ if (error) {
+ TRACE_ERROR_NUMBER("Set default template returned error", error);
+ return error;
+ }
+ }
+
+ *template = global_dispatcher->default_template;
+ TRACE_FLOW_NUMBER("elapi_get_default_template. Exit returning", error);
+ return error;
+}
+
+
+
+/* Function to log raw key value pairs without creating an event */
+int elapi_dsp_msg(struct elapi_dispatcher *dispatcher,
+ struct collection_item *template,
+ ...)
+{
+ int error = EOK;
+ va_list args;
+
+ TRACE_FLOW_STRING("elapi_dsp_msg", "Entry");
+
+ va_start(args, template);
+
+ error = elapi_dsp_msg_with_vargs(dispatcher, template, args);
+
+ va_end(args);
+
+ TRACE_FLOW_STRING("elapi_dsp_msg.", "Exit");
+ return error;
+}
+
+/********** Advanced dispatcher managment functions **********/
+
+/* Managing the sink collection */
+int elapi_alter_dispatcher(struct elapi_dispatcher *dispatcher,
+ const char *sink,
+ int action)
+{
+
+ /* FIXME: FUNCTION IS NOT IMPLEMENTED YET */
+ return EOK;
+}
+
+/* Get sink list */
+char **elapi_get_sink_list(struct elapi_dispatcher *dispatcher)
+{
+
+ /* FIXME: FUNCTION IS NOT IMPLEMENTED YET */
+ return NULL;
+}
+
+/* Free sink list */
+void elapi_free_sink_list(char **sink_list)
+{
+
+ /* FIXME: FUNCTION IS NOT IMPLEMENTED YET */
+
+}
+
+
+/******************** High level interface ************************************/
+/* This interface is not thread safe but hides the dispatcher. */
+
+/* This function will use internal default template */
+int elapi_create_simple_event(struct collection_item **event, ...)
+{
+ int error = EOK;
+ struct collection_item *evt = NULL;
+ va_list args;
+ struct collection_item *template = NULL;
+
+ TRACE_FLOW_STRING("elapi_create_simple_event", "Entry");
+
+ /* Check storage */
+ if (event == NULL ) {
+ TRACE_ERROR_STRING("Event storage must be provided", "");
+ return EINVAL;
+ }
+
+ *event = NULL;
+
+ /* Get default template */
+ error = elapi_get_default_template(&template);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to get default template. Error", error);
+ return error;
+ }
+
+ va_start(args, event);
+
+ /* Create event */
+ error = elapi_create_event_with_vargs(&evt,
+ template,
+ NULL,
+ 0,
+ args);
+
+ va_end(args);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to create event using arg list. Error", error);
+ col_destroy_collection(evt);
+ return error;
+ }
+
+ *event = evt;
+
+ TRACE_FLOW_STRING("elapi_create_simple_event", "Exit");
+ return error;
+}
+
+/* Log key value pairs */
+int elapi_msg(struct collection_item *template, ...)
+{
+ int error = EOK;
+ va_list args;
+ struct collection_item *use_template;
+
+ TRACE_FLOW_STRING("elapi_msg", "Entry");
+
+ if (!template) {
+ /* Get default template */
+ error = elapi_get_default_template(&use_template);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to get default template. Error", error);
+ return error;
+ }
+ }
+ else use_template = template;
+
+ va_start(args, template);
+
+ error = elapi_dsp_msg_with_vargs(global_dispatcher, use_template, args);
+
+ va_end(args);
+
+ TRACE_FLOW_NUMBER("elapi_msg Exit:", error);
+ return error;
+}
+
+/* Log event */
+int elapi_log(struct collection_item *event)
+{
+ int error;
+
+ TRACE_FLOW_STRING("elapi_log", "Entry");
+
+ /* If dispatcher was not initialized do it automatically */
+ if (global_dispatcher == NULL) elapi_init(NULL, NULL);
+ error = elapi_dsp_log(global_dispatcher, event);
+
+ TRACE_FLOW_NUMBER("elapi_log Exit:", error);
+ return error;
+}
+
+/* Get dispatcher if you want to add sink to a default dispatcher or do some advanced operations */
+struct elapi_dispatcher *elapi_get_dispatcher(void)
+{
+ TRACE_FLOW_STRING("elapi_get_dispatcher was called.", "Returning default dispatcher.");
+ return global_dispatcher;
+
+}
+
+/* Close ELAPI */
+void elapi_close(void)
+{
+ TRACE_FLOW_STRING("elapi_close","Entry");
+
+ /* Destroy global dispatcher */
+ elapi_destroy_dispatcher(global_dispatcher);
+ global_dispatcher = NULL;
+
+ TRACE_FLOW_STRING("elapi_close","Exit");
+}
+
+/* Function to initialize ELAPI library in the single threaded applications */
+int elapi_init(const char *appname, const char *config_path)
+{
+ int error = EOK;
+
+ TRACE_FLOW_STRING("elapi_init","Entry");
+
+ /* Clean the dispatcher if needed */
+ elapi_close();
+
+ /* Create global dispatcher */
+ error = elapi_create_dispatcher(&global_dispatcher, appname, config_path);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to create default dispatcher. Error", error);
+ return error;
+ }
+
+ /* Install a cleanup callback */
+ if (!elapi_close_registered) {
+ if (atexit(elapi_close)) {
+ TRACE_ERROR_NUMBER("Failed to install cleanup callback. Error", ENOSYS);
+ /* NOTE: Could not find a better error for this case */
+ return ENOSYS;
+ }
+ elapi_close_registered = 1;
+ }
+
+ TRACE_FLOW_NUMBER("elapi_init Exit:",error);
+ return error;
+}