summaryrefslogtreecommitdiffstats
path: root/common/elapi/providers/file/file_fmt_csv.c
diff options
context:
space:
mode:
authorDmitri Pal <dpal@redhat.com>2009-09-02 19:41:06 -0400
committerStephen Gallagher <sgallagh@redhat.com>2009-09-08 19:26:27 -0400
commit13cf6a9c9d37a14ff46f6d512aab402616359570 (patch)
tree81b8dae12607b7df36c422191575c1a99009c060 /common/elapi/providers/file/file_fmt_csv.c
parentc5461b548d303e6e66e20048544814338b46efb5 (diff)
downloadsssd-13cf6a9c9d37a14ff46f6d512aab402616359570.tar.gz
sssd-13cf6a9c9d37a14ff46f6d512aab402616359570.tar.xz
sssd-13cf6a9c9d37a14ff46f6d512aab402616359570.zip
ELAPI Adding file provider and CSV format
This patch creates the infrastructure for logging of the event from the top of the interface to the bottom. It is a start. A lot of functionality is left aside. The attempt of this patch is pass event from caller of the ELAPI interface via targets to sinks then to providers and do serialization creating entity that is ready to be written to a file. It also implements more specific provider related configuration parameters. Also it addresses couple suggestions that were brought up against previous patch. ELAPI Correcting issues This patch addresses the issues found during the review of the previous patches and addresses ticket #166.
Diffstat (limited to 'common/elapi/providers/file/file_fmt_csv.c')
-rw-r--r--common/elapi/providers/file/file_fmt_csv.c536
1 files changed, 536 insertions, 0 deletions
diff --git a/common/elapi/providers/file/file_fmt_csv.c b/common/elapi/providers/file/file_fmt_csv.c
new file mode 100644
index 000000000..a81111330
--- /dev/null
+++ b/common/elapi/providers/file/file_fmt_csv.c
@@ -0,0 +1,536 @@
+/*
+ ELAPI
+
+ Module contains functions related to outputting events in CSV format.
+
+ 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 <errno.h> /* for errors */
+#include <stdlib.h> /* for free() */
+#include <string.h> /* for strcmp() */
+
+#include "collection.h"
+#include "file_fmt_csv.h"
+#include "collection_tools.h"
+#include "ini_config.h"
+#include "trace.h"
+#include "config.h"
+
+/* Reasonable size for one event */
+/* FIXME: may be it would make sense to make it configurable ? */
+#define FILE_CSV_BLOCK 256
+
+/* Calculate the potential size of the item */
+static unsigned file_csv_data_len(struct file_csv_cfg *cfg,
+ int type,
+ int raw_len)
+{
+ int serialized_len = 0;
+
+ TRACE_FLOW_STRING("col_get_data_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:
+ if ((cfg->csvqualifier) &&
+ (cfg->csvescchar)) serialized_len = raw_len * 2;
+ else 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;
+ }
+
+ if (cfg->csvqualifier) serialized_len += 2;
+
+ TRACE_FLOW_STRING("col_get_data_len","Exit point");
+ return (uint32_t)serialized_len;
+}
+
+/* Copy data escaping characters */
+int file_copy_esc(char *dest,
+ const char *source,
+ unsigned char what_to_esc,
+ unsigned char what_to_use)
+{
+ int i = 0;
+ int j = 0;
+
+ while (source[i]) {
+ if ((source[i] == what_to_use) ||
+ (source[i] == what_to_esc)) {
+
+ dest[j] = what_to_use;
+ j++;
+
+ }
+ dest[j] = source[i];
+ i++;
+ j++;
+ }
+
+ return j;
+}
+
+/* Serialize item into the csv format */
+int file_serialize_csv(struct elapi_data_out *out_data,
+ int type,
+ int length,
+ void *data,
+ void *mode_cfg)
+{
+ int error = EOK;
+ struct file_csv_cfg *cfg;
+ uint32_t projected_len;
+ uint32_t used_len;
+ int first = 1;
+ int i;
+
+ TRACE_FLOW_STRING("file_serialize_csv", "Entry");
+
+ cfg = (struct file_csv_cfg *)mode_cfg;
+
+ /* Get projected length of the item */
+ projected_len = file_csv_data_len(cfg, type, length);
+
+ TRACE_INFO_NUMBER("Expected data length: ", projected_len);
+
+ /* Make sure we have enough space */
+ if (out_data->buffer != NULL) {
+ TRACE_INFO_STRING("Not a first time use.", "Adding length overhead");
+ if (cfg->csvseparator) projected_len++;
+ projected_len += cfg->csvnumsp;
+ first = 0;
+ }
+ else {
+ /* Add null terminating zero */
+ projected_len++;
+ }
+
+ /* Grow buffer if needed */
+ error = elapi_grow_data(out_data,
+ projected_len,
+ FILE_CSV_BLOCK);
+ if (error) {
+ TRACE_ERROR_NUMBER("Error. Failed to allocate memory.", error);
+ return error;
+ }
+
+ /* Now everything should fit */
+ if (!first) {
+ /* Add separator if any */
+ if (cfg->csvseparator) {
+ out_data->buffer[out_data->length] = cfg->csvseparator;
+ out_data->length++;
+ }
+
+ /* Add spaces if any */
+ memset(&out_data->buffer[out_data->length],
+ cfg->csvspace,
+ cfg->csvnumsp);
+ }
+
+ /* Add qualifier */
+ if (cfg->csvqualifier) {
+ out_data->buffer[out_data->length] = cfg->csvqualifier;
+ out_data->length++;
+ }
+
+ /* Add the value */
+ switch (type) {
+ case COL_TYPE_STRING:
+
+ if ((cfg->csvqualifier) && (cfg->csvescchar)) {
+ /* Qualify and escape */
+ used_len = file_copy_esc((char *)&out_data->buffer[out_data->length],
+ (const char *)(data),
+ cfg->csvqualifier,
+ cfg->csvescchar);
+ }
+ else {
+ /* No escaping so just copy without trailing 0 */
+ /* Item's length includes trailing 0 for data items */
+ used_len = length - 1;
+ memcpy(&out_data->buffer[out_data->length],
+ (const char *)(data),
+ used_len);
+ }
+ break;
+
+ case COL_TYPE_BINARY:
+
+ for (i = 0; i < length; i++)
+ sprintf((char *)&out_data->buffer[out_data->length + i * 2],
+ "%02X", (unsigned int)(((const unsigned char *)(data))[i]));
+ used_len = length * 2;
+ 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;
+
+ /* Add qualifier */
+ if (cfg->csvqualifier) {
+ out_data->buffer[out_data->length] = cfg->csvqualifier;
+ out_data->length++;
+ }
+
+ /* The "length" member of the structure does not account
+ * for the 0 symbol but we made sure that it fits
+ * when we asked for the memory at the top.
+ */
+ out_data->buffer[out_data->length] = '\0';
+
+ TRACE_INFO_STRING("Data: ", out_data->buffer);
+
+ TRACE_FLOW_STRING("file_serialize_csv.", "Exit");
+ return error;
+
+}
+
+/* Function that reads the specific configuration
+ * information about the format of the output
+ */
+int file_get_csv_cfg(void **storage,
+ const char *name,
+ struct collection_item *ini_config,
+ const char *appname)
+{
+ int error = EOK;
+ struct collection_item *cfg_item = NULL;
+ struct file_csv_cfg *cfg= NULL;
+ const char *qual;
+ const char *sep;
+ const char *esc;
+ const char *space;
+
+ TRACE_FLOW_STRING("file_get_csv_cfg", "Entry");
+
+ /* Allocate memory for configuration */
+ cfg = (struct file_csv_cfg *)malloc(sizeof(struct file_csv_cfg));
+ if (cfg == NULL) {
+ TRACE_ERROR_NUMBER("Failed to allocate storage for CSV configuration", ENOMEM);
+ return ENOMEM;
+ }
+
+ /*********** Qualifier *************/
+
+ /* Get qualifier */
+ error = get_config_item(name,
+ FILE_CSV_QUAL,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read qualifier attribute returned error", error);
+ free(cfg);
+ return error;
+ }
+
+ /* Do we have qualifier? */
+ if (cfg_item == NULL) {
+ /* There is no qualifier - use default */
+ cfg->csvqualifier = FILE_CSV_DEF_QUAL;
+ }
+ else {
+ /* Get qualifier from configuration */
+ error = EOK;
+ qual = get_const_string_config_value(cfg_item, &error);
+ if (error) {
+ TRACE_ERROR_STRING("Failed to get value from configuration.", "Fatal Error!");
+ free(cfg);
+ return error;
+ }
+
+ if (qual[0] == '\0') cfg->csvqualifier = '\0';
+ else if(qual[1] != '\0') {
+ TRACE_ERROR_STRING("Qualifier has more than one symbol.", "Fatal Error!");
+ free(cfg);
+ return EINVAL;
+ }
+ else cfg->csvqualifier = qual[0];
+ }
+
+ /*********** Separator *************/
+
+ /* Get separator */
+ error = get_config_item(name,
+ FILE_CSV_SEP,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read separator attribute returned error", error);
+ free(cfg);
+ return error;
+ }
+
+ /* Do we have separator? */
+ if (cfg_item == NULL) {
+ /* There is no separator - use default */
+ cfg->csvseparator = FILE_CSV_DEF_SEP;
+ }
+ else {
+ /* Get separator from configuration */
+ error = EOK;
+ sep = get_const_string_config_value(cfg_item, &error);
+ if (error) {
+ TRACE_ERROR_STRING("Failed to get value from configuration.", "Fatal Error!");
+ free(cfg);
+ return error;
+ }
+
+ if (sep[0] == '\0') cfg->csvseparator = '\0';
+ else if(sep[1] != '\0') {
+ TRACE_ERROR_STRING("Separator has more than one symbol.", "Fatal Error!");
+ free(cfg);
+ return EINVAL;
+ }
+ else cfg->csvseparator = sep[0];
+ }
+
+ /*********** Escape symbol *************/
+
+ /* Get escape symbol */
+ error = get_config_item(name,
+ FILE_CSV_ESCSYM,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read esc symbol attribute returned error", error);
+ free(cfg);
+ return error;
+ }
+
+ /* Do we have esc symbol? */
+ if (cfg_item == NULL) {
+ /* There is no esc symbol - use default */
+ cfg->csvescchar = FILE_CSV_DEF_ESC;
+ }
+ else {
+ /* Get esc symbol from configuration */
+ error = EOK;
+ esc = get_const_string_config_value(cfg_item, &error);
+ if (error) {
+ TRACE_ERROR_STRING("Failed to get value from configuration.", "Fatal Error!");
+ free(cfg);
+ return error;
+ }
+
+ if (esc[0] == '\0') cfg->csvescchar = '\0';
+ else if(esc[1] != '\0') {
+ TRACE_ERROR_STRING("Esc symbol has more than one symbol.", "Fatal Error!");
+ free(cfg);
+ return EINVAL;
+ }
+ else cfg->csvescchar = esc[0];
+ }
+
+ /*********** Space *************/
+
+ /* Get space */
+ error = get_config_item(name,
+ FILE_CSV_SPACE,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read space attribute returned error", error);
+ free(cfg);
+ return error;
+ }
+
+ /* Do we have space? */
+ if (cfg_item == NULL) {
+ /* There is no esc symbol - use default */
+ cfg->csvspace = FILE_CSV_DEF_SPC;
+ }
+ else {
+ /* Get file name from configuration */
+ error = EOK;
+ space = get_const_string_config_value(cfg_item, &error);
+ if (error) {
+ TRACE_ERROR_STRING("Failed to get value from configuration.", "Fatal Error!");
+ free(cfg);
+ return error;
+ }
+
+ /* Determine what to use as a space symbol */
+ if (space[0] == '\0') cfg->csvspace = ' ';
+ else if(strcmp(space, FILE_CSV_SP) == 0) cfg->csvspace = ' ';
+ else if(strcmp(space, FILE_CSV_TAB) == 0) cfg->csvspace = '\t';
+ else if(strcmp(space, FILE_CSV_CR) == 0) cfg->csvspace = '\n';
+ else {
+ TRACE_ERROR_STRING("Esc symbol has more than one symbol.", "Fatal Error!");
+ free(cfg);
+ return EINVAL;
+ }
+ }
+
+ /*********** Number of spaces *************/
+ /* Get number of spaces */
+
+ cfg_item = NULL;
+ error = get_config_item(name,
+ FILE_CSV_NUMSP,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read number of spaces attribute returned error", error);
+ free(cfg);
+ return error;
+ }
+
+ /* Do we have number of spaces? */
+ if (cfg_item == NULL) {
+ /* There is no attribute - assume default */
+ TRACE_INFO_STRING("No attribute.", "Assume no spaces");
+ cfg->csvnumsp = 0;
+ }
+ else {
+ cfg->csvnumsp = (uint32_t) get_unsigned_config_value(cfg_item, 1, 0, &error);
+ if (error) {
+ TRACE_ERROR_STRING("Invalid number of spaces value", "Fatal Error!");
+ free(cfg);
+ return EINVAL;
+ }
+ /* Check for right range */
+ if (cfg->csvnumsp > FILE_MAXSPACE) {
+ TRACE_ERROR_STRING("Too many spaces - not allowed", "Fatal Error!");
+ free(cfg);
+ return ERANGE;
+ }
+ }
+
+ /*********** Header *************/
+ /* Next is header field */
+
+ cfg_item = NULL;
+ error = get_config_item(name,
+ FILE_CSV_HEADER,
+ ini_config,
+ &cfg_item);
+ if (error) {
+ TRACE_ERROR_NUMBER("Attempt to read header attribute returned error", error);
+ free(cfg);
+ return error;
+ }
+
+ /* Do we have header? */
+ if (cfg_item == NULL) {
+ /* There is no attribute - assume default */
+ TRACE_INFO_STRING("No attribute.", "Assume no header");
+ cfg->csvheader = 0;
+ }
+ else {
+ cfg->csvheader = (uint32_t) get_bool_config_value(cfg_item, '\0', &error);
+ if (error) {
+ TRACE_ERROR_STRING("Invalid csv header value", "Fatal Error!");
+ free(cfg);
+ return EINVAL;
+ }
+ }
+
+ *((struct file_csv_cfg **)storage) = cfg;
+
+ TRACE_FLOW_STRING("file_get_csv_cfg", "Entry");
+ return error;
+}
+
+#ifdef ELAPI_VERBOSE
+
+void file_print_fmt_csv(void *data)
+{
+ struct file_csv_cfg *cfg;
+
+ cfg = (struct file_csv_cfg *)(data);
+ if (cfg == NULL) {
+ printf("CSV Configuration is undefined!\n");
+ return;
+ }
+
+ printf("CSV Configuration:\n");
+ printf(" Qualifier: ");
+ if (cfg->csvqualifier != '\0') printf("[%c]\n", cfg->csvqualifier);
+ else printf("[undefined]\n");
+
+ printf(" Separator: ");
+ if (cfg->csvseparator != '\0') printf("[%c]\n", cfg->csvseparator);
+ else printf("[undefined]\n");
+
+ printf(" Escape: ");
+ if (cfg->csvescchar != '\0') printf("[%c]\n", cfg->csvescchar);
+ else printf("[undefined]\n");
+
+ printf(" Space: [%c] [ASCII: %d]\n", cfg->csvspace, (int)(cfg->csvspace));
+ printf(" Number of spaces: [%d]\n", cfg->csvnumsp);
+ printf(" Header: [%s]\n", ((cfg->csvheader > 0) ? "yes" : "no"));
+ printf("CSV Configuration END\n");
+
+}
+#endif