summaryrefslogtreecommitdiffstats
path: root/common/elapi/elapi_event.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/elapi/elapi_event.c')
-rw-r--r--common/elapi/elapi_event.c1112
1 files changed, 1112 insertions, 0 deletions
diff --git a/common/elapi/elapi_event.c b/common/elapi/elapi_event.c
new file mode 100644
index 000000000..9322f8217
--- /dev/null
+++ b/common/elapi/elapi_event.c
@@ -0,0 +1,1112 @@
+/*
+ ELAPI
+
+ Implementation of the ELAPI event interface.
+
+ 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 <sys/types.h> /* for getpid() */
+#include <unistd.h> /* for getpid() */
+#include <stdlib.h> /* for realloc() */
+#include <syslog.h> /* for contants releted to severity */
+#include <unistd.h> /* for gethostname() */
+#include <errno.h> /* for errors */
+#include <string.h> /* for memset() and other */
+#include <netdb.h> /* for gethostbyname() */
+#include <sys/socket.h> /* for inet_ntop() */
+#include <arpa/inet.h> /* for inet_ntop() */
+#include <ctype.h> /* for isspace() */
+#include <stdarg.h> /* for va_arg() */
+#include <string.h> /* for strndup() */
+#include <ifaddrs.h> /* for getifaddrs() */
+
+#include "elapi_priv.h"
+#include "elapi_event.h"
+#include "trace.h"
+#include "config.h"
+
+#include "collection_tools.h"
+
+/* Internal return states from key processing */
+#define E_LIST_EMPTY 0
+#define E_LIST_ERROR 1
+#define E_LIST_LAST 2
+#define E_LIST_ADD 3
+#define E_LIST_REMOVE 4
+
+#define LOCALHOSTDOMAIN "localhost.localdomain"
+#define LOCALHOST "localhost"
+#define LOCALADDRESS "127.0.0.1"
+#define LOCALADDRESSV6 "::1"
+
+const char *undefined = "undefined";
+const char *str_yes = "yes";
+const char *str_no = "no";
+const char *str_true = "true";
+const char *str_false = "false";
+
+/* Default event template */
+struct collection_item *default_template = NULL;
+
+
+/* Function to add host identity information to the template */
+int add_host_identity(struct collection_item *tpl, unsigned base)
+{
+ char hostname[NI_MAXHOST + 1];
+ int error = EOK;
+ int gai_ret_host = 0;
+ int gai_ret_addr = 0;
+ char host[NI_MAXHOST];
+ char address[NI_MAXHOST];
+ char *hnm, *haddr;
+ struct ifaddrs *ifaddr, *ifa;
+ int family;
+ int set_hostname = 0;
+ int set_ip = 0;
+
+ TRACE_FLOW_STRING("add_host_identity", "Entry");
+
+ memset(hostname, 0, sizeof(hostname));
+
+ /* The goal here to collect information about the host.
+ * there is no need to actually use it for establishing
+ * any connections.
+ * It is a best effort attempt.
+ */
+
+ /* If we are not asked for hostname then say we already have it */
+ if (!(base & E_HAVE_HOSTNAME)) set_hostname = 1;
+ /* If we are not asked for ip then say we already have it */
+ if (!(base & E_HAVE_HOSTIP)) set_ip = 1;
+
+ if (getifaddrs(&ifaddr) == 0) {
+
+ TRACE_FLOW_STRING("getifaddrs", "Ok");
+
+ /* Walk through linked list, maintaining head pointer so we
+ can free list later */
+
+ ifa = ifaddr;
+ while (ifa != NULL) {
+
+ TRACE_FLOW_STRING("Top of the loop", "");
+
+ if (!ifa->ifa_addr) {
+ ifa = ifa->ifa_next;
+ continue;
+ }
+
+ family = ifa->ifa_addr->sa_family;
+
+ TRACE_FLOW_NUMBER("Family", family);
+
+ /* For an AF_INET* interface address, display the address */
+ if (family == AF_INET || family == AF_INET6) {
+
+ TRACE_FLOW_NUMBER("Got right family", family);
+ /* Make sure the address is cleared - will help in comparisons */
+ memset(host, 0, sizeof(host));
+ memset(address, 0, sizeof(address));
+
+ gai_ret_host = getnameinfo(ifa->ifa_addr,
+ (family == AF_INET) ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6),
+ host,
+ NI_MAXHOST,
+ NULL,
+ 0,
+ 0 /* Gets host name */);
+
+ gai_ret_addr = getnameinfo(ifa->ifa_addr,
+ (family == AF_INET) ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6),
+ address,
+ NI_MAXHOST,
+ NULL,
+ 0,
+ NI_NUMERICHOST /* Gets address as string */);
+
+ /* If we have not set host name set it */
+ if(!set_hostname) {
+
+ TRACE_FLOW_STRING("Host name is not set", "");
+
+ hnm = NULL;
+ /* Use host name returned by gethostname() as main host name */
+ if (!gethostname(hostname, NI_MAXHOST)) hnm = hostname;
+ else {
+ /* We we able to get a host name ? */
+ if (gai_ret_host == 0) {
+ TRACE_INFO_STRING("getnameinfo returned:", host);
+ hnm = host;
+ }
+ }
+
+ /* Do we have a host meaningful host name? */
+ if ((hnm) &&
+ ((strncasecmp(hnm, LOCALHOST, sizeof(LOCALHOST)) == 0 ) ||
+ (strncasecmp(hnm, LOCALHOSTDOMAIN, sizeof(LOCALHOSTDOMAIN)) == 0 ) ||
+ (strncasecmp(hnm, address, sizeof(address) == 0)))) hnm = NULL;
+
+ /* If host name is not NULL it would work for us */
+ if (hnm) {
+ TRACE_INFO_STRING("Adding host name:", hnm);
+ error = col_add_str_property(tpl, NULL, E_HOSTNAME, hnm, 0);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
+ freeifaddrs(ifaddr);
+ return error;
+ }
+ /* Done with the name */
+ set_hostname = 1;
+ }
+ }
+
+ /* If we have not set processed ip address do it */
+ if(!set_ip) {
+
+ TRACE_FLOW_STRING("Address is not set", "");
+
+ haddr = NULL;
+ if (gai_ret_addr == 0) {
+ TRACE_INFO_STRING("getnameinfo returned:", address);
+ if ((strncasecmp(address, LOCALADDRESS, sizeof(LOCALADDRESS)) != 0 ) &&
+ (strncasecmp(address, LOCALADDRESSV6, sizeof(LOCALADDRESSV6)) != 0 )) {
+ TRACE_INFO_STRING("Not an unhelpful address", "");
+ haddr = address;
+ }
+ }
+
+ if (haddr) {
+ TRACE_INFO_STRING("Adding host address:", haddr);
+ error = col_add_str_property(tpl, NULL, E_HOSTIP, haddr, 0);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
+ freeifaddrs(ifaddr);
+ return error;
+ }
+ set_ip = 1;
+ }
+ }
+
+ /* If we have a name and we are told to deal with alias names */
+ if ((set_hostname) && (base & E_HAVE_HOSTALIAS)) {
+
+ TRACE_INFO_NUMBER("gai_ret_host:", gai_ret_host);
+ TRACE_INFO_STRING("host:", host);
+ TRACE_INFO_STRING("address:", address);
+ TRACE_INFO_STRING("they are:", strncasecmp(host, address, sizeof(address)) == 0 ? "same" : "different");
+
+ /* Do we have a host meaningful host name? */
+ if ((gai_ret_host != 0) ||
+ ((gai_ret_host == 0) &&
+ ((strncasecmp(host, LOCALHOST, sizeof(LOCALHOST)) == 0 ) ||
+ (strncasecmp(host, LOCALHOSTDOMAIN, sizeof(LOCALHOSTDOMAIN)) == 0 ) ||
+ (strncasecmp(host, address, sizeof(address)) == 0)))) hnm = NULL;
+ else hnm = host;
+
+ if (hnm) {
+ TRACE_INFO_STRING("Adding alias host name:", hnm);
+ error = col_add_str_property(tpl, NULL, E_HOSTALIAS, hnm, 0);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
+ freeifaddrs(ifaddr);
+ return error;
+ }
+ }
+ }
+
+ /* If we got then main IP and we are told to deal with opther IPs */
+ if ((set_ip) && (base & E_HAVE_HOSTIPS)) {
+
+ /* Do we have a host meaningful host name? */
+ if ((gai_ret_addr != 0) ||
+ ((gai_ret_addr == 0) &&
+ ((strncasecmp(address, LOCALADDRESS, sizeof(LOCALADDRESS)) == 0 ) ||
+ (strncasecmp(address, LOCALADDRESSV6, sizeof(LOCALADDRESSV6)) == 0 )))) haddr = address;
+ else haddr = address;
+
+ if (haddr) {
+ TRACE_INFO_STRING("Adding alias host IP:", haddr);
+ error = col_add_str_property(tpl, NULL, E_HOSTIPS, haddr, 0);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
+ freeifaddrs(ifaddr);
+ return error;
+ }
+ }
+ }
+ }
+ TRACE_INFO_NUMBER("Moving to next", ifa->ifa_next);
+ ifa = ifa->ifa_next;
+ TRACE_INFO_NUMBER("Moved to", ifa);
+ }
+
+ freeifaddrs(ifaddr);
+ }
+
+ /* Make sure that we really have the name after all */
+ if (!set_hostname) {
+ TRACE_INFO_STRING("No host name using default:", undefined);
+ error = col_add_str_property(tpl, NULL, E_HOSTNAME, undefined, 0);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
+ return error;
+ }
+ }
+
+ /* Make sure that we really have the IP after all */
+ if (!set_ip) {
+ TRACE_INFO_STRING("No host name using default:", undefined);
+ error = col_add_str_property(tpl, NULL, E_HOSTIP, undefined, 0);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
+ return error;
+ }
+ }
+
+ TRACE_FLOW_STRING("add_host_identity", "Exit");
+ return error;
+}
+
+/* Add base elements to template collection */
+static int add_base_elements(struct collection_item *tpl, unsigned base)
+{
+ int error = EOK;
+ int pass_base;
+
+ TRACE_FLOW_STRING("add_base_elements", "Entry");
+
+ /* Populate the template using base */
+ if (base & E_HAVE_TIMESTAMP) {
+ /* Value is the format string for strftime() */
+ error = col_add_str_property(tpl, NULL, E_TIMESTAMP, E_TIMESTAMP_FORMAT, sizeof(E_TIMESTAMP_FORMAT));
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add timestamp. Error", error);
+ return error;
+ }
+ }
+
+ if (base & E_HAVE_UTCTIME) {
+ /* Value does not matter */
+ error = col_add_int_property(tpl, NULL, E_UTCTIME, 0);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add UTC time. Error", error);
+ return error;
+ }
+ }
+
+ if (base & E_HAVE_PID) {
+ /* Value is the current pid */
+ error = col_add_long_property(tpl, NULL, E_PID, (long)getpid());
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add pid. Error", error);
+ return error;
+ }
+ }
+
+ if (base & E_HAVE_APPNAME) {
+ /* Value does not matter */
+ error = col_add_str_property(tpl, NULL, E_APPNAME, "", 1);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add application name. Error", error);
+ return error;
+ }
+ }
+
+ if (base & E_HAVE_SEVERITY) {
+ /* Value is the default severity */
+ error = col_add_int_property(tpl, NULL, E_SEVERITY, LOG_USER | LOG_INFO);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add pid. Error", error);
+ return error;
+ }
+ }
+
+ /* If we need to add aliases or other IPs call the function */
+ if ((base & E_HAVE_HOSTNAME) ||
+ (base & E_HAVE_HOSTIP) ||
+ (base & E_HAVE_HOSTALIAS) ||
+ (base & E_HAVE_HOSTIPS)) {
+
+ pass_base = base;
+
+ /* make sure we have extensions on top of the basic data */
+ if ((base & E_HAVE_HOSTALIAS) && (!(base & E_HAVE_HOSTNAME))) pass_base |= E_HAVE_HOSTNAME;
+ if ((base & E_HAVE_HOSTIPS) && (!(base & E_HAVE_HOSTIP))) pass_base |= E_HAVE_HOSTIP;
+
+ error = add_host_identity(tpl, pass_base);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add host identity. Error", error);
+ return error;
+ }
+ }
+
+ TRACE_FLOW_STRING("add_base_elements", "Exit");
+ return error;
+}
+
+
+/* Internal untility function to tokenize a string */
+static int interprete_key(char *key,
+ int *type,
+ char **property,
+ int *prop_len,
+ int *has_len,
+ int *bool_type)
+{
+ int adjust_by = 0;
+ char *start = NULL;
+ char *cursor = NULL;
+ char *end = NULL;
+ int ret = E_LIST_EMPTY;
+
+ TRACE_FLOW_STRING("interprete_key", "Entry");
+
+ TRACE_INFO_STRING("Key", key);
+
+ /* Initialize passed in data */
+ *has_len = 0;
+ *property = NULL;
+ *type = COL_TYPE_STRING;
+
+ cursor = key;
+
+ while (isspace(*cursor)) cursor++;
+
+ /* End of string - we are done */
+ if (*cursor == '\0') {
+ TRACE_ERROR_STRING("Empty key - end of processing!", "");
+ return E_LIST_EMPTY;
+ }
+
+ /* This is the beginning of the formatted token */
+ if (*cursor == '-') {
+
+ /* This is a remove attribute case */
+
+ cursor++;
+ /* Skip spaces if any */
+ while (isspace(*cursor)) cursor++;
+
+ /* Mark the start of the actual property */
+ start = cursor;
+
+ /* Now we need to extract the name of the property */
+ /* We will not be nice here - the add_property will validate if the name is ok */
+ while ((*cursor != '\0') && (!isspace(*cursor))) cursor++;
+
+ /* End of string - we are done */
+ if (cursor == start) {
+ TRACE_ERROR_STRING("Invalid key - end of processing!", "");
+ return E_LIST_EMPTY;
+ }
+
+ *prop_len = cursor - start;
+ *property = start;
+ TRACE_INFO_STRING("We are told to remove the property!", *property);
+ ret = E_LIST_REMOVE;
+ }
+ else if (*cursor == '%') {
+
+ /* We got a full key with format string */
+
+ cursor++;
+ if ((*cursor == '*') && (*(cursor+1) == 's') && (*(cursor+2) == '(')) {
+ *type = COL_TYPE_STRING;
+ *has_len = 1;
+ adjust_by = 3;
+ }
+ else if ((*cursor == 's') && (*(cursor+1) == '(')) {
+ *type = COL_TYPE_STRING;
+ adjust_by = 2;
+ }
+ else if (((*cursor == 'i')||(*cursor == 'd')) && (*(cursor+1) == '(')) {
+ *type = COL_TYPE_INTEGER;
+ adjust_by = 2;
+ }
+ else if ((*cursor == 'u') && (*(cursor+1) == '(')) {
+ *type = COL_TYPE_INTEGER;
+ adjust_by = 2;
+ }
+ else if ((*cursor == 'l') && ((*(cursor+1) == 'i')||(*(cursor+1) == 'd')) && (*(cursor+2) == '(')) {
+ *type = COL_TYPE_LONG;
+ adjust_by = 3;
+ }
+ else if ((*cursor == 'l') && (*(cursor+1) == 'u') && (*(cursor+2) == '(')) {
+ *type = COL_TYPE_LONG;
+ adjust_by = 3;
+ }
+ else if (((*cursor == 'f')||(*cursor == 'e')) && (*(cursor+1) == '(')) {
+ *type = COL_TYPE_DOUBLE;
+ adjust_by = 2;
+ }
+ else if (((*cursor == 's') || (*cursor == 'd')) && (*(cursor+1) == 'b') && (*(cursor+2) == '(')) {
+ *type = COL_TYPE_BOOL;
+ adjust_by = 3;
+ if (*cursor == 's') *bool_type = 1;
+ else *bool_type = 0;
+ }
+ else if ((*cursor == 'n') && (*(cursor+1) == '(')) {
+ *type = COL_TYPE_BINARY;
+ adjust_by = 2;
+ }
+ else {
+ TRACE_ERROR_STRING("Invalid key - end of processing!", key);
+ return E_LIST_ERROR;
+ }
+
+ cursor += adjust_by;
+
+ /* Skip spaces if any */
+ while (isspace(*cursor)) cursor++;
+
+ start = cursor;
+
+ /* Now we need to extract the name of the property */
+ /* We will not be nice here - the add_property will validate if the name is ok */
+ while ((*cursor != '\0') && (*cursor != ')') && (!isspace(*cursor))) cursor++;
+
+ /* End of string - we are done */
+ if ((*cursor == '\0') || (cursor == start)) {
+ TRACE_ERROR_STRING("Invalid key - end of processing!", "");
+ return E_LIST_EMPTY;
+ }
+
+ end = cursor;
+
+ /* Skip spaces if any */
+ while (isspace(*cursor)) cursor++;
+
+ /* Check that end of the string is in proper format */
+ if ((*cursor != ')') && (*(cursor + 1) != '\0')) {
+ TRACE_ERROR_STRING("Invalid key - missing ')' .", key);
+ return E_LIST_ERROR;
+ }
+
+ *property = start;
+ *prop_len = end - start;
+
+ TRACE_INFO_STRING("Property:", *property);
+ TRACE_INFO_NUMBER("Property len:", *prop_len);
+ ret = E_LIST_ADD;
+ }
+ else {
+ /* Just got a key */
+ /* Mark the start of the actual property */
+ start = cursor;
+
+ /* Now we need to extract the name of the property */
+ /* We will not be nice here - the add_property will validate if the name is ok */
+ while ((*cursor != '\0') && (!isspace(*cursor))) cursor++;
+
+ /* End of string - we are done */
+ if (cursor == start) {
+ TRACE_ERROR_STRING("Invalid key - end of processing!", "");
+ return E_LIST_EMPTY;
+ }
+
+ *prop_len = cursor - start;
+ *property = start;
+ TRACE_INFO_STRING("We are told to add/update the property (or last)!", *property);
+
+ if(strncmp(*property, E_EOARG, *prop_len) == 0) ret = E_LIST_LAST;
+ else ret = E_LIST_ADD;
+ }
+
+ TRACE_INFO_STRING("Returning Property:",*property);
+ TRACE_INFO_NUMBER("Returning Property len:", *prop_len);
+ TRACE_INFO_NUMBER("Returning Type:", *type);
+ TRACE_INFO_NUMBER("Returning Has length:", *has_len);
+
+
+ TRACE_FLOW_STRING("interprete_key", "Exit");
+
+ return ret;
+}
+
+/* Make sure that the right string is given for bool value */
+static int convert_bool(char *data_str, unsigned char *data_bool)
+{
+ TRACE_FLOW_STRING("convert_bool", "Called");
+ TRACE_INFO_STRING("Data", data_str);
+
+ if ((strncasecmp(data_str, str_true, sizeof(str_true)) == 0) ||
+ (strncasecmp(data_str, str_yes, sizeof(str_yes)) == 0)) {
+ TRACE_INFO_STRING("Matched TRUE", "");
+ *data_bool = '\1';
+ return 1;
+ }
+ if ((strncasecmp(data_str, str_false, sizeof(str_false)) == 0) ||
+ (strncasecmp(data_str, str_no, sizeof(str_no)) == 0)) {
+ TRACE_INFO_STRING("Matched FALSE", "");
+ *data_bool = '\0';
+ return 1;
+ }
+ TRACE_INFO_STRING("Matched NOTHING", "");
+ return 0;
+}
+
+
+/* Process argument list */
+/* Update collection based on the passed in arguments */
+static int process_arg_list(struct collection_item *col,
+ va_list args)
+{
+ int error = EOK;
+ char *arg = NULL;
+ char *propcopy = NULL;
+ int ret = 0;
+ int type = 0;
+ char *property = NULL;
+ int prop_len = 0;
+ int has_len = 0;
+ int bool_type = 0;
+ char *data_str = NULL;
+ int data_int = 0;
+ unsigned int data_uint = 0;
+ long data_long = 0;
+ unsigned long data_ulong = 0;
+ void *data_bin = NULL;
+ double data_dbl = 0.;
+ int length = 0;
+ void *data = NULL;
+ unsigned char data_bool = '\0';
+
+ TRACE_FLOW_STRING("process_arg_list", "Entry.");
+
+ /* We will break from the loop when we find the last item */
+ while (1) {
+
+ /* Get next key */
+ arg = va_arg(args, char *);
+
+ if (arg == NULL) {
+ TRACE_ERROR_STRING("Invalid NULL argument.", "Key can't be NULL");
+ return EINVAL;
+ }
+
+ /* Interprete the key.
+ * It can be just " key ",
+ * it can be " - key ",
+ * or it can be a formatted string
+ * something like " %*s( key )".
+ * Function will deal with all cases.
+ * Passed in variables initialized and updated inside
+ */
+ ret = interprete_key(arg,
+ &type,
+ &property,
+ &prop_len,
+ &has_len,
+ &bool_type);
+
+ if (ret == E_LIST_LAST) {
+ TRACE_INFO_STRING("Process found last key", arg);
+ break;
+ }
+
+ if ((ret == E_LIST_ADD) || (ret == E_LIST_REMOVE)) {
+ /* We need to create a dup of the string */
+ propcopy = strndup(property, prop_len);
+ if (propcopy == NULL) {
+ TRACE_ERROR_STRING("Failed to allocate property", arg);
+ return ENOMEM;
+ }
+
+ TRACE_INFO_STRING("Processing property", propcopy);
+
+ /* Are we supposed to add? */
+ if (ret == E_LIST_ADD) {
+
+
+ /* NOTE: We are not going to check if the key value pairs
+ * are consistent.
+ * It can be made a bit more bullet proof by adding
+ * significant complexity to the code but I do not
+ * think it makes much sense to do so.
+ * There is no way to prevent the argument mismatch
+ * issues 100%. Printf can crash if aguments are
+ * missed or bad, so do we...
+ */
+
+ /* Get data */
+ switch(type) {
+
+ case COL_TYPE_STRING: data_str = va_arg(args, char *);
+ data = (void *)data_str;
+ if (has_len) length = va_arg(args, int);
+ else length = strlen(data_str) + 1;
+ TRACE_INFO_STRING("Adding string:", data_str);
+ TRACE_INFO_NUMBER("Length:",length);
+ break;
+
+ case COL_TYPE_BINARY: data_bin = va_arg(args, void *);
+ data = (void *)data_bin;
+ length = va_arg(args, int);
+ break;
+
+ case COL_TYPE_INTEGER: data_int = va_arg(args, int);
+ data = (void *)(&data_int);
+ length = sizeof(int);
+ break;
+
+ case COL_TYPE_UNSIGNED: data_uint = va_arg(args, unsigned int);
+ data = (void *)(&data_uint);
+ length = sizeof(unsigned int);
+ break;
+
+ case COL_TYPE_LONG: data_long = va_arg(args, long);
+ data = (void *)(&data_long);
+ length = sizeof(long);
+ break;
+
+ case COL_TYPE_ULONG: data_ulong = va_arg(args, unsigned long);
+ data = (void *)(&data_ulong);
+ length = sizeof(unsigned long);
+ break;
+
+ case COL_TYPE_DOUBLE: data_dbl = va_arg(args, double);
+ data = (void *)(&data_dbl);
+ length = sizeof(double);
+ break;
+
+ case COL_TYPE_BOOL: if (bool_type) {
+ /* It is a string */
+ data_str = va_arg(args,char *);
+ /* Check if it is a valid str */
+ if (!(convert_bool(data_str, &data_bool))) {
+ TRACE_ERROR_STRING("Failed to to convert bool value", data_str);
+ free(propcopy);
+ return EINVAL;
+ }
+ }
+ else {
+ /* It is an int */
+ data_int = va_arg(args, int);
+ if (data_int) data_bool = 1;
+ else data_bool = 0;
+ }
+
+ data = (void *)(&data_bool);
+ length = sizeof(unsigned char);
+ break;
+
+ default:
+ TRACE_ERROR_STRING("Invalid or unknown type", propcopy);
+ free(propcopy);
+ return EINVAL;
+ }
+
+ /* Insert or update */
+ error = col_insert_property_with_ref(col,
+ NULL,
+ COL_DSP_END,
+ NULL,
+ 0,
+ COL_INSERT_DUPOVER,
+ propcopy,
+ type,
+ data,
+ length,
+ NULL);
+ if (error) {
+ TRACE_ERROR_STRING("Error inserting property", property);
+ free(propcopy);
+ return error;
+ }
+ }
+ else {
+ /* Remove case */
+ while (error != ENOENT) {
+ error = col_delete_property(col,
+ propcopy,
+ COL_TYPE_ANY,
+ COL_TRAVERSE_DEFAULT);
+
+ if ((error) && (error != ENOENT)) {
+ TRACE_ERROR_STRING("Error deleting property", propcopy);
+ free(propcopy);
+ return error;
+ }
+ }
+ error = EOK;
+ }
+ free(propcopy);
+ }
+ else {
+ /* Errors related to key interpretation are handled here */
+ TRACE_ERROR_STRING("Invalid arg", arg);
+ return EINVAL;
+ }
+ } /* end of arg processing loop */
+
+ TRACE_FLOW_STRING("process_arg_list", "Exit");
+ return error;
+}
+
+static int get_default_template(struct collection_item **template)
+{
+ int error = EOK;
+
+ TRACE_FLOW_STRING("get_default_template", "Entry");
+
+ if (!default_template) {
+ TRACE_INFO_STRING("Default template does not exit", "");
+ error = elapi_set_default_template(E_BASE_DEFV1);
+ if (error) {
+ TRACE_ERROR_NUMBER("Set default template returned error", error);
+ return error;
+ }
+ }
+
+ *template = default_template;
+ TRACE_FLOW_NUMBER("get_default_template. Exit returning", error);
+ return error;
+}
+
+/* Cleanup callback installed when global template is used */
+void clean_template(void)
+{
+ TRACE_FLOW_STRING("clean_template", "Entry");
+ elapi_destroy_event_template(default_template);
+ TRACE_FLOW_STRING("clean_template", "Exit");
+}
+
+/*****************************************************************************/
+
+/* Create event template */
+int elapi_create_event_template(struct collection_item **template,
+ unsigned base, ...)
+{
+ int error = EOK;
+ struct collection_item *tpl = NULL;
+ va_list args;
+
+ TRACE_FLOW_STRING("elapi_create_event_template", "Entry");
+
+ if (template == NULL ) {
+ TRACE_ERROR_STRING("Template storage must be provided", "");
+ return EINVAL;
+ }
+
+ *template = NULL;
+
+ /* Create collection */
+ error = col_create_collection(&tpl, E_TEMPLATE_NAME, COL_CLASS_ELAPI_TEMPLATE);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to create collection. Error", error);
+ return error;
+ }
+
+ /* Add elements using base mask */
+ error = add_base_elements(tpl, base);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add base elements. Error", error);
+ col_destroy_collection(tpl);
+ return error;
+ }
+
+ /* Process varible arguments */
+ va_start(args, base);
+
+ /* Process variable argument list */
+ error = process_arg_list(tpl, args);
+
+ va_end(args);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to process argument list. Error", error);
+ col_destroy_collection(tpl);
+ return error;
+ }
+
+ *template = tpl;
+
+ TRACE_FLOW_STRING("elapi_create_event_template", "Exit");
+ return error;
+}
+
+/* Function to destroy event template */
+void elapi_destroy_event_template(struct collection_item *template)
+{
+ TRACE_FLOW_STRING("elapi_destroy_event_template", "Entry");
+
+ col_destroy_collection(template);
+
+ TRACE_FLOW_STRING("elapi_destroy_event_template", "Exit");
+}
+
+/* Create event */
+int elapi_create_event(struct collection_item **event,
+ struct collection_item *template,
+ struct collection_item *collection,
+ int mode, ...)
+{
+ int error = EOK;
+ struct collection_item *evt = NULL;
+ va_list args;
+
+ TRACE_FLOW_STRING("elapi_create_event", "Entry");
+
+ /* Check storage */
+ if (event == NULL ) {
+ TRACE_ERROR_STRING("Event storage must be provided", "");
+ return EINVAL;
+ }
+
+ /* Check for template */
+ if (template == NULL ) {
+ TRACE_ERROR_STRING("Template argument is missing", "");
+ return EINVAL;
+ }
+
+ *event = NULL;
+
+ /* Create collection */
+ error = col_create_collection(&evt, E_EVENT_NAME, COL_CLASS_ELAPI_EVENT);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to create collection. Error", error);
+ return error;
+ }
+
+ /* Add elements from the template */
+ error = col_add_collection_to_collection(evt, NULL, NULL,
+ (struct collection_item *)template,
+ COL_ADD_MODE_FLAT);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add elements from the template. Error", error);
+ col_destroy_collection(evt);
+ return error;
+ }
+
+ /* Add elements from the template */
+ if (collection != NULL) {
+ error = col_add_collection_to_collection(evt, NULL, NULL, collection, mode);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add elements from external collection. Error", error);
+ col_destroy_collection(evt);
+ return error;
+ }
+ }
+
+ /* Process varible arguments */
+ va_start(args, mode);
+
+ /* Process variable argument list */
+ error = process_arg_list(evt, args);
+
+ va_end(args);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to process argument list. Error", error);
+ col_destroy_collection(evt);
+ return error;
+ }
+
+ *event = evt;
+
+ TRACE_FLOW_STRING("elapi_create_event", "Exit");
+ return error;
+}
+
+/* Add/Updates/Removes the event attributes based on the and provided key value pairs */
+int elapi_modify_event(struct collection_item *event,
+ struct collection_item *collection,
+ int mode, ...)
+{
+ int error = EOK;
+ va_list args;
+
+ TRACE_FLOW_STRING("elapi_modify_event", "Entry");
+
+ /* Check event */
+ if (event == NULL ) {
+ TRACE_ERROR_STRING("Event must be provided", "");
+ return EINVAL;
+ }
+
+ /* Add elements from the template */
+ if (collection != NULL) {
+ error = col_add_collection_to_collection(event, NULL, NULL, collection, mode);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add elements from external collection. Error", error);
+ col_destroy_collection(event);
+ return error;
+ }
+ }
+
+ /* Process varible arguments */
+ va_start(args, mode);
+
+ /* Process variable argument list */
+ error = process_arg_list(event, args);
+
+ va_end(args);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to process argument list. Error", error);
+ return error;
+ }
+
+ TRACE_FLOW_STRING("elapi_modify_event", "Exit");
+ return error;
+}
+
+/* Create a copy of the event */
+int elapi_copy_event(struct collection_item **new_event,
+ struct collection_item *source_event)
+{
+ int error = EOK;
+
+ TRACE_FLOW_STRING("elapi_copy_event", "Entry");
+
+ error = col_copy_collection(new_event,
+ source_event,
+ NULL);
+
+ TRACE_FLOW_NUMBER("elapi_copy_event. Exit Returning", error);
+ return error;
+}
+
+/* Function to destroy event. */
+void elapi_destroy_event(struct collection_item *event)
+{
+ TRACE_FLOW_STRING("elapi_destroy_event", "Entry");
+
+ col_destroy_collection(event);
+
+ TRACE_FLOW_STRING("elapi_destroy_event", "Exit");
+}
+
+/* Initializes default internal template */
+int elapi_set_default_template(unsigned base, ...)
+{
+ int error = EOK;
+ struct collection_item *tpl;
+ va_list args;
+
+ TRACE_FLOW_STRING("elapi_set_default_template", "Entry");
+
+ /* Clean previous instance of the default template */
+ elapi_destroy_event_template(default_template);
+ default_template = NULL;
+
+ /* Create collection */
+ error = col_create_collection(&tpl, E_TEMPLATE_NAME, COL_CLASS_ELAPI_TEMPLATE);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to create collection. Error", error);
+ return error;
+ }
+
+ /* Add elements using base mask */
+ error = add_base_elements(tpl, base);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add base elements. Error", error);
+ col_destroy_collection(tpl);
+ return error;
+ }
+
+ /* Process varible arguments */
+ va_start(args, base);
+
+ /* Process variable argument list */
+ error = process_arg_list(tpl, args);
+
+ va_end(args);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to process argument list. Error", error);
+ col_destroy_collection(tpl);
+ return error;
+ }
+
+ /* Install a cleanup callback */
+ if (atexit(clean_template)) {
+ TRACE_ERROR_NUMBER("Failed to install cleanup callback. Error", ENOSYS);
+ col_destroy_collection(tpl);
+ /* NOTE: Could not find a better error for this case */
+ return ENOSYS;
+ }
+
+ default_template = tpl;
+
+ TRACE_FLOW_STRING("elapi_set_default_template", "Exit");
+ return error;
+}
+
+
+/* This function will use internal default template */
+int elapi_create_simple_event(struct collection_item **event, ...)
+{
+ int error = EOK;
+ struct collection_item *evt = NULL;
+ va_list args;
+ struct collection_item *template;
+
+ TRACE_FLOW_STRING("elapi_create_simple_event", "Entry");
+
+ /* Check storage */
+ if (event == NULL ) {
+ TRACE_ERROR_STRING("Event storage must be provided", "");
+ return EINVAL;
+ }
+
+ *event = NULL;
+
+ error = get_default_template(&template);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to get default template. Error", error);
+ return error;
+ }
+
+ /* Create collection */
+ error = col_create_collection(&evt, E_EVENT_NAME, COL_CLASS_ELAPI_EVENT);
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to create collection. Error", error);
+ return error;
+ }
+
+ /* Add elements from the template */
+ error = col_add_collection_to_collection(evt, NULL, NULL,
+ template,
+ COL_ADD_MODE_FLAT);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to add elements from the template. Error", error);
+ col_destroy_collection(evt);
+ return error;
+ }
+
+ /* Process varible arguments */
+ va_start(args, event);
+
+ /* Process variable argument list */
+ error = process_arg_list(evt, args);
+
+ va_end(args);
+
+ if (error) {
+ TRACE_ERROR_NUMBER("Failed to process argument list. Error", error);
+ col_destroy_collection(evt);
+ return error;
+ }
+
+ *event = evt;
+
+ TRACE_FLOW_STRING("elapi_create_simple_event", "Exit");
+ return error;
+}