>From 99f0d63316dfddad83ffad3a4898e5ace52b26d9 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Wed, 14 Oct 2009 13:24:12 -0400
Subject: [PATCH] ELAPI: Foundation for testing async processing

This patch adds a unit test for async interface.
As as result the interface changed.
It does not do anything yet. The actual work
will be done in the next patch.
---
 common/elapi/configure.ac                |    4 +
 common/elapi/elapi_async.c               |   69 ++----
 common/elapi/elapi_async.h               |   26 +--
 common/elapi/elapi_internal.c            |    5 +-
 common/elapi/elapi_log.c                 |    2 +-
 common/elapi/elapi_test/Makefile.am      |    9 +-
 common/elapi/elapi_test/configure.ac     |    6 +-
 common/elapi/elapi_test/elapi_async_ut.c |  389 ++++++++++++++++++++++++++++++
 8 files changed, 442 insertions(+), 68 deletions(-)
 create mode 100644 common/elapi/elapi_test/elapi_async_ut.c

diff --git a/common/elapi/configure.ac b/common/elapi/configure.ac
index 82be136..b632945 100644
--- a/common/elapi/configure.ac
+++ b/common/elapi/configure.ac
@@ -22,6 +22,10 @@ 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"])])
 
+# Enable verbose build
+AC_ARG_ENABLE([verbose],
+              [AS_HELP_STRING([--enable-verbose],[build with verbose output])],
+              [AC_DEFINE([ELAPI_VERBOSE],[1],[add verbose output])],[])
 
 m4_include(conf_macros.m4)
 
diff --git a/common/elapi/elapi_async.c b/common/elapi/elapi_async.c
index 0c404c0..b490ece 100644
--- a/common/elapi/elapi_async.c
+++ b/common/elapi/elapi_async.c
@@ -29,42 +29,34 @@
 
 /* Functions to set and get data from file descriptor data. */
 /* Functions return EINVAL if passed in argument is invalid. */
-int elapi_set_fd_priv(struct elapi_fd_data *fd_data,
-                      void *priv_data_to_set)
+void elapi_set_fd_priv(struct elapi_fd_data *fd_data,
+                       void *priv_data_to_set)
 {
-    int error = EOK;
-
     TRACE_FLOW_STRING("elapi_set_fd_priv", "Entry");
 
     /* Check arguments */
-    if (fd_data == NULL) {
-        TRACE_ERROR_NUMBER("Invalid argument. Error", EINVAL);
-        return EINVAL;
+    if (fd_data) {
+        fd_data->ext_data = priv_data_to_set;
+        TRACE_FLOW_STRING("elapi_set_fd_priv", "Exit");
+    }
+    else {
+        TRACE_ERROR_STRING("Invalid argument.", "Internal error");
     }
 
-    fd_data->ext_data = priv_data_to_set;
-
-    TRACE_FLOW_STRING("elapi_set_fd_priv", "Exit");
-    return error;
 }
 
-int elapi_get_fd_priv(struct elapi_fd_data *fd_data,
-                      void **priv_data_to_get)
+void *elapi_get_fd_priv(struct elapi_fd_data *fd_data)
 {
-    int error = EOK;
-
     TRACE_FLOW_STRING("elapi_get_fd_priv", "Entry");
 
     /* Check arguments */
-    if ((fd_data == NULL) || (priv_data_to_get == NULL))  {
-        TRACE_ERROR_NUMBER("Invalid argument. Error", EINVAL);
-        return EINVAL;
+    if (!fd_data)  {
+        TRACE_ERROR_STRING("Invalid argument.", "Internal error");
+        return NULL;
     }
 
-    *priv_data_to_get = fd_data->ext_data;
-
     TRACE_FLOW_STRING("elapi_get_fd_priv", "Exit");
-    return error;
+    return fd_data->ext_data;
 }
 
 /* Cleanup function */
@@ -78,43 +70,34 @@ void elapi_destroy_fd_data(struct elapi_fd_data *fd_data)
 
 
 /* Functions to set and get custom data from timer data. */
-/* Functions return EINVAL if passed in argument is invalid. */
-int elapi_set_tm_priv(struct elapi_tm_data *tm_data,
-                      void *priv_data_to_set)
+void elapi_set_tm_priv(struct elapi_tm_data *tm_data,
+                       void *priv_data_to_set)
 {
-    int error = EOK;
-
     TRACE_FLOW_STRING("elapi_set_tm_priv", "Entry");
 
     /* Check arguments */
-    if (tm_data == NULL) {
-        TRACE_ERROR_NUMBER("Invalid argument. Error", EINVAL);
-        return EINVAL;
+    if (tm_data) {
+        tm_data->ext_data = priv_data_to_set;
+        TRACE_FLOW_STRING("elapi_set_tm_priv", "Exit");
+    }
+    else {
+        TRACE_ERROR_STRING("Invalid argument.", "Internal error");
     }
 
-    tm_data->ext_data = priv_data_to_set;
-
-    TRACE_FLOW_STRING("elapi_set_tm_priv", "Exit");
-    return error;
 }
 
-int elapi_get_tm_priv(struct elapi_tm_data *tm_data,
-                      void **priv_data_to_get)
+void *elapi_get_tm_priv(struct elapi_tm_data *tm_data)
 {
-    int error = EOK;
-
     TRACE_FLOW_STRING("elapi_get_tm_priv", "Entry");
 
     /* Check arguments */
-    if ((tm_data == NULL) || (priv_data_to_get == NULL))  {
-        TRACE_ERROR_NUMBER("Invalid argument. Error", EINVAL);
-        return EINVAL;
+    if (!tm_data)  {
+        TRACE_ERROR_STRING("Invalid argument.", "Internal error");
+        return NULL;
     }
 
-    *priv_data_to_get = tm_data->ext_data;
-
     TRACE_FLOW_STRING("elapi_get_tm_priv", "Exit");
-    return error;
+    return tm_data->ext_data;
 }
 
 /* Cleanup function */
diff --git a/common/elapi/elapi_async.h b/common/elapi/elapi_async.h
index 2133176..0ae675f 100644
--- a/common/elapi/elapi_async.h
+++ b/common/elapi/elapi_async.h
@@ -32,20 +32,18 @@ struct elapi_fd_data;
 struct elapi_tm_data;
 
 /* Functions to set and get data from file descriptor data. */
-/* Functions return EINVAL if passed in argument is invalid. */
-int elapi_set_fd_priv(struct elapi_fd_data *fd_data,
-                      void *priv_data_to_set);
-int elapi_get_fd_priv(struct elapi_fd_data *fd_data,
-                      void **priv_data_to_get);
+void elapi_set_fd_priv(struct elapi_fd_data *fd_data,
+                       void *priv_data_to_set);
+void *elapi_get_fd_priv(struct elapi_fd_data *fd_data);
+
 /* Cleanup function */
 void elapi_destroy_fd_data(struct elapi_fd_data *fd_data);
 
 /* Functions to set and get custom data from timer data. */
-/* Functions return EINVAL if passed in argument is invalid. */
-int elapi_set_tm_priv(struct elapi_tm_data *tm_data,
-                      void *priv_data_to_set);
-int elapi_get_tm_priv(struct elapi_tm_data *tm_data,
-                      void **priv_data_to_get);
+void elapi_set_tm_priv(struct elapi_tm_data *tm_data,
+                       void *priv_data_to_set);
+void *elapi_get_tm_priv(struct elapi_tm_data *tm_data);
+
 /* Cleanup function */
 void elapi_destroy_tm_data(struct elapi_tm_data *tm_data);
 
@@ -68,16 +66,14 @@ typedef int (*elapi_add_fd)(int fd,
  * file descriptor from the event loop.
  * Provided by caller of the ELAPI interface.
  */
-typedef int (*elapi_rem_fd)(int fd,
-                            struct elapi_fd_data *fd_data,
+typedef int (*elapi_rem_fd)(struct elapi_fd_data *fd_data,
                             void *ext_fd_data);
 
 /* Signature of the function to set
  * file descriptor for read/write operation.
  * Provided by caller of the ELAPI interface.
  */
-typedef int (*elapi_set_fd)(int fd,
-                            unsigned flags,
+typedef int (*elapi_set_fd)(unsigned flags,
                             struct elapi_fd_data *fd_data,
                             void *ext_fd_data);
 
@@ -91,8 +87,6 @@ typedef int (*elapi_add_tm)(struct timeval tv,
 
 /* Signature of the function to add timer.
  * Provided by caller of the ELAPI interface.
- * Caller must be aware that the timeval strcuture
- * is allocated on stack.
  */
 typedef int (*elapi_rem_tm)(struct elapi_tm_data *tm_data,
                             void *ext_tm_data);
diff --git a/common/elapi/elapi_internal.c b/common/elapi/elapi_internal.c
index fbbc015..44a393c 100644
--- a/common/elapi/elapi_internal.c
+++ b/common/elapi/elapi_internal.c
@@ -22,14 +22,14 @@
 #include <stdio.h>      /* for printf() - temporarily */
 #include <stdlib.h>     /* for malloc() */
 
+#include "config.h"
 #include "elapi_priv.h"
 #include "elapi_event.h"
 #include "elapi_sink.h"
 #include "trace.h"
-#include "config.h"
 #include "ini_config.h"
 
-#include "collection_tools.h" /*temporarily */
+#include "collection_tools.h" /* temporarily */
 
 /* Buffer size for time string */
 #define MAX_TIMESTR         200
@@ -749,5 +749,4 @@ void elapi_print_dispatcher(struct elapi_dispatcher *handle)
     printf("DISPATCHER END\n\n");
     fflush(stdout);
 }
-
 #endif
diff --git a/common/elapi/elapi_log.c b/common/elapi/elapi_log.c
index 22a375b..61c7659 100644
--- a/common/elapi/elapi_log.c
+++ b/common/elapi/elapi_log.c
@@ -340,7 +340,7 @@ int elapi_create_dispatcher_adv(struct elapi_dispatcher **dispatcher,
     if (async_ctx) {
         TRACE_INFO_STRING("Async data is present", "");
         handle->async_ctx = malloc(sizeof(struct elapi_async_ctx));
-        if (handle->async_ctx != NULL) {
+        if (handle->async_ctx == NULL) {
             TRACE_ERROR_NUMBER("Failed to allocate async context", ENOMEM);
             elapi_destroy_dispatcher(handle);
             return ENOMEM;
diff --git a/common/elapi/elapi_test/Makefile.am b/common/elapi/elapi_test/Makefile.am
index d9f1bb0..4ae2d93 100644
--- a/common/elapi/elapi_test/Makefile.am
+++ b/common/elapi/elapi_test/Makefile.am
@@ -13,7 +13,7 @@ if HAVE_GCC
         -Wcast-align -Wwrite-strings
 endif
 
-AM_CPPFLAGS = -I$(topdir) -I$(topdir)/ini -I$(topdir)/trace -I$(topdir)/collection -I$(topdir)/elapi \
+AM_CPPFLAGS = -I$(srcdir) -I$(topdir) -I$(topdir)/ini -I$(topdir)/trace -I$(topdir)/collection -I$(topdir)/elapi \
               -I$(topdir)/elapi/providers/file $(TRACE_LEVEL)
 
 ACLOCAL_AMFLAGS = -I m4
@@ -53,10 +53,13 @@ libelapi_test_la_SOURCES = \
     ../providers/file/file_fmt_csv.h
 
 # Build unit test
-check_PROGRAMS = elapi_ut
+check_PROGRAMS = elapi_ut elapi_async_ut
 elapi_ut_SOURCES = elapi_ut.c
 elapi_ut_LDADD = libelapi_test.la ../../ini/libini_config.la ../../collection/libcollection.la -ldl
+elapi_async_ut_SOURCES = elapi_async_ut.c
+elapi_async_ut_LDADD = libelapi_test.la ../../ini/libini_config.la ../../collection/libcollection.la -ldl \
+    -ltalloc -ltevent
 
-TESTS = elapi_ut
+TESTS = elapi_ut elapi_async_ut
 
 tests: all $(check_PROGRAMS)
diff --git a/common/elapi/elapi_test/configure.ac b/common/elapi/elapi_test/configure.ac
index 44524e7..625705a 100644
--- a/common/elapi/elapi_test/configure.ac
+++ b/common/elapi/elapi_test/configure.ac
@@ -14,6 +14,8 @@ AM_SILENT_RULES
 
 AC_CONFIG_HEADERS([config.h])
 
+AC_CHECK_HEADERS([talloc.h tevent.h])
+
 # Enable trace build
 AC_ARG_ENABLE([trace],
               [AS_HELP_STRING([--enable-trace[=LEVEL]],[build with low level tracing enabled])],
@@ -21,10 +23,10 @@ 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"])])
 
-# Enable trace build
+# Enable verbose build
 AC_ARG_ENABLE([verbose],
               [AS_HELP_STRING([--enable-verbose],[build with verbose output])],
-              [AC_DEFINE([ELAPI_VERBOSE],[],[add verbose output])],[])
+              [AC_DEFINE([ELAPI_VERBOSE],[1],[add verbose output])],[])
 
 m4_include(../def_macros.m4)
 
diff --git a/common/elapi/elapi_test/elapi_async_ut.c b/common/elapi/elapi_test/elapi_async_ut.c
new file mode 100644
index 0000000..095883e
--- /dev/null
+++ b/common/elapi/elapi_test/elapi_async_ut.c
@@ -0,0 +1,389 @@
+/*
+    ELAPI
+
+    Unit test for the asynchronous 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 <stdio.h>
+#include <stdarg.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <errno.h>
+#define TRACE_HOME
+#include "config.h"
+#include "trace.h"
+#include "elapi.h"
+#include "collection_tools.h"
+
+/* THIS IS A PRIVATE HEADER - included for debugging purposes only! */
+#include "elapi_priv.h"
+
+#define APPNAME             "elapi_async_ut"
+#define ELAPI_CONFIG_FILE   "elapi_ut.conf"
+#define TEST_SEC            2
+#define TEST_EVENT_NUM      5
+
+struct test_env {
+    struct tevent_context *ev;
+    int count;
+    struct elapi_dispatcher *dispatcher;
+    struct collection_item *tpl;
+};
+
+void add_timer(struct test_env *tenv);
+void clean_env(struct test_env *tenv, int keep);
+
+
+/* Handler that processes the fd when it is ready */
+void elapi_fd_handler(struct tevent_context *ev,
+                      struct tevent_fd *fde,
+                      uint16_t flags,
+                      void *private_data)
+{
+    int error;
+    TRACE_FLOW_STRING("elapi_fd_handler", "Entry");
+
+    /* Call ELAPI function to process FD */
+    error = elapi_process_fd((struct elapi_fd_data *)private_data);
+    if (error) {
+        TRACE_INFO_NUMBER("Function elapi_process_fd returned error", error);
+    }
+
+    TRACE_FLOW_STRING("elapi_fd_handler", "Exit");
+}
+
+/* Function to add file descriptor using tevent library */
+int elapi_tevent_add_fd(int fd,
+                        unsigned flags,
+                        struct elapi_fd_data *fd_data,
+                        void *ext_fd_data)
+{
+    struct tevent_fd *fdevent = NULL;
+    uint16_t te_flags = 0;
+
+    TRACE_FLOW_STRING("elapi_tevent_add_fd", "Entry");
+
+    if (flags & ELAPI_FD_READ) te_flags |= TEVENT_FD_READ;
+    if (flags & ELAPI_FD_WRITE) te_flags |= TEVENT_FD_WRITE;
+
+    /* Create a new file event */
+    fdevent = tevent_add_fd(ext_fd_data,
+                            ext_fd_data,
+                            fd,
+                            te_flags,
+                            elapi_fd_handler,
+                            fd_data);
+
+    if (!fdevent) {
+        TRACE_ERROR_NUMBER("Failed to to allocated FD event", ENOMEM);
+        return ENOMEM;
+    }
+
+    /* Save the event data into the ELAPI handle */
+    elapi_set_fd_priv(fd_data, fdevent);
+
+    TRACE_FLOW_STRING("elapi_tevent_add_fd", "Exit");
+    return EOK;
+}
+
+/* Function to remove file descriptor using tevent library */
+int elapi_tevent_rem_fd(struct elapi_fd_data *fd_data,
+                        void *ext_fd_data)
+{
+    struct tevent_fd *fdevent = NULL;
+
+    TRACE_FLOW_STRING("elapi_tevent_rem_fd", "Entry");
+
+    /* Get the event data from the ELAPI handle */
+    fdevent = elapi_get_fd_priv(fd_data);
+
+    /* Call the ELAPI cleanup function */
+    elapi_destroy_fd_data(fd_data);
+
+    /* Free the event data */
+    talloc_free(fdevent);
+
+    TRACE_FLOW_STRING("elapi_tevent_rem_fd", "Exit");
+    return EOK;
+}
+
+/* Function to set file descriptor using tevent library */
+int elapi_tevent_set_fd(unsigned flags,
+                        struct elapi_fd_data *fd_data,
+                        void *ext_fd_data)
+{
+    struct tevent_fd *fdevent = NULL;
+
+    TRACE_FLOW_STRING("elapi_tevent_set_fd", "Entry");
+
+    /* Get the event data from the ELAPI handle */
+    fdevent = (struct tevent_fd *)elapi_get_fd_priv(fd_data);
+    if (!fdevent) {
+        TRACE_ERROR_STRING("Important data lost", "Internal error");
+        return EINVAL;
+    }
+
+    if (flags == ELAPI_FD_READ) TEVENT_FD_READABLE(fdevent);
+    else if (flags == ELAPI_FD_WRITE) TEVENT_FD_WRITEABLE(fdevent);
+    else {
+        TRACE_ERROR_NUMBER("Invalid value for flags", flags);
+    }
+
+    TRACE_FLOW_STRING("elapi_tevent_set_fd", "Exit");
+    return EOK;
+}
+
+/* Handler that processes the timer event */
+void elapi_tm_handler(struct tevent_context *ev,
+                      struct tevent_timer *te,
+                      struct timeval current_time,
+                      void *private_data)
+{
+    int error;
+    TRACE_FLOW_STRING("elapi_tm_handler", "Entry");
+
+    /* Call ELAPI function to process timer */
+    error = elapi_process_tm((struct elapi_tm_data *)private_data);
+    if (error) {
+        TRACE_INFO_NUMBER("Function elapi_process_tm returned error", error);
+    }
+
+    TRACE_FLOW_STRING("elapi_tm_handler", "Exit");
+}
+
+/* Function to add timer using tevent library */
+int elapi_tevent_add_tm(struct timeval tv,
+                        struct elapi_tm_data *tm_data,
+                        void *ext_tm_data)
+{
+    struct tevent_timer *tmevent = NULL;
+
+    TRACE_FLOW_STRING("elapi_tevent_add_tm", "Entry");
+
+    tmevent = tevent_add_timer(ext_tm_data,
+                               ext_tm_data,
+                               tv,
+                               elapi_tm_handler,
+                               tm_data);
+
+    if (!tmevent) {
+        TRACE_ERROR_NUMBER("Failed to to allocated TM event", ENOMEM);
+        return ENOMEM;
+    }
+
+    /* Save the event data into the ELAPI handle */
+    elapi_set_tm_priv(tm_data, tmevent);
+
+    TRACE_FLOW_STRING("elapi_tevent_add_tm", "Exit");
+    return EOK;
+}
+
+/* Function to remove timer event using tevent library */
+int elapi_tevent_rem_tm(struct elapi_tm_data *tm_data,
+                        void *ext_fd_data)
+{
+    struct tevent_timer *tmevent = NULL;
+
+    TRACE_FLOW_STRING("elapi_tevent_rem_tm", "Entry");
+
+    /* Get the event data from the ELAPI handle */
+    tmevent = (struct tevent_timer *)elapi_get_tm_priv(tm_data);
+    if (!tmevent) {
+        TRACE_ERROR_STRING("Important data lost", "Internal error");
+        return EINVAL;
+    }
+
+    /* Call the ELAPI cleanup function */
+    elapi_destroy_tm_data(tm_data);
+
+    /* Free the event data */
+    talloc_free(tmevent);
+
+    TRACE_FLOW_STRING("elapi_tevent_rem_tm", "Exit");
+    return EOK;
+}
+
+/* Function that emits events */
+void event_emitter(struct tevent_context *ev,
+                   struct tevent_timer *te,
+                   struct timeval current_time,
+                   void *private_data)
+{
+    struct test_env *tenv;
+    int error;
+
+    TRACE_FLOW_STRING("event_emitter", "Entry.");
+
+    tenv = (struct test_env *)private_data;
+
+    /* Emit a new ELAPI event */
+    if (tenv->count < TEST_EVENT_NUM) {
+
+        error = elapi_dsp_msg(E_TARGET_DEBUG,
+                              tenv->dispatcher,
+                              tenv->tpl,
+                              E_MESSAGE,
+                              "date = %(R_stamp__), "
+                              "pid = %(__pid__), "
+                              "hostname = %(__host__), "
+                              "ip = %(__ip__), "
+                              "num = %(num)"  ,
+                              "%d(num),", tenv->count,
+                              E_EOARG);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to create message", error);
+            clean_env(tenv, 1);
+            return;
+        }
+        add_timer(tenv);
+        tenv->count++;
+    }
+    else {
+        printf("Shutting down!!!\n");
+        clean_env(tenv, 1);
+    }
+
+    TRACE_FLOW_STRING("event_emitter", "Exit.");
+}
+
+/* Small wrapper to add a specific timer */
+void add_timer(struct test_env *tenv)
+{
+    struct timeval tv;
+    struct tevent_timer *tmr;
+
+    TRACE_FLOW_STRING("add_timer", "Entry.");
+
+    tv = tevent_timeval_current_ofs(TEST_SEC, 0);
+
+    tmr = tevent_add_timer(tenv->ev, tenv->ev, tv, event_emitter, (void *)tenv);
+
+    if (!tmr) {
+        TRACE_ERROR_STRING("Failed to create emitter", "");
+    }
+    TRACE_FLOW_STRING("add_timer", "Exit.");
+}
+
+void clean_env(struct test_env *tenv, int keep)
+{
+    TRACE_FLOW_STRING("clean_env", "Entry.");
+
+    /* Deal with the dispatcher */
+    if (tenv->dispatcher) {
+        TRACE_INFO_STRING("Cleaning dispatcher", "");
+       /* Print dispatcher */
+        elapi_print_dispatcher(tenv->dispatcher);
+        /* Free dispatcher */
+        elapi_destroy_dispatcher(tenv->dispatcher);
+        tenv->dispatcher = NULL;
+    }
+
+    /* Deal with the template */
+    if (tenv->tpl) {
+        TRACE_INFO_STRING("Cleaning template", "");
+        /* Free template */
+        elapi_destroy_event_tplt(tenv->tpl);
+        tenv->tpl = NULL;
+    }
+
+    /* Deal with the event context */
+    if ((tenv->ev) && (!keep)) {
+        TRACE_INFO_STRING("Cleaning event context", "");
+        /* Free context */
+        talloc_free(tenv->ev);
+        tenv->ev = NULL;
+    }
+
+    TRACE_FLOW_STRING("clean_env", "Exit.");
+}
+
+/* Main function of the unit test */
+int main(int argc, char *argv[])
+{
+    int error = EOK;
+    struct elapi_async_ctx *elapi_async_ctx = NULL;
+    struct test_env tenv = { NULL, 0, NULL, NULL};
+
+
+    /* The event context is the top level structure.
+     * Everything else should hang off that */
+    tenv.ev = tevent_context_init(talloc_autofree_context());
+    if (tenv.ev == NULL) {
+        TRACE_ERROR_NUMBER("Failed to init event context", ENOMEM);
+        return ENOMEM;
+    }
+
+    /* Create template */
+    error = elapi_create_event_tplt(&(tenv.tpl),
+                                    E_BASE_DEFV1 | E_BASE_HOSTEXT,
+                                    E_EOARG);
+
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to init event context", error);
+        clean_env(&tenv, 0);
+        return error;
+    }
+
+
+    /* Set up ELAPI async context */
+    error = elapi_create_asctx(&elapi_async_ctx,
+                               elapi_tevent_add_fd,
+                               elapi_tevent_rem_fd,
+                               elapi_tevent_set_fd,
+                               (void *)(tenv.ev),
+                               elapi_tevent_add_tm,
+                               elapi_tevent_rem_tm,
+                               (void *)(tenv.ev));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to create elapi async context.", error);
+        clean_env(&tenv, 0);
+        return error;
+    }
+
+    /* Create dispatcher using the context */
+    error = elapi_create_dispatcher_adv(&(tenv.dispatcher),
+                                        APPNAME,
+                                        ELAPI_CONFIG_FILE,
+                                        elapi_async_ctx);
+
+    /* Clear a copy of context we have allocated */
+    elapi_destroy_asctx(elapi_async_ctx);
+
+    /* Check error */
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to create elapi dispatcher.", error);
+        clean_env(&tenv, 0);
+        return error;
+    }
+
+    /* Add timer that will emit events */
+    add_timer(&tenv);
+
+    /* Print dispatcher */
+    elapi_print_dispatcher(tenv.dispatcher);
+
+    TRACE_INFO_STRING("Just before the loop", "");
+
+    /* Loop until done. */
+    tevent_loop_wait(tenv.ev);
+
+    TRACE_INFO_STRING("Just after the loop", "");
+
+    clean_env(&tenv, 0);
+
+    return EOK;
+}
-- 
1.5.5.6

