From 188772ea0b3479352ae93552014d45fd1bc8e804 Mon Sep 17 00:00:00 2001 From: Christoph Lipka Date: Mon, 13 Jul 2015 17:07:47 +0900 Subject: Parse INI files Offline Logstorage, Multinode and potentially other DLT extensions retrieve their configuration from a configuration file in INI file format. To avoid code duplications, this helper functionality should be used to read configuration files. Signed-off-by: Christoph Lipka --- src/daemon/CMakeLists.txt | 2 +- src/shared/dlt_config_file_parser.c | 575 ++++++++++++++++++++++++++++++++++++ src/shared/dlt_config_file_parser.h | 147 +++++++++ 3 files changed, 723 insertions(+), 1 deletion(-) create mode 100644 src/shared/dlt_config_file_parser.c create mode 100644 src/shared/dlt_config_file_parser.h diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index d7d0d3a..70758cd 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -20,7 +20,7 @@ if(WITH_SYSTEMD_WATCHDOG OR WITH_SYSTEMD) message( STATUS "Added ${systemd_SRCS} to dlt-daemon") endif(WITH_SYSTEMD_WATCHDOG OR WITH_SYSTEMD) -set(dlt_daemon_SRCS dlt-daemon.c dlt_daemon_common.c dlt_daemon_connection.c dlt_daemon_event_handler.c dlt_daemon_socket.c dlt_daemon_serial.c dlt_daemon_client.c dlt_daemon_offline_logstorage.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_user_shared.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_common.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_shm.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_offline_trace.c ${CMAKE_SOURCE_DIR}/src/offlinelogstorage/dlt_offline_logstorage.c) +set(dlt_daemon_SRCS dlt-daemon.c dlt_daemon_common.c dlt_daemon_connection.c dlt_daemon_event_handler.c dlt_daemon_socket.c dlt_daemon_serial.c dlt_daemon_client.c dlt_daemon_offline_logstorage.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_user_shared.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_common.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_shm.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_offline_trace.c ${CMAKE_SOURCE_DIR}/src/offlinelogstorage/dlt_offline_logstorage.c ${CMAKE_SOURCE_DIR}/src/shared/dlt_config_file_parser.c) add_executable(dlt-daemon ${dlt_daemon_SRCS} ${systemd_SRCS}) target_link_libraries(dlt-daemon rt ${CMAKE_THREAD_LIBS_INIT}) diff --git a/src/shared/dlt_config_file_parser.c b/src/shared/dlt_config_file_parser.c new file mode 100644 index 0000000..96a6ac2 --- /dev/null +++ b/src/shared/dlt_config_file_parser.c @@ -0,0 +1,575 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2015, Advanced Driver Information Technology + * This code is developed by Advanced Driver Information Technology. + * Copyright of Advanced Driver Information Technology, Bosch and DENSO. + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author + * Christoph Lipka + * + * \copyright Copyright © 2015 Advanced Driver Information Technology. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt_config_file_parser.c + */ + +#include "dlt_config_file_parser.h" +#include +#include +#include +#include +#include +#include "dlt_common.h" +#include "dlt-daemon_cfg.h" + +/* internal defines */ +#define DLT_CONFIG_FILE_NEW_SECTION 0x0a +#define DLT_CONFIG_FILE_NEW_DATA 0x0b + + +/* internal helper functions */ + +/** + * dlt_config_file_trim_line + * + * Trim all whitespace from a string + * + * @param line String to remove whitespace from + */ +static void dlt_config_file_trim_line(char *line) +{ + if (line == NULL) + return; + + char *i = line; + char *j = line; + + while(*j != '\0') + { + *i = *j++; + if(!isspace(*i)) + i++; + } + *i = '\0'; +} + +/** + * dlt_config_file_ignore_line + * + * Check if a line has to be ignored, because it contains a comment or is empty + * + * @param line Line of configuration file + * @return 0 if ignore, -1 do not ignore + */ +static int dlt_config_file_ignore_line(char *line) +{ + int i = 0; + int len = strlen(line); + + for (i = 0; i < len; i++) + { + if (line[i] == '#' || line[i] == ';' || line[i] == '\n' || line[i] == '\0') + return 0; /* ignore */ + else + return -1; /* do not ignore */ + } + + return -1; +} + +/** + * dlt_config_file_is_section_name + * + * Check if section name already used + * + * @param file DltConfigFile + * @param name Name of section + * @return 0, section name not used, -1 section name already used + */ +static int dlt_config_file_is_section_name(DltConfigFile *file, char *name) +{ + int i = 0; + + if (file == NULL || name == NULL) + return -1; + + for (i = 0; i < file->num_sections; i++) + { + DltConfigFileSection *s = &file->sections[i]; + + if (strncmp(s->name, name, DLT_CONFIG_FILE_ENTRY_MAX_LEN) == 0) + return -1; + } + + return 0; /* section name not used */ +} + +/** + * dlt_config_file_set_section + * + * Store section in internal data structure + * + * @param file DltConfigFile + * @param name Name of section + * @return 0 on success, else -1 + */ +static int dlt_config_file_set_section(DltConfigFile *file, char *name) +{ + int section = file->num_sections; + /* check if adding another section would exceed max number of sections */ + if (section+1 >= DLT_CONFIG_FILE_SECTIONS_MAX) + { + dlt_log(LOG_WARNING, "Cannot store more sections\n"); + return -1; /* reached max number of sections */ + } + + /* do not store section with same name again */ + if (dlt_config_file_is_section_name(file, name) != 0) + { + dlt_log(LOG_WARNING, "Cannot store section name again\n"); + return -1; + } + + DltConfigFileSection *s = &file->sections[section]; + + /* alloc data for entries */ + s->name = calloc(sizeof(char), DLT_CONFIG_FILE_ENTRY_MAX_LEN + 1); + if (s->name == NULL) + { + dlt_log(LOG_ERR, "Cannot allocate memory for internal data structure\n"); + return -1; + } + + s->keys = calloc(sizeof(char), DLT_CONFIG_FILE_ENTRY_MAX_LEN * DLT_CONFIG_FILE_KEYS_MAX + 1); + if (s->keys == NULL) + { + free(s->name); + dlt_log(LOG_ERR, "Cannot allocate memory for internal data structure\n"); + return -1; + } + + /* create hsearch table for data inside the section */ + if (hcreate_r(DLT_CONFIG_FILE_KEYS_MAX, &s->data) == 0) + { + dlt_log(LOG_ERR, "Cannot create hash table object\n"); + free(s->name); + free(s->keys); + return -1; + } + + strncpy(file->sections[section].name, name, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + file->num_sections += 1; + return 0; +} + +/** + * dlt_config_file_set_section_data + * + * Store data pair of a section + * + * @param file DltConfigFile + * @param str1 string used for key + * @param str2 string used for value + * @return 0 on success, else -1 + */ +static int dlt_config_file_set_section_data(DltConfigFile *file, char *str1, char *str2) +{ + ENTRY item; + ENTRY *ret; + + if (file == NULL || str1 == NULL || str2 == NULL) + return -1; + + DltConfigFileSection *s = &file->sections[file->num_sections-1]; + int key_number = s->num_entries; + + if (key_number+1 >= DLT_CONFIG_FILE_KEYS_MAX) + { + dlt_log(LOG_WARNING, "Cannot store more keys in section\n"); + return -1; /* reached max number of keys per section */ + } + + /* copy data into structure */ + strncpy(&s->keys[key_number * DLT_CONFIG_FILE_ENTRY_MAX_LEN], str1, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + + item.key = strdup(str1); + item.data = strdup(str2); + + /* check if already stored */ + if (hsearch_r(item, FIND, &ret, &s->data) == 0) + { + if (hsearch_r(item, ENTER, &ret, &s->data) == 0) + { + free(item.key); + free(item.data); + + dlt_log(LOG_ERR, "Cannot add data to hash table\n"); + return -1; + } + } + else /* already exist, ignore */ + { + char error_str[DLT_DAEMON_TEXTBUFSIZE] = {'\0'}; + snprintf(error_str, DLT_DAEMON_TEXTBUFSIZE, "Key \"%s\" already exists! New data will be ignored\n", item.key); + dlt_log(LOG_WARNING, error_str); + return -1; + } + + s->num_entries += 1; + + return 0; +} + +/** + * dlt_config_file_has_section + * + * Check if a certain line in config file is a section header + * + * @param line Line in configuration file + * @return 0 if section header, else -1 + */ +static int dlt_config_file_line_has_section(char *line) +{ + line = line; /* avoid compiler warnings */ + if(line[0] == '[') /* section found */ + return 0; + else + return -1; +} + +/** + * dlt_config_file_get_section_name_from_string + * + * Extract section name from line + * + * @param line Line in configuration file containing a section header + * @return 0 on success, else -1 + */ +static int dlt_config_file_get_section_name_from_string(char *line, char *name) +{ + int i = 0; + int j = 0; + + if (line == NULL || name == NULL) + return -1; + + for(i = 0; i < DLT_CONFIG_FILE_ENTRY_MAX_LEN; i++) + { + if(line[i] == '[' || isspace(line[i])) + continue; + else if (line[i] == ']' || line[i] == '\n' || line[i] == '\0') + break; + else + name[j++] = line[i]; + } + + return 0; +} + +/** + * dlt_config_file_get_key_value + * + * Get key and value from a line of configuration file + * + * @param line Line on configuration file + * @param[out] str1 String to be used as key + * @param[out] str2 String to be used as value + * @return 0 on success, else -1 + */ +static int dlt_config_file_get_key_value(char *line, char *str1, char *str2) +{ + char *delimiter = "="; + char *ptr; + char *save_ptr; + + if (line == NULL || str1 == NULL || str2 == NULL) + return -1; + + ptr = strtok_r(line, delimiter, &save_ptr); + + if (ptr != NULL) /* get key */ + strncpy(str1, ptr, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + else + return -1; + + ptr = strtok_r(NULL, delimiter, &save_ptr); + + if (ptr != NULL) + strncpy(str2, ptr, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + else + return -1; + + return 0; +} + +/** + * dlt_config_file_read_line + * + * Read line from configuration file + * + * @param line Line from configuration file + * @param[out] str1 String contains section header or key + * @param[out] str2 String contains value or is empty + * @return 0 on success, else -1 + */ +static int dlt_config_file_read_line(char *line, char *str1, char *str2) +{ + if (line == NULL || str1 == NULL || str2 == NULL) + return -1; + + /* reset values to zero */ + memset(str1, 0, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + memset(str2, 0, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + + /* check if line contains a section */ + if ((dlt_config_file_line_has_section(line)) == 0) + { + /* retrieve section name */ + if (dlt_config_file_get_section_name_from_string(line, str1) != 0) + return -1; + + return DLT_CONFIG_FILE_NEW_SECTION; + } + + /* copy strings as key value pair into str1, str2 */ + if (dlt_config_file_get_key_value(line, str1, str2) != 0) + return -1; + + return DLT_CONFIG_FILE_NEW_DATA; +} + +/** + * dlt_config_file_read_file + * + * Read configuration file line by line and fill internal structures + * + * @param file DltConfigFile + * @param hdl FILE handle of opened configuration file + */ +static void dlt_config_file_read_file(DltConfigFile *file, FILE *hdl) +{ + int ret = 0; + char error_str[DLT_DAEMON_TEXTBUFSIZE] = {'\0'}; + char line[DLT_CONFIG_FILE_LINE_MAX_LEN] = {'\0'}; + char str1[DLT_CONFIG_FILE_ENTRY_MAX_LEN] = {'\0'}; + char str2[DLT_CONFIG_FILE_ENTRY_MAX_LEN] = {'\0'}; + int line_number = 0; + int is_section_valid = -1; /* to check if section name is given twice or invalid */ + + /* read configuration file line by line */ + while (fgets(line, DLT_CONFIG_FILE_LINE_MAX_LEN, hdl) != NULL) + { + line_number++; + + /* ignore empty and comment lines */ + if (dlt_config_file_ignore_line(line) == 0) + continue; + + /* trim line end */ + dlt_config_file_trim_line(line); + + /* parse content of line */ + ret = dlt_config_file_read_line(line, str1, str2); + + switch(ret) + { + case DLT_CONFIG_FILE_NEW_SECTION: /* store str1 as new section */ + is_section_valid = -1; + if ((ret = dlt_config_file_set_section(file, str1)) == 0) + { + is_section_valid = 0; + } + break; + case DLT_CONFIG_FILE_NEW_DATA: /* store str1 and str2 as new data for section */ + if (is_section_valid == 0) + { + ret = dlt_config_file_set_section_data(file, str1, str2); + } + break; + default: /* something is wrong with the line */ + snprintf(error_str, DLT_DAEMON_TEXTBUFSIZE, "Line (%d) \"%s\" is invalid\n", line_number, line); + dlt_log(LOG_WARNING, error_str); + } + } +} + +/** + * dlt_config_file_find_section + * + * Find a section + * + * @param file DltConfigFile + * @param section Name of section + * @return number of section on success, else -1 + */ +static int dlt_config_file_find_section(const DltConfigFile *file, + const char *section) +{ + int i = 0; + + if (file == NULL || section == NULL || file->num_sections <= 0) + { + dlt_log(LOG_WARNING, "Section cannot be found due to invalid parameters\n"); + return -1; + } + + for (i = 0; i < file->num_sections; i++) + { + DltConfigFileSection *s = &file->sections[i]; + if (strncmp(s->name, section, DLT_CONFIG_FILE_ENTRY_MAX_LEN) == 0) + return i; + } + + return -1; +} + +/************************** interface implementation ***************************/ +DltConfigFile *dlt_config_file_init(char *file_name) +{ + DltConfigFile *file; + FILE *hdl = NULL; + + if (file_name == NULL || (strlen(file_name) >= DLT_CONFIG_FILE_PATH_MAX_LEN)) + { + dlt_log(LOG_ERR, "Given configuration file invalid\n"); + return NULL; + } + + file = calloc(sizeof(DltConfigFile), 1); + if (file == NULL) + { + dlt_log(LOG_ERR, "Setup internal data structure to parse config file failed\n"); + return NULL; + } + + file->sections = calloc(sizeof(DltConfigFileSection), DLT_CONFIG_FILE_SECTIONS_MAX); + + /* open file */ + if ((hdl = fopen(file_name, "r")) == NULL) + { + dlt_log(LOG_ERR, "Cannot open configuration file\n"); + free(file); + return NULL; + } + + dlt_config_file_read_file(file, hdl); + + /* all information stored internally */ + fclose(hdl); + + return file; +} + +void dlt_config_file_release(DltConfigFile *file) +{ + int i = 0; + int j = 0; + ENTRY entry; + ENTRY *found; + + if(file != NULL){ + int max = file->num_sections; + for (i = 0; i < max; i++) + { + DltConfigFileSection *s = &file->sections[i]; + free(s->name); + + /* free data in hashtable */ + if (s->keys != NULL || &s->data != NULL) + { + for (j = 0; j < s->num_entries; j++) + { + entry.key = (s->keys + j * DLT_CONFIG_FILE_ENTRY_MAX_LEN); + hsearch_r (entry, FIND, &found, &s->data); + free(found->key); + free(found->data); + } + + hdestroy_r(&s->data); + free(s->keys); + } + } + + free(file->sections); + free(file); + } +} + +int dlt_config_file_get_section_name(const DltConfigFile *file, + int num, + char *name) +{ + if (file == NULL || name == NULL || num < 0 || num >= file->num_sections) + return -1; + + strncpy(name, (file->sections + num)->name, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + + return 0; +} + +int dlt_config_file_get_num_sections(const DltConfigFile *file, int *num) +{ + if(file == NULL || file->num_sections < 0) + return -1; + + *num = file->num_sections; + + return 0; +} + +int dlt_config_file_get_value(const DltConfigFile *file, + const char *section, + const char *key, char *value) +{ + DltConfigFileSection *s = NULL; + int num_section = 0; + ENTRY entry; + ENTRY *found; + + if (file == NULL || section == NULL || key == NULL || value == NULL) + return -1; + + /* clean value */ + memset(value, 0, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + + num_section = dlt_config_file_find_section(file, section); + if (num_section == -1) + return -1; + + s = (file->sections + num_section); + + /* check if available */ + entry.key = strdup(key); + if (entry.key == NULL) + { + dlt_log(LOG_CRIT, "Not enougth memory to duplicate key string\n"); + return -1; + } + + if (hsearch_r(entry, FIND, &found, &s->data) == 0) /* not found */ + { + dlt_log(LOG_WARNING, "Entry does not exist in section\n"); + free(entry.key); + return -1; + } + else /* found */ + { + strncpy(value, (char *)found->data, DLT_CONFIG_FILE_ENTRY_MAX_LEN); + free(entry.key); + return 0; + } +} diff --git a/src/shared/dlt_config_file_parser.h b/src/shared/dlt_config_file_parser.h new file mode 100644 index 0000000..c38a8b8 --- /dev/null +++ b/src/shared/dlt_config_file_parser.h @@ -0,0 +1,147 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2015, Advanced Driver Information Technology + * This code is developed by Advanced Driver Information Technology. + * Copyright of Advanced Driver Information Technology, Bosch and DENSO. + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Christoph Lipka + * + * \copyright Copyright © 2015 Advanced Driver Information Technology. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt_config_file_parser.h + */ + + +/******************************************************************************* +** ** +** SRC-MODULE: dlt_config_file_parser.h ** +** ** +** TARGET : linux ** +** ** +** PROJECT : DLT ** +** ** +** AUTHOR : Christoph Lipka clipka@jp.adit-jv.com ** +** ** +** PURPOSE : ** +** ** +** REMARKS : ** +** ** +** PLATFORM DEPENDANT [yes/no]: yes ** +** ** +** TO BE CHANGED BY USER [yes/no]: no ** +** ** +*******************************************************************************/ + +/******************************************************************************* +** Author Identity ** +******************************************************************************** +** ** +** Initials Name Company ** +** -------- ------------------------- ---------------------------------- ** +** cl Christoph Lipka ADIT ** +*******************************************************************************/ + +#ifndef _DLT_CONFIG_FILE_PARSER_H_ +#define _DLT_CONFIG_FILE_PARSER_H_ + +#include + +/* definitions */ +#define DLT_CONFIG_FILE_PATH_MAX_LEN 100 /* absolute path including filename */ +#define DLT_CONFIG_FILE_ENTRY_MAX_LEN 100 /* Entry for section, key and value */ +#define DLT_CONFIG_FILE_LINE_MAX_LEN 210 +#define DLT_CONFIG_FILE_SECTIONS_MAX 100 +#define DLT_CONFIG_FILE_KEYS_MAX 25 /* Maximal keys per section */ + +/* Config file section structure */ +typedef struct +{ + int num_entries; /* number of entries */ + char *name; /* name of section */ + char *keys; /* keys */ + struct hsearch_data data; /* hash table object used by hsearch_r */ +} DltConfigFileSection; + +typedef struct +{ + int num_sections; /* number of sections */ + DltConfigFileSection *sections; /* sections */ +} DltConfigFile; + +/** + * dlt_config_file_init + * + * Load the configuration file and stores all data in + * internal data structures. + * + * @param file_name File to be opened + * @return Pointer to DltConfigFile object or NULL on error + */ +DltConfigFile *dlt_config_file_init(char *file_name); + +/** + * dlt_config_file_release + * + * Release config file and frees all internal data. Has to be called after + * after all data is read. + * + * @param file DltConfigFile + */ +void dlt_config_file_release(DltConfigFile *file); + +/** + * dlt_config_file_get_section_name + * + * Get name of section number. + * + * @param[in] file DltConfigFile + * @param[in] num Number of section + * @param[out] section Section name + * @return 0 on success, else -1 + */ +int dlt_config_file_get_section_name(const DltConfigFile *file, + int num, + char *name); + +/** + * dlt_config_file_get_num_sections + * + * Get the number of sections inside configuration file + * + * @param[in] file DltConfigFile + * @param[out] num Number of sections inside configuration file + * @return 0 on success, else -1 + */ +int dlt_config_file_get_num_sections(const DltConfigFile *file, int *num); + +/** + * dlt_config_file_get_value + * + * Get value of key in specified section. + * + * @param[in] file DltConfigFile + * @param[in] section Name of section + * @param[in] key Key + * @param[out] value Value + * @return 0 on success, else -1 + */ +int dlt_config_file_get_value(const DltConfigFile *file, + const char *section, + const char *key, + char *value); +#endif -- cgit v1.2.1