From 735ac5cb6596ac7219881a8be1b215825de9401b Mon Sep 17 00:00:00 2001 From: Dmitri Pal Date: Mon, 13 Jul 2009 19:35:31 -0400 Subject: ELAPI First part of the interface This is just a part of the interface, a beginning. Most likely some of the functions will be altered but it is a starting point. For example in future there will be a way to override some of the parts of the default template using the application configuration file. Removed obfuscation of the data types based on discussion with Simo. --- common/Makefile.am | 7 +- common/collection/collection.c | 2 + common/configure.ac | 2 +- common/elapi/Makefile.am | 31 ++ common/elapi/configure.ac | 26 + common/elapi/elapi.h | 25 + common/elapi/elapi.pc.in | 11 + common/elapi/elapi_event.c | 1112 ++++++++++++++++++++++++++++++++++++++++ common/elapi/elapi_event.h | 168 ++++++ common/elapi/elapi_priv.h | 32 ++ common/elapi/elapi_ut.c | 243 +++++++++ common/elapi/m4/.dir | 0 12 files changed, 1655 insertions(+), 4 deletions(-) create mode 100644 common/elapi/Makefile.am create mode 100644 common/elapi/configure.ac create mode 100644 common/elapi/elapi.h create mode 100644 common/elapi/elapi.pc.in create mode 100644 common/elapi/elapi_event.c create mode 100644 common/elapi/elapi_event.h create mode 100644 common/elapi/elapi_priv.h create mode 100644 common/elapi/elapi_ut.c create mode 100644 common/elapi/m4/.dir (limited to 'common') diff --git a/common/Makefile.am b/common/Makefile.am index d3329c9b1..445a84356 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -1,5 +1,5 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = trace collection ini dhash path_utils +SUBDIRS = trace collection ini dhash path_utils elapi if SINGLELIB # Build all components as a single shared library lib_LTLIBRARIES = libsssd_util.la @@ -7,11 +7,12 @@ libsssd_util_la_SOURCES = libsssd_util_la_LIBADD = \ collection/libcollection.la \ ini/libini_config.la \ - dhash/libdhash.la + dhash/libdhash.la \ + elapi/libelapi.la libsssd_util_la_CFLAGS = $(AM_CFLAGS) \ -I ./collection \ -I ./ini \ -I ./dhash \ + -I ./elapi \ -I ./trace endif - diff --git a/common/collection/collection.c b/common/collection/collection.c index 1e3fe5c3f..69475b048 100644 --- a/common/collection/collection.c +++ b/common/collection/collection.c @@ -384,6 +384,7 @@ int col_insert_item_into_current(struct collection_item *collection, current = parent->next; item->next = current->next; parent->next = item; + if (header->last == current) header->last = item; col_delete_item(current); header->count--; TRACE_FLOW_STRING("col_insert_item_into_current", "Dup overwrite exit"); @@ -397,6 +398,7 @@ int col_insert_item_into_current(struct collection_item *collection, current = parent->next; item->next = current->next; parent->next = item; + if (header->last == current) header->last = item; col_delete_item(current); header->count--; TRACE_FLOW_STRING("col_insert_item_into_current", "Dup overwrite exit"); diff --git a/common/configure.ac b/common/configure.ac index 89b3309ec..dc6b42c97 100644 --- a/common/configure.ac +++ b/common/configure.ac @@ -21,7 +21,7 @@ AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] ],[AC_SUBST([TR AC_CONFIG_FILES([Makefile trace/Makefile]) -AC_CONFIG_SUBDIRS([collection dhash ini path_utils]) +AC_CONFIG_SUBDIRS([collection dhash ini path_utils elapi]) AC_DEFUN([WITH_SINGLELIB], [ AC_ARG_WITH([singlelib], diff --git a/common/elapi/Makefile.am b/common/elapi/Makefile.am new file mode 100644 index 000000000..0698a8944 --- /dev/null +++ b/common/elapi/Makefile.am @@ -0,0 +1,31 @@ +TRACE_LEVEL=@TRACE_VAR@ + +topdir=$(srcdir)/.. + +AM_CFLAGS = +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) + +ACLOCAL_AMFLAGS = -I m4 + +# Set up the pkg-config file +pkgconfigdir = $(libdir)/pkgconfig +dist_noinst_DATA = elapi.pc + +# Build library +noinst_LTLIBRARIES = libelapi.la +libelapi_la_SOURCES = \ + elapi_event.c \ + elapi_event.h \ + elapi_event_priv.h \ + elapi.h + +# Build unit test +noinst_PROGRAMS = elapi_ut +elapi_ut_SOURCES = elapi_ut.c +elapi_ut_LDADD = ../collection/libcollection.la libelapi.la diff --git a/common/elapi/configure.ac b/common/elapi/configure.ac new file mode 100644 index 000000000..e5c19ae75 --- /dev/null +++ b/common/elapi/configure.ac @@ -0,0 +1,26 @@ +AC_INIT([elapi],[0.0.1],[freeipa-devel@redhat.com]) +AC_CONFIG_SRCDIR([elapi.h]) +AC_CONFIG_AUX_DIR([build]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_PROG_CC +AC_PROG_LIBTOOL +AC_CONFIG_MACRO_DIR([m4]) +AC_PROG_INSTALL + +AM_CONDITIONAL([HAVE_GCC], [test "$ac_cv_prog_gcc" = yes]) + +m4_pattern_allow([AM_SILENT_RULES]) +AM_SILENT_RULES + +AC_CONFIG_HEADERS([config.h]) + +# Enable trace build +AC_ARG_ENABLE([trace], + [AS_HELP_STRING([--enable-trace[=LEVEL]],[build with low level tracing enabled])], + [trace_level="$enableval"], + [trace_level="0"]) +AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] ],[AC_SUBST([TRACE_VAR],["-DTRACE_LEVEL=$trace_level"])]) + + +AC_CONFIG_FILES([Makefile elapi.pc]) +AC_OUTPUT diff --git a/common/elapi/elapi.h b/common/elapi/elapi.h new file mode 100644 index 000000000..d4e9da4e8 --- /dev/null +++ b/common/elapi/elapi.h @@ -0,0 +1,25 @@ +/* + ELAPI + + Aggregated header file for the ELAPI interface. + + Copyright (C) Dmitri Pal 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 . +*/ + +#ifndef ELAPI_H +#define ELAPI_H + +#include "elapi_event.h" + +#endif diff --git a/common/elapi/elapi.pc.in b/common/elapi/elapi.pc.in new file mode 100644 index 000000000..73d2556ea --- /dev/null +++ b/common/elapi/elapi.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: Event logging API (ELAPI) library +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lelapi +Cflags: -I${includedir} +URL: http://fedorahosted.org/sssd/ diff --git a/common/elapi/elapi_event.c b/common/elapi/elapi_event.c new file mode 100644 index 000000000..9322f8217 --- /dev/null +++ b/common/elapi/elapi_event.c @@ -0,0 +1,1112 @@ +/* + ELAPI + + Implementation of the ELAPI event interface. + + Copyright (C) Dmitri Pal 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 . +*/ + +#define _GNU_SOURCE +#include /* for getpid() */ +#include /* for getpid() */ +#include /* for realloc() */ +#include /* for contants releted to severity */ +#include /* for gethostname() */ +#include /* for errors */ +#include /* for memset() and other */ +#include /* for gethostbyname() */ +#include /* for inet_ntop() */ +#include /* for inet_ntop() */ +#include /* for isspace() */ +#include /* for va_arg() */ +#include /* for strndup() */ +#include /* for getifaddrs() */ + +#include "elapi_priv.h" +#include "elapi_event.h" +#include "trace.h" +#include "config.h" + +#include "collection_tools.h" + +/* Internal return states from key processing */ +#define E_LIST_EMPTY 0 +#define E_LIST_ERROR 1 +#define E_LIST_LAST 2 +#define E_LIST_ADD 3 +#define E_LIST_REMOVE 4 + +#define LOCALHOSTDOMAIN "localhost.localdomain" +#define LOCALHOST "localhost" +#define LOCALADDRESS "127.0.0.1" +#define LOCALADDRESSV6 "::1" + +const char *undefined = "undefined"; +const char *str_yes = "yes"; +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) +{ + char hostname[NI_MAXHOST + 1]; + int error = EOK; + int gai_ret_host = 0; + int gai_ret_addr = 0; + char host[NI_MAXHOST]; + char address[NI_MAXHOST]; + char *hnm, *haddr; + struct ifaddrs *ifaddr, *ifa; + int family; + int set_hostname = 0; + int set_ip = 0; + + 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. + * It is a best effort attempt. + */ + + /* If we are not asked for hostname then say we already have it */ + if (!(base & E_HAVE_HOSTNAME)) set_hostname = 1; + /* 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) { + + TRACE_FLOW_STRING("getifaddrs", "Ok"); + + /* Walk through linked list, maintaining head pointer so we + can free list later */ + + ifa = ifaddr; + while (ifa != NULL) { + + TRACE_FLOW_STRING("Top of the loop", ""); + + if (!ifa->ifa_addr) { + ifa = ifa->ifa_next; + continue; + } + + family = ifa->ifa_addr->sa_family; + + TRACE_FLOW_NUMBER("Family", family); + + /* For an AF_INET* interface address, display the address */ + 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)); + + gai_ret_host = getnameinfo(ifa->ifa_addr, + (family == AF_INET) ? sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6), + host, + NI_MAXHOST, + NULL, + 0, + 0 /* Gets host name */); + + gai_ret_addr = getnameinfo(ifa->ifa_addr, + (family == AF_INET) ? sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6), + address, + NI_MAXHOST, + NULL, + 0, + NI_NUMERICHOST /* Gets address as string */); + + /* If we have not set host name set it */ + if(!set_hostname) { + + TRACE_FLOW_STRING("Host name is not set", ""); + + hnm = NULL; + /* Use host name returned by gethostname() as main host name */ + if (!gethostname(hostname, NI_MAXHOST)) hnm = hostname; + else { + /* We we able to get a host name ? */ + if (gai_ret_host == 0) { + TRACE_INFO_STRING("getnameinfo returned:", host); + hnm = host; + } + } + + /* 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; + + /* If host name is not NULL it would work for us */ + if (hnm) { + TRACE_INFO_STRING("Adding host name:", hnm); + error = col_add_str_property(tpl, NULL, E_HOSTNAME, hnm, 0); + if (error) { + TRACE_ERROR_NUMBER("Failed to add host name. Error", error); + freeifaddrs(ifaddr); + return error; + } + /* Done with the name */ + set_hostname = 1; + } + } + + /* If we have not set processed ip address do it */ + if(!set_ip) { + + TRACE_FLOW_STRING("Address is not set", ""); + + haddr = NULL; + if (gai_ret_addr == 0) { + TRACE_INFO_STRING("getnameinfo returned:", address); + if ((strncasecmp(address, LOCALADDRESS, sizeof(LOCALADDRESS)) != 0 ) && + (strncasecmp(address, LOCALADDRESSV6, sizeof(LOCALADDRESSV6)) != 0 )) { + TRACE_INFO_STRING("Not an unhelpful address", ""); + haddr = address; + } + } + + if (haddr) { + TRACE_INFO_STRING("Adding host address:", haddr); + error = col_add_str_property(tpl, NULL, E_HOSTIP, haddr, 0); + if (error) { + TRACE_ERROR_NUMBER("Failed to add host name. Error", error); + freeifaddrs(ifaddr); + return error; + } + set_ip = 1; + } + } + + /* If we have a name and we are told to deal with alias names */ + if ((set_hostname) && (base & E_HAVE_HOSTALIAS)) { + + TRACE_INFO_NUMBER("gai_ret_host:", gai_ret_host); + TRACE_INFO_STRING("host:", host); + TRACE_INFO_STRING("address:", address); + 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; + else hnm = host; + + if (hnm) { + TRACE_INFO_STRING("Adding alias host name:", hnm); + error = col_add_str_property(tpl, NULL, E_HOSTALIAS, hnm, 0); + if (error) { + TRACE_ERROR_NUMBER("Failed to add host name. Error", error); + freeifaddrs(ifaddr); + return error; + } + } + } + + /* If we got then main IP and we are told to deal with opther IPs */ + 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; + else haddr = address; + + if (haddr) { + TRACE_INFO_STRING("Adding alias host IP:", haddr); + error = col_add_str_property(tpl, NULL, E_HOSTIPS, haddr, 0); + if (error) { + TRACE_ERROR_NUMBER("Failed to add host name. Error", error); + freeifaddrs(ifaddr); + return error; + } + } + } + } + TRACE_INFO_NUMBER("Moving to next", ifa->ifa_next); + ifa = ifa->ifa_next; + TRACE_INFO_NUMBER("Moved to", ifa); + } + + freeifaddrs(ifaddr); + } + + /* Make sure that we really have the name after all */ + if (!set_hostname) { + TRACE_INFO_STRING("No host name using default:", undefined); + error = col_add_str_property(tpl, NULL, E_HOSTNAME, undefined, 0); + if (error) { + TRACE_ERROR_NUMBER("Failed to add host name. Error", error); + return error; + } + } + + /* Make sure that we really have the IP after all */ + if (!set_ip) { + TRACE_INFO_STRING("No host name using default:", undefined); + error = col_add_str_property(tpl, NULL, E_HOSTIP, undefined, 0); + if (error) { + TRACE_ERROR_NUMBER("Failed to add host name. Error", error); + return error; + } + } + + TRACE_FLOW_STRING("add_host_identity", "Exit"); + return error; +} + +/* Add base elements to template collection */ +static int add_base_elements(struct collection_item *tpl, unsigned base) +{ + int error = EOK; + int pass_base; + + TRACE_FLOW_STRING("add_base_elements", "Entry"); + + /* Populate the template using base */ + if (base & E_HAVE_TIMESTAMP) { + /* Value is the format string for strftime() */ + error = col_add_str_property(tpl, NULL, E_TIMESTAMP, E_TIMESTAMP_FORMAT, sizeof(E_TIMESTAMP_FORMAT)); + if (error) { + TRACE_ERROR_NUMBER("Failed to add timestamp. Error", error); + return error; + } + } + + if (base & E_HAVE_UTCTIME) { + /* Value does not matter */ + error = col_add_int_property(tpl, NULL, E_UTCTIME, 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()); + if (error) { + TRACE_ERROR_NUMBER("Failed to add pid. Error", error); + return error; + } + } + + if (base & E_HAVE_APPNAME) { + /* Value does not matter */ + error = col_add_str_property(tpl, NULL, E_APPNAME, "", 1); + if (error) { + TRACE_ERROR_NUMBER("Failed to add application name. Error", error); + return error; + } + } + + if (base & E_HAVE_SEVERITY) { + /* Value is the default severity */ + error = col_add_int_property(tpl, NULL, E_SEVERITY, LOG_USER | LOG_INFO); + if (error) { + TRACE_ERROR_NUMBER("Failed to add pid. Error", error); + return error; + } + } + + /* If we need to add aliases or other IPs call the function */ + if ((base & E_HAVE_HOSTNAME) || + (base & E_HAVE_HOSTIP) || + (base & E_HAVE_HOSTALIAS) || + (base & E_HAVE_HOSTIPS)) { + + pass_base = base; + + /* make sure we have extensions on top of the basic data */ + if ((base & E_HAVE_HOSTALIAS) && (!(base & E_HAVE_HOSTNAME))) pass_base |= E_HAVE_HOSTNAME; + if ((base & E_HAVE_HOSTIPS) && (!(base & E_HAVE_HOSTIP))) pass_base |= E_HAVE_HOSTIP; + + error = add_host_identity(tpl, pass_base); + if (error) { + TRACE_ERROR_NUMBER("Failed to add host identity. Error", error); + return error; + } + } + + TRACE_FLOW_STRING("add_base_elements", "Exit"); + return error; +} + + +/* 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) +{ + int adjust_by = 0; + char *start = NULL; + char *cursor = NULL; + char *end = NULL; + int ret = E_LIST_EMPTY; + + TRACE_FLOW_STRING("interprete_key", "Entry"); + + TRACE_INFO_STRING("Key", key); + + /* Initialize passed in data */ + *has_len = 0; + *property = NULL; + *type = COL_TYPE_STRING; + + cursor = key; + + while (isspace(*cursor)) cursor++; + + /* End of string - we are done */ + if (*cursor == '\0') { + TRACE_ERROR_STRING("Empty key - end of processing!", ""); + return E_LIST_EMPTY; + } + + /* This is the beginning of the formatted token */ + if (*cursor == '-') { + + /* This is a remove attribute case */ + + cursor++; + /* Skip spaces if any */ + while (isspace(*cursor)) cursor++; + + /* Mark the start of the actual property */ + start = cursor; + + /* Now we need to extract the name of the property */ + /* We will not be nice here - the add_property will validate if the name is ok */ + while ((*cursor != '\0') && (!isspace(*cursor))) cursor++; + + /* End of string - we are done */ + if (cursor == start) { + TRACE_ERROR_STRING("Invalid key - end of processing!", ""); + return E_LIST_EMPTY; + } + + *prop_len = cursor - start; + *property = start; + TRACE_INFO_STRING("We are told to remove the property!", *property); + ret = E_LIST_REMOVE; + } + else if (*cursor == '%') { + + /* We got a full key with format string */ + + cursor++; + if ((*cursor == '*') && (*(cursor+1) == 's') && (*(cursor+2) == '(')) { + *type = COL_TYPE_STRING; + *has_len = 1; + adjust_by = 3; + } + else if ((*cursor == 's') && (*(cursor+1) == '(')) { + *type = COL_TYPE_STRING; + adjust_by = 2; + } + else if (((*cursor == 'i')||(*cursor == 'd')) && (*(cursor+1) == '(')) { + *type = COL_TYPE_INTEGER; + adjust_by = 2; + } + else if ((*cursor == 'u') && (*(cursor+1) == '(')) { + *type = COL_TYPE_INTEGER; + adjust_by = 2; + } + else if ((*cursor == 'l') && ((*(cursor+1) == 'i')||(*(cursor+1) == 'd')) && (*(cursor+2) == '(')) { + *type = COL_TYPE_LONG; + adjust_by = 3; + } + else if ((*cursor == 'l') && (*(cursor+1) == 'u') && (*(cursor+2) == '(')) { + *type = COL_TYPE_LONG; + adjust_by = 3; + } + else if (((*cursor == 'f')||(*cursor == 'e')) && (*(cursor+1) == '(')) { + *type = COL_TYPE_DOUBLE; + adjust_by = 2; + } + else if (((*cursor == 's') || (*cursor == 'd')) && (*(cursor+1) == 'b') && (*(cursor+2) == '(')) { + *type = COL_TYPE_BOOL; + adjust_by = 3; + if (*cursor == 's') *bool_type = 1; + else *bool_type = 0; + } + else if ((*cursor == 'n') && (*(cursor+1) == '(')) { + *type = COL_TYPE_BINARY; + adjust_by = 2; + } + else { + TRACE_ERROR_STRING("Invalid key - end of processing!", key); + return E_LIST_ERROR; + } + + cursor += adjust_by; + + /* Skip spaces if any */ + while (isspace(*cursor)) cursor++; + + start = cursor; + + /* Now we need to extract the name of the property */ + /* We will not be nice here - the add_property will validate if the name is ok */ + while ((*cursor != '\0') && (*cursor != ')') && (!isspace(*cursor))) cursor++; + + /* End of string - we are done */ + if ((*cursor == '\0') || (cursor == start)) { + TRACE_ERROR_STRING("Invalid key - end of processing!", ""); + return E_LIST_EMPTY; + } + + end = cursor; + + /* Skip spaces if any */ + while (isspace(*cursor)) cursor++; + + /* Check that end of the string is in proper format */ + if ((*cursor != ')') && (*(cursor + 1) != '\0')) { + TRACE_ERROR_STRING("Invalid key - missing ')' .", key); + return E_LIST_ERROR; + } + + *property = start; + *prop_len = end - start; + + TRACE_INFO_STRING("Property:", *property); + TRACE_INFO_NUMBER("Property len:", *prop_len); + ret = E_LIST_ADD; + } + else { + /* Just got a key */ + /* Mark the start of the actual property */ + start = cursor; + + /* Now we need to extract the name of the property */ + /* We will not be nice here - the add_property will validate if the name is ok */ + while ((*cursor != '\0') && (!isspace(*cursor))) cursor++; + + /* End of string - we are done */ + if (cursor == start) { + TRACE_ERROR_STRING("Invalid key - end of processing!", ""); + return E_LIST_EMPTY; + } + + *prop_len = cursor - start; + *property = start; + TRACE_INFO_STRING("We are told to add/update the property (or last)!", *property); + + if(strncmp(*property, E_EOARG, *prop_len) == 0) ret = E_LIST_LAST; + else ret = E_LIST_ADD; + } + + TRACE_INFO_STRING("Returning Property:",*property); + TRACE_INFO_NUMBER("Returning Property len:", *prop_len); + TRACE_INFO_NUMBER("Returning Type:", *type); + TRACE_INFO_NUMBER("Returning Has length:", *has_len); + + + TRACE_FLOW_STRING("interprete_key", "Exit"); + + return ret; +} + +/* Make sure that the right string is given for bool value */ +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)) { + 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)) { + TRACE_INFO_STRING("Matched FALSE", ""); + *data_bool = '\0'; + return 1; + } + TRACE_INFO_STRING("Matched NOTHING", ""); + return 0; +} + + +/* Process argument list */ +/* Update collection based on the passed in arguments */ +static int process_arg_list(struct collection_item *col, + va_list args) +{ + int error = EOK; + char *arg = NULL; + char *propcopy = NULL; + int ret = 0; + int type = 0; + char *property = NULL; + int prop_len = 0; + int has_len = 0; + int bool_type = 0; + char *data_str = NULL; + int data_int = 0; + unsigned int data_uint = 0; + long data_long = 0; + unsigned long data_ulong = 0; + void *data_bin = NULL; + double data_dbl = 0.; + int length = 0; + void *data = NULL; + unsigned char data_bool = '\0'; + + TRACE_FLOW_STRING("process_arg_list", "Entry."); + + /* We will break from the loop when we find the last item */ + while (1) { + + /* Get next key */ + arg = va_arg(args, char *); + + if (arg == NULL) { + TRACE_ERROR_STRING("Invalid NULL argument.", "Key can't be NULL"); + return EINVAL; + } + + /* Interprete the key. + * It can be just " key ", + * it can be " - key ", + * or it can be a formatted string + * something like " %*s( key )". + * 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); + + if (ret == E_LIST_LAST) { + TRACE_INFO_STRING("Process found last key", arg); + break; + } + + if ((ret == E_LIST_ADD) || (ret == E_LIST_REMOVE)) { + /* We need to create a dup of the string */ + propcopy = strndup(property, prop_len); + if (propcopy == NULL) { + TRACE_ERROR_STRING("Failed to allocate property", arg); + return ENOMEM; + } + + TRACE_INFO_STRING("Processing property", propcopy); + + /* Are we supposed to add? */ + if (ret == E_LIST_ADD) { + + + /* NOTE: We are not going to check if the key value pairs + * are consistent. + * It can be made a bit more bullet proof by adding + * significant complexity to the code but I do not + * think it makes much sense to do so. + * There is no way to prevent the argument mismatch + * issues 100%. Printf can crash if aguments are + * missed or bad, so do we... + */ + + /* Get data */ + switch(type) { + + case COL_TYPE_STRING: data_str = va_arg(args, char *); + data = (void *)data_str; + if (has_len) length = va_arg(args, int); + else length = strlen(data_str) + 1; + TRACE_INFO_STRING("Adding string:", data_str); + TRACE_INFO_NUMBER("Length:",length); + break; + + case COL_TYPE_BINARY: data_bin = va_arg(args, void *); + data = (void *)data_bin; + length = va_arg(args, int); + break; + + case COL_TYPE_INTEGER: data_int = va_arg(args, int); + data = (void *)(&data_int); + length = sizeof(int); + break; + + case COL_TYPE_UNSIGNED: data_uint = va_arg(args, unsigned int); + data = (void *)(&data_uint); + length = sizeof(unsigned int); + break; + + case COL_TYPE_LONG: data_long = va_arg(args, long); + data = (void *)(&data_long); + length = sizeof(long); + break; + + case COL_TYPE_ULONG: data_ulong = va_arg(args, unsigned long); + data = (void *)(&data_ulong); + length = sizeof(unsigned long); + break; + + case COL_TYPE_DOUBLE: data_dbl = va_arg(args, double); + data = (void *)(&data_dbl); + length = sizeof(double); + break; + + case COL_TYPE_BOOL: if (bool_type) { + /* It is a string */ + data_str = va_arg(args,char *); + /* Check if it is a valid str */ + if (!(convert_bool(data_str, &data_bool))) { + TRACE_ERROR_STRING("Failed to to convert bool value", data_str); + free(propcopy); + return EINVAL; + } + } + else { + /* It is an int */ + data_int = va_arg(args, int); + if (data_int) data_bool = 1; + else data_bool = 0; + } + + data = (void *)(&data_bool); + length = sizeof(unsigned char); + break; + + default: + TRACE_ERROR_STRING("Invalid or unknown type", propcopy); + free(propcopy); + return EINVAL; + } + + /* Insert or update */ + error = col_insert_property_with_ref(col, + NULL, + COL_DSP_END, + NULL, + 0, + COL_INSERT_DUPOVER, + propcopy, + type, + data, + length, + NULL); + if (error) { + TRACE_ERROR_STRING("Error inserting property", property); + free(propcopy); + return error; + } + } + else { + /* Remove case */ + while (error != ENOENT) { + error = col_delete_property(col, + propcopy, + COL_TYPE_ANY, + COL_TRAVERSE_DEFAULT); + + if ((error) && (error != ENOENT)) { + TRACE_ERROR_STRING("Error deleting property", propcopy); + free(propcopy); + return error; + } + } + error = EOK; + } + free(propcopy); + } + else { + /* Errors related to key interpretation are handled here */ + TRACE_ERROR_STRING("Invalid arg", arg); + return EINVAL; + } + } /* end of arg processing loop */ + + TRACE_FLOW_STRING("process_arg_list", "Exit"); + 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 error = EOK; + struct collection_item *tpl = NULL; + va_list args; + + TRACE_FLOW_STRING("elapi_create_event_template", "Entry"); + + if (template == NULL ) { + TRACE_ERROR_STRING("Template storage must be provided", ""); + return EINVAL; + } + + *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; + } + + *template = tpl; + + TRACE_FLOW_STRING("elapi_create_event_template", "Exit"); + return error; +} + +/* Function to destroy event template */ +void elapi_destroy_event_template(struct collection_item *template) +{ + TRACE_FLOW_STRING("elapi_destroy_event_template", "Entry"); + + col_destroy_collection(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, ...) +{ + int error = EOK; + struct collection_item *evt = NULL; + va_list args; + + TRACE_FLOW_STRING("elapi_create_event", "Entry"); + + /* Check storage */ + 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 */ + 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, + (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 */ + if (collection != NULL) { + error = col_add_collection_to_collection(evt, NULL, NULL, collection, mode); + if (error) { + TRACE_ERROR_NUMBER("Failed to add elements from external collection. Error", error); + col_destroy_collection(evt); + return error; + } + } + + /* 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); + return error; + } + + *event = evt; + + TRACE_FLOW_STRING("elapi_create_event", "Exit"); + return error; +} + +/* Add/Updates/Removes the event attributes based on the and provided key value pairs */ +int elapi_modify_event(struct collection_item *event, + struct collection_item *collection, + int mode, ...) +{ + int error = EOK; + va_list args; + + TRACE_FLOW_STRING("elapi_modify_event", "Entry"); + + /* Check event */ + if (event == NULL ) { + TRACE_ERROR_STRING("Event must be provided", ""); + return EINVAL; + } + + /* Add elements from the template */ + if (collection != NULL) { + error = col_add_collection_to_collection(event, NULL, NULL, collection, mode); + if (error) { + TRACE_ERROR_NUMBER("Failed to add elements from external collection. Error", error); + col_destroy_collection(event); + return error; + } + } + + /* Process varible arguments */ + va_start(args, mode); + + /* Process variable argument list */ + error = process_arg_list(event, args); + + va_end(args); + + if (error) { + TRACE_ERROR_NUMBER("Failed to process argument list. Error", error); + return error; + } + + TRACE_FLOW_STRING("elapi_modify_event", "Exit"); + return error; +} + +/* Create a copy of the event */ +int elapi_copy_event(struct collection_item **new_event, + struct collection_item *source_event) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_copy_event", "Entry"); + + error = col_copy_collection(new_event, + source_event, + NULL); + + TRACE_FLOW_NUMBER("elapi_copy_event. Exit Returning", error); + return error; +} + +/* Function to destroy event. */ +void elapi_destroy_event(struct collection_item *event) +{ + TRACE_FLOW_STRING("elapi_destroy_event", "Entry"); + + col_destroy_collection(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 new file mode 100644 index 000000000..d1e0dd128 --- /dev/null +++ b/common/elapi/elapi_event.h @@ -0,0 +1,168 @@ +/* + ELAPI + + Header file for the ELAPI event interface. + + Copyright (C) Dmitri Pal 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 . +*/ + +#ifndef ELAPI_EVENT_H +#define ELAPI_EVENT_H + +#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 */ + +/* There is a special optional attribute of the event named "message". + * It is a string that contains text specific to each event. + * This string can contain placeholders that will be automatically + * replaced by the values that correspond to other attributes in + * the message. For example message can be: + * "Connected to remote %(server)". + * The token %(server) will be replaced by value + * in the attribute "server" in the event. + */ +#define E_MESSAGE "message" + +/* Base argument in the template creation function is a bit mask. + * Each supported predefined element corresponds to its bit in + * the mask. + */ +#define E_HAVE_TIMESTAMP 0x00000001 +#define E_HAVE_UTCTIME 0x00000002 +#define E_HAVE_PID 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 + +/* Convenient bitmasks */ +#define E_BASE_TIME ( E_HAVE_TIMESTAMP | E_HAVE_UTCTIME ) +#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 TIME_ARRAY_SIZE 100 + + +/* End of arg list special value */ +#define E_EOARG "" + + + +/***************************************************************************/ +/* TREAD SAFE ELAPI EVENT API */ +/***************************************************************************/ + +/* In the thread safe API the caller is responsible for + * carrying context information. It is usually allocated + * when a "create" function is called + * and should be deleted using "destroy" function. + */ + +/* Create an event template. + * One can create an event template + * and specify what fields should be + * populated in the event at its creation. + * Possible supported fields are listed above. + * Base parameter specifies the base collection of + * attributes. See above. The value of 0 will default + * to the current version of default combination + * which might change as the API evolves. + * The variable list can be used to initialize template. + * It can be initialized by providing key value pairs. + * ...base, key, value, key, value); + * If the key does not contain format specifier + * the value should be a NULL terminated string. + * See examples for the specific syntax. + * If key starts with "-" like "-foo" + * it means that attribute should be removed. + * In this case the value should not be provided + * and next argument should be next key. + * The attributes selected by base argument will + * be internally and automatically initialized + * if there is no key - value pair provided. + * The timestamps will we overwritten each time + * the event is created so initializing them + * does not make sense unless you use the base + * that does not include them. + * The list of key value pairs should be terminated by special + * argument E_EOARG. + */ +int elapi_create_event_template(struct collection_item **template, + unsigned base, ...); + +/* Function to destroy event template */ +void elapi_destroy_event_template(struct collection_item *template); + +/***************************************************************************/ +/* Creates a new event using template (must be provided) + * and adds additional fields using collection + * if provided and/or key value pairs if provided. + * Mode specifies how the collection should be + * copied into event. + * See example for details about format specification. + */ +int elapi_create_event(struct collection_item **event, + struct collection_item *template, + struct collection_item *collection, + int mode, ...); + +/* Add/Updates/Removes the event attributes based on the and provided key value pairs */ +int elapi_modify_event(struct collection_item *event, + struct collection_item *collection, + int mode, ...); + +/* Create a copy of the event */ +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); + +/***************************************************************************/ +/* TREAD UNSAFE ELAPI EVENT API - for simple use cases */ +/***************************************************************************/ +/* Initializes default internal template */ +int elapi_set_default_template(unsigned base, ...); + +/* This function will use internal default template. + * Hides all complexity from the caller. + */ +int elapi_create_simple_event(struct collection_item **event, ...); + + +#endif diff --git a/common/elapi/elapi_priv.h b/common/elapi/elapi_priv.h new file mode 100644 index 000000000..feb9a7f67 --- /dev/null +++ b/common/elapi/elapi_priv.h @@ -0,0 +1,32 @@ +/* + ELAPI + + Private header file continaing internal data for the ELAPI interface. + + Copyright (C) Dmitri Pal 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 . +*/ + +#ifndef ELAPI_PRIV_H +#define ELAPI_PRIV_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 + +/* Names for the collections */ +#define E_TEMPLATE_NAME "template" +#define E_EVENT_NAME "event" + +#endif diff --git a/common/elapi/elapi_ut.c b/common/elapi/elapi_ut.c new file mode 100644 index 000000000..b9493da12 --- /dev/null +++ b/common/elapi/elapi_ut.c @@ -0,0 +1,243 @@ +/* + ELAPI + + Unit test for the ELAPI event interface. + + Copyright (C) Dmitri Pal 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 . +*/ + +#include +#define TRACE_HOME +#include "trace.h" +#include "elapi.h" +#include "collection_tools.h" + +int simple_event_test(void) +{ + int error = 0; + struct collection_item *event; + char bin[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + printf("Simple test START:\n"); + + error = elapi_set_default_template( + E_BASE_DEFV1 | E_BASE_HOSTEXT, + "%n( bin )", bin, 8, + " %sb( logical1 )", "false", + "%sb( logical2 )", "YES", + " %db(logical3)", 1, + "%d(int_number),", -200, + "%u(unsigned_number)", 300, + "%ld(long_number)", -1234567, + "%lu(long_unsigned_number)", 123456789, + "%s(just_string)", "string", + "%*s(sub_string)", "truncated string", 10, /* Expect word truncated */ + "%e(double_number)", 3.141592 * 3, + "simple", "value", + "-" E_UTCTIME, /* Remove UTCTIME from the list */ + E_MESSAGE, + "%(stamp), %s(sub_string), %(int_number), %(unsigned_number), %(long_unsigned_number), %(bin), %e(double_number)", + E_EOARG); + + if (error) { + printf("Failed to set default template! %d\n", error); + return error; + } + + error = elapi_create_simple_event( + &event, + " %db(foo_logical)", 0, + "%d(foo_int_number),", -2000, + "%u(foo_unsigned_number)", 3000, + "%ld(foo_long_number)", -7654321, + E_EOARG); + + if (error) { + printf("Failed to set create event! %d\n", error); + return error; + } + + col_debug_collection(event, COL_TRAVERSE_DEFAULT); + col_debug_collection(event, COL_TRAVERSE_FLAT); + + elapi_destroy_event(event); + + printf("Simple test success!\n"); + + return error; +} + +int complex_event_test(void) +{ + int error = 0; + struct collection_item *template; + struct collection_item *event, *event_copy; + char bin[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + struct collection_item *col; + + printf("Complex test START:\n"); + + error = elapi_create_event_template( + &template, + E_BASE_DEFV1 | E_BASE_HOSTEXT, + "%lu(long_unsigned_number)", 123456789, + "%s(just_string)", "string", + "%*s(sub_string)", "truncated string", 10, /* Expect word truncated */ + "%e(double_number)", 3.141592 * 3, + "simple", "value", + "-" E_UTCTIME, /* Remove UTCTIME from the list */ + E_MESSAGE, + "%(stamp), %s(sub_string), %(int_number), %(unsigned_number), %(long_unsigned_number), %(bin), %e(double_number)", + E_EOARG); + + if (error) { + printf("Failed to set create template %d\n", error); + return error; + } + + error = elapi_create_event( + &event, + template, + NULL, + 0, + " %db(evt_logical)", 0, + "%d(evt_int_number),", -2000, + "%u(evt_unsigned_number)", 3000, + "%ld(evt_long_number)", -7654321, + E_EOARG); + + if (error) { + printf("Failed to set create template %d\n", error); + elapi_destroy_event_template(template); + return error; + } + + col_debug_collection(template, COL_TRAVERSE_FLAT); + col_debug_collection(event, COL_TRAVERSE_FLAT); + + + elapi_destroy_event_template(template); + elapi_destroy_event(event); + + error = elapi_create_event_template( + &template, + E_BASE_DEFV1 | E_BASE_HOSTEXT, + "%n( bin )", bin, 8, + " %sb( logical1 )", "false", + "%sb( logical2 )", "YES", + " %db(logical3)", 1, + "%d(int_number),", -200, + "%u(unsigned_number)", 300, + "%ld(long_number)", -1234567, + E_MESSAGE, + "%(stamp), %s(sub_string), %(int_number), %(unsigned_number), %(long_unsigned_number), %(bin), %e(double_number)", + E_EOARG); + + if (error) { + printf("Failed to set create template %d\n", error); + return error; + } + + if ((error = col_create_collection(&col, "test", 0)) || + /* We are forsing 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); + printf("Failed to add property. Error %d\n", error); + return error; + } + + error = elapi_create_event( + &event, + template, + col, + COL_ADD_MODE_FLAT, + E_MESSAGE, + "%(stamp) a good message", + "-int_number", + E_EOARG); + + if (error) { + printf("Failed to set create template %d\n", error); + elapi_destroy_event_template(template); + col_destroy_collection(col); + return error; + } + + col_destroy_collection(col); + + col_debug_collection(template, COL_TRAVERSE_FLAT); + 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)) || + (error = col_add_long_property(col, NULL, "zzz2", 100000000L))) { + elapi_destroy_event_template(template); + printf("Failed to add property. Error %d\n", error); + elapi_destroy_event(event); + return error; + } + + error = elapi_modify_event( + event, + col, + COL_ADD_MODE_REFERENCE, + "-"E_MESSAGE, + "bin", "bin-string", + E_EOARG); + + if (error) { + printf("Failed to set create template %d\n", error); + elapi_destroy_event(event); + col_destroy_collection(col); + return error; + } + + 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); + return error; + } + + elapi_destroy_event(event); + col_debug_collection(event_copy, COL_TRAVERSE_FLAT); + elapi_destroy_event(event_copy); + + return error; +} + + +/* Main function of the unit test */ + +int main(int argc, char *argv[]) +{ + int error = 0; + + printf("Start\n"); + if ((error = simple_event_test()) || + (error = complex_event_test())) { + printf("Failed!\n"); + } + else printf("Success!\n"); + /* Add other tests here ... */ + return error; +} diff --git a/common/elapi/m4/.dir b/common/elapi/m4/.dir new file mode 100644 index 000000000..e69de29bb -- cgit