summaryrefslogtreecommitdiffstats
path: root/dispatcher/elapi_dispatcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'dispatcher/elapi_dispatcher.c')
-rw-r--r--dispatcher/elapi_dispatcher.c688
1 files changed, 688 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 */
+}