summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorDmitri Pal <dpal@redhat.com>2009-07-13 19:35:31 -0400
committerStephen Gallagher <sgallagh@redhat.com>2009-07-15 11:19:46 -0400
commit735ac5cb6596ac7219881a8be1b215825de9401b (patch)
tree5a3b83f81260e93ad9f863b94843c9409dbc7aa3 /common
parent9689454cb2d3d3b3c9e02a32ab9db13f49e8d5a2 (diff)
downloadsssd-735ac5cb6596ac7219881a8be1b215825de9401b.tar.gz
sssd-735ac5cb6596ac7219881a8be1b215825de9401b.tar.xz
sssd-735ac5cb6596ac7219881a8be1b215825de9401b.zip
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.
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