summaryrefslogtreecommitdiffstats
path: root/dispatcher
diff options
context:
space:
mode:
authorDmitri Pal <dpal@dpal.csb>2009-02-23 10:38:06 -0500
committerDmitri Pal <dpal@dpal.csb>2009-02-23 10:38:06 -0500
commitd986aeb99fa33967374290bf7ce75eab76c6d446 (patch)
treee85af838cd95b43c4f1be5bc2203a1a3474ba589 /dispatcher
downloadelapi_draft-d986aeb99fa33967374290bf7ce75eab76c6d446.tar.gz
elapi_draft-d986aeb99fa33967374290bf7ce75eab76c6d446.tar.xz
elapi_draft-d986aeb99fa33967374290bf7ce75eab76c6d446.zip
Initial commit.
Diffstat (limited to 'dispatcher')
-rw-r--r--dispatcher/elapi_dispatcher.c688
-rw-r--r--dispatcher/elapi_dispatcher.h68
-rw-r--r--dispatcher/elapi_dispatcher_ut.c201
3 files changed, 957 insertions, 0 deletions
diff --git a/dispatcher/elapi_dispatcher.c b/dispatcher/elapi_dispatcher.c
new file mode 100644
index 0000000..cecf5bf
--- /dev/null
+++ b/dispatcher/elapi_dispatcher.c
@@ -0,0 +1,688 @@
+/* Copyright */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <dlfcn.h>
+#include "elapi_debug.h"
+#include "elapi_dispatcher.h"
+#include "elapi_collection.h"
+#include "elapi_sink.h"
+
+#define SINK_COLLECTION "sinks"
+
+char def_application_name[] = "unknown";
+
+char *default_sinks[] = { "ipa","kernel","syslog","file","stderr", NULL };
+
+
+/* Structure to pass data from logging function to sinks */
+struct sink_context {
+ struct collection_item *event;
+ struct dispatcher_handle *handle;
+ char *previous;
+ int previous_status;
+};
+
+/* The structure to hold a command and a result of the command execution */
+struct get_sink {
+ int action;
+ int found;
+};
+
+
+#ifdef ELAPI_LOG_DEBUG
+#define DEBUG_SINK(sink_data) print_sink(sink_data);
+#else
+#define DEBUG_SINK(sink_data) ;
+#endif
+
+/* Debug function */
+static void print_sink(struct sink_descriptor *sink_data)
+{
+ DEBUG_NUMBER("SINK data address",sink_data);
+ DEBUG_NUMBER("SINK data DBLOCK address",&(sink_data->dblock));
+ DEBUG_NUMBER("SINK data DBLOCK internals",(&(sink_data->dblock))->internal_data);
+}
+
+
+static void init_sink(struct sink_descriptor *sink_data, int status)
+{
+ int error;
+ struct timeval tv;
+
+ DEBUG_STRING("init_sink","Entry");
+ DEBUG_STRING("In init_sink application name:",sink_data->dblock.appname);
+
+ /* Pass in the data block storage */
+ error = (sink_data->sink_cpb_block).init_cb(&(sink_data->dblock));
+ DEBUG_NUMBER("DBLOCK in init_sink",&(sink_data->dblock));
+ if(error != EOK) {
+ DEBUG_NUMBER("Failed to initialize the sink:",error);
+ (void)gettimeofday(&tv,NULL);
+ sink_data->suspended = ELAPI_SINK_SUSPENDED;
+ sink_data->lasttry = tv.tv_sec;
+ }
+ else {
+ DEBUG_NUMBER("Initialized the sink. Status set to:",status);
+ sink_data->suspended = status;
+ sink_data->lasttry = 0;
+ }
+ DEBUG_SINK(sink_data);
+ DEBUG_STRING("init_sink","Exit");
+}
+
+static void wash_sink(struct sink_descriptor *sink_data)
+{
+ int error;
+ struct timeval tv;
+
+ DEBUG_STRING("wash_sink","Entry");
+ DEBUG_NUMBER("Descriptor",sink_data);
+ DEBUG_NUMBER("Capability",&(sink_data->sink_cpb_block));
+ DEBUG_NUMBER("Callback",(sink_data->sink_cpb_block).close_cb);
+ DEBUG_NUMBER("DBLOCK in wash",&(sink_data->dblock));
+
+ /* Pass in the data block storage */
+ (sink_data->sink_cpb_block).close_cb(&(sink_data->dblock));
+
+ DEBUG_STRING("wash_sink","Exit");
+}
+
+
+/* Function to load sink library */
+static int load_sink(struct sink_descriptor *sink_data,char *sink_name)
+{
+ char sink_lib_name[SINK_LIB_NAME_SIZE];
+ capability_fn get_lib_info;
+ void *lib_handle;
+ char *lib_error;
+ int error = EOK;
+
+ DEBUG_STRING("load_sink","Entry");
+
+ sprintf(sink_lib_name,SINK_NAME_TEMPLATE,sink_name);
+ DEBUG_STRING("Name of the library to load",sink_lib_name);
+
+ /* Load library */
+ sink_data->lib_handle = dlopen (sink_lib_name, RTLD_LAZY);
+ if (!(sink_data->lib_handle)) {
+ DEBUG_STRING("Dlopen returned error",dlerror());
+ return ELIBACC;
+ }
+
+ /* Clear any existing error */
+ dlerror();
+ /* Get addres to the main entry point */
+ get_lib_info = (capability_fn)(dlsym(sink_data->lib_handle, SINK_ENTRY_POINT));
+ if ((lib_error = dlerror()) != NULL) {
+ DEBUG_STRING("Dlsym returned error",lib_error);
+ return ELIBACC;
+ }
+
+ /* Init data */
+ get_lib_info(&(sink_data->sink_cpb_block));
+
+ /* Call library initialization function */
+ init_sink(sink_data,ELAPI_SINK_OK);
+
+ /* If we return ELIBACC it would indicate that the desired library is missing */
+
+ DEBUG_STRING("load_sink","Returning Success");
+ return EOK;
+}
+
+
+/* Function to add a sink in the list */
+static int add_sink_to_list(struct collection_item *sink_list,
+ char *sink,
+ char *appname)
+{
+ int error = EOK;
+ int found = 0;
+ struct sink_descriptor sink_data;
+
+ DEBUG_STRING("add_sink_to_list","Entry");
+ error = is_item_in_collection(sink_list,
+ sink,
+ ELAPI_TYPE_ANY,
+ ELAPI_TRAVERSE_DEFAULT,
+ &found);
+ if(error) {
+ DEBUG_NUMBER("Search returned error",error);
+ return error;
+ }
+
+ /* Check if it was found */
+ if(found) {
+ DEBUG_STRING("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;
+ DEBUG_STRING("add_sink_to_list - saving appname:",sink_data.dblock.appname);
+
+ /* Try to load the sink library */
+ 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=add_binary_property(sink_list,NULL,
+ sink,(void *)(&sink_data),
+ sizeof(struct sink_descriptor));
+ if(error != 0) {
+ DEBUG_NUMBER("Failed to add sink data as property",error);
+ return error;
+ }
+
+ DEBUG_NUMBER("add_sink_to_list returning",error);
+ return error;
+}
+
+/* Handler to change the sink status data */
+static int update_sink_handle(char *sink,
+ int sink_len,
+ int type,
+ void *data,
+ int length,
+ void *passed_data,
+ int *stop)
+{
+ int error = EOK;
+ struct sink_descriptor *sink_data;
+ struct get_sink *get_sink_op;
+
+ DEBUG_STRING("update_sink_handler","Entry.");
+
+ sink_data = (struct sink_descriptor *)(data);
+ get_sink_op = (struct get_sink *)(passed_data);
+
+ switch(get_sink_op->action) {
+ case ELAPI_SINK_ACTION_DISABLE:
+ DEBUG_STRING("Disabling sink:",sink);
+ wash_sink(sink_data);
+ sink_data->suspended = ELAPI_SINK_DISABLED;
+ break;
+ case ELAPI_SINK_ACTION_ENABLE:
+ DEBUG_STRING("Enabling sink:",sink);
+ wash_sink(sink_data);
+ /* Init sink will set the correct status */
+ init_sink(sink_data,ELAPI_SINK_OK);
+ break;
+ case ELAPI_SINK_ACTION_PULSE:
+ DEBUG_STRING("Pulsing sink:",sink);
+ wash_sink(sink_data);
+ init_sink(sink_data,ELAPI_SINK_PULSE);
+ break;
+ default:
+ DEBUG_STRING("update_sink_handler","ERROR Invalid argument");
+ return EINVAL;
+ }
+
+ *stop = 1;
+ /* Indicate that we found item */
+ get_sink_op->found = 1;
+
+ DEBUG_STRING("update_sink_handler","Return.");
+ return EOK;
+}
+
+/* Handler to clean sinks at the end */
+static int close_sink_handler(char *sink,
+ int sink_len,
+ int type,
+ void *data,
+ int length,
+ void *passed_data,
+ int *stop)
+{
+ int error = EOK;
+ struct sink_descriptor *sink_data;
+
+ DEBUG_STRING("close_sink_handler","Entry.");
+ DEBUG_STRING("Sink:",sink);
+
+ if(type == ELAPI_TYPE_COLLECTION) return EOK;
+
+ sink_data = (struct sink_descriptor *)(data);
+
+ wash_sink(sink_data);
+
+ if(sink_data->lib_handle != NULL) {
+ DEBUG_STRING("Closing lib handle","");
+ dlclose(sink_data->lib_handle);
+ sink_data->lib_handle = NULL;
+ }
+
+ DEBUG_STRING("close_sink_handler","Return.");
+ return EOK;
+}
+
+
+/* Handler for logging through the sinks */
+static int sink_handler(char *sink,
+ int sink_len,
+ int type,
+ void *data,
+ int length,
+ void *passed_data,
+ int *stop)
+{
+ struct sink_context *sink_env;
+ struct sink_descriptor *sink_data;
+ int status = 0;
+ int error = EOK;
+ struct timeval tv;
+
+ DEBUG_STRING("sink_handler","Entry.");
+ DEBUG_STRING("Sink:",sink);
+
+ sink_env = (struct sink_context *)(passed_data);
+ sink_data = (struct sink_descriptor *)(data);
+
+ /* When porcessing header just initialize and continue */
+ if(type == ELAPI_TYPE_COLLECTION) {
+ sink_env->previous = NULL;
+ sink_env->previous_status = 0;
+ return error;
+ }
+
+ /* Check if the sink is healthy to use */
+ if(sink_data->suspended!= ELAPI_SINK_OK) {
+ DEBUG_NUMBER("Sink is suspended:",sink_data->suspended);
+ if(sink_data->suspended == ELAPI_SINK_DISABLED) {
+ DEBUG_STRING("Sink is suspended by caller. Skipping sink.",sink);
+ return EOK;
+ }
+ if(sink_data->suspended == ELAPI_SINK_PULSE) {
+ DEBUG_STRING("Sink is suspended by caller for one time. Skipping sink but resetting to Ok.",sink);
+ sink_data->suspended = ELAPI_SINK_OK;
+ return EOK;
+ }
+ if(sink_data->suspended == ELAPI_SINK_SUSPENDED) {
+ DEBUG_STRING("Sink is suspended is it time to retry.",sink);
+ /* Is the sink permanently suspended ? */
+ if((sink_data->sink_cpb_block).retry_interval == SINK_NEVER_RETRY) {
+ DEBUG_STRING("Sink is suspended forever since the sink tells not to retry.",sink);
+ /* For future: should we delete the sink ?*/
+ /* IMO we should not becuase the calling
+ * application can potentially do something to fix the issue and
+ * explicitely re-enable the sink after it. */
+ return EOK;
+ }
+ /* Check interval */
+ (void)gettimeofday(&tv,NULL);
+ if(difftime(tv.tv_sec,sink_data->lasttry + (sink_data->sink_cpb_block).retry_interval) < 0) {
+ DEBUG_STRING("Sink is suspended is it not time to retry.",sink);
+ return EOK;
+ }
+
+ /* Time to retry a suspended sink */
+ init_sink(sink_data,ELAPI_SINK_OK);
+ if(sink_data->suspended == ELAPI_SINK_SUSPENDED) {
+ DEBUG_STRING("Sink is still suspended. Problem still exists.",sink);
+ return EOK;
+ }
+
+ }
+ else {
+ DEBUG_STRING("Status is invalid for sink.",sink);
+ return EINVAL;
+ }
+ }
+
+
+ /* Call router function */
+ status = (sink_env->handle)->router(sink,
+ sink_env->previous,
+ sink_env->previous_status,
+ sink_env->event,
+ sink_data,
+ (sink_env->handle)->custom_data,
+ &error);
+ /* Check the status */
+ switch(status) {
+ case ELAPI_DISPATCHER_SKIP: /* Ignore current sink as if it is abcent */
+ DEBUG_STRING("Ignoring sink.",sink);
+ /* Do not change suspended status */
+ /* Do not change previous fields */
+ break;
+ case ELAPI_DISPATCHER_DONE: /* Sink is done with this event */
+ DEBUG_STRING("Done with this event.",sink);
+ *stop = 1;
+ sink_data->suspended = ELAPI_SINK_OK;
+ break;
+ case ELAPI_DISPATCHER_NEXT: /* Sink thinks somone else would want to log this event */
+ DEBUG_STRING("Go to the next sink.",sink);
+ sink_env->previous = sink;
+ sink_env->previous_status = status;
+ sink_data->suspended = ELAPI_SINK_OK;
+ break;
+ case ELAPI_DISPATCHER_ERROR: /* An error occured */
+ DEBUG_NUMBER("Go to the next sink due to error.",error);
+
+ /* FIXME: In future may be store the error and log it later
+ * when the sink recovers from failure.
+ * Alternatively may be log it into another sink. */
+
+ /* Record the fact that the sink returned error */
+ sink_env->previous = sink;
+ sink_env->previous_status = status;
+
+ /* Suspend this sink */
+ sink_data->suspended = ELAPI_SINK_SUSPENDED;
+ sink_data->lasttry=tv.tv_sec;
+ wash_sink(sink_data);
+ break;
+
+ default: /* This should not happen - codding error */
+ DEBUG_STRING("Status is invalid for sink.",sink);
+ return EINVAL;
+ }
+
+ DEBUG_STRING("sink_handler","Success Exit.");
+
+ return EOK;
+}
+
+
+/* Router function returns status not error */
+static int default_router(char *sink,
+ char *previous_sink,
+ int previous_status,
+ struct collection_item *event,
+ struct sink_descriptor *sink_data,
+ void *custom_data,
+ int *error)
+{
+ DEBUG_STRING("default_router","Entry");
+
+ /* FIXME
+ * default implementation of the routing function:
+ * Log every event into every facility regardless
+ * of the outcome.
+ */
+
+
+ *error = log_event_to_sink(sink_data,event,custom_data);
+
+
+
+ DEBUG_STRING("default_router","Returning");
+ return ELAPI_DISPATCHER_NEXT;
+}
+
+
+
+/* ================== SINK LIST MANAGEMENT ======================== */
+
+/* Function to create a list of sinks */
+static int construct_sink_list(struct dispatcher_handle *handle)
+{
+ int error = EOK;
+ char **current_sink;
+
+ DEBUG_STRING("construct_sink_list","Entry");
+
+ /* Allocate collection to store sinks */
+ error=create_collection(&(handle->sink_list),SINK_COLLECTION);
+ if(error != 0) {
+ DEBUG_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) {
+
+ DEBUG_STRING("Current sink",*current_sink);
+ DEBUG_STRING("Will use appname:",handle->appname);
+
+ /* Load sink */
+ error = add_sink_to_list(handle->sink_list,*current_sink,handle->appname);
+ if((error != 0) && (error != ELIBACC)) {
+ DEBUG_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) {
+ DEBUG_NUMBER("No sinks","");
+ /* No cleanup here. */
+ /* Return "Cannot access a needed shared library" */
+ return ELIBACC;
+ }
+
+ DEBUG_STRING("construct_sink_list","Returning success");
+ return EOK;
+}
+
+/* Function to delete sink list collection */
+static void delete_sink_list(struct dispatcher_handle *handle)
+{
+ DEBUG_STRING("delete_sink_list","Entry point");
+ if(handle->sink_list != (struct collection_item *)(NULL)) {
+ DEBUG_STRING("delete_sink_list","Deleting sink collection");
+ destroy_collection(handle->sink_list);
+ }
+ DEBUG_STRING("delete_sink_list","Exit");
+}
+
+
+/* ========================= MAIN INTERFACE FUNCTIONS ============================ */
+
+/* Function to create an audit dispatcher */
+int create_audit_dispatcher(struct dispatcher_handle **dispatcher,
+ const char *appname,
+ char **desired_sinks,
+ event_router_fn desired_router,
+ void *custom_data)
+{
+ struct dispatcher_handle *handle;
+ int error = EOK;
+
+ DEBUG_STRING("create_audit_dispatcher","Entry point");
+
+ /* Make sure the memory for handle is passed in */
+ if(dispatcher == (struct dispatcher_handle **)(NULL)) {
+ DEBUG_STRING("create_audit_dispatcher","Invalid parameter.");
+ return EINVAL;
+ }
+
+ /* Allocate memory */
+ handle = (struct dispatcher_handle *) malloc(sizeof(struct dispatcher_handle));
+ if(handle == (struct dispatcher_handle *)(NULL)) {
+ error = errno;
+ DEBUG_NUMBER("Memory allocation failed. Error",error);
+ return error;
+ }
+
+ /* Save application name in the handle */
+ if(appname != NULL) handle->appname = strdup(appname);
+ else handle->appname = strdup(def_application_name);
+
+ DEBUG_STRING("Application name:",handle->appname);
+
+ /* Check error */
+ if(handle->appname == NULL) {
+ error = errno;
+ DEBUG_NUMBER("Memory allocation failed. Error",error);
+ return error;
+ }
+
+ /* Check if there is no desired sinks provided */
+ if(desired_sinks != (char **)(NULL)) handle->sinks = desired_sinks;
+ else handle->sinks = default_sinks;
+
+ /* Check the router. If it is empty use the default router */
+ if(desired_router != (event_router_fn)(NULL)) handle->router = desired_router;
+ else handle->router = default_router;
+
+ handle->custom_data = custom_data;
+
+ /* Create the list of sinks */
+ error = construct_sink_list(handle);
+ if(error != EOK) {
+ DEBUG_NUMBER("Failed to create sink list. Error",error);
+ destroy_audit_dispatcher(handle);
+ return error;
+ }
+
+ *dispatcher = handle;
+
+ DEBUG_STRING("create_audit_dispatcher","Returning Success.");
+ return EOK;
+}
+
+/* Function to clean memory associated with the audit dispatcher */
+void destroy_audit_dispatcher(struct dispatcher_handle *dispatcher)
+{
+ DEBUG_STRING("destroy_audit_dispatcher","Entry");
+ if(dispatcher != (struct dispatcher_handle *)(NULL)) {
+ /* Offload libs */
+ (void)traverse_collection(dispatcher->sink_list,ELAPI_TRAVERSE_ONELEVEL,close_sink_handler,(void *)(NULL));
+ DEBUG_STRING("Deleting sink list.","");
+ delete_sink_list(dispatcher);
+ DEBUG_STRING("Freeing application name.","");
+ free((void *)(dispatcher->appname));
+ DEBUG_STRING("Freeing dispatcher.","");
+ free((void *)(dispatcher));
+ }
+ DEBUG_STRING("destroy_audit_dispatcher","Exit");
+}
+
+
+
+/* Log evento into a specific sink */
+int log_event_to_sink(struct sink_descriptor *sink_data,
+ struct collection_item *event,
+ void *custom_data)
+{
+ int error = EOK;
+ struct sink_capability *sink_cpb;
+
+ DEBUG_STRING("log_event_to_sink","Entry");
+
+ sink_cpb = &(sink_data->sink_cpb_block);
+
+ DEBUG_NUMBER("DBLOCK in dispatcher",&(sink_data->dblock));
+ DEBUG_NUMBER("internal data in dispatcher",(&(sink_data->dblock))->internal_data);
+ DEBUG_SINK(sink_data);
+
+ /* Format (serialize the event) */
+ error = sink_cpb->format_cb(&(sink_data->dblock),event);
+ if(error != EOK) {
+ DEBUG_NUMBER("Format function returned error",error);
+ return error;
+ }
+
+ /* Submit the event */
+ error = sink_cpb->submit_cb(&(sink_data->dblock));
+ DEBUG_NUMBER("Format function returned error",error);
+
+ /* Clean internal per event data in any case */
+ sink_cpb->cleanup_cb(&(sink_data->dblock));
+
+ DEBUG_STRING("log_event_to_sink","Return");
+ return error;
+
+}
+
+
+/* Function to clean memory associated with the audit dispatcher */
+void log_audit_event(struct dispatcher_handle *dispatcher, struct collection_item *event)
+{
+ struct sink_context sink_env;
+
+ DEBUG_STRING("log_audit_event","Entry");
+ if((dispatcher == (struct dispatcher_handle *)(NULL)) ||
+ (event == (struct collection_item *)(NULL))) {
+ DEBUG_STRING("log_audit_event","ERROR Invalid argument");
+ DEBUG_NUMBER("Dispatcher",dispatcher);
+ DEBUG_NUMBER("Event",event);
+ DEBUG_STRING("log_audit_event","ERROR Return");
+ return;
+ }
+
+ sink_env.handle = dispatcher;
+ sink_env.event = event;
+
+ /* Logging an event is just iterating through the sinks and calling the sink_handler */
+ (void)traverse_collection(dispatcher->sink_list,ELAPI_TRAVERSE_ONELEVEL,sink_handler,(void *)(&sink_env));
+
+ DEBUG_STRING("log_audit_event","Return");
+}
+
+/* Managing the sink collection */
+int alter_audit_dispatcher(struct dispatcher_handle *dispatcher,
+ char *sink,
+ int action)
+{
+ int error;
+ struct get_sink get_sink_op;
+
+ DEBUG_STRING("alter_audit_dispatcher","Entry");
+ if((dispatcher == (struct dispatcher_handle *)(NULL)) ||
+ (sink == (char *)(NULL))) {
+ DEBUG_STRING("log_audit_event","ERROR Invalid argument");
+ DEBUG_NUMBER("Dispatcher",dispatcher);
+ DEBUG_NUMBER("Sink",sink);
+ DEBUG_STRING("log_audit_event","ERROR Return");
+ return EINVAL;
+ }
+
+ switch(action) {
+ case ELAPI_SINK_ACTION_ADD:
+ /* Try to add it and return whatever the attmpt returned */
+ error = add_sink_to_list(dispatcher->sink_list,sink,dispatcher->appname);
+ DEBUG_NUMBER("alter_audit_dispatcher ADD returning",error);
+ return error;
+ case ELAPI_SINK_ACTION_DELETE:
+ /* Try to delete the sink */
+ error = delete_property(dispatcher->sink_list,
+ sink,
+ ELAPI_TYPE_ANY,
+ ELAPI_TRAVERSE_DEFAULT);
+ DEBUG_NUMBER("alter_audit_dispatcher DELETE returning",error);
+ return error;
+ case ELAPI_SINK_ACTION_DISABLE:
+ case ELAPI_SINK_ACTION_ENABLE:
+ case ELAPI_SINK_ACTION_PULSE:
+ /* Try to modify the sink */
+ get_sink_op.action = action;
+ get_sink_op.found = 0;
+ error = get_item_and_do(dispatcher->sink_list,
+ sink,
+ ELAPI_TYPE_ANY,
+ ELAPI_TRAVERSE_DEFAULT,
+ update_sink_handle,
+ (void *)(&get_sink_op));
+ if(get_sink_op.found == 0) {
+ DEBUG_STRING("alter_audit_dispatcher DISABLE/ENABLE/PULSE sink not found","");
+ return EINVAL;
+ }
+ DEBUG_NUMBER("alter_audit_dispatcher DISABLE/ENABLE/PULSE returning",error);
+ return error;
+ default:
+ DEBUG_STRING("alter_audit_dispatcher","Invalid action");
+ return EINVAL;
+ }
+
+ /* Unreachable */
+}
diff --git a/dispatcher/elapi_dispatcher.h b/dispatcher/elapi_dispatcher.h
new file mode 100644
index 0000000..909505a
--- /dev/null
+++ b/dispatcher/elapi_dispatcher.h
@@ -0,0 +1,68 @@
+/* Copyright */
+
+#ifndef ELAPI_DISPATCHER_H
+#define ELAPI_DISPATCHER_H
+
+#include "elapi_collection.h"
+#include "elapi_sink.h"
+
+/* Status values returned by the routing function */
+#define ELAPI_DISPATCHER_SKIP 0
+#define ELAPI_DISPATCHER_DONE 1
+#define ELAPI_DISPATCHER_NEXT 2
+#define ELAPI_DISPATCHER_ERROR 3
+
+
+/* Actions that can be taken against a sink */
+#define ELAPI_SINK_ACTION_ADD 0 /* Add a new sink */
+#define ELAPI_SINK_ACTION_DELETE 1 /* Delete a sink */
+#define ELAPI_SINK_ACTION_DISABLE 2 /* Disable a sink */
+#define ELAPI_SINK_ACTION_PULSE 3 /* Disable a sink temporarily for one call */
+#define ELAPI_SINK_ACTION_ENABLE 4 /* Enable sink */
+
+
+/* Event routing function */
+typedef int (*event_router_fn)(char *sink,
+ char *previous_sink,
+ int previous_status,
+ struct collection_item *event,
+ struct sink_descriptor *sink_data,
+ void *custom_data,
+ int *error);
+
+struct dispatcher_handle {
+ char **sinks;
+ char *appname;
+ event_router_fn router;
+ struct collection_item *sink_list;
+ int sink_counter;
+ void *custom_data;
+};
+
+/* Function to create a dispatcher */
+int create_audit_dispatcher(struct dispatcher_handle **dispatcher, /* Handle of the dispatcher will be stored in this variable */
+ const char *appname, /* Application name. Passed to the sinks to do initialization */
+ char **desired_sinks, /* List of names of the sinks to use. If NULL the default will be used */
+ event_router_fn desired_router, /* The rounting function. If NULL the default will be used */
+ void *custom_data); /* Custom data that can be used in the router function. */
+
+
+/* Function to clean memory associated with the audit dispatcher */
+void destroy_audit_dispatcher(struct dispatcher_handle *dispatcher);
+
+/* Function to log an event */
+void log_audit_event(struct dispatcher_handle *dispatcher, struct collection_item *event);
+
+/* Advanced functions */
+/* Managing the sink collection */
+int alter_audit_dispatcher(struct dispatcher_handle *dispatcher, /* Dispatcher */
+ char *sink, /* Sink to change */
+ int action); /* Action to perform for sink */
+
+/* This function is exposed in case you are providing your own routing callback */
+int log_event_to_sink(struct sink_descriptor *sink_data,
+ struct collection_item *event,
+ void *custom_data);
+
+
+#endif
diff --git a/dispatcher/elapi_dispatcher_ut.c b/dispatcher/elapi_dispatcher_ut.c
new file mode 100644
index 0000000..119d40a
--- /dev/null
+++ b/dispatcher/elapi_dispatcher_ut.c
@@ -0,0 +1,201 @@
+/* Copyright */
+
+#include <stdio.h>
+#include <malloc.h>
+#include "elapi_dispatcher.h"
+#include "elapi_collection.h"
+#include "elapi_debug.h"
+#include "elapi_util.h"
+#include "elapi_tools.h"
+
+struct collection_item *event;
+struct collection_item *peer;
+struct collection_item *socket;
+struct collection_item *host;
+
+
+int construct_event()
+{
+ char binary_dump[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+ int found = 0;
+
+ int error = EOK;
+
+ DEBUG_STRING("construct_event","Entry.");
+
+ if((error=create_collection(&peer,"peer")) ||
+ (error=add_str_property(peer,NULL,"hostname","peerhost.mytest.com",0)) ||
+ (error=add_str_property(peer,NULL,"IPv4","10.10.10.10",12)) || /* Expect trailing zero to be truncated */
+ (error=add_str_property(peer,NULL,"IPv6","bla:bla:bla:bla:bla:bla",0))) {
+ printf("Failed to add property. Error %d",error);
+ destroy_collection(peer);
+ return error;
+ }
+
+ if((error=create_collection(&host,"host")) ||
+ (error=add_str_property(host,NULL,"hostname","myhost.mytest.com",0)) ||
+ (error=add_str_property(host,NULL,"IPv4","20.20.20.20",13)) ||
+ (error=add_str_property(host,NULL,"IPv6","bla:bla:bla:bla:bla:bla",0))) {
+ printf("Failed to add property. Error %d",error);
+ destroy_collection(peer);
+ destroy_collection(host);
+ return error;
+ }
+
+
+ if((error=create_collection(&socket,"socket")) ||
+ (error=add_int_property(socket,NULL,"id",1)) ||
+ (error=add_long_property(socket,NULL,"packets",100000000L)) ||
+ (error=add_binary_property(socket,NULL,"stack",binary_dump,sizeof(binary_dump)))) {
+ destroy_collection(peer);
+ destroy_collection(host);
+ destroy_collection(socket);
+ printf("Failed to add property. Error %d\n",error);
+ return error;
+ }
+
+ /* Embed peer host into the socket2 as reference */
+ error = add_collection_to_collection(socket,NULL,"peer",peer,ELAPI_ADD_MODE_REFERENCE);
+ if(error) {
+ destroy_collection(peer);
+ destroy_collection(host);
+ destroy_collection(socket);
+ printf("Failed to create collection. Error %d\n",error);
+ return error;
+ }
+
+ /* Construct event */
+ if((error=create_collection(&event,"event")) ||
+ (error=add_str_property(event,NULL,"escape1","ds\\sd=ewrw===sada",0)) ||
+ (error=add_str_property(event,NULL,"escape2","dss,d,=,ewrw===sada",0)) ||
+ (error=add_str_property(event,NULL,"escape3","ds\\sd=ewrw,()===sada",0)) ||
+ (error=add_str_property(event,NULL,"escape4","dssd=ewrw===))))sada",0)) ||
+ (error=add_str_property(event,NULL,"escape5","d\\ss(((((d=ew(()()()(),(,,\\\\\\\\rw===sada",0)) ||
+ (error=add_int_property(event,NULL,"something",100))) {
+ destroy_collection(peer);
+ destroy_collection(host);
+ destroy_collection(socket);
+ destroy_collection(event);
+ printf("Failed to create collection. Error %d\n",error);
+ return error;
+ }
+
+ /* Add host to event */
+ error = add_collection_to_collection(event,NULL,NULL,host,ELAPI_ADD_MODE_REFERENCE);
+ if(error) {
+ destroy_collection(peer);
+ destroy_collection(host);
+ destroy_collection(socket);
+ destroy_collection(event);
+ printf("Failed to add collections. Error %d\n",error);
+ return error;
+ }
+
+ error = add_collection_to_collection(event,NULL,NULL,socket,ELAPI_ADD_MODE_REFERENCE);
+ if(error) {
+ destroy_collection(peer);
+ destroy_collection(host);
+ destroy_collection(socket);
+ destroy_collection(event);
+ printf("Failed to add collections. Error %d\n",error);
+ return error;
+ }
+
+ debug_collection(event,ELAPI_TRAVERSE_DEFAULT);
+
+ return EOK;
+}
+
+
+int main()
+{
+ int error = EOK;
+ struct dispatcher_handle *dispatcher;
+ char *sinks[]= { "foo", "stderr", "bar", NULL };
+
+ printf("Test start\n");
+
+
+ error = create_audit_dispatcher(&dispatcher,"my_app",sinks,NULL,NULL);
+ printf("create_audit_dispatcher returned %d\n", error);
+
+ /* Try to log with invalid parameters */
+ printf("%s","=================\nNegative test1\n");
+ log_audit_event(NULL, NULL);
+ printf("%s","=================\nNegative test2\n");
+ log_audit_event(dispatcher, NULL);
+
+ printf("%s","=================\nCreating collection\n");
+ error = construct_event();
+ if(error) {
+ printf("Error creating event %d\n",error);
+ return error;
+ }
+
+
+ printf("%s","=================\nLogging peer - expect success\n");
+ print_collection(peer);
+ log_audit_event(dispatcher, peer);
+ printf("%s","=================\nLogging host - expect success\n");
+ print_collection(host);
+ log_audit_event(dispatcher, host);
+ printf("%s","=================\nLogging socket - expect success\n");
+ print_collection(socket);
+ log_audit_event(dispatcher, socket);
+ printf("%s","=================\nLogging event - expect success\n");
+ print_collection(event);
+ log_audit_event(dispatcher, event);
+
+ /* Try to alter list of sinks */
+ printf("%s","=================\nSeries of negative test cases.\n");
+ /* Deleting non exisintg sink */
+ error = alter_audit_dispatcher(dispatcher,"far2",ELAPI_SINK_ACTION_DELETE);
+ if(error == 0) printf("%s","Expected failure got success\n");
+ else printf("Expected failure. Error : %d\n",error);
+
+ /* Editing non exisintg sink */
+ error = alter_audit_dispatcher(dispatcher,"far2",ELAPI_SINK_ACTION_ENABLE);
+ if(error == 0) printf("%s","Expected failure got success\n");
+ else printf("Expected failure. Error : %d\n",error);
+
+ /* Adding duplicate */
+ error = alter_audit_dispatcher(dispatcher,"bar",ELAPI_SINK_ACTION_ADD);
+ if(error == 0) printf("%s","Expected failure got success\n");
+ else printf("Expected failure. Error : %d\n",error);
+
+ /* Adding new */
+ error = alter_audit_dispatcher(dispatcher,"far2",ELAPI_SINK_ACTION_ADD);
+ if(error != 0) printf("Expected success got failure %d\n",error);
+ else printf("Success : %d\n",error);
+
+ /* Enable */
+ error = alter_audit_dispatcher(dispatcher,"far2",ELAPI_SINK_ACTION_ENABLE);
+ if(error != 0) printf("Expected success got failure %d\n",error);
+ else printf("Success : %d\n",error);
+
+ /* Pulse */
+ error = alter_audit_dispatcher(dispatcher,"far2",ELAPI_SINK_ACTION_PULSE);
+ if(error != 0) printf("Expected success got failure %d\n",error);
+ else printf("Success : %d\n",error);
+
+ /* Disable */
+ error = alter_audit_dispatcher(dispatcher,"far2",ELAPI_SINK_ACTION_DISABLE);
+ if(error != 0) printf("Expected success got failure %d\n",error);
+ else printf("Success : %d\n",error);
+
+ /* Delete */
+ error = alter_audit_dispatcher(dispatcher,"far2",ELAPI_SINK_ACTION_DELETE);
+ if(error != 0) printf("Expected success got failure %d\n",error);
+ else printf("Success : %d\n",error);
+
+ destroy_audit_dispatcher(dispatcher);
+ destroy_collection(peer);
+ destroy_collection(host);
+ destroy_collection(socket);
+ destroy_collection(event);
+ printf("Test end\n");
+ return error;
+}
+
+
+