summaryrefslogtreecommitdiffstats
path: root/common/elapi/elapi_subst.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/elapi/elapi_subst.c')
-rw-r--r--common/elapi/elapi_subst.c395
1 files changed, 395 insertions, 0 deletions
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;
+}