summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am7
-rw-r--r--common/collection/collection.c2
-rw-r--r--common/configure.ac2
-rw-r--r--common/elapi/Makefile.am31
-rw-r--r--common/elapi/configure.ac26
-rw-r--r--common/elapi/elapi.h25
-rw-r--r--common/elapi/elapi.pc.in11
-rw-r--r--common/elapi/elapi_event.c1112
-rw-r--r--common/elapi/elapi_event.h168
-rw-r--r--common/elapi/elapi_priv.h32
-rw-r--r--common/elapi/elapi_ut.c243
-rw-r--r--common/elapi/m4/.dir0
12 files changed, 1655 insertions, 4 deletions
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 <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_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 <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 getpid() */
+#include <unistd.h> /* for getpid() */
+#include <stdlib.h> /* for realloc() */
+#include <syslog.h> /* for contants releted to severity */
+#include <unistd.h> /* for gethostname() */
+#include <errno.h> /* for errors */
+#include <string.h> /* for memset() and other */
+#include <netdb.h> /* for gethostbyname() */
+#include <sys/socket.h> /* for inet_ntop() */
+#include <arpa/inet.h> /* for inet_ntop() */
+#include <ctype.h> /* for isspace() */
+#include <stdarg.h> /* for va_arg() */
+#include <string.h> /* for strndup() */
+#include <ifaddrs.h> /* 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 <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_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 "<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 <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_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 <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/>.
+*/
+
+#include <stdio.h>
+#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
--- /dev/null
+++ b/common/elapi/m4/.dir