>From 7d8d9e28837dd0f5a74e1a17b4c6483b87f67995 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Fri, 7 Aug 2009 15:50:34 -0400
Subject: [PATCH] ELAPI Next round of functionality - logging part of the interface

a) Added the main logging interface which
   allows creating dispatcher and logging messages or events.
   Can't actully log anything yet since the sinks are stubbed out.
b) Made default template be a part of the default
   dispatcher.
c) Updated UNIT test.
d) Some of the calls are stubbed out but they are there
   to indicate where next round of work will be.
---
 common/elapi/Makefile.am      |   20 ++-
 common/elapi/conf_macros.m4   |   56 ++++
 common/elapi/configure.ac     |    6 +
 common/elapi/elapi.h          |    1 +
 common/elapi/elapi_async.h    |   72 +++++
 common/elapi/elapi_event.c    |  363 +++++++++---------------
 common/elapi/elapi_event.h    |   36 +--
 common/elapi/elapi_internal.c |  232 +++++++++++++++
 common/elapi/elapi_log.c      |  632 +++++++++++++++++++++++++++++++++++++++++
 common/elapi/elapi_log.h      |  117 ++++++++
 common/elapi/elapi_priv.h     |   82 ++++++
 common/elapi/elapi_sink.h     |   78 +++++
 common/elapi/elapi_ut.c       |   83 +++++-
 common/elapi/elapi_ut.conf    |    6 +
 14 files changed, 1521 insertions(+), 263 deletions(-)
 create mode 100644 common/elapi/conf_macros.m4
 create mode 100644 common/elapi/elapi_async.h
 create mode 100644 common/elapi/elapi_internal.c
 create mode 100644 common/elapi/elapi_log.c
 create mode 100644 common/elapi/elapi_log.h
 create mode 100644 common/elapi/elapi_sink.h
 create mode 100644 common/elapi/elapi_ut.conf

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

