From 8140cea7b4e3d3c9c6003eb6ae30e5e0fdd7c1ae Mon Sep 17 00:00:00 2001 From: Dmitri Pal Date: Thu, 17 Sep 2009 00:01:09 -0400 Subject: ELAPI Event resolver Started working on the async processing and realised that I need to have a good copy of the event with all the fields resolved so this patch has some foundation for the async functions (module elapi_async.c) but they are mostly stubbed out. The actual code will be added down the road. Instead the patch focuses on the code introduced in elapi_resolve.c module and the use of the functions from it. It also adds the implementation of the high level calls that initialize ELAPI with the external callbacks to be used during async processing (elapi_log.c). --- common/elapi/Makefile.am | 6 +- common/elapi/elapi_async.c | 153 +++++++++++++++++ common/elapi/elapi_async.h | 37 +--- common/elapi/elapi_event.c | 27 ++- common/elapi/elapi_event.h | 5 + common/elapi/elapi_fd.h | 42 +++++ common/elapi/elapi_internal.c | 6 - common/elapi/elapi_log.c | 126 +++++++++++++- common/elapi/elapi_log.h | 19 ++- common/elapi/elapi_priv.h | 107 +++++++----- common/elapi/elapi_resolve.c | 330 ++++++++++++++++++++++++++++++++++++ common/elapi/elapi_test/Makefile.am | 4 + common/elapi/elapi_test/elapi_ut.c | 21 ++- common/elapi/elapi_tm.h | 40 +++++ 14 files changed, 828 insertions(+), 95 deletions(-) create mode 100644 common/elapi/elapi_async.c create mode 100644 common/elapi/elapi_fd.h create mode 100644 common/elapi/elapi_resolve.c create mode 100644 common/elapi/elapi_tm.h (limited to 'common') diff --git a/common/elapi/Makefile.am b/common/elapi/Makefile.am index d548a629..08fd2d77 100644 --- a/common/elapi/Makefile.am +++ b/common/elapi/Makefile.am @@ -51,12 +51,16 @@ libelapi_la_SOURCES = \ elapi_event.c \ elapi_log.c \ elapi_internal.c \ - elapi_event.h \ elapi_sink.c \ + elapi_resolve.c \ + elapi_async.c \ + elapi_event.h \ elapi_priv.h \ elapi_sink.h \ elapi_log.h \ elapi_async.h \ + elapi_fd.h \ + elapi_tm.h \ elapi.h libelapi_la_LIBADD = libprovider.la libelapibasic.la diff --git a/common/elapi/elapi_async.c b/common/elapi/elapi_async.c new file mode 100644 index 00000000..0c404c04 --- /dev/null +++ b/common/elapi/elapi_async.c @@ -0,0 +1,153 @@ +/* + ELAPI + + Implementation for the ELAPI async processing interface. + + Copyright (C) Dmitri Pal 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 . +*/ + +#define _GNU_SOURCE +#include /* for errors */ + +#include "elapi_async.h" +/* Private headers that deal with fd and tm structure definitions */ +#include "elapi_fd.h" +#include "elapi_tm.h" +#include "trace.h" +#include "config.h" + +/* 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 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; + } + + 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) +{ + 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; + } + + *priv_data_to_get = fd_data->ext_data; + + TRACE_FLOW_STRING("elapi_get_fd_priv", "Exit"); + return error; +} + +/* Cleanup function */ +void elapi_destroy_fd_data(struct elapi_fd_data *fd_data) +{ + TRACE_FLOW_STRING("elapi_destroy_fd_data", "Entry"); + + + TRACE_FLOW_STRING("elapi_destroy_fd_data", "Exit"); +} + + +/* 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 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; + } + + 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) +{ + 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; + } + + *priv_data_to_get = tm_data->ext_data; + + TRACE_FLOW_STRING("elapi_get_tm_priv", "Exit"); + return error; +} + +/* Cleanup function */ +void elapi_destroy_tm_data(struct elapi_tm_data *tm_data) +{ + TRACE_FLOW_STRING("elapi_destroy_tm_data", "Entry"); + + + TRACE_FLOW_STRING("elapi_destroy_tm_data", "Exit"); +} + + +/* Public interfaces ELAPI exposes to handle fd or timer + * events (do not confuse with log events). + */ +int elapi_process_fd(struct elapi_fd_data *fd_data) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_process_fd", "Entry"); + + + TRACE_FLOW_STRING("elapi_process_fd", "Exit"); + return error; +} + +int elapi_process_tm(struct elapi_tm_data *tm_data) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_process_tm", "Entry"); + + + TRACE_FLOW_STRING("elapi_process_tm", "Exit"); + return error; +} diff --git a/common/elapi/elapi_async.h b/common/elapi/elapi_async.h index f9fbd9e3..21331766 100644 --- a/common/elapi/elapi_async.h +++ b/common/elapi/elapi_async.h @@ -38,7 +38,7 @@ int elapi_set_fd_priv(struct elapi_fd_data *fd_data, int elapi_get_fd_priv(struct elapi_fd_data *fd_data, void **priv_data_to_get); /* Cleanup function */ -int elapi_destroy_fd_data(struct elapi_fd_data *fd_data); +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. */ @@ -47,7 +47,7 @@ int elapi_set_tm_priv(struct elapi_tm_data *tm_data, int elapi_get_tm_priv(struct elapi_tm_data *tm_data, void **priv_data_to_get); /* Cleanup function */ -int elapi_destroy_tm_data(struct elapi_tm_data *tm_data); +void elapi_destroy_tm_data(struct elapi_tm_data *tm_data); /* Public interfaces ELAPI exposes to handle fd or timer * events (do not confuse with log events). @@ -84,10 +84,8 @@ typedef int (*elapi_set_fd)(int fd, /* 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_add_tm)(struct timeval *tv, +typedef int (*elapi_add_tm)(struct timeval tv, struct elapi_tm_data *tm_data, void *ext_tm_data); @@ -101,34 +99,5 @@ typedef int (*elapi_rem_tm)(struct elapi_tm_data *tm_data, -/* Structure that contains the pointer to functions - * that needed to be provided to enable async processing. - */ -struct elapi_async_ctx { - elapi_add_fd add_fd_cb; - elapi_rem_fd rem_fd_cb; - elapi_set_fd set_fd_cb; - void *ext_fd_data; - elapi_add_tm add_tm_cb; - elapi_rem_tm rem_tm_cb; - void *ext_tm_data; -}; - -/* Interface to create the async context */ -int elapi_create_asctx(struct elapi_async_ctx **ctx, - elapi_add_fd add_fd_cb, - elapi_rem_fd rem_fd_cb, - elapi_set_fd set_fd_cb, - void *ext_fd_data, - elapi_add_tm add_tm_cb, - elapi_rem_tm rem_tm_cb, - void *ext_tm_data); - -/* Function to free the async context */ -void elapi_destroy_asctx(struct elapi_async_ctx *ctx); - -/* Function to validate the consistency of the - * async context */ -int elapi_check_asctx(struct elapi_async_ctx *ctx); #endif diff --git a/common/elapi/elapi_event.c b/common/elapi/elapi_event.c index 735de599..e1baf424 100644 --- a/common/elapi/elapi_event.c +++ b/common/elapi/elapi_event.c @@ -449,7 +449,7 @@ static int interpret_key(char *key, adjust_by = 2; } else if ((*cursor == 'u') && (*(cursor+1) == '(')) { - *type = COL_TYPE_INTEGER; + *type = COL_TYPE_UNSIGNED; adjust_by = 2; } else if ((*cursor == 'l') && ((*(cursor+1) == 'i')||(*(cursor+1) == 'd')) && (*(cursor+2) == '(')) { @@ -457,7 +457,7 @@ static int interpret_key(char *key, adjust_by = 3; } else if ((*cursor == 'l') && (*(cursor+1) == 'u') && (*(cursor+2) == '(')) { - *type = COL_TYPE_LONG; + *type = COL_TYPE_ULONG; adjust_by = 3; } else if (((*cursor == 'f')||(*cursor == 'e')) && (*(cursor+1) == '(')) { @@ -866,6 +866,7 @@ int elapi_create_event_with_vargs(struct collection_item **event, { int error = EOK; struct collection_item *evt = NULL; + const char *alias; TRACE_FLOW_STRING("elapi_create_event_with_vargs", "Entry"); @@ -897,9 +898,17 @@ int elapi_create_event_with_vargs(struct collection_item **event, } } - /* Add elements from the template */ + /* Add elements from the collection */ if (collection != NULL) { - error = col_add_collection_to_collection(evt, NULL, NULL, collection, mode); + /* If we are told to use FLAT DOT mode + * add collection with prefixing here. + */ + if (mode == COL_ADD_MODE_FLATDOT) { + alias = col_get_item_property(collection, NULL); + } + else alias = NULL; + + error = col_add_collection_to_collection(evt, NULL, alias, collection, mode); if (error) { TRACE_ERROR_NUMBER("Failed to add elements from external collection. Error", error); col_destroy_collection(evt); @@ -955,6 +964,7 @@ int elapi_modify_event(struct collection_item *event, { int error = EOK; va_list args; + const char *alias; TRACE_FLOW_STRING("elapi_modify_event", "Entry"); @@ -966,7 +976,14 @@ int elapi_modify_event(struct collection_item *event, /* Add elements from the template */ if (collection != NULL) { - error = col_add_collection_to_collection(event, NULL, NULL, collection, mode); + /* If we are told to use FLAT DOT mode + * add collection with prefixing here. + */ + if (mode == COL_ADD_MODE_FLATDOT) { + alias = col_get_item_property(collection, NULL); + } + else alias = NULL; + error = col_add_collection_to_collection(event, NULL, alias, collection, mode); if (error) { TRACE_ERROR_NUMBER("Failed to add elements from external collection. Error", error); col_destroy_collection(event); diff --git a/common/elapi/elapi_event.h b/common/elapi/elapi_event.h index dfaba771..6a5fe044 100644 --- a/common/elapi/elapi_event.h +++ b/common/elapi/elapi_event.h @@ -46,6 +46,11 @@ */ #define E_MESSAGE "__message__" + +/* Standard prefix for internal attributes */ +#define E_PREFIX "__" +#define E_PREFIX_LEN 2 + /* Base argument in the template creation function is a bit mask. * Each supported predefined element corresponds to its bit in * the mask. diff --git a/common/elapi/elapi_fd.h b/common/elapi/elapi_fd.h new file mode 100644 index 00000000..48f2722c --- /dev/null +++ b/common/elapi/elapi_fd.h @@ -0,0 +1,42 @@ +/* + ELAPI + + Private header to define internal structure of the ELAPI fd data. + + Copyright (C) Dmitri Pal 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 . +*/ + +#ifndef ELAPI_FD_H +#define ELAPI_FD_H + +#include "elapi_priv.h" + +/* Structure that holds ELAPI file descriptor's watch data */ +struct elapi_fd_data { + int fd; + void *ext_data; + struct elapi_dispatcher *handle; + struct elapi_sink_ctx *sink_ctx; + struct collection_item *event; +}; + +/* Create the fd data structure for the event */ +int elapi_create_fd_data(struct elapi_fd_data **fd_data, + int fd, + void *ext_data, + struct elapi_sink_ctx *sink_ctx, + struct collection_item *event); + + +#endif diff --git a/common/elapi/elapi_internal.c b/common/elapi/elapi_internal.c index d0fd48dc..d80725b2 100644 --- a/common/elapi/elapi_internal.c +++ b/common/elapi/elapi_internal.c @@ -74,11 +74,6 @@ int elapi_tgt_cb(const char *target, TRACE_INFO_STRING("Current event will be logged into the target:", target); - /* FIXME THIS IS A PLACEHOLDER FUNCTION FOR NOW */ - - printf("\n\n\nPROCESSING EVENT:\n"); - col_debug_collection(target_data->event, COL_TRAVERSE_DEFAULT); - /* Log event */ error = elapi_tgt_submit(target_data->handle, context, target_data->event); if (error) { @@ -86,7 +81,6 @@ int elapi_tgt_cb(const char *target, return error; } - TRACE_FLOW_STRING("elapi_tgt_cb", "Exit."); return EOK; } diff --git a/common/elapi/elapi_log.c b/common/elapi/elapi_log.c index 322f5f8a..ee4c1a0c 100644 --- a/common/elapi/elapi_log.c +++ b/common/elapi/elapi_log.c @@ -97,6 +97,101 @@ static int elapi_dsp_msg_with_vargs(uint32_t target, /********** Main functions of the interface **********/ +/* Function to free the async context */ +void elapi_destroy_asctx(struct elapi_async_ctx *ctx) +{ + TRACE_FLOW_STRING("elapi_destroy_asctx", "Entry"); + + free(ctx); + + TRACE_FLOW_STRING("elapi_destroy_asctx", "Exit"); +} + +/* Function to validate the consistency of the + * async context */ +static int elapi_check_asctx(struct elapi_async_ctx *ctx) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_check_asctx", "Entry"); + + /* Check callbacks */ + if ((ctx->add_fd_cb == NULL) || + (ctx->rem_fd_cb == NULL) || + (ctx->set_fd_cb == NULL) || + (ctx->add_tm_cb == NULL) || + (ctx->rem_tm_cb == NULL)) { + TRACE_ERROR_NUMBER("One of the callbacks is missing. Error", EINVAL); + return EINVAL; + } + + /* We do not check the data pointers. + * Why? Becuase thought it is a bad approach + * the data the callbacks will use + * can be a global (bad but can be!). + * So forcing caller to provide non-NULL + * data pointers is a bit too much. + */ + + TRACE_FLOW_STRING("elapi_check_asctx", "Exit"); + return error; +} + +/* Interface to create the async context */ +int elapi_create_asctx(struct elapi_async_ctx **ctx, + elapi_add_fd add_fd_cb, + elapi_rem_fd rem_fd_cb, + elapi_set_fd set_fd_cb, + void *ext_fd_data, + elapi_add_tm add_tm_cb, + elapi_rem_tm rem_tm_cb, + void *ext_tm_data) +{ + int error = EOK; + struct elapi_async_ctx *ctx_new; + + TRACE_FLOW_STRING("elapi_create_asctx", "Entry"); + + /* Allocate data, copy it and then check. + * Why this order? Why not check first + * without allocating memory and wasting + * cycles for it? + * Becuase the check function can be used + * in other place to validate that the context + * is correct. Allocating and freeing + * data is not an overhead since + * it is going to catch development + * error that would not exist in the final + * product. Otherwise the progam just + * would not run. + */ + + ctx_new = (struct elapi_async_ctx *)malloc(sizeof(struct elapi_async_ctx)); + if (ctx_new == NULL) { + TRACE_ERROR_NUMBER("Failed to allocate memory for the context", ENOMEM); + return ENOMEM; + } + + ctx_new->add_fd_cb = add_fd_cb; + ctx_new->rem_fd_cb = rem_fd_cb; + ctx_new->set_fd_cb = set_fd_cb; + ctx_new->add_tm_cb = add_tm_cb; + ctx_new->rem_tm_cb = rem_tm_cb; + ctx_new->ext_fd_data = ext_fd_data; + ctx_new->ext_tm_data = ext_tm_data; + + error = elapi_check_asctx(ctx_new); + if (error) { + TRACE_ERROR_NUMBER("Check context failed", error); + elapi_destroy_asctx(ctx_new); + return error; + } + + *ctx = ctx_new; + + TRACE_FLOW_STRING("elapi_create_asctx", "Exit"); + return error; +} /* Function to create a dispatcher */ int elapi_create_dispatcher_adv(struct elapi_dispatcher **dispatcher, @@ -128,7 +223,14 @@ int elapi_create_dispatcher_adv(struct elapi_dispatcher **dispatcher, return EINVAL; } - /* FIXME: Check if context is valid */ + /* Check if context is valid */ + if (async_ctx) { + error = elapi_check_asctx(async_ctx); + if (error) { + TRACE_ERROR_NUMBER("Check context failed", error); + return error; + } + } /* Check what is passed in the config_path */ if (config_path) { @@ -251,6 +353,14 @@ int elapi_create_dispatcher_adv(struct elapi_dispatcher **dispatcher, handle->async_ctx = NULL; } + /* Build the list of the items we know how to resolve */ + error = elapi_init_resolve_list(&(handle->resolve_list)); + if (error != EOK) { + TRACE_ERROR_NUMBER("Failed to create list of resolvers. Error", error); + elapi_destroy_dispatcher(handle); + return error; + } + *dispatcher = handle; TRACE_FLOW_STRING("elapi_create_dispatcher_adv", "Returning Success."); @@ -316,6 +426,8 @@ void elapi_destroy_dispatcher(struct elapi_dispatcher *dispatcher) free_ini_config(dispatcher->ini_config); TRACE_INFO_STRING("Deleting targets name array.", ""); free_string_config_array(dispatcher->targets); + TRACE_INFO_STRING("Unbind resolver iterator.", ""); + col_unbind_iterator(dispatcher->resolve_list); TRACE_INFO_STRING("Freeing dispatcher.", ""); free(dispatcher); } @@ -330,6 +442,7 @@ int elapi_dsp_log(uint32_t target, { int error = EOK; struct elapi_tgt_data target_data; + struct collection_item *resolved_event; TRACE_FLOW_STRING("elapi_dsp_log", "Entry"); @@ -339,9 +452,16 @@ int elapi_dsp_log(uint32_t target, return EINVAL; } + /* Create a resolved event */ + error = elapi_resolve_event(&resolved_event, event, dispatcher); + if (error) { + TRACE_ERROR_NUMBER("Failed to create event context. Error", error); + return error; + } + /* Wrap parameters into one argument and pass on */ target_data.handle = dispatcher; - target_data.event = event; + target_data.event = resolved_event; target_data.target_mask = target; TRACE_INFO_NUMBER("Target mask is:", target_data.target_mask); @@ -352,6 +472,8 @@ int elapi_dsp_log(uint32_t target, elapi_tgt_cb, (void *)(&target_data)); + elapi_destroy_event(resolved_event); + TRACE_FLOW_NUMBER("elapi_dsp_log Exit. Returning", error); return error; } diff --git a/common/elapi/elapi_log.h b/common/elapi/elapi_log.h index 7d783553..5417caa7 100644 --- a/common/elapi/elapi_log.h +++ b/common/elapi/elapi_log.h @@ -42,6 +42,23 @@ struct elapi_dispatcher; */ /********** Main functions of the interface **********/ +/* Structure that contains the pointer to functions + * that needed to be provided to enable async processing. + */ +struct elapi_async_ctx; + +/* Interface to create the async context */ +int elapi_create_asctx(struct elapi_async_ctx **ctx, + elapi_add_fd add_fd_cb, + elapi_rem_fd rem_fd_cb, + elapi_set_fd set_fd_cb, + void *ext_fd_data, + elapi_add_tm add_tm_cb, + elapi_rem_tm rem_tm_cb, + void *ext_tm_data); + +/* Function to free the async context */ +void elapi_destroy_asctx(struct elapi_async_ctx *ctx); /* Function to create a dispatcher */ int elapi_create_dispatcher(struct elapi_dispatcher **dispatcher, /* Handle of the dispatcher will be stored in this variable */ @@ -52,7 +69,7 @@ int elapi_create_dispatcher(struct elapi_dispatcher **dispatcher, /* Handle of 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. */ - struct elapi_async_ctx *async_ctx); /* Async context. */ + struct elapi_async_ctx *ctx); /* Async context. */ /* Function to clean memory associated with the dispatcher */ void elapi_destroy_dispatcher(struct elapi_dispatcher *dispatcher); diff --git a/common/elapi/elapi_priv.h b/common/elapi/elapi_priv.h index fb8cd3e8..27b0079f 100644 --- a/common/elapi/elapi_priv.h +++ b/common/elapi/elapi_priv.h @@ -21,6 +21,7 @@ #define ELAPI_PRIV_H #include +#include #include "collection.h" #include "elapi_async.h" @@ -33,6 +34,7 @@ #define COL_CLASS_ELAPI_SINK COL_CLASS_ELAPI_BASE + 2 #define COL_CLASS_ELAPI_TARGET COL_CLASS_ELAPI_BASE + 3 #define COL_CLASS_ELAPI_SINK_REF COL_CLASS_ELAPI_BASE + 4 +#define COL_CLASS_ELAPI_RES_ITEM COL_CLASS_ELAPI_BASE + 5 /* Names for the collections */ #define E_TEMPLATE_NAME "template" @@ -51,6 +53,7 @@ #define ELAPI_SINK_ONERROR "onerror" #define ELAPI_SINK_TIMEOUT "timeout" #define ELAPI_SINK_SYNCH "synch" +#define ELAPI_RESOLVE_ITEM "res_item" /* Default timout before dispatcher tries to revive sink. * The actual value is configurable on per sink basis @@ -73,6 +76,22 @@ #define ELAPI_ONERROR_REVIVE 0 #define ELAPI_ONERROR_DISABLE 1 +/* Structure that contains the pointer to functions + * that needed to be provided to enable async processing. + */ +struct elapi_async_ctx { + /* Callbacks related to file descriptor. */ + elapi_add_fd add_fd_cb; + elapi_rem_fd rem_fd_cb; + elapi_set_fd set_fd_cb; + /* File descriptor callback external data. */ + void *ext_fd_data; + /* Callbacks for timer */ + elapi_add_tm add_tm_cb; + elapi_rem_tm rem_tm_cb; + /* Timer's external data */ + void *ext_tm_data; +}; struct elapi_dispatcher { /* Application name */ @@ -87,24 +106,14 @@ struct elapi_dispatcher { struct collection_item *sink_list; /* Configuration */ struct collection_item *ini_config; + /* Items to resolve */ + struct collection_iterator *resolve_list; /* Default event template */ struct collection_item *default_template; /* Async processing related data */ struct elapi_async_ctx *async_ctx; - /* Indicator of our synch mode - * FIXME: Do we need it? - */ - uint32_t async_mode; - /* Time offset */ - int32_t offset; }; -/* Structure to pass data from logging function to targets */ -struct elapi_tgt_data { - struct collection_item *event; - struct elapi_dispatcher *handle; - uint32_t target_mask; -}; /* This is a structure that holds the information * about the target. @@ -165,41 +174,32 @@ struct elapi_sink_ctx { }; -/* The structure to hold the event and its context */ -/* FIXME The event should be turned into this object - * on the high level before going - * into any target. - * and then this should be passed around - * instead of the actual event. +/* A helper structure that holds data + * needed to resolve the event. */ -struct elapi_event_ctx { - /* This is a copy of the event */ - /* We have to copy it for two reasons: - * a) It needs to be flattened so - * that we do not get unnecesary naming - * collisions if same key appears on different - * levels - * b) In case of async logging we need - * the original event until we are sure - * it is actually logged. If we do not - * keep it around the application can modify - * it or delete it before we figured out - * that sink is broken and we need to fail over. - * If in this case we go to another sink - * and if we do not have the original event - * we are screwed. - */ +struct elapi_resolve_data { + /* Reference to the event */ struct collection_item *event; - /* Reference count */ - int refcount; - /* Event time */ + /* Reference back to dispatcher */ + struct elapi_dispatcher *handle; + /* Time related data */ time_t tm; - /* Resolved message */ - char *message; + /* Structured UTC time */ + struct tm utc_time; + /* Structured local time */ + struct tm local_time; /* Time offset */ - int32_t offset; + int offset; }; +/* Structure to pass data from logging function to targets */ +struct elapi_tgt_data { + struct collection_item *event; + struct elapi_dispatcher *handle; + uint32_t target_mask; +}; + + /* Lookup structure for searching for providers */ struct elapi_prvdr_lookup { const char *name; @@ -213,6 +213,26 @@ struct elapi_get_sink { int found; }; +/* Signature of the item resolution function */ +typedef int (*elapi_rslv_cb)(struct elapi_resolve_data *resolver, + struct collection_item *item, + int *skip); + +/* Structure to hold type-callback tuples */ +struct elapi_rslv_item_data { + int type; + elapi_rslv_cb resolve_cb; +}; + +/* Structure to hold name-data tuples */ +struct elapi_resolve_list { + const char *name; + struct elapi_rslv_item_data resolve_item; +}; + +/* Function to initialize resolution list */ +int elapi_init_resolve_list(struct collection_iterator **list); + /* Function to create event using arg list */ int elapi_create_event_with_vargs(struct collection_item **event, struct collection_item *template, @@ -296,6 +316,11 @@ int elapi_tgt_submit(struct elapi_dispatcher *handle, /* Create list of targets for a dispatcher */ int elapi_tgt_mklist(struct elapi_dispatcher *handle); +/* Create event context */ +int elapi_resolve_event(struct collection_item **final_event, + struct collection_item *event, + struct elapi_dispatcher *handle); + /* Send ELAPI config errors into a file */ void elapi_dump_ini_err(struct collection_item *error_list); diff --git a/common/elapi/elapi_resolve.c b/common/elapi/elapi_resolve.c new file mode 100644 index 00000000..5570eee0 --- /dev/null +++ b/common/elapi/elapi_resolve.c @@ -0,0 +1,330 @@ +/* + ELAPI + + Module contains functions to resolve the event. + + Copyright (C) Dmitri Pal 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 . +*/ + +#define _GNU_SOURCE +#include /* for errors */ +#include /* for strcmp() */ + +#include "elapi_priv.h" +#include "elapi_event.h" +/* #include "elapi_subst.h" */ +#include "trace.h" +#include "config.h" + +/*****************************************/ +/* Individual callbacks are defined here */ +/*****************************************/ +/* Timestamp resoltion callback */ +int elapi_timestamp_cb(struct elapi_resolve_data *resolver, + struct collection_item *item, + int *skip) +{ + int error = EOK; + char timestamp[TIME_ARRAY_SIZE + 1]; + int length; + + TRACE_FLOW_STRING("elapi_timestamp_cb", "Entry"); + + /* Construct the time stamp */ + length = strftime(timestamp, + TIME_ARRAY_SIZE, + (const char *)(col_get_item_data(item)), + &(resolver->local_time)); + + /* Update the time stamp item */ + error = col_modify_str_item(item, + NULL, + timestamp, + length + 1); + + TRACE_FLOW_NUMBER("elapi_timestamp_cb. Exit. Returning", error); + return error; +} + +/* UTC time resolution callback */ +int elapi_utctime_cb(struct elapi_resolve_data *resolver, + struct collection_item *item, + int *skip) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_utctime_cb", "Entry"); + + /* Update the UTC item */ + error = col_modify_int_item(item, + NULL, + (int)(resolver->tm)); + + TRACE_FLOW_NUMBER("elapi_utctime_cb. Exit. Returning", error); + return error; +} + +/* Offset resolution callback */ +int elapi_offset_cb(struct elapi_resolve_data *resolver, + struct collection_item *item, + int *skip) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_offset_cb", "Entry"); + + /* Update the offset item */ + error = col_modify_int_item(item, + NULL, + (int)(resolver->offset)); + + TRACE_FLOW_NUMBER("elapi_offset_cb. Exit. Returning", error); + return error; +} + + +/* Message resolution callback */ +int elapi_message_cb(struct elapi_resolve_data *resolver, + struct collection_item *item, + int *skip) +{ + int error = EOK; + /* int length; */ + /* char *result; */ + + TRACE_FLOW_STRING("elapi_message_cb", "Entry"); + + /* FIXME: Resolve message here */ + /* Function is not yet implemented ... + error = elapi_sprintf(&result, + &length, + (const char *)col_get_item_data(item), + resolver->event); + if (error) { + TRACE_ERROR_NUMBER("Failed to build message", error); + return error; + } + + error = col_modify_str_item(item, + NULL, + result; + length + 1); + free(result); + if (error) { + TRACE_ERROR_NUMBER("Failed to modify message item", error); + return error; + } + */ + + TRACE_FLOW_NUMBER("elapi_message_cb. Exit. Returning", error); + return error; +} + + +/*****************************************/ +/* Array of structures for resolution of + * the different event properties. + */ +struct elapi_resolve_list elapi_known_fields[] = { + { E_TIMESTAMP, { COL_TYPE_STRING, elapi_timestamp_cb }}, + { E_UTCTIME, { COL_TYPE_INTEGER, elapi_utctime_cb }}, + { E_OFFSET, { COL_TYPE_INTEGER, elapi_offset_cb }}, + { E_MESSAGE, { COL_TYPE_STRING, elapi_message_cb }}, + /* ADD NEW CALLBACKS HERE */ + { NULL, { COL_TYPE_ANY, NULL }} +}; + + + + +/*****************************************/ +/* A callback function to do substitutions + * of different properties as we copy the event. + */ +static int elapi_resolve_item(struct collection_item *item, + void *ext_data, + int *skip) +{ + int error = EOK; + struct elapi_resolve_data *resolver; + struct collection_item *res_item; + struct elapi_rslv_item_data *rslv_pair; + int res; + + TRACE_FLOW_STRING("elapi_resolve_item", "Entry"); + + /* Do we care about this field ? */ + if (strncmp(col_get_item_property(item, NULL), + E_PREFIX, + E_PREFIX_LEN) != 0) { + TRACE_FLOW_STRING("elapi_resolve_item. Skipping resoltion.", "Exit"); + return EOK; + } + + /* This is an internal field that might need resolution */ + resolver = (struct elapi_resolve_data *)ext_data; + + /* NOTE: This iteration loop uses advanced iterator + * capabilities. Read more about it before you decide + * to use this code as an example. + */ + while (1) { + + /* Advance to next item in the list */ + error = col_iterate_collection(resolver->handle->resolve_list, + &res_item); + if (error) { + TRACE_ERROR_NUMBER("Failed to iterate collection", error); + return error; + } + + /* Are we done ? This means we looped and did not find + * the item. */ + if (res_item == NULL) break; + + /* Compare items */ + res = col_compare_items(item, + res_item, + COL_CMPIN_PROP_EQU, + NULL); + if (res == 0) { + /* Item names are the same, so drill down and get expected type. */ + rslv_pair = *((struct elapi_rslv_item_data **)col_get_item_data(res_item)); + /* Make sure that types matched too */ + if (rslv_pair->type == col_get_item_type(item)) { + /* This is the item we need to resolve so resolve */ + error = rslv_pair->resolve_cb(resolver, + item, + skip); + if (error) { + TRACE_ERROR_NUMBER("Failed to resolve item", error); + return error; + } + + /* Pin down the iterator here */ + col_pin_iterator(resolver->handle->resolve_list); + + /* Break out of loop */ + break; + } + } + } + TRACE_FLOW_STRING("elapi_resolve_item", "Exit"); + return error; +} + + +/* Resolve event */ +int elapi_resolve_event(struct collection_item **final_event, + struct collection_item *event, + struct elapi_dispatcher *handle) +{ + int error = EOK; + struct elapi_resolve_data resolver; + struct collection_item *new_event; + time_t local; + time_t utc; + + TRACE_FLOW_STRING("elapi_create_event_ctx", "Entry"); + + /* Prepeare the resolver */ + resolver.event = event; + resolver.handle = handle; + /* Get seconds */ + resolver.tm = time(NULL); + /* Convert to local and UTC structured time */ + localtime_r(&resolver.tm, &(resolver.local_time)); + gmtime_r(&resolver.tm, &(resolver.utc_time)); + /* Convert back */ + utc = mktime(&(resolver.utc_time)); + local = mktime(&(resolver.local_time)); + /* Get offset - it is safe to typecast to int here */ + resolver.offset = (int)(difftime(local, utc)); + + /* NOTE: We will use FLATDOT mode. + * We will see what people have to say + * about this approach... + */ + error = col_copy_collection_with_cb(&new_event, + event, + NULL, + COL_COPY_FLATDOT, + elapi_resolve_item, + (void *)&resolver); + if (error) { + TRACE_ERROR_NUMBER("Failed to resolve the event", error); + return error; + } + + *final_event = new_event; + + TRACE_FLOW_STRING("elapi_create_event_ctx", "Exit"); + return error; +} + +/* Function to initialize resolution list */ +int elapi_init_resolve_list(struct collection_iterator **list) +{ + int error = EOK; + struct elapi_resolve_list *current; + struct collection_item *col = NULL; + struct collection_iterator *iterator; + struct elapi_rslv_item_data *bin_data; + + TRACE_FLOW_STRING("elapi_init_resolve_list", "Entry"); + + /* Create collection of fields that we know how to process */ + error = col_create_collection(&col, + ELAPI_RESOLVE_ITEM, + COL_CLASS_ELAPI_RES_ITEM); + + if (error) { + TRACE_ERROR_NUMBER("Failed to create collection", error); + return error; + } + + /* Loop through the static array and turn it into a collection */ + current = elapi_known_fields; + while (current->name) { + bin_data = &(current->resolve_item); + error = col_add_binary_property(col, + NULL, + current->name, + (void *)&bin_data, + sizeof(struct elapi_rslv_item_data *)); + if (error) { + TRACE_ERROR_NUMBER("Failed to add item resolver", error); + col_destroy_collection(col); + return error; + } + + current++; + } + + /* Now bind iterator */ + error = col_bind_iterator(&iterator, col, COL_TRAVERSE_FLAT); + if (error) { + TRACE_ERROR_NUMBER("Failed to bind collection", error); + col_destroy_collection(col); + return error; + } + + /* We do not need the collection itself - we have iterator */ + col_destroy_collection(col); + + *list = iterator; + + TRACE_FLOW_STRING("elapi_init_resolve_list", "Exit"); + return error; +} diff --git a/common/elapi/elapi_test/Makefile.am b/common/elapi/elapi_test/Makefile.am index 76f06e0c..dcf1707c 100644 --- a/common/elapi/elapi_test/Makefile.am +++ b/common/elapi/elapi_test/Makefile.am @@ -30,11 +30,15 @@ libelapi_test_la_SOURCES = \ ../elapi_sink.c \ ../elapi_basic.c \ ../elapi_basic.h \ + ../elapi_resolve.c \ + ../elapi_async.c \ ../elapi_event.h \ ../elapi_priv.h \ ../elapi_sink.h \ ../elapi_log.h \ ../elapi_async.h \ + ../elapi_fd.h \ + ../elapi_tm.h \ ../elapi.h \ ../providers/file/file_provider.c \ ../providers/file/file_provider.h \ diff --git a/common/elapi/elapi_test/elapi_ut.c b/common/elapi/elapi_test/elapi_ut.c index 0a823432..1046ed0e 100644 --- a/common/elapi/elapi_test/elapi_ut.c +++ b/common/elapi/elapi_test/elapi_ut.c @@ -208,8 +208,8 @@ int complex_event_test(void) return error; } - col_debug_collection(template, COL_TRAVERSE_FLAT); - col_debug_collection(event, COL_TRAVERSE_FLAT); + col_debug_collection(template, COL_TRAVERSE_DEFAULT); + col_debug_collection(event, COL_TRAVERSE_DEFAULT); error = elapi_log(E_TARGET_DEBUG, event); @@ -233,8 +233,9 @@ int complex_event_test(void) "%d(int_number),", -200, "%u(unsigned_number)", 300, "%ld(long_number)", -1234567, + "%lu(long_unsigned)", -1234567, E_MESSAGE, - "%(stamp), %s(sub_string), %(int_number), %(unsigned_number), %(long_unsigned_number), %(bin), %e(double_number)", + "%(stamp), %(sub_string), %(int_number), %(unsigned_number), %(long_unsigned_number), %(bin), %(double_number)", E_EOARG); if (error) { @@ -270,8 +271,11 @@ int complex_event_test(void) col_destroy_collection(col); - col_debug_collection(template, COL_TRAVERSE_FLAT); - col_debug_collection(event, COL_TRAVERSE_FLAT); + col_debug_collection(template, COL_TRAVERSE_DEFAULT); + + printf("\nPRINTING EVENT\n\n"); + printf("\nPRINTING EVENT, removed message added bin\n\n"); + col_debug_collection(event, COL_TRAVERSE_DEFAULT); if ((error = col_create_collection(&col, "test", 0)) || @@ -300,6 +304,10 @@ int complex_event_test(void) return error; } + printf("\nPRINTING EVENT, removed message, added bin,\n" + "added test collection with zzz & zzz2\n\n"); + + col_debug_collection(event, COL_TRAVERSE_DEFAULT); col_destroy_collection(col); if ((error = col_create_collection(&col, "flat", 0)) || @@ -326,6 +334,9 @@ int complex_event_test(void) return error; } + printf("\nPRINTING EVENT, added flat collection with zzz & zzz2\n\n"); + + col_debug_collection(event, COL_TRAVERSE_DEFAULT); col_destroy_collection(col); error = elapi_copy_event(&event_copy, event); diff --git a/common/elapi/elapi_tm.h b/common/elapi/elapi_tm.h new file mode 100644 index 00000000..e9d50e4b --- /dev/null +++ b/common/elapi/elapi_tm.h @@ -0,0 +1,40 @@ +/* + ELAPI + + Private header to define internal structure of the ELAPI timer data. + + Copyright (C) Dmitri Pal 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 . +*/ + +#ifndef ELAPI_TM_H +#define ELAPI_TM_H + +#include "elapi_priv.h" + +/* Structure that holds ELAPI timer watch data */ +struct elapi_tm_data { + void *ext_data; + struct elapi_dispatcher *handle; + struct elapi_sink_ctx *sink_ctx; + struct collection_item *event; +}; + +/* Create the tm data structure for the event */ +int elapi_create_tm_data(struct elapi_tm_data **tm_data, + void *ext_data, + struct elapi_sink_ctx *sink_ctx, + struct collection_item *event); + + +#endif -- cgit