diff options
Diffstat (limited to 'common/elapi')
-rw-r--r-- | common/elapi/Makefile.am | 19 | ||||
-rw-r--r-- | common/elapi/conf_macros.m4 | 56 | ||||
-rw-r--r-- | common/elapi/configure.ac | 6 | ||||
-rw-r--r-- | common/elapi/elapi.h | 1 | ||||
-rw-r--r-- | common/elapi/elapi_async.h | 72 | ||||
-rw-r--r-- | common/elapi/elapi_event.c | 363 | ||||
-rw-r--r-- | common/elapi/elapi_event.h | 40 | ||||
-rw-r--r-- | common/elapi/elapi_internal.c | 231 | ||||
-rw-r--r-- | common/elapi/elapi_log.c | 631 | ||||
-rw-r--r-- | common/elapi/elapi_log.h | 117 | ||||
-rw-r--r-- | common/elapi/elapi_priv.h | 82 | ||||
-rw-r--r-- | common/elapi/elapi_sink.h | 78 | ||||
-rw-r--r-- | common/elapi/elapi_ut.c | 83 | ||||
-rw-r--r-- | common/elapi/elapi_ut.conf | 6 |
14 files changed, 1522 insertions, 263 deletions
diff --git a/common/elapi/Makefile.am b/common/elapi/Makefile.am index 49c9b518..92274429 100644 --- a/common/elapi/Makefile.am +++ b/common/elapi/Makefile.am @@ -1,15 +1,23 @@ TRACE_LEVEL=@TRACE_VAR@ +DEFAULT_CONF_DIR=@elapiconfdir@ +DEFAULT_CONF_APP_DIR=@elapiconfappdir@ +APP_NAME=@appname@ +APP_NAME_SIZE=@appnamesize@ topdir=$(srcdir)/.. -AM_CFLAGS = +AM_CFLAGS = -DELAPI_DEFAULT_CONFIG_DIR=\"$(DEFAULT_CONF_DIR)\" \ + -DELAPI_DEFAULT_CONFIG_APP_DIR=\"$(DEFAULT_CONF_APP_DIR)\" \ + -DELAPI_DEFAULT_APP_NAME=\"$(APP_NAME)\" \ + -DELAPI_DEFAULT_APP_NAME_SIZE=$(APP_NAME_SIZE) + if HAVE_GCC AM_CFLAGS += \ -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual \ -Wcast-align -Wwrite-strings endif -AM_CPPFLAGS = -I$(topdir) -I$(topdir)/trace -I$(topdir)/collection $(TRACE_LEVEL) +AM_CPPFLAGS = -I$(topdir) -I$(topdir)/ini -I$(topdir)/trace -I$(topdir)/collection $(TRACE_LEVEL) ACLOCAL_AMFLAGS = -I m4 @@ -21,11 +29,16 @@ dist_noinst_DATA = elapi.pc noinst_LTLIBRARIES = libelapi.la libelapi_la_SOURCES = \ elapi_event.c \ + elapi_log.c \ + elapi_internal.c \ elapi_event.h \ elapi_priv.h \ + elapi_sink.h \ + elapi_log.h \ + elapi_async.h \ elapi.h # Build unit test noinst_PROGRAMS = elapi_ut elapi_ut_SOURCES = elapi_ut.c -elapi_ut_LDADD = ../collection/libcollection.la libelapi.la +elapi_ut_LDADD = libelapi.la ../ini/libini_config.la ../collection/libcollection.la diff --git a/common/elapi/conf_macros.m4 b/common/elapi/conf_macros.m4 new file mode 100644 index 00000000..e20dfdba --- /dev/null +++ b/common/elapi/conf_macros.m4 @@ -0,0 +1,56 @@ + +AC_DEFUN([WITH_CONFIG_DIR], + [ AC_ARG_WITH([config-dir], + [AC_HELP_STRING([--with-config-dir=DIR], + [The name of the default ELAPI config directory [SYSCONFDIR/elapi]] + ) + ] + ) + elapiconfdir="$sysconfdir/elapi" + if test x"$with_config_dir" != x; then + elapiconfdir=$with_config_dir + fi + AC_SUBST(elapiconfdir) + ]) + +AC_DEFUN([WITH_CONFIG_APP_DIR], + [ AC_ARG_WITH([config-app-dir], + [AC_HELP_STRING([--with-config-app-dir=DIR], + [The name of the ELAPI application config directory [SYSCONFDIR/elapi/apps.d]] + ) + ] + ) + elapiconfappdir="$sysconfdir/elapi/apps.d" + if test x"$with_config_app_dir" != x; then + elapiconfappdir=$with_config_app_dir + fi + AC_SUBST(elapiconfappdir) + ]) + +AC_DEFUN([WITH_APP_NAME], + [ AC_ARG_WITH([app-name], + [AC_HELP_STRING([--with-app-name=<name>], + [The name of the default ELAPI application [default]] + ) + ] + ) + appname="default" + if test x"$with_app_name" != x; then + appname=$with_app_name + fi + AC_SUBST(appname) + ]) + +AC_DEFUN([WITH_APP_NAME_SIZE], + [ AC_ARG_WITH([app-name-size], + [AC_HELP_STRING([--with-app-name-size=<size>], + [The maximum size of the name for an ELAPI application [127]] + ) + ] + ) + appnamesize="127" + if test x"$with_app_name_size" != x; then + appnamesize=$with_app_name_size + fi + AC_SUBST(appnamesize) + ]) diff --git a/common/elapi/configure.ac b/common/elapi/configure.ac index e5c19ae7..2ebf0d08 100644 --- a/common/elapi/configure.ac +++ b/common/elapi/configure.ac @@ -21,6 +21,12 @@ AC_ARG_ENABLE([trace], [trace_level="0"]) AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] ],[AC_SUBST([TRACE_VAR],["-DTRACE_LEVEL=$trace_level"])]) +m4_include(conf_macros.m4) + +WITH_CONFIG_DIR +WITH_CONFIG_APP_DIR +WITH_APP_NAME +WITH_APP_NAME_SIZE AC_CONFIG_FILES([Makefile elapi.pc]) AC_OUTPUT diff --git a/common/elapi/elapi.h b/common/elapi/elapi.h index d4e9da4e..21f68d3f 100644 --- a/common/elapi/elapi.h +++ b/common/elapi/elapi.h @@ -21,5 +21,6 @@ #define ELAPI_H #include "elapi_event.h" +#include "elapi_log.h" #endif diff --git a/common/elapi/elapi_async.h b/common/elapi/elapi_async.h new file mode 100644 index 00000000..24cb7f8b --- /dev/null +++ b/common/elapi/elapi_async.h @@ -0,0 +1,72 @@ +/* + ELAPI + + Header file for the ELAPI async processing 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/>. +*/ + +#ifndef ELAPI_ASYNC_H +#define ELAPI_ASYNC_H + +#include <sys/time.h> + +/* Signature ELAPI callback function that + * should be called when the event loop got an event on the + * socket or file descriptor. + * ELAPI will always try to write to sockets in async way + * if the sink has this capability. + * So this is the callback that will always be + * invoked when we get ACK from the process receiving events. + */ +typedef int (*elapi_fd_callback)(int fd, /* File descriptor */ + void *elapi_data); /* ELAPI supplied data */ + +/* Signature ELAPI callback function that + * should be called when the event loop got a timer driven event. + */ +typedef int (*elapi_timer_callback)(void *elapi_data); /* ELAPI supplied data */ + +/* Signature of the supplied by caller function that ELAPI + * will call to add the fd into the application event processing loop. + */ +typedef int (*elapi_add_fd)(int fd, /* File descriptor to add */ + void *callers_data, /* Data that the function + * would need to do its work */ + elapi_fd_callback handle, /* Callback to call when event happens */ + void *elapi_data); /* Data to pass to the callback */ + +/* Signature of the supplied by caller function that ELAPI + * will call to remove the fd from the application event processing loop. + * The implementation of the function should assume that + * ELAPI will close file/socket descriptor after colling this function. + */ +typedef int (*elapi_rem_fd)(int fd, /* File descriptor to add */ + void *callers_data); /* Data that the function + * would need to do its work */ + +/* Signature of the supplied by caller function that ELAPI + * will call to add a new timer event to the application event processing loop. + */ +typedef int (*elapi_add_timer)(struct timeval timer, /* Timer */ + void *callers_data, /* Data that the function + * would need to do its work */ + elapi_timer_callback handle, /* Callback to call when event happens */ + void *elapi_data); /* Data to pass to the callback */ + + + + + +#endif diff --git a/common/elapi/elapi_event.c b/common/elapi/elapi_event.c index fe3b3a5e..97cb2a13 100644 --- a/common/elapi/elapi_event.c +++ b/common/elapi/elapi_event.c @@ -58,12 +58,9 @@ const char *str_no = "no"; const char *str_true = "true"; const char *str_false = "false"; -/* Default event template */ -struct collection_item *default_template = NULL; - /* Function to add host identity information to the template */ -int add_host_identity(struct collection_item *tpl, unsigned base) +static int add_host_identity(struct collection_item *tpl, unsigned base) { char hostname[NI_MAXHOST + 1]; int error = EOK; @@ -79,8 +76,6 @@ int add_host_identity(struct collection_item *tpl, unsigned base) TRACE_FLOW_STRING("add_host_identity", "Entry"); - memset(hostname, 0, sizeof(hostname)); - /* The goal here to collect information about the host. * there is no need to actually use it for establishing * any connections. @@ -92,7 +87,7 @@ int add_host_identity(struct collection_item *tpl, unsigned base) /* If we are not asked for ip then say we already have it */ if (!(base & E_HAVE_HOSTIP)) set_ip = 1; - if (getifaddrs(&ifaddr) == 0) { + if (getifaddrs(&ifaddr) == EOK) { TRACE_FLOW_STRING("getifaddrs", "Ok"); @@ -117,9 +112,11 @@ int add_host_identity(struct collection_item *tpl, unsigned base) if (family == AF_INET || family == AF_INET6) { TRACE_FLOW_NUMBER("Got right family", family); - /* Make sure the address is cleared - will help in comparisons */ - memset(host, 0, sizeof(host)); - memset(address, 0, sizeof(address)); + + /* getnameinfo function claims that it returns NULL + * terminated strings. Well... + * We will trust it here and not clear memory using memset. + */ gai_ret_host = getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : @@ -146,10 +143,14 @@ int add_host_identity(struct collection_item *tpl, unsigned base) hnm = NULL; /* Use host name returned by gethostname() as main host name */ - if (!gethostname(hostname, NI_MAXHOST)) hnm = hostname; + if (gethostname(hostname, NI_MAXHOST) == EOK) { + /* Make sure hostname is NULL terminated */ + hostname[NI_MAXHOST] = '\0'; + hnm = hostname; + } else { /* We we able to get a host name ? */ - if (gai_ret_host == 0) { + if (gai_ret_host == EOK) { TRACE_INFO_STRING("getnameinfo returned:", host); hnm = host; } @@ -157,9 +158,9 @@ int add_host_identity(struct collection_item *tpl, unsigned base) /* Do we have a host meaningful host name? */ if ((hnm) && - ((strncasecmp(hnm, LOCALHOST, sizeof(LOCALHOST)) == 0 ) || - (strncasecmp(hnm, LOCALHOSTDOMAIN, sizeof(LOCALHOSTDOMAIN)) == 0 ) || - (strncasecmp(hnm, address, sizeof(address) == 0)))) hnm = NULL; + ((strcasecmp(hnm, LOCALHOST) == 0 ) || + (strcasecmp(hnm, LOCALHOSTDOMAIN) == 0 ) || + (strcasecmp(hnm, address) == 0))) hnm = NULL; /* If host name is not NULL it would work for us */ if (hnm) { @@ -181,10 +182,10 @@ int add_host_identity(struct collection_item *tpl, unsigned base) TRACE_FLOW_STRING("Address is not set", ""); haddr = NULL; - if (gai_ret_addr == 0) { + if (gai_ret_addr == EOK) { TRACE_INFO_STRING("getnameinfo returned:", address); - if ((strncasecmp(address, LOCALADDRESS, sizeof(LOCALADDRESS)) != 0 ) && - (strncasecmp(address, LOCALADDRESSV6, sizeof(LOCALADDRESSV6)) != 0 )) { + if ((strcasecmp(address, LOCALADDRESS) != 0 ) && + (strcasecmp(address, LOCALADDRESSV6) != 0 )) { TRACE_INFO_STRING("Not an unhelpful address", ""); haddr = address; } @@ -211,11 +212,11 @@ int add_host_identity(struct collection_item *tpl, unsigned base) TRACE_INFO_STRING("they are:", strncasecmp(host, address, sizeof(address)) == 0 ? "same" : "different"); /* Do we have a host meaningful host name? */ - if ((gai_ret_host != 0) || - ((gai_ret_host == 0) && - ((strncasecmp(host, LOCALHOST, sizeof(LOCALHOST)) == 0 ) || - (strncasecmp(host, LOCALHOSTDOMAIN, sizeof(LOCALHOSTDOMAIN)) == 0 ) || - (strncasecmp(host, address, sizeof(address)) == 0)))) hnm = NULL; + if ((gai_ret_host != EOK) || + ((gai_ret_host == EOK) && + ((strcasecmp(host, LOCALHOST) == 0 ) || + (strcasecmp(host, LOCALHOSTDOMAIN) == 0 ) || + (strcasecmp(host, address) == 0)))) hnm = NULL; else hnm = host; if (hnm) { @@ -233,10 +234,10 @@ int add_host_identity(struct collection_item *tpl, unsigned base) if ((set_ip) && (base & E_HAVE_HOSTIPS)) { /* Do we have a host meaningful host name? */ - if ((gai_ret_addr != 0) || - ((gai_ret_addr == 0) && - ((strncasecmp(address, LOCALADDRESS, sizeof(LOCALADDRESS)) == 0 ) || - (strncasecmp(address, LOCALADDRESSV6, sizeof(LOCALADDRESSV6)) == 0 )))) haddr = address; + if ((gai_ret_addr != EOK) || + ((gai_ret_addr == EOK) && + ((strcasecmp(address, LOCALADDRESS) == 0 ) || + (strcasecmp(address, LOCALADDRESSV6) == 0 )))) haddr = address; else haddr = address; if (haddr) { @@ -286,7 +287,7 @@ int add_host_identity(struct collection_item *tpl, unsigned base) static int add_base_elements(struct collection_item *tpl, unsigned base) { int error = EOK; - int pass_base; + unsigned pass_base; TRACE_FLOW_STRING("add_base_elements", "Entry"); @@ -309,6 +310,15 @@ static int add_base_elements(struct collection_item *tpl, unsigned base) } } + if (base & E_HAVE_OFFSET) { + /* Value does not matter */ + error = col_add_int_property(tpl, NULL, E_OFFSET, 0); + if (error) { + TRACE_ERROR_NUMBER("Failed to add UTC time. Error", error); + return error; + } + } + if (base & E_HAVE_PID) { /* Value is the current pid */ error = col_add_long_property(tpl, NULL, E_PID, (long)getpid()); @@ -361,12 +371,12 @@ static int add_base_elements(struct collection_item *tpl, unsigned base) /* Internal untility function to tokenize a string */ -static int interprete_key(char *key, - int *type, - char **property, - int *prop_len, - int *has_len, - int *bool_type) +static int interpret_key(char *key, + int *type, + char **property, + int *prop_len, + int *has_len, + int *bool_type) { int adjust_by = 0; char *start = NULL; @@ -374,7 +384,7 @@ static int interprete_key(char *key, char *end = NULL; int ret = E_LIST_EMPTY; - TRACE_FLOW_STRING("interprete_key", "Entry"); + TRACE_FLOW_STRING("interpret_key", "Entry"); TRACE_INFO_STRING("Key", key); @@ -533,7 +543,7 @@ static int interprete_key(char *key, TRACE_INFO_NUMBER("Returning Has length:", *has_len); - TRACE_FLOW_STRING("interprete_key", "Exit"); + TRACE_FLOW_STRING("interpret_key", "Exit"); return ret; } @@ -544,14 +554,14 @@ static int convert_bool(char *data_str, unsigned char *data_bool) TRACE_FLOW_STRING("convert_bool", "Called"); TRACE_INFO_STRING("Data", data_str); - if ((strncasecmp(data_str, str_true, sizeof(str_true)) == 0) || - (strncasecmp(data_str, str_yes, sizeof(str_yes)) == 0)) { + if ((strcasecmp(data_str, str_true) == 0) || + (strcasecmp(data_str, str_yes) == 0)) { TRACE_INFO_STRING("Matched TRUE", ""); *data_bool = '\1'; return 1; } - if ((strncasecmp(data_str, str_false, sizeof(str_false)) == 0) || - (strncasecmp(data_str, str_no, sizeof(str_no)) == 0)) { + if ((strcasecmp(data_str, str_false) == 0) || + (strcasecmp(data_str, str_no) == 0)) { TRACE_INFO_STRING("Matched FALSE", ""); *data_bool = '\0'; return 1; @@ -599,7 +609,7 @@ static int process_arg_list(struct collection_item *col, return EINVAL; } - /* Interprete the key. + /* Interpret the key. * It can be just " key ", * it can be " - key ", * or it can be a formatted string @@ -607,12 +617,12 @@ static int process_arg_list(struct collection_item *col, * Function will deal with all cases. * Passed in variables initialized and updated inside */ - ret = interprete_key(arg, - &type, - &property, - &prop_len, - &has_len, - &bool_type); + ret = interpret_key(arg, + &type, + &property, + &prop_len, + &has_len, + &bool_type); if (ret == E_LIST_LAST) { TRACE_INFO_STRING("Process found last key", arg); @@ -621,12 +631,16 @@ static int process_arg_list(struct collection_item *col, if ((ret == E_LIST_ADD) || (ret == E_LIST_REMOVE)) { /* We need to create a dup of the string */ - propcopy = strndup(property, prop_len); + propcopy = malloc(prop_len + 1); if (propcopy == NULL) { TRACE_ERROR_STRING("Failed to allocate property", arg); return ENOMEM; } + /* Copy property */ + memcpy(propcopy, property, prop_len); + propcopy[prop_len] = '\0'; + TRACE_INFO_STRING("Processing property", propcopy); /* Are we supposed to add? */ @@ -759,45 +773,18 @@ static int process_arg_list(struct collection_item *col, return error; } -static int get_default_template(struct collection_item **template) -{ - int error = EOK; - - TRACE_FLOW_STRING("get_default_template", "Entry"); - - if (!default_template) { - 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 = default_template; - TRACE_FLOW_NUMBER("get_default_template. Exit returning", error); - return error; -} - -/* Cleanup callback installed when global template is used */ -void clean_template(void) -{ - TRACE_FLOW_STRING("clean_template", "Entry"); - elapi_destroy_event_template(default_template); - TRACE_FLOW_STRING("clean_template", "Exit"); -} /*****************************************************************************/ - /* Create event template */ -int elapi_create_event_template(struct collection_item **template, - unsigned base, ...) +int elapi_create_event_template_with_vargs(struct collection_item **template, + unsigned base, + va_list args) { int error = EOK; struct collection_item *tpl = NULL; - va_list args; - TRACE_FLOW_STRING("elapi_create_event_template", "Entry"); + TRACE_FLOW_STRING("elapi_create_event_template_with_vargs", "Entry"); if (template == NULL ) { TRACE_ERROR_STRING("Template storage must be provided", ""); @@ -821,14 +808,9 @@ int elapi_create_event_template(struct collection_item **template, return error; } - /* Process varible arguments */ - va_start(args, base); - /* Process variable argument list */ error = process_arg_list(tpl, args); - va_end(args); - if (error) { TRACE_ERROR_NUMBER("Failed to process argument list. Error", error); col_destroy_collection(tpl); @@ -837,6 +819,30 @@ int elapi_create_event_template(struct collection_item **template, *template = tpl; + TRACE_FLOW_STRING("elapi_create_event_template_with_vargs", "Exit"); + return error; +} + + +/* Create event template */ +int elapi_create_event_template(struct collection_item **template, + unsigned base, ...) +{ + int error = EOK; + va_list args; + + TRACE_FLOW_STRING("elapi_create_event_template", "Entry"); + + /* Process varible arguments */ + va_start(args, base); + + /* Create template using arguments */ + error = elapi_create_event_template_with_vargs(template, + base, + args); + + va_end(args); + TRACE_FLOW_STRING("elapi_create_event_template", "Exit"); return error; } @@ -851,30 +857,24 @@ void elapi_destroy_event_template(struct collection_item *template) TRACE_FLOW_STRING("elapi_destroy_event_template", "Exit"); } -/* Create event */ -int elapi_create_event(struct collection_item **event, - struct collection_item *template, - struct collection_item *collection, - int mode, ...) + +/* Create event from template, colection and arguments */ +int elapi_create_event_with_vargs(struct collection_item **event, + struct collection_item *template, + struct collection_item *collection, + int mode, va_list args) { int error = EOK; struct collection_item *evt = NULL; - va_list args; - TRACE_FLOW_STRING("elapi_create_event", "Entry"); + TRACE_FLOW_STRING("elapi_create_event_with_vargs", "Entry"); /* Check storage */ - if (event == NULL ) { + if (event == NULL) { TRACE_ERROR_STRING("Event storage must be provided", ""); return EINVAL; } - /* Check for template */ - if (template == NULL ) { - TRACE_ERROR_STRING("Template argument is missing", ""); - return EINVAL; - } - *event = NULL; /* Create collection */ @@ -885,13 +885,16 @@ int elapi_create_event(struct collection_item **event, } /* Add elements from the template */ - error = col_add_collection_to_collection(evt, NULL, NULL, - (struct collection_item *)template, - COL_ADD_MODE_FLAT); - if (error) { - TRACE_ERROR_NUMBER("Failed to add elements from the template. Error", error); - col_destroy_collection(evt); - return error; + /* Check for template */ + if (template != NULL) { + error = col_add_collection_to_collection(evt, NULL, NULL, + (struct collection_item *)template, + COL_ADD_MODE_FLAT); + if (error) { + TRACE_ERROR_NUMBER("Failed to add elements from the template. Error", error); + col_destroy_collection(evt); + return error; + } } /* Add elements from the template */ @@ -904,14 +907,9 @@ int elapi_create_event(struct collection_item **event, } } - /* Process varible arguments */ - va_start(args, mode); - /* Process variable argument list */ error = process_arg_list(evt, args); - va_end(args); - if (error) { TRACE_ERROR_NUMBER("Failed to process argument list. Error", error); col_destroy_collection(evt); @@ -920,6 +918,32 @@ int elapi_create_event(struct collection_item **event, *event = evt; + TRACE_FLOW_STRING("elapi_create_event_with_vargs", "Exit"); + return error; +} + + +/* Create event a wrapper around a function with arg list */ +int elapi_create_event(struct collection_item **event, + struct collection_item *template, + struct collection_item *collection, + int mode, ...) +{ + int error = EOK; + va_list args; + + TRACE_FLOW_STRING("elapi_create_event", "Entry"); + + va_start(args, mode); + + error = elapi_create_event_with_vargs(event, + template, + collection, + mode, + args); + va_end(args); + + TRACE_FLOW_STRING("elapi_create_event", "Exit"); return error; } @@ -992,122 +1016,3 @@ void elapi_destroy_event(struct collection_item *event) TRACE_FLOW_STRING("elapi_destroy_event", "Exit"); } - -/* Initializes default internal template */ -int elapi_set_default_template(unsigned base, ...) -{ - int error = EOK; - struct collection_item *tpl; - va_list args; - - TRACE_FLOW_STRING("elapi_set_default_template", "Entry"); - - /* Clean previous instance of the default template */ - elapi_destroy_event_template(default_template); - default_template = NULL; - - /* Create collection */ - error = col_create_collection(&tpl, E_TEMPLATE_NAME, COL_CLASS_ELAPI_TEMPLATE); - if (error) { - TRACE_ERROR_NUMBER("Failed to create collection. Error", error); - return error; - } - - /* Add elements using base mask */ - error = add_base_elements(tpl, base); - if (error) { - TRACE_ERROR_NUMBER("Failed to add base elements. Error", error); - col_destroy_collection(tpl); - return error; - } - - /* Process varible arguments */ - va_start(args, base); - - /* Process variable argument list */ - error = process_arg_list(tpl, args); - - va_end(args); - - if (error) { - TRACE_ERROR_NUMBER("Failed to process argument list. Error", error); - col_destroy_collection(tpl); - return error; - } - - /* Install a cleanup callback */ - if (atexit(clean_template)) { - TRACE_ERROR_NUMBER("Failed to install cleanup callback. Error", ENOSYS); - col_destroy_collection(tpl); - /* NOTE: Could not find a better error for this case */ - return ENOSYS; - } - - default_template = tpl; - - TRACE_FLOW_STRING("elapi_set_default_template", "Exit"); - return error; -} - - -/* 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; - - 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; - - error = get_default_template(&template); - if (error) { - TRACE_ERROR_NUMBER("Failed to get default template. Error", error); - return error; - } - - /* Create collection */ - error = col_create_collection(&evt, E_EVENT_NAME, COL_CLASS_ELAPI_EVENT); - if (error) { - TRACE_ERROR_NUMBER("Failed to create collection. Error", error); - return error; - } - - /* Add elements from the template */ - error = col_add_collection_to_collection(evt, NULL, NULL, - template, - COL_ADD_MODE_FLAT); - - if (error) { - TRACE_ERROR_NUMBER("Failed to add elements from the template. Error", error); - col_destroy_collection(evt); - return error; - } - - /* Process varible arguments */ - va_start(args, event); - - /* Process variable argument list */ - error = process_arg_list(evt, args); - - va_end(args); - - if (error) { - TRACE_ERROR_NUMBER("Failed to process argument list. Error", error); - col_destroy_collection(evt); - return error; - } - - *event = evt; - - TRACE_FLOW_STRING("elapi_create_simple_event", "Exit"); - return error; -} diff --git a/common/elapi/elapi_event.h b/common/elapi/elapi_event.h index d1e0dd12..f1833396 100644 --- a/common/elapi/elapi_event.h +++ b/common/elapi/elapi_event.h @@ -23,16 +23,17 @@ #include "collection.h" /* Possible predefined elements of the event */ -#define E_TIMESTAMP "stamp" /* string - the value is the format for strftime() - * default is standard format for current locale. */ -#define E_UTCTIME "time" /* int - UTC time as unix time in seconds since 1970 */ -#define E_PID "pid" /* int - Process ID of the current process */ -#define E_APPNAME "appnm" /* string - Name of the current application */ -#define E_HOSTNAME "host" /* string - Name of the current host */ -#define E_HOSTIP "ip" /* string - IP address */ -#define E_SEVERITY "sev" /* int - Same as "priority" in syslog() */ -#define E_HOSTALIAS "halias" /* string - List of alternative host names */ -#define E_HOSTIPS "iplist" /* string - List of alternative IP addresses */ +#define E_TIMESTAMP "__stamp__" /* string - the value is the format for strftime() + * default is standard format for current locale. */ +#define E_UTCTIME "__time__" /* int - UTC time as unix time in seconds since 1970 */ +#define E_OFFSET "__loco__" /* int - local time displacement */ +#define E_PID "__pid__" /* int - Process ID of the current process */ +#define E_APPNAME "__appnm__" /* string - Name of the current application */ +#define E_HOSTNAME "__host__" /* string - Name of the current host */ +#define E_HOSTIP "__ip__" /* string - IP address */ +#define E_SEVERITY "__sev__" /* int - Same as "priority" in syslog() */ +#define E_HOSTALIAS "__halias__" /* string - List of alternative host names */ +#define E_HOSTIPS "__iplist__" /* string - List of alternative IP addresses */ /* There is a special optional attribute of the event named "message". * It is a string that contains text specific to each event. @@ -43,7 +44,7 @@ * The token %(server) will be replaced by value * in the attribute "server" in the event. */ -#define E_MESSAGE "message" +#define E_MESSAGE "__message__" /* Base argument in the template creation function is a bit mask. * Each supported predefined element corresponds to its bit in @@ -51,23 +52,24 @@ */ #define E_HAVE_TIMESTAMP 0x00000001 #define E_HAVE_UTCTIME 0x00000002 -#define E_HAVE_PID 0x00000004 +#define E_HAVE_OFFSET 0x00000004 #define E_HAVE_APPNAME 0x00000010 #define E_HAVE_HOSTNAME 0x00000020 #define E_HAVE_HOSTIP 0x00000040 #define E_HAVE_SEVERITY 0x00000100 #define E_HAVE_HOSTALIAS 0x00000200 #define E_HAVE_HOSTIPS 0x00000400 +#define E_HAVE_PID 0x00001000 /* Convenient bitmasks */ -#define E_BASE_TIME ( E_HAVE_TIMESTAMP | E_HAVE_UTCTIME ) +#define E_BASE_TIME ( E_HAVE_TIMESTAMP | E_HAVE_UTCTIME | E_HAVE_OFFSET) #define E_BASE_HOST ( E_HAVE_HOSTIP | E_HAVE_HOSTNAME ) #define E_BASE_APP ( E_HAVE_APPNAME | E_HAVE_PID ) #define E_BASE_HOSTEXT ( E_HAVE_HOSTALIAS | E_HAVE_HOSTIPS ) #define E_BASE_DEFV1 ( E_BASE_TIME | E_BASE_HOST | E_BASE_APP | E_HAVE_SEVERITY ) /* The default time stamp format */ -#define E_TIMESTAMP_FORMAT "%c" +#define E_TIMESTAMP_FORMAT "%F" #define TIME_ARRAY_SIZE 100 @@ -145,12 +147,6 @@ int elapi_copy_event(struct collection_item **new_event, struct collection_item *source_event); /* Function to destroy event. */ -/* Keep in mind that as soon as event is logged - * it is automatically destroyed so the - * function should be used only in cases - * when you have an event - * that you use as a template for other events. - */ void elapi_destroy_event(struct collection_item *event); /***************************************************************************/ @@ -159,6 +155,10 @@ void elapi_destroy_event(struct collection_item *event); /* Initializes default internal template */ int elapi_set_default_template(unsigned base, ...); +/* Retrieve default template */ +int elapi_get_default_template(struct collection_item **template); + + /* This function will use internal default template. * Hides all complexity from the caller. */ diff --git a/common/elapi/elapi_internal.c b/common/elapi/elapi_internal.c new file mode 100644 index 00000000..2e93a355 --- /dev/null +++ b/common/elapi/elapi_internal.c @@ -0,0 +1,231 @@ +/* + 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 <errno.h> /* for errors */ +#include <stdio.h> /* for printf() - temporarily */ + +#include "elapi_priv.h" +#include "elapi_event.h" +#include "elapi_sink.h" +#include "trace.h" +#include "config.h" +#include "ini_config.h" + +#include "collection_tools.h" /*temporarily */ + +/* Buffer size for time string */ +#define MAX_TIMESTR 200 + +/* I was told during review that I have to hard code the name. + * So it is hardcoded now. + */ +#define ELAPI_DEFAULT_ERROR_FILE "elapiconf.err" + +/* Handler for logging through the sinks */ +int elapi_internal_sink_handler(const char *sink, + int sink_len, + int type, + void *data, + int length, + void *passed_data, + int *stop) +{ + struct elapi_sink_context *sink_env; + TRACE_FLOW_STRING("elapi_internal_sink_handler", "Entry."); + + /* FIXME THIS IS A PLACEHOLDER FUNCTION FOR NOW */ + + sink_env = (struct elapi_sink_context *)(passed_data); + + if (type == COL_TYPE_COLLECTION) { + printf("\n\n\nPROCESSING EVENT:\n"); + col_debug_collection(sink_env->event, COL_TRAVERSE_DEFAULT); + } + else printf("Sink: %s\n", sink); + + TRACE_FLOW_STRING("elapi_internal_sink_handler", "Exit."); + return EOK; +} + +/* Internal sink cleanup function */ +int elapi_internal_sink_cleanup_handler(const char *sink, + int sink_len, + int type, + void *data, + int length, + void *passed_data, + int *stop) +{ + TRACE_FLOW_STRING("elapi_internal_sink_cleanup_handler", "Entry."); + + /* FIXME THIS IS A PLACEHOLDER FUNCTION FOR NOW */ + + if (type != COL_TYPE_COLLECTION) printf("Cleaning Sink: %s\n", sink); + + TRACE_FLOW_STRING("elapi_internal_sink_cleanup_handler", "Exit."); + return EOK; +} + +/* Function to add a sink to the collection */ +int elapi_internal_add_sink_to_collection(struct collection_item *sink_list, + char *sink, + char *appname) +{ + int error = EOK; + int found = 0; + struct sink_descriptor sink_data; + + TRACE_FLOW_STRING("elapi_internal_add_sink_to_collection", "Entry"); + error = col_is_item_in_collection(sink_list, + sink, + COL_TYPE_ANY, + COL_TRAVERSE_DEFAULT, + &found); + if (error) { + TRACE_ERROR_NUMBER("Search returned error", error); + return error; + } + + /* Check if it was found */ + if (found) { + TRACE_ERROR_NUMBER("Attempt to add an exiting sink.", ""); + return EINVAL; + } + + /* Save the pointer to application name into the sink's data block */ + sink_data.dblock.appname = appname; + TRACE_INFO_STRING("add_sink_to_list - saving appname:", sink_data.dblock.appname); + + /* Try to load the sink library */ + + /* FIXME - we need to have at least one sink implemented to enable this code. + * It is a placeholder for now. + error = load_sink(&sink_data, sink); + if (error != 0) { + DEBUG_NUMBER("Failed to load sink", error); + return error; + } + */ + + + /* We got a valid sink so add it to the collection */ + error = col_add_binary_property(sink_list, NULL, + sink, (void *)(&sink_data), + sizeof(struct sink_descriptor)); + if (error != 0) { + TRACE_ERROR_NUMBER("Failed to add sink data as property", error); + return error; + } + + TRACE_FLOW_NUMBER("elapi_internal_add_sink_to_collection returning", error); + return error; +} + +/* Function to create a list of sinks */ +int elapi_internal_construct_sink_list(struct elapi_dispatcher *handle) +{ + int error = EOK; + char **current_sink; + + TRACE_FLOW_STRING("elapi_internal_construct_sink_list", "Entry"); + + /* Allocate collection to store sinks */ + error = col_create_collection(&(handle->sink_list), + ELAPI_SINKS, + COL_CLASS_ELAPI_SINK); + if (error != 0) { + TRACE_ERROR_NUMBER("Failed to create sink collection. Error", error); + /* No cleanup here. + * The calling function will call a cleanup + * of the dispatcher as a whole.*/ + return error; + } + + current_sink = handle->sinks; + handle->sink_counter = 0; + + /* Add sinks as properties to the sink collection */ + while (*current_sink != NULL) { + + TRACE_INFO_STRING("Current sink", *current_sink); + TRACE_INFO_STRING("Will use appname:", handle->appname); + + /* Load sink */ + error = elapi_internal_add_sink_to_collection(handle->sink_list, + *current_sink, + handle->appname); + if ((error != 0) && (error != ELIBACC)) { + TRACE_ERROR_NUMBER("Failed to add sink", error); + /* No cleanup here. */ + return error; + } + + handle->sink_counter++; + current_sink++; + } + + /* Check if we have any sinks available */ + if (handle->sink_counter == 0) { + TRACE_ERROR_NUMBER("No sinks", ELIBACC); + /* No cleanup here. */ + /* Return "Cannot access a needed shared library" */ + return ELIBACC; + } + + TRACE_FLOW_STRING("elapi_internal_construct_sink_list", "Returning success"); + return EOK; +} + +/* If we failed to read configuration record this in the local file */ +void elapi_internal_dump_errors_to_file(struct collection_item *error_list) +{ + FILE *efile; + char timestr[MAX_TIMESTR]; + time_t time_in_sec; + struct tm *time_as_struct; + struct tm time_data; + + TRACE_FLOW_STRING("elapi_internal_dump_errors_to_file", "Entry point"); + + efile = fopen(ELAPI_DEFAULT_ERROR_FILE, "a"); + if (efile == NULL) { + TRACE_ERROR_STRING("No output available.", "Returning."); + return; + } + + time_in_sec = time(NULL); + time_as_struct = localtime_r(&time_in_sec, &time_data); + + fprintf(efile, "\n\n%*s\n\n", 80, "="); + + if ((time_as_struct != NULL) && + (strftime(timestr, sizeof(timestr), E_TIMESTAMP_FORMAT, time_as_struct) == 0)) { + fprintf(efile, "%s\n", timestr); + } + else { + TRACE_FLOW_STRING("elapi_internal_dump_errors_to_file", "Was not able to process time."); + } + + fprintf(efile, "\n"); + print_file_parsing_errors(efile, error_list); + + fclose(efile); + TRACE_FLOW_STRING("elapi_internal_dump_errors_to_file", "Exit"); +} diff --git a/common/elapi/elapi_log.c b/common/elapi/elapi_log.c new file mode 100644 index 00000000..7c0d72ab --- /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; +} diff --git a/common/elapi/elapi_log.h b/common/elapi/elapi_log.h new file mode 100644 index 00000000..6fff82b6 --- /dev/null +++ b/common/elapi/elapi_log.h @@ -0,0 +1,117 @@ +/* + ELAPI + + Header file for 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/>. +*/ + +#ifndef ELAPI_LOG_H +#define ELAPI_LOG_H + +#include "elapi_async.h" + +/* Opaque dispatcher structure */ +struct elapi_dispatcher; + + +/******************** Low level thread safe interface ************************************/ +/* This interface should be used if application plans to control the dispatcher, + * implement its own sinks that can be added dynamically or implements it own routing function. + */ + +/********** Main functions of the interface **********/ + +/* Function to create a dispatcher */ +int elapi_create_dispatcher(struct elapi_dispatcher **dispatcher, /* Handle of the dispatcher will be stored in this variable */ + const char *appname, /* Application name. Passed to the sinks to do initialization */ + const char *config_path); /* See notes below in the elapi_init() function. */ + +/* A more advanced function to create a dispatcher */ +int elapi_create_dispatcher_adv(struct elapi_dispatcher **dispatcher, /* Handle of the dispatcher will be stored in this variable */ + const char *appname, /* Application name. Passed to the sinks to do initialization */ + const char *config_path, /* See notes below in the elapi_init() function. */ + elapi_add_fd add_fd_add_fn, /* Caller's function to add file descriptor */ + elapi_rem_fd add_fd_rem_fn, /* Caller's function to remove file descriptor */ + elapi_add_timer add_timer_fn, /* Caller's function to add timer */ + void *callers_data); /* Data that needs to be passed when caller's callback is called. */ + +/* Function to clean memory associated with the dispatcher */ +void elapi_destroy_dispatcher(struct elapi_dispatcher *dispatcher); + +/* Function to log an event */ +int elapi_dsp_log(struct elapi_dispatcher *dispatcher, struct collection_item *event); + +/* Function to log raw key value pairs without creating an event */ +int elapi_dsp_msg(struct elapi_dispatcher *dispatcher, + struct collection_item *template, + ...); + +/********** Advanced dispatcher management functions **********/ + +/* Managing the sink collection */ +int elapi_alter_dispatcher(struct elapi_dispatcher *dispatcher, /* Dispatcher */ + const char *sink, /* Sink to change */ + int action); /* Action to perform for sink */ + +/* Get sink list */ +char **elapi_get_sink_list(struct elapi_dispatcher *dispatcher); + +/* Free sink list */ +void elapi_free_sink_list(char **sink_list); + + +/******************** High level interface ************************************/ +/* This interface is not thread safe but convenient. It hides the dispatcher. */ + +/* Function to initialize ELAPI library in the single threaded applications */ +/* If config_path = NULL the configuration will be read from the standard locations: + * - First from the global configuration file "elapi.conf" located in the directory + * defined at the compile time by the ELAPI_DEFAULT_CONFIG_DIR constant. + * This file is assumed to contain common ELAPI configuration for this host; + * - Second from the file with name constructed from appname by appending to it + * suffix ".conf". The file will be looked in the directory pointed by + * ELAPI_DEFAULT_CONFIG_APP_DIR constant that is defined at compile time. + * The data from second file overwrites and complements the data from the first + * one. + * It is expected that applications will take advantage of the common + * central convention so config_path should be NULL in most cases. + * + * If config_path points to a file the function will try to read the file + * as if it is a configuration file. The appname is ignored in this case. + * If config_path points to a directory, the function will try to read + * configuration from the file with name constructed by appending suffix ".conf" + * to appname. The file will be looked up in that directory. + * If the config_path is neither file or directory the default values will be used + * to initialize dispatcher. + * + * In case appname is NULL a default value defined by build time constant + * ELAPI_DEFAULT_APP_NAME will be used. + */ +int elapi_init(const char *appname, const char *config_path); + +/* Log key value pairs */ +int elapi_msg(struct collection_item *template, ...); + + +/* Log event */ +int elapi_log(struct collection_item *event); + +/* Get dispatcher if you want to add sink to a default dispatcher or do some advanced operations */ +struct elapi_dispatcher *elapi_get_dispatcher(void); + +/* Close audit */ +void elapi_close(void); + +#endif diff --git a/common/elapi/elapi_priv.h b/common/elapi/elapi_priv.h index feb9a7f6..c536f4ec 100644 --- a/common/elapi/elapi_priv.h +++ b/common/elapi/elapi_priv.h @@ -20,13 +20,95 @@ #ifndef ELAPI_PRIV_H #define ELAPI_PRIV_H +#include "collection.h" +#include "elapi_async.h" /* Classes of the collections used by ELAPI internally */ #define COL_CLASS_ELAPI_BASE 21000 #define COL_CLASS_ELAPI_EVENT COL_CLASS_ELAPI_BASE + 0 #define COL_CLASS_ELAPI_TEMPLATE COL_CLASS_ELAPI_BASE + 1 +#define COL_CLASS_ELAPI_SINK COL_CLASS_ELAPI_BASE + 2 /* Names for the collections */ #define E_TEMPLATE_NAME "template" #define E_EVENT_NAME "event" + +#define ELAPI_DISPATCHER "dispatcher" +#define ELAPI_SINKS "sinks" + +struct elapi_dispatcher { + char **sinks; + int need_to_free; + char *appname; + /*event_router_fn router; - FIXME - not defined yet */ + struct collection_item *sink_list; + int sink_counter; + struct collection_item *ini_config; + /* Default event template */ + struct collection_item *default_template; + /* Async processing related data */ + elapi_add_fd add_fd_add_fn; + elapi_rem_fd add_fd_rem_fn; + elapi_add_timer add_timer_fn; + void *callers_data; + int async_mode; +}; + +/* Structure to pass data from logging function to sinks */ +struct elapi_sink_context { + struct collection_item *event; + struct elapi_dispatcher *handle; + char *format; + char *previous; + int previous_status; +}; + +/* The structure to hold a command and a result of the command execution */ +struct elapi_get_sink { + int action; + int found; +}; + +/* Function to create event using arg list */ +int elapi_create_event_with_vargs(struct collection_item **event, + struct collection_item *template, + struct collection_item *collection, + int mode, va_list args); + +/* Function to create event template using arg list */ +int elapi_create_event_template_with_vargs(struct collection_item **template, + unsigned base, + va_list args); + +/* Sink handler function */ +int elapi_internal_sink_handler(const char *sink, + int sink_len, + int type, + void *data, + int length, + void *passed_data, + int *stop); + +/* Internal sink cleanup function */ +int elapi_internal_sink_cleanup_handler(const char *sink, + int sink_len, + int type, + void *data, + int length, + void *passed_data, + int *stop); + + +/* Create list of the sinks */ +int elapi_internal_construct_sink_list(struct elapi_dispatcher *handle); + +/* Function to add a sink to the collection */ +int elapi_internal_add_sink_to_collection(struct collection_item *sink_list, + char *sink, + char *appname); + +/* Send ELAPI config errors into a file */ +void elapi_internal_dump_errors_to_file(struct collection_item *error_list); + + #endif diff --git a/common/elapi/elapi_sink.h b/common/elapi/elapi_sink.h new file mode 100644 index 00000000..40b12a26 --- /dev/null +++ b/common/elapi/elapi_sink.h @@ -0,0 +1,78 @@ +/* + ELAPI + + Common sink interface header. + + 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/>. +*/ + +#ifndef ELAPI_SINK_H +#define ELAPI_SINK_H + +#include <time.h> +#include "collection.h" + +#define ELAPI_SINK_OK 0 /* Sink can be used for logging */ +#define ELAPI_SINK_SUSPENDED 1 /* Sink is temporary disabled due to recoverable error */ +#define ELAPI_SINK_DISABLED 2 /* Sink is explicitely disabled by the application */ +#define ELAPI_SINK_PULSE 3 /* Sink is disabled for this one event */ + +#define SINK_LIB_NAME_SIZE 100 +#define SINK_ENTRY_POINT "get_sink_info" +#define SINK_NAME_TEMPLATE "libelapi_sink_%s.so" +#define SINK_NEVER_RETRY -1 + +/* Flags related to loading sinks */ +#define SINK_FLAG_NO_LIMIT 0x00000000 /* NO limits to loading and manipulating this sink - default */ +#define SINK_FLAG_LOAD_SINGLE 0x00000001 /* Only allow one instance of the sink per process */ + +struct data_descriptor { + char *appname; + void *config; + void *internal_data; +}; + +/* Log facility callbacks */ +typedef int (*init_fn)(struct data_descriptor *dblock); +typedef void (*cleanup_fn)(struct data_descriptor *dblock); +typedef int (*format_fn)(struct data_descriptor *dblock, const char *format_str, struct collection_item *event); +typedef int (*submit_fn)(struct data_descriptor *dblock); +typedef void (*close_fn)(struct data_descriptor *dblock); + +struct sink_capability { + int retry_interval; + int flags; + int instance; + init_fn init_cb; + cleanup_fn cleanup_cb; + format_fn format_cb; + submit_fn submit_cb; + close_fn close_cb; +}; + +/* The only open function the link can expose */ +typedef void (*capability_fn)(struct sink_capability *sink_cpb_block); + +struct sink_descriptor { + struct sink_capability sink_cpb_block; + struct data_descriptor dblock; + int suspended; + time_t lasttry; + void *lib_handle; +}; + +/*Standard capability function */ +void get_sink_info(struct sink_capability *sink_cpb_block); + +#endif diff --git a/common/elapi/elapi_ut.c b/common/elapi/elapi_ut.c index b9493da1..2866411f 100644 --- a/common/elapi/elapi_ut.c +++ b/common/elapi/elapi_ut.c @@ -68,11 +68,21 @@ int simple_event_test(void) return error; } - col_debug_collection(event, COL_TRAVERSE_DEFAULT); - col_debug_collection(event, COL_TRAVERSE_FLAT); + error = elapi_log(event); elapi_destroy_event(event); + if (error) { + printf("Failed to log event! %d\n", error); + return error; + } + + error = elapi_msg(NULL, "a", "b", "c", "d", E_EOARG); + if (error) { + printf("Failed to log event! %d\n", error); + return error; + } + printf("Simple test success!\n"); return error; @@ -81,10 +91,11 @@ int simple_event_test(void) int complex_event_test(void) { int error = 0; - struct collection_item *template; - struct collection_item *event, *event_copy; + struct collection_item *template = NULL; + struct collection_item *event = NULL, *event_copy = NULL; char bin[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; - struct collection_item *col; + struct collection_item *col = NULL; + struct elapi_dispatcher *dispatcher = NULL; printf("Complex test START:\n"); @@ -126,10 +137,18 @@ int complex_event_test(void) col_debug_collection(template, COL_TRAVERSE_FLAT); col_debug_collection(event, COL_TRAVERSE_FLAT); + error = elapi_log(event); - elapi_destroy_event_template(template); elapi_destroy_event(event); + if (error) { + printf("Failed to log event! %d\n", error); + return error; + } + + + elapi_destroy_event_template(template); + error = elapi_create_event_template( &template, E_BASE_DEFV1 | E_BASE_HOSTEXT, @@ -150,7 +169,7 @@ int complex_event_test(void) } if ((error = col_create_collection(&col, "test", 0)) || - /* We are forsing overwrite with different type */ + /* We are forcing overwrite with different type */ (error = col_add_int_property(col, NULL, "unsigned_number", 1)) || (error = col_add_long_property(col, NULL, "bin", 100000000L))) { elapi_destroy_event_template(template); @@ -181,8 +200,6 @@ int complex_event_test(void) col_debug_collection(event, COL_TRAVERSE_FLAT); - elapi_destroy_event_template(template); - if ((error = col_create_collection(&col, "test", 0)) || /* We are forsing overwrite with different type */ (error = col_add_int_property(col, NULL, "zzz", 1)) || @@ -204,6 +221,7 @@ int complex_event_test(void) if (error) { printf("Failed to set create template %d\n", error); elapi_destroy_event(event); + elapi_destroy_event_template(template); col_destroy_collection(col); return error; } @@ -211,17 +229,60 @@ int complex_event_test(void) col_destroy_collection(col); error = elapi_copy_event(&event_copy, event); - if (error) { printf("Failed to set create template %d\n", error); elapi_destroy_event(event); + elapi_destroy_event_template(template); + return error; + } + + error = elapi_create_dispatcher(&dispatcher, "elapi_ut", "./sdfdsdf"); + if (error) { + elapi_destroy_event(event); + elapi_destroy_event(event_copy); + elapi_destroy_event_template(template); + printf("Failed to create dispatcher %d\n", error); return error; } + error = elapi_dsp_log(dispatcher, event); + elapi_destroy_event(event); - col_debug_collection(event_copy, COL_TRAVERSE_FLAT); + + if (error) { + elapi_destroy_event(event_copy); + elapi_destroy_event_template(template); + printf("Failed to log event! %d\n", error); + return error; + } + + error = elapi_dsp_log(dispatcher, event_copy); + elapi_destroy_event(event_copy); + if (error) { + elapi_destroy_event_template(template); + printf("Failed to log event! %d\n", error); + return error; + } + + error = elapi_dsp_msg(dispatcher, template, "a", "b", "c", "d", E_EOARG); + if (error) { + elapi_destroy_event_template(template); + printf("Failed to log event! %d\n", error); + return error; + } + + error = elapi_dsp_msg(dispatcher, NULL, "a", "b", "c", "d", E_EOARG); + if (error) { + elapi_destroy_event_template(template); + printf("Failed to log event! %d\n", error); + return error; + } + + elapi_destroy_event_template(template); + elapi_destroy_dispatcher(dispatcher); + return error; } diff --git a/common/elapi/elapi_ut.conf b/common/elapi/elapi_ut.conf new file mode 100644 index 00000000..19cbeb71 --- /dev/null +++ b/common/elapi/elapi_ut.conf @@ -0,0 +1,6 @@ +[dispatcher] +sinks = foo, bar, baz + + + +; test |