summaryrefslogtreecommitdiffstats
path: root/common/elapi/elapi_subst.c
diff options
context:
space:
mode:
authorDmitri Pal <dpal@redhat.com>2009-09-27 22:16:17 -0400
committerStephen Gallagher <sgallagh@redhat.com>2009-10-05 10:32:08 -0400
commita78d04984240a3d04045402c964d9d8c5be463ef (patch)
tree0353869fca520c545daea46deb43069d2a172e9a /common/elapi/elapi_subst.c
parent8140cea7b4e3d3c9c6003eb6ae30e5e0fdd7c1ae (diff)
downloadsssd-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/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;
+}