diff options
author | Dmitri Pal <dpal@redhat.com> | 2009-09-27 22:16:17 -0400 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2009-10-05 10:32:08 -0400 |
commit | a78d04984240a3d04045402c964d9d8c5be463ef (patch) | |
tree | 0353869fca520c545daea46deb43069d2a172e9a /common/elapi | |
parent | 8140cea7b4e3d3c9c6003eb6ae30e5e0fdd7c1ae (diff) | |
download | sssd-a78d04984240a3d04045402c964d9d8c5be463ef.tar.gz sssd-a78d04984240a3d04045402c964d9d8c5be463ef.tar.xz sssd-a78d04984240a3d04045402c964d9d8c5be463ef.zip |
ELAPI Resolving message attribute
This patch continues work started
with the previous patch.
It resolves message attribute.
Message attribute is a special attribute
in the event that may contain
references to other attributes in the
event. When message is resolved the
references are replaced with actual
values of the referenced attributes.
Diffstat (limited to 'common/elapi')
-rw-r--r-- | common/elapi/Makefile.am | 1 | ||||
-rw-r--r-- | common/elapi/elapi_event.h | 18 | ||||
-rw-r--r-- | common/elapi/elapi_priv.h | 11 | ||||
-rw-r--r-- | common/elapi/elapi_resolve.c | 131 | ||||
-rw-r--r-- | common/elapi/elapi_subst.c | 395 | ||||
-rw-r--r-- | common/elapi/elapi_test/Makefile.am | 1 | ||||
-rw-r--r-- | common/elapi/elapi_test/elapi_ut.c | 30 | ||||
-rw-r--r-- | common/elapi/providers/file/file_fmt_csv.c | 6 |
8 files changed, 536 insertions, 57 deletions
diff --git a/common/elapi/Makefile.am b/common/elapi/Makefile.am index 08fd2d773..53f2c3eff 100644 --- a/common/elapi/Makefile.am +++ b/common/elapi/Makefile.am @@ -54,6 +54,7 @@ libelapi_la_SOURCES = \ elapi_sink.c \ elapi_resolve.c \ elapi_async.c \ + elapi_subst.c \ elapi_event.h \ elapi_priv.h \ elapi_sink.h \ diff --git a/common/elapi/elapi_event.h b/common/elapi/elapi_event.h index 6a5fe044e..3e52dfdb6 100644 --- a/common/elapi/elapi_event.h +++ b/common/elapi/elapi_event.h @@ -22,11 +22,15 @@ #include "collection.h" -/* Possible predefined elements of the event */ -#define E_TIMESTAMP "__stamp__" /* string - the value is the format for strftime() +/* Possible predefined elements of the event. + * First letter R means that it is a property + * resolvable at the logging time. + */ + +#define E_TIMESTAMP "R_stamp__" /* string - the value is the format for strftime() * default is standard format for current locale. */ -#define E_UTCTIME "__time__" /* int - UTC time as unix time in seconds since 1970 */ -#define E_OFFSET "__loco__" /* int - local time displacement */ +#define E_UTCTIME "R_time__" /* int - UTC time as unix time in seconds since 1970 */ +#define E_OFFSET "R_loco__" /* int - local time displacement */ #define E_PID "__pid__" /* int - Process ID of the current process */ #define E_APPNAME "__appnm__" /* string - Name of the current application */ #define E_HOSTNAME "__host__" /* string - Name of the current host */ @@ -44,11 +48,11 @@ * The token %(server) will be replaced by value * in the attribute "server" in the event. */ -#define E_MESSAGE "__message__" +#define E_MESSAGE "R_message__" -/* Standard prefix for internal attributes */ -#define E_PREFIX "__" +/* Standard prefix for internal resolvable attributes */ +#define E_PREFIX "R_" #define E_PREFIX_LEN 2 /* Base argument in the template creation function is a bit mask. diff --git a/common/elapi/elapi_priv.h b/common/elapi/elapi_priv.h index 27b0079ff..e7480f90b 100644 --- a/common/elapi/elapi_priv.h +++ b/common/elapi/elapi_priv.h @@ -24,6 +24,7 @@ #include <stdarg.h> #include "collection.h" +#include "elapi_basic.h" #include "elapi_async.h" #include "elapi_sink.h" @@ -178,8 +179,8 @@ struct elapi_sink_ctx { * needed to resolve the event. */ struct elapi_resolve_data { - /* Reference to the event */ - struct collection_item *event; + /* Reference to the message item inside event */ + struct collection_item *message; /* Reference back to dispatcher */ struct elapi_dispatcher *handle; /* Time related data */ @@ -321,6 +322,12 @@ int elapi_resolve_event(struct collection_item **final_event, struct collection_item *event, struct elapi_dispatcher *handle); +/* Function to place the event items into a formatted string */ +int elapi_sprintf(struct elapi_data_out *out_data, + const char *format_str, + struct collection_item *event); + + /* 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 index 5570eee0c..ca2601b70 100644 --- a/common/elapi/elapi_resolve.c +++ b/common/elapi/elapi_resolve.c @@ -23,17 +23,17 @@ #include "elapi_priv.h" #include "elapi_event.h" -/* #include "elapi_subst.h" */ +#include "elapi_basic.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) +/* Timestamp resolution callback */ +static int elapi_timestamp_cb(struct elapi_resolve_data *resolver, + struct collection_item *item, + int *skip) { int error = EOK; char timestamp[TIME_ARRAY_SIZE + 1]; @@ -58,9 +58,9 @@ int elapi_timestamp_cb(struct elapi_resolve_data *resolver, } /* UTC time resolution callback */ -int elapi_utctime_cb(struct elapi_resolve_data *resolver, - struct collection_item *item, - int *skip) +static int elapi_utctime_cb(struct elapi_resolve_data *resolver, + struct collection_item *item, + int *skip) { int error = EOK; @@ -76,9 +76,9 @@ int elapi_utctime_cb(struct elapi_resolve_data *resolver, } /* Offset resolution callback */ -int elapi_offset_cb(struct elapi_resolve_data *resolver, - struct collection_item *item, - int *skip) +static int elapi_offset_cb(struct elapi_resolve_data *resolver, + struct collection_item *item, + int *skip) { int error = EOK; @@ -93,45 +93,23 @@ int elapi_offset_cb(struct elapi_resolve_data *resolver, return error; } - /* Message resolution callback */ -int elapi_message_cb(struct elapi_resolve_data *resolver, - struct collection_item *item, - int *skip) +static 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; - } + /* Save pointer to message item */ + resolver->message = item; - 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; + TRACE_FLOW_NUMBER("elapi_message_cb.", "Exit"); + return EOK; } + /*****************************************/ /* Array of structures for resolution of * the different event properties. @@ -172,6 +150,8 @@ static int elapi_resolve_item(struct collection_item *item, return EOK; } + TRACE_FLOW_STRING("Item to resolve: ", col_get_item_property(item, NULL)); + /* This is an internal field that might need resolution */ resolver = (struct elapi_resolve_data *)ext_data; @@ -224,6 +204,53 @@ static int elapi_resolve_item(struct collection_item *item, return error; } +/* Message resolution function */ +int elapi_resolve_message(struct collection_item *item, + struct collection_item *event) +{ + int error = EOK; + struct elapi_data_out *serialized; + + TRACE_FLOW_STRING("elapi_resolve_message", "Entry"); + + /* I prefer to allocate it rather than do it on stack. + * The main reason is that I would have to memset + * the struct to init it. + * So why not just use the interface we already have. + */ + error = elapi_alloc_serialized_data(&serialized); + if (error) { + TRACE_ERROR_NUMBER("Failed to allocate serialized data", error); + return error; + } + + /* Call string substitution */ + error = elapi_sprintf(serialized, + (const char *)col_get_item_data(item), + event); + if (error) { + TRACE_ERROR_NUMBER("Failed to build message", error); + elapi_free_serialized_data(serialized); + return error; + } + + /* Put the resolved value into the item */ + error = col_modify_str_item(item, + NULL, + (char *)serialized->buffer, + serialized->length + 1); + + /* Clean data regardless of the result */ + elapi_free_serialized_data(serialized); + + if (error) { + TRACE_ERROR_NUMBER("Failed to modify message item", error); + return error; + } + + TRACE_FLOW_NUMBER("elapi_resolve_message. Exit. Returning", error); + return error; +} /* Resolve event */ int elapi_resolve_event(struct collection_item **final_event, @@ -238,8 +265,8 @@ int elapi_resolve_event(struct collection_item **final_event, TRACE_FLOW_STRING("elapi_create_event_ctx", "Entry"); - /* Prepeare the resolver */ - resolver.event = event; + /* Prepare the resolver */ + resolver.message = NULL; resolver.handle = handle; /* Get seconds */ resolver.tm = time(NULL); @@ -267,6 +294,22 @@ int elapi_resolve_event(struct collection_item **final_event, return error; } + if (resolver.message) { + /* Now resolve message. We need to do it last since + * we do not know the order of the properties + * and message can be referencing properties + * that are later than message in the list + * and have not been resolved yet. + */ + error = elapi_resolve_message(resolver.message, + new_event); + if (error) { + TRACE_ERROR_NUMBER("Failed to resolve the event", error); + col_destroy_collection(new_event); + return error; + } + } + *final_event = new_event; TRACE_FLOW_STRING("elapi_create_event_ctx", "Exit"); @@ -279,7 +322,7 @@ 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 collection_iterator *iterator = NULL; struct elapi_rslv_item_data *bin_data; TRACE_FLOW_STRING("elapi_init_resolve_list", "Entry"); diff --git a/common/elapi/elapi_subst.c b/common/elapi/elapi_subst.c new file mode 100644 index 000000000..da507aa5b --- /dev/null +++ b/common/elapi/elapi_subst.c @@ -0,0 +1,395 @@ +/* + ELAPI + + Module contains functions related to format substitution + + 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 <string.h> +#include <stdio.h> +#include "elapi_priv.h" +#include "trace.h" +#include "config.h" + +/* Reasonable size for one event */ +/* FIXME: may be it would make sense to make it configurable ? */ +#define ELAPI_SUBST_BLOCK 256 + +/* Calculate the potential size of the item */ +static unsigned elapi_get_item_len(int type, int raw_len) +{ + int serialized_len = 0; + + TRACE_FLOW_STRING("elapi_get_item_len", "Entry point"); + + switch (type) { + case COL_TYPE_INTEGER: + case COL_TYPE_UNSIGNED: + case COL_TYPE_LONG: + case COL_TYPE_ULONG: + serialized_len = MAX_LONG_STRING_LEN; + break; + + case COL_TYPE_STRING: + serialized_len = raw_len; + break; + + case COL_TYPE_BINARY: + serialized_len = raw_len * 2; + break; + + case COL_TYPE_DOUBLE: + serialized_len = MAX_DOUBLE_STRING_LEN; + break; + + case COL_TYPE_BOOL: + serialized_len = MAX_BOOL_STRING_LEN; + break; + + default: + serialized_len = 0; + break; + } + + TRACE_FLOW_STRING("elapi_get_item_len", "Exit point"); + return (uint32_t)serialized_len; +} + + +/* Function to serialize one item */ +static int elapi_sprintf_item(struct elapi_data_out *out_data, + struct collection_item *item) +{ + int error = EOK; + uint32_t projected_len; + uint32_t used_len; + uint32_t item_len; + void *data; + int type; + int i; + + TRACE_FLOW_STRING("elapi_sprintf_item", "Entry"); + + /* Get projected length of the item */ + item_len = col_get_item_length(item); + type = col_get_item_type(item); + projected_len = elapi_get_item_len(type, item_len); + + TRACE_INFO_NUMBER("Expected data length: ", projected_len); + + /* Make sure we have enough space */ + if (out_data->buffer == NULL) { + TRACE_INFO_STRING("First time use.", ""); + /* Add null terminating zero */ + projected_len++; + } + + /* Grow buffer if needed */ + error = elapi_grow_data(out_data, + projected_len, + ELAPI_SUBST_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Error. Failed to allocate memory.", error); + return error; + } + + data = col_get_item_data(item); + + /* Add the value */ + switch (type) { + case COL_TYPE_STRING: + + /* Item's length includes trailing 0 for data items */ + used_len = item_len - 1; + memcpy(&out_data->buffer[out_data->length], + (const char *)(data), + used_len); + out_data->buffer[out_data->length + used_len] = '\0'; + break; + + case COL_TYPE_BINARY: + + for (i = 0; i < item_len; i++) { + sprintf((char *)&out_data->buffer[out_data->length + i * 2], + "%02X", (unsigned int)(((const unsigned char *)(data))[i])); + } + used_len = item_len * 2; + /* We need it here for the case item_len = 0 */ + out_data->buffer[out_data->length + used_len] = '\0'; + break; + + case COL_TYPE_INTEGER: + used_len = sprintf((char *)&out_data->buffer[out_data->length], + "%d", *((const int *)(data))); + break; + + case COL_TYPE_UNSIGNED: + used_len = sprintf((char *)&out_data->buffer[out_data->length], + "%u", *((const unsigned int *)(data))); + break; + + case COL_TYPE_LONG: + used_len = sprintf((char *)&out_data->buffer[out_data->length], + "%ld", *((const long *)(data))); + break; + + case COL_TYPE_ULONG: + used_len = sprintf((char *)&out_data->buffer[out_data->length], + "%lu", *((const unsigned long *)(data))); + break; + + case COL_TYPE_DOUBLE: + used_len = sprintf((char *)&out_data->buffer[out_data->length], + "%.4f", *((const double *)(data))); + break; + + case COL_TYPE_BOOL: + used_len = sprintf((char *)&out_data->buffer[out_data->length], + "%s", + (*((const unsigned char *)(data))) ? "true" : "false"); + break; + + default: + out_data->buffer[out_data->length] = '\0'; + used_len = 0; + break; + } + + /* Adjust length */ + out_data->length += used_len; + + TRACE_INFO_STRING("Data: ", (char *)out_data->buffer); + + TRACE_FLOW_STRING("elapi_sprintf_item", "Exit"); + return error; + +} + +/* Lookup item hoping that items in message are somewhat ordered. + * If there is some ordering we will save on performance. + * If not we will not loos against a standard lookup. + */ +static struct collection_item *elapi_lookup_item(const char *start, + int length, + struct collection_iterator *iterator) +{ + int error = EOK; + struct collection_item *found = NULL; + const char *property; + int property_len; + uint64_t hash; + + TRACE_FLOW_STRING("elapi_lookup_item", "Entry"); + + /* Prepare hash */ + hash = col_make_hash(start, length, NULL); + + /* 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(iterator, + &found); + if (error) { + TRACE_ERROR_NUMBER("Failed to iterate collection", error); + return NULL; + } + + /* Are we done ? This means we looped and did not find + * the item. */ + if (found == NULL) break; + + property = col_get_item_property(found, &property_len); + + /* Compare item and the property */ + if ((hash == col_get_item_hash(found)) && + (length == property_len) && + (strncasecmp(start, property, length) == 0)) { + /* This is our item !!! */ + /* Pin down the iterator here */ + col_pin_iterator(iterator); + + /* Break out of loop */ + break; + } + } + + TRACE_FLOW_STRING("elapi_lookup_item", "Exit"); + return found; +} + + +/* Function to parse format string */ +static const char *elapi_parse_format(const char *start, + int *length, + struct collection_item **item, + struct collection_iterator *iterator) +{ + const char *runner; + const char *bracket; + const char *name_start; + + TRACE_FLOW_STRING("elapi_parse_format", "Entry"); + if ((start == NULL) || (*start == '\0')) { + TRACE_FLOW_STRING("String is empty", "Return"); + return NULL; + } + + runner = start; + + while (1) { + /* First check for end of the string */ + if (*runner == '\0') { + TRACE_FLOW_STRING("Found last token", start); + *length = runner - start; + return runner; + } + + /* Is it the beginning of the field substitution? */ + if (*runner == '%') { + /* Check for bracket */ + if (*(runner + 1) == '(') { + /* Search for closing one */ + name_start = runner + 2; + bracket = name_start; + while (1) { + /* Check for the end */ + if (*bracket == '\0') { + TRACE_FLOW_STRING("No closing bracket", start); + *length = bracket - start; + return bracket; + } + /* Did we find closing backet? */ + if (*bracket == ')') { + TRACE_FLOW_STRING("Bracket is found: ", name_start); + /* There might be specific format specifiers */ + if (*name_start == '!') { + /* Force rewind of the + * iterator */ + col_rewind_iterator(iterator); + name_start++; + } + + /* FIXME: Add other specifiers here... + * Specifier that can be supported in future + * might expand multi value property + * to a list of values separated by + * provided symbol. + */ + + *item = elapi_lookup_item(name_start, + bracket - name_start, + iterator); + bracket++; + if (*item == NULL) { + /* The item is not known (or error) */ + TRACE_FLOW_STRING("No item in event", name_start); + *length = bracket - start; + return bracket; + } + + /* Item is found */ + TRACE_FLOW_STRING("Item found: ", name_start); + *length = runner - start; + return bracket; + } + bracket++; + } + } + } + runner++; + } + /* This point is unreachable */ +} + +/* Function to place the event items into the formatted string */ +int elapi_sprintf(struct elapi_data_out *out_data, + const char *format_str, + struct collection_item *event) +{ + const char *start; + int length; + struct collection_item *item; + struct collection_iterator *iterator = NULL; + const char *result; + int error; + + TRACE_FLOW_STRING("elapi_sprintf", "Entry"); + + /* Create iterator - by thus time cevent is resolved and should + * be a flattened collection. At least this is the assumption. + */ + error = col_bind_iterator(&iterator, event, COL_TRAVERSE_IGNORE); + if (error) { + TRACE_ERROR_NUMBER("Failed to bind iterator", error); + return error; + } + + start = format_str; + + while(1) { + + item = NULL; + length = 0; + + /* Parse format definition */ + result = elapi_parse_format(start, &length, &item, iterator); + if (result == NULL) { + TRACE_INFO_STRING("Done parsing string", ""); + break; + } + + /* Apply parsed data */ + if (length > 0) { + error = elapi_grow_data(out_data, + length + 1, + ELAPI_SUBST_BLOCK); + if (error) { + TRACE_ERROR_NUMBER("Error. Failed to allocate memory.", error); + col_unbind_iterator(iterator); + return error; + } + + memcpy(&out_data->buffer[out_data->length], + (const char *)(start), + length); + + out_data->length += length; + /* We asked for this one extra byte above */ + out_data->buffer[out_data->length] = '\0'; + } + + if (item != NULL) { + TRACE_INFO_NUMBER("Need to output item", error); + error = elapi_sprintf_item(out_data, item); + if (error) { + TRACE_ERROR_NUMBER("Error. Failed to allocate memory.", error); + col_unbind_iterator(iterator); + return error; + } + } + + start = result; + } + + col_unbind_iterator(iterator); + + TRACE_FLOW_STRING("elapi_sprintf", "Exit"); + return error; +} diff --git a/common/elapi/elapi_test/Makefile.am b/common/elapi/elapi_test/Makefile.am index dcf1707cc..c0eac8a5d 100644 --- a/common/elapi/elapi_test/Makefile.am +++ b/common/elapi/elapi_test/Makefile.am @@ -32,6 +32,7 @@ libelapi_test_la_SOURCES = \ ../elapi_basic.h \ ../elapi_resolve.c \ ../elapi_async.c \ + ../elapi_subst.c \ ../elapi_event.h \ ../elapi_priv.h \ ../elapi_sink.h \ diff --git a/common/elapi/elapi_test/elapi_ut.c b/common/elapi/elapi_test/elapi_ut.c index 1046ed0e6..ab0f88d14 100644 --- a/common/elapi/elapi_test/elapi_ut.c +++ b/common/elapi/elapi_test/elapi_ut.c @@ -76,7 +76,7 @@ int simple_event_test(void) printf("Simple test START:\n"); error = elapi_set_default_template( - E_BASE_DEFV1 | E_BASE_HOSTEXT, + E_BASE_DEFV1 | E_BASE_HOSTEXT /* FIXME Ticket #207 */, "%n( bin )", bin, 8, " %sb( logical1 )", "false", "%sb( logical2 )", "YES", @@ -391,6 +391,34 @@ int complex_event_test(void) return error; } + error = elapi_dsp_msg(E_TARGET_DEBUG, + dispatcher, + template, + E_MESSAGE, + "date = %(R_stamp__), pid = %(__pid__), " + "hostname = %(__host__), %(__halias__), " + "ip = %(__ip__), [%(__iplist__);%(!__iplist__);%(__iplist__)]" , + E_EOARG); + if (error) { + elapi_destroy_event_template(template); + printf("Failed to log event! %d\n", error); + return error; + } + + error = elapi_dsp_msg(E_TARGET_DEBUG, + dispatcher, + template, + E_MESSAGE, + "date = %(R_stamp__), pid = %(__pid__), " + "hostname = %(__host__), %(__halias__), " + "ip = %(__ip__), [%(__iplist__);%(__iplist__);%(__iplist__)]" , + E_EOARG); + if (error) { + elapi_destroy_event_template(template); + printf("Failed to log event! %d\n", error); + return error; + } + elapi_destroy_event_template(template); elapi_print_dispatcher(dispatcher); diff --git a/common/elapi/providers/file/file_fmt_csv.c b/common/elapi/providers/file/file_fmt_csv.c index e55d0b1c1..1a198711e 100644 --- a/common/elapi/providers/file/file_fmt_csv.c +++ b/common/elapi/providers/file/file_fmt_csv.c @@ -40,7 +40,7 @@ static unsigned file_csv_data_len(struct file_csv_cfg *cfg, { int serialized_len = 0; - TRACE_FLOW_STRING("col_get_data_len", "Entry point"); + TRACE_FLOW_STRING("file_csv_data_len", "Entry point"); switch (type) { case COL_TYPE_INTEGER: @@ -75,7 +75,7 @@ static unsigned file_csv_data_len(struct file_csv_cfg *cfg, if (cfg->csvqualifier) serialized_len += 2; - TRACE_FLOW_STRING("col_get_data_len","Exit point"); + TRACE_FLOW_STRING("file_csv_data_len", "Exit point"); return (uint32_t)serialized_len; } @@ -249,7 +249,7 @@ int file_serialize_csv(struct elapi_data_out *out_data, */ out_data->buffer[out_data->length] = '\0'; - TRACE_INFO_STRING("Data: ", out_data->buffer); + TRACE_INFO_STRING("Data: ", (char *)out_data->buffer); TRACE_FLOW_STRING("file_serialize_csv.", "Exit"); return error; |