|
@@ -20,34 +20,55 @@
|
|
|
#include <libgen.h>
|
|
|
|
|
|
#include "configuration.h"
|
|
|
-#include "container-executor.h"
|
|
|
+#include "util.h"
|
|
|
|
|
|
+#define __STDC_FORMAT_MACROS
|
|
|
#include <inttypes.h>
|
|
|
#include <errno.h>
|
|
|
#include <unistd.h>
|
|
|
-#include <stdio.h>
|
|
|
#include <stdlib.h>
|
|
|
#include <string.h>
|
|
|
#include <sys/stat.h>
|
|
|
-#include <sys/types.h>
|
|
|
-#include <limits.h>
|
|
|
|
|
|
#define MAX_SIZE 10
|
|
|
|
|
|
+static const char COMMENT_BEGIN_CHAR = '#';
|
|
|
+static const char SECTION_LINE_BEGIN_CHAR = '[';
|
|
|
+static const char SECTION_LINE_END_CHAR = ']';
|
|
|
+
|
|
|
+//clean up method for freeing section
|
|
|
+void free_section(struct section *section) {
|
|
|
+ int i = 0;
|
|
|
+ for (i = 0; i < section->size; i++) {
|
|
|
+ if (section->kv_pairs[i]->key != NULL) {
|
|
|
+ free((void *) section->kv_pairs[i]->key);
|
|
|
+ }
|
|
|
+ if (section->kv_pairs[i]->value != NULL) {
|
|
|
+ free((void *) section->kv_pairs[i]->value);
|
|
|
+ }
|
|
|
+ free(section->kv_pairs[i]);
|
|
|
+ }
|
|
|
+ if (section->kv_pairs) {
|
|
|
+ free(section->kv_pairs);
|
|
|
+ section->kv_pairs = NULL;
|
|
|
+ }
|
|
|
+ if (section->name) {
|
|
|
+ free(section->name);
|
|
|
+ section->name = NULL;
|
|
|
+ }
|
|
|
+ section->size = 0;
|
|
|
+}
|
|
|
+
|
|
|
//clean up method for freeing configuration
|
|
|
-void free_configurations(struct configuration *cfg) {
|
|
|
+void free_configuration(struct configuration *cfg) {
|
|
|
int i = 0;
|
|
|
for (i = 0; i < cfg->size; i++) {
|
|
|
- if (cfg->confdetails[i]->key != NULL) {
|
|
|
- free((void *)cfg->confdetails[i]->key);
|
|
|
+ if (cfg->sections[i] != NULL) {
|
|
|
+ free_section(cfg->sections[i]);
|
|
|
}
|
|
|
- if (cfg->confdetails[i]->value != NULL) {
|
|
|
- free((void *)cfg->confdetails[i]->value);
|
|
|
- }
|
|
|
- free(cfg->confdetails[i]);
|
|
|
}
|
|
|
- if (cfg->size > 0) {
|
|
|
- free(cfg->confdetails);
|
|
|
+ if (cfg->sections) {
|
|
|
+ free(cfg->sections);
|
|
|
}
|
|
|
cfg->size = 0;
|
|
|
}
|
|
@@ -64,13 +85,13 @@ static int is_only_root_writable(const char *file) {
|
|
|
}
|
|
|
if (file_stat.st_uid != 0) {
|
|
|
fprintf(ERRORFILE, "File %s must be owned by root, but is owned by %" PRId64 "\n",
|
|
|
- file, (int64_t)file_stat.st_uid);
|
|
|
+ file, (int64_t) file_stat.st_uid);
|
|
|
return 0;
|
|
|
}
|
|
|
if ((file_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
|
|
|
fprintf(ERRORFILE,
|
|
|
- "File %s must not be world or group writable, but is %03lo\n",
|
|
|
- file, (unsigned long)file_stat.st_mode & (~S_IFMT));
|
|
|
+ "File %s must not be world or group writable, but is %03lo\n",
|
|
|
+ file, (unsigned long) file_stat.st_mode & (~S_IFMT));
|
|
|
return 0;
|
|
|
}
|
|
|
return 1;
|
|
@@ -81,9 +102,9 @@ static int is_only_root_writable(const char *file) {
|
|
|
*
|
|
|
* NOTE: relative path names are resolved relative to the second argument not getwd(3)
|
|
|
*/
|
|
|
-char *resolve_config_path(const char* file_name, const char *root) {
|
|
|
+char *resolve_config_path(const char *file_name, const char *root) {
|
|
|
const char *real_fname = NULL;
|
|
|
- char buffer[EXECUTOR_PATH_MAX*2 + 1];
|
|
|
+ char buffer[EXECUTOR_PATH_MAX * 2 + 1];
|
|
|
|
|
|
if (file_name[0] == '/') {
|
|
|
real_fname = file_name;
|
|
@@ -95,7 +116,7 @@ char *resolve_config_path(const char* file_name, const char *root) {
|
|
|
#ifdef HAVE_CANONICALIZE_FILE_NAME
|
|
|
char * ret = (real_fname == NULL) ? NULL : canonicalize_file_name(real_fname);
|
|
|
#else
|
|
|
- char * ret = (real_fname == NULL) ? NULL : realpath(real_fname, NULL);
|
|
|
+ char *ret = (real_fname == NULL) ? NULL : realpath(real_fname, NULL);
|
|
|
#endif
|
|
|
#ifdef DEBUG
|
|
|
fprintf(stderr,"ret = %s\n", ret);
|
|
@@ -111,10 +132,19 @@ char *resolve_config_path(const char* file_name, const char *root) {
|
|
|
* configuration and potentially cause damage.
|
|
|
* returns 0 if permissions are ok
|
|
|
*/
|
|
|
-int check_configuration_permissions(const char* file_name) {
|
|
|
+int check_configuration_permissions(const char *file_name) {
|
|
|
+ if (!file_name) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
// copy the input so that we can modify it with dirname
|
|
|
- char* dir = strdup(file_name);
|
|
|
- char* buffer = dir;
|
|
|
+ char *dir = strdup(file_name);
|
|
|
+ if (!dir) {
|
|
|
+ fprintf(stderr, "Failed to make a copy of filename in %s.\n", __func__);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ char *buffer = dir;
|
|
|
do {
|
|
|
if (!is_only_root_writable(dir)) {
|
|
|
free(buffer);
|
|
@@ -126,139 +156,397 @@ int check_configuration_permissions(const char* file_name) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Read a line from the the config file and return it without the newline.
|
|
|
+ * The caller must free the memory allocated.
|
|
|
+ */
|
|
|
+static char *read_config_line(FILE *conf_file) {
|
|
|
+ char *line = NULL;
|
|
|
+ size_t linesize = 100000;
|
|
|
+ ssize_t size_read = 0;
|
|
|
+ size_t eol = 0;
|
|
|
+
|
|
|
+ line = (char *) malloc(linesize);
|
|
|
+ if (line == NULL) {
|
|
|
+ fprintf(ERRORFILE, "malloc failed while reading configuration file.\n");
|
|
|
+ exit(OUT_OF_MEMORY);
|
|
|
+ }
|
|
|
+ size_read = getline(&line, &linesize, conf_file);
|
|
|
|
|
|
-void read_config(const char* file_name, struct configuration *cfg) {
|
|
|
- FILE *conf_file;
|
|
|
- char *line;
|
|
|
- char *equaltok;
|
|
|
- char *temp_equaltok;
|
|
|
- size_t linesize = 1000;
|
|
|
- int size_read = 0;
|
|
|
+ //feof returns true only after we read past EOF.
|
|
|
+ //so a file with no new line, at last can reach this place
|
|
|
+ //if size_read returns negative check for eof condition
|
|
|
+ if (size_read == -1) {
|
|
|
+ free(line);
|
|
|
+ line = NULL;
|
|
|
+ if (!feof(conf_file)) {
|
|
|
+ fprintf(ERRORFILE, "Line read returned -1 without eof\n");
|
|
|
+ exit(INVALID_CONFIG_FILE);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ eol = strlen(line) - 1;
|
|
|
+ if (line[eol] == '\n') {
|
|
|
+ //trim the ending new line
|
|
|
+ line[eol] = '\0';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return line;
|
|
|
+}
|
|
|
|
|
|
- if (file_name == NULL) {
|
|
|
- fprintf(ERRORFILE, "Null configuration filename passed in\n");
|
|
|
- exit(INVALID_CONFIG_FILE);
|
|
|
+/**
|
|
|
+ * Return if the given line is a comment line.
|
|
|
+ *
|
|
|
+ * @param line the line to check
|
|
|
+ *
|
|
|
+ * @return 1 if the line is a comment line, 0 otherwise
|
|
|
+ */
|
|
|
+static int is_comment_line(const char *line) {
|
|
|
+ if (line != NULL) {
|
|
|
+ return (line[0] == COMMENT_BEGIN_CHAR);
|
|
|
}
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- #ifdef DEBUG
|
|
|
- fprintf(LOGFILE, "read_config :Conf file name is : %s \n", file_name);
|
|
|
- #endif
|
|
|
+/**
|
|
|
+ * Return if the given line is a section start line.
|
|
|
+ *
|
|
|
+ * @param line the line to check
|
|
|
+ *
|
|
|
+ * @return 1 if the line is a section start line, 0 otherwise
|
|
|
+ */
|
|
|
+static int is_section_start_line(const char *line) {
|
|
|
+ size_t len = 0;
|
|
|
+ if (line != NULL) {
|
|
|
+ len = strlen(line) - 1;
|
|
|
+ return (line[0] == SECTION_LINE_BEGIN_CHAR
|
|
|
+ && line[len] == SECTION_LINE_END_CHAR);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- //allocate space for ten configuration items.
|
|
|
- cfg->confdetails = (struct confentry **) malloc(sizeof(struct confentry *)
|
|
|
- * MAX_SIZE);
|
|
|
- cfg->size = 0;
|
|
|
- conf_file = fopen(file_name, "r");
|
|
|
- if (conf_file == NULL) {
|
|
|
- fprintf(ERRORFILE, "Invalid conf file provided : %s \n", file_name);
|
|
|
- exit(INVALID_CONFIG_FILE);
|
|
|
+/**
|
|
|
+ * Return the name of the section from the given section start line. The
|
|
|
+ * caller must free the memory used.
|
|
|
+ *
|
|
|
+ * @param line the line to extract the section name from
|
|
|
+ *
|
|
|
+ * @return string with the name of the section, NULL otherwise
|
|
|
+ */
|
|
|
+static char *get_section_name(const char *line) {
|
|
|
+ char *name = NULL;
|
|
|
+ size_t len;
|
|
|
+
|
|
|
+ if (is_section_start_line(line)) {
|
|
|
+ // length of the name is the line - 2(to account for '[' and ']')
|
|
|
+ len = strlen(line) - 2;
|
|
|
+ name = (char *) malloc(len + 1);
|
|
|
+ if (name == NULL) {
|
|
|
+ fprintf(ERRORFILE, "malloc failed while reading section name.\n");
|
|
|
+ exit(OUT_OF_MEMORY);
|
|
|
+ }
|
|
|
+ strncpy(name, line + sizeof(char), len);
|
|
|
+ name[len] = '\0';
|
|
|
+ }
|
|
|
+ return name;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Read an entry for the section from the line. Function returns 0 if an entry
|
|
|
+ * was found, non-zero otherwise. Return values less than 0 indicate an error
|
|
|
+ * with the config file.
|
|
|
+ *
|
|
|
+ * @param line the line to read the entry from
|
|
|
+ * @param section the struct to read the entry into
|
|
|
+ *
|
|
|
+ * @return 0 if an entry was found
|
|
|
+ * <0 for config file errors
|
|
|
+ * >0 for issues such as empty line
|
|
|
+ *
|
|
|
+ */
|
|
|
+static int read_section_entry(const char *line, struct section *section) {
|
|
|
+ char *equaltok;
|
|
|
+ char *temp_equaltok;
|
|
|
+ const char *splitter = "=";
|
|
|
+ char *buffer;
|
|
|
+ size_t len = 0;
|
|
|
+ if (line == NULL || section == NULL) {
|
|
|
+ fprintf(ERRORFILE, "NULL params passed to read_section_entry");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ len = strlen(line);
|
|
|
+ if (len == 0) {
|
|
|
+ return 1;
|
|
|
}
|
|
|
- while(!feof(conf_file)) {
|
|
|
- line = (char *) malloc(linesize);
|
|
|
- if(line == NULL) {
|
|
|
- fprintf(ERRORFILE, "malloc failed while reading configuration file.\n");
|
|
|
+ if ((section->size) % MAX_SIZE == 0) {
|
|
|
+ section->kv_pairs = (struct kv_pair **) realloc(
|
|
|
+ section->kv_pairs,
|
|
|
+ sizeof(struct kv_pair *) * (MAX_SIZE + section->size));
|
|
|
+ if (section->kv_pairs == NULL) {
|
|
|
+ fprintf(ERRORFILE,
|
|
|
+ "Failed re-allocating memory for configuration items\n");
|
|
|
exit(OUT_OF_MEMORY);
|
|
|
}
|
|
|
- size_read = getline(&line,&linesize,conf_file);
|
|
|
+ }
|
|
|
|
|
|
- //feof returns true only after we read past EOF.
|
|
|
- //so a file with no new line, at last can reach this place
|
|
|
- //if size_read returns negative check for eof condition
|
|
|
- if (size_read == -1) {
|
|
|
- free(line);
|
|
|
- if(!feof(conf_file)){
|
|
|
- exit(INVALID_CONFIG_FILE);
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
+ buffer = strdup(line);
|
|
|
+ if (!buffer) {
|
|
|
+ fprintf(ERRORFILE, "Failed to allocating memory for line, %s\n", __func__);
|
|
|
+ exit(OUT_OF_MEMORY);
|
|
|
+ }
|
|
|
+
|
|
|
+ //tokenize first to get key and list of values.
|
|
|
+ //if no equals is found ignore this line, can be an empty line also
|
|
|
+ equaltok = strtok_r(buffer, splitter, &temp_equaltok);
|
|
|
+ if (equaltok == NULL) {
|
|
|
+ fprintf(ERRORFILE, "Error with line '%s', no '=' found\n", buffer);
|
|
|
+ exit(INVALID_CONFIG_FILE);
|
|
|
+ }
|
|
|
+ section->kv_pairs[section->size] = (struct kv_pair *) malloc(
|
|
|
+ sizeof(struct kv_pair));
|
|
|
+ if (section->kv_pairs[section->size] == NULL) {
|
|
|
+ fprintf(ERRORFILE, "Failed allocating memory for single section item\n");
|
|
|
+ exit(OUT_OF_MEMORY);
|
|
|
+ }
|
|
|
+ memset(section->kv_pairs[section->size], 0,
|
|
|
+ sizeof(struct kv_pair));
|
|
|
+ section->kv_pairs[section->size]->key = trim(equaltok);
|
|
|
+
|
|
|
+ equaltok = strtok_r(NULL, splitter, &temp_equaltok);
|
|
|
+ if (equaltok == NULL) {
|
|
|
+ // this can happen because no value was set
|
|
|
+ // e.g. banned.users=#this is a comment
|
|
|
+ int has_values = 1;
|
|
|
+ if (strstr(line, splitter) == NULL) {
|
|
|
+ fprintf(ERRORFILE, "configuration tokenization failed, error with line %s\n", line);
|
|
|
+ has_values = 0;
|
|
|
}
|
|
|
- int eol = strlen(line) - 1;
|
|
|
- if(line[eol] == '\n') {
|
|
|
- //trim the ending new line
|
|
|
- line[eol] = '\0';
|
|
|
+
|
|
|
+ // It is not a valid line, free memory.
|
|
|
+ free((void *) section->kv_pairs[section->size]->key);
|
|
|
+ free((void *) section->kv_pairs[section->size]);
|
|
|
+ section->kv_pairs[section->size] = NULL;
|
|
|
+ free(buffer);
|
|
|
+
|
|
|
+ // Return -1 when no values
|
|
|
+ if (!has_values) {
|
|
|
+ return -1;
|
|
|
}
|
|
|
- //comment line
|
|
|
- if(line[0] == '#') {
|
|
|
- free(line);
|
|
|
- continue;
|
|
|
+
|
|
|
+ // Return 2 for comments
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef DEBUG
|
|
|
+ fprintf(LOGFILE, "read_config : Adding conf value : %s \n", equaltok);
|
|
|
+#endif
|
|
|
+
|
|
|
+ section->kv_pairs[section->size]->value = trim(equaltok);
|
|
|
+ section->size++;
|
|
|
+ free(buffer);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Remove any trailing comment from the supplied line. Function modifies the
|
|
|
+ * argument provided.
|
|
|
+ *
|
|
|
+ * @param line the line from which to remove the comment
|
|
|
+ */
|
|
|
+static void trim_comment(char *line) {
|
|
|
+ char *begin_comment = NULL;
|
|
|
+ if (line != NULL) {
|
|
|
+ begin_comment = strchr(line, COMMENT_BEGIN_CHAR);
|
|
|
+ if (begin_comment != NULL) {
|
|
|
+ *begin_comment = '\0';
|
|
|
}
|
|
|
- //tokenize first to get key and list of values.
|
|
|
- //if no equals is found ignore this line, can be an empty line also
|
|
|
- equaltok = strtok_r(line, "=", &temp_equaltok);
|
|
|
- if(equaltok == NULL) {
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Allocate a section struct and initialize it. The memory must be freed by
|
|
|
+ * the caller. Function calls exit if any error occurs.
|
|
|
+ *
|
|
|
+ * @return pointer to the allocated section struct
|
|
|
+ *
|
|
|
+ */
|
|
|
+static struct section *allocate_section() {
|
|
|
+ struct section *section = (struct section *) malloc(sizeof(struct section));
|
|
|
+ if (section == NULL) {
|
|
|
+ fprintf(ERRORFILE, "malloc failed while allocating section.\n");
|
|
|
+ exit(OUT_OF_MEMORY);
|
|
|
+ }
|
|
|
+ section->name = NULL;
|
|
|
+ section->kv_pairs = NULL;
|
|
|
+ section->size = 0;
|
|
|
+ return section;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Populate the given section struct with fields from the config file.
|
|
|
+ *
|
|
|
+ * @param conf_file the file to read from
|
|
|
+ * @param section pointer to the section struct to populate
|
|
|
+ *
|
|
|
+ */
|
|
|
+static void populate_section_fields(FILE *conf_file, struct section *section) {
|
|
|
+ char *line;
|
|
|
+ long int offset = 0;
|
|
|
+ while (!feof(conf_file)) {
|
|
|
+ offset = ftell(conf_file);
|
|
|
+ line = read_config_line(conf_file);
|
|
|
+ if (line != NULL) {
|
|
|
+ if (!is_comment_line(line)) {
|
|
|
+ trim_comment(line);
|
|
|
+ if (!is_section_start_line(line)) {
|
|
|
+ if (section->name != NULL) {
|
|
|
+ if (read_section_entry(line, section) < 0) {
|
|
|
+ fprintf(ERRORFILE, "Error parsing line %s", line);
|
|
|
+ exit(INVALID_CONFIG_FILE);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ fprintf(ERRORFILE, "Line '%s' doesn't belong to a section\n",
|
|
|
+ line);
|
|
|
+ exit(INVALID_CONFIG_FILE);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (section->name == NULL) {
|
|
|
+ section->name = get_section_name(line);
|
|
|
+ if (strlen(section->name) == 0) {
|
|
|
+ fprintf(ERRORFILE, "Empty section name");
|
|
|
+ exit(INVALID_CONFIG_FILE);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // we've reached the next section
|
|
|
+ fseek(conf_file, offset, SEEK_SET);
|
|
|
+ free(line);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
free(line);
|
|
|
- continue;
|
|
|
- }
|
|
|
- cfg->confdetails[cfg->size] = (struct confentry *) malloc(
|
|
|
- sizeof(struct confentry));
|
|
|
- if(cfg->confdetails[cfg->size] == NULL) {
|
|
|
- fprintf(LOGFILE,
|
|
|
- "Failed allocating memory for single configuration item\n");
|
|
|
- goto cleanup;
|
|
|
}
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- #ifdef DEBUG
|
|
|
- fprintf(LOGFILE, "read_config : Adding conf key : %s \n", equaltok);
|
|
|
- #endif
|
|
|
-
|
|
|
- memset(cfg->confdetails[cfg->size], 0, sizeof(struct confentry));
|
|
|
- cfg->confdetails[cfg->size]->key = (char *) malloc(
|
|
|
- sizeof(char) * (strlen(equaltok)+1));
|
|
|
- strcpy((char *)cfg->confdetails[cfg->size]->key, equaltok);
|
|
|
- equaltok = strtok_r(NULL, "=", &temp_equaltok);
|
|
|
- if (equaltok == NULL) {
|
|
|
- fprintf(LOGFILE, "configuration tokenization failed \n");
|
|
|
- goto cleanup;
|
|
|
- }
|
|
|
- //means value is commented so don't store the key
|
|
|
- if(equaltok[0] == '#') {
|
|
|
- free(line);
|
|
|
- free((void *)cfg->confdetails[cfg->size]->key);
|
|
|
- free(cfg->confdetails[cfg->size]);
|
|
|
- continue;
|
|
|
+/**
|
|
|
+ * Read the section current section from the conf file. Section start is
|
|
|
+ * marked by lines of the form '[section-name]' and continue till the next
|
|
|
+ * section.
|
|
|
+ */
|
|
|
+static struct section *read_section(FILE *conf_file) {
|
|
|
+ struct section *section = allocate_section();
|
|
|
+ populate_section_fields(conf_file, section);
|
|
|
+ if (section->name == NULL) {
|
|
|
+ free_section(section);
|
|
|
+ section = NULL;
|
|
|
+ }
|
|
|
+ return section;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Merge two sections and free the second one after the merge, if desired.
|
|
|
+ * @param section1 the first section
|
|
|
+ * @param section2 the second section
|
|
|
+ * @param free_second_section free the second section if set
|
|
|
+ */
|
|
|
+static void merge_sections(struct section *section1, struct section *section2, const int free_second_section) {
|
|
|
+ int i = 0;
|
|
|
+ section1->kv_pairs = (struct kv_pair **) realloc(
|
|
|
+ section1->kv_pairs,
|
|
|
+ sizeof(struct kv_pair *) * (section1->size + section2->size));
|
|
|
+ if (section1->kv_pairs == NULL) {
|
|
|
+ fprintf(ERRORFILE,
|
|
|
+ "Failed re-allocating memory for configuration items\n");
|
|
|
+ exit(OUT_OF_MEMORY);
|
|
|
+ }
|
|
|
+ for (i = 0; i < section2->size; ++i) {
|
|
|
+ section1->kv_pairs[section1->size + i] = section2->kv_pairs[i];
|
|
|
+ }
|
|
|
+ section1->size += section2->size;
|
|
|
+ if (free_second_section) {
|
|
|
+ free(section2->name);
|
|
|
+ memset(section2, 0, sizeof(*section2));
|
|
|
+ free(section2);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int read_config(const char *file_path, struct configuration *cfg) {
|
|
|
+ FILE *conf_file;
|
|
|
+
|
|
|
+ if (file_path == NULL) {
|
|
|
+ fprintf(ERRORFILE, "Null configuration filename passed in\n");
|
|
|
+ return INVALID_CONFIG_FILE;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef DEBUG
|
|
|
+ fprintf(LOGFILE, "read_config :Conf file name is : %s \n", file_path);
|
|
|
+#endif
|
|
|
+
|
|
|
+ cfg->size = 0;
|
|
|
+ conf_file = fopen(file_path, "r");
|
|
|
+ if (conf_file == NULL) {
|
|
|
+ fprintf(ERRORFILE, "Invalid conf file provided, unable to open file"
|
|
|
+ " : %s \n", file_path);
|
|
|
+ return (INVALID_CONFIG_FILE);
|
|
|
+ }
|
|
|
+
|
|
|
+ cfg->sections = (struct section **) malloc(
|
|
|
+ sizeof(struct section *) * MAX_SIZE);
|
|
|
+ if (!cfg->sections) {
|
|
|
+ fprintf(ERRORFILE,
|
|
|
+ "Failed to allocate memory for configuration sections\n");
|
|
|
+ exit(OUT_OF_MEMORY);
|
|
|
+ }
|
|
|
+
|
|
|
+ // populate any entries in the older format(no sections)
|
|
|
+ cfg->sections[cfg->size] = allocate_section();
|
|
|
+ cfg->sections[cfg->size]->name = strdup("");
|
|
|
+ populate_section_fields(conf_file, cfg->sections[cfg->size]);
|
|
|
+ if (cfg->sections[cfg->size]) {
|
|
|
+ if (cfg->sections[cfg->size]->size) {
|
|
|
+ cfg->size++;
|
|
|
+ } else {
|
|
|
+ free_section(cfg->sections[cfg->size]);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- #ifdef DEBUG
|
|
|
- fprintf(LOGFILE, "read_config : Adding conf value : %s \n", equaltok);
|
|
|
- #endif
|
|
|
-
|
|
|
- cfg->confdetails[cfg->size]->value = (char *) malloc(
|
|
|
- sizeof(char) * (strlen(equaltok)+1));
|
|
|
- strcpy((char *)cfg->confdetails[cfg->size]->value, equaltok);
|
|
|
- if((cfg->size + 1) % MAX_SIZE == 0) {
|
|
|
- cfg->confdetails = (struct confentry **) realloc(cfg->confdetails,
|
|
|
- sizeof(struct confentry **) * (MAX_SIZE + cfg->size));
|
|
|
- if (cfg->confdetails == NULL) {
|
|
|
- fprintf(LOGFILE,
|
|
|
- "Failed re-allocating memory for configuration items\n");
|
|
|
- goto cleanup;
|
|
|
+ // populate entries in the sections format
|
|
|
+ while (!feof(conf_file)) {
|
|
|
+ cfg->sections[cfg->size] = NULL;
|
|
|
+ struct section *new_section = read_section(conf_file);
|
|
|
+ if (new_section != NULL) {
|
|
|
+ struct section *existing_section =
|
|
|
+ get_configuration_section(new_section->name, cfg);
|
|
|
+ if (existing_section != NULL) {
|
|
|
+ merge_sections((struct section *) existing_section, new_section, 1);
|
|
|
+ } else {
|
|
|
+ cfg->sections[cfg->size] = new_section;
|
|
|
}
|
|
|
}
|
|
|
- if(cfg->confdetails[cfg->size]) {
|
|
|
- cfg->size++;
|
|
|
- }
|
|
|
|
|
|
- free(line);
|
|
|
+ // Check if we need to expand memory for sections.
|
|
|
+ if (cfg->sections[cfg->size]) {
|
|
|
+ if ((cfg->size + 1) % MAX_SIZE == 0) {
|
|
|
+ cfg->sections = (struct section **) realloc(cfg->sections,
|
|
|
+ sizeof(struct sections *) * (MAX_SIZE + cfg->size));
|
|
|
+ if (cfg->sections == NULL) {
|
|
|
+ fprintf(ERRORFILE,
|
|
|
+ "Failed re-allocating memory for configuration items\n");
|
|
|
+ exit(OUT_OF_MEMORY);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cfg->size++;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- //close the file
|
|
|
fclose(conf_file);
|
|
|
|
|
|
if (cfg->size == 0) {
|
|
|
- fprintf(ERRORFILE, "Invalid configuration provided in %s\n", file_name);
|
|
|
- exit(INVALID_CONFIG_FILE);
|
|
|
+ free_configuration(cfg);
|
|
|
+ fprintf(ERRORFILE, "Invalid configuration provided in %s\n", file_path);
|
|
|
+ return INVALID_CONFIG_FILE;
|
|
|
}
|
|
|
-
|
|
|
- //clean up allocated file name
|
|
|
- return;
|
|
|
- //free spaces alloced.
|
|
|
- cleanup:
|
|
|
- if (line != NULL) {
|
|
|
- free(line);
|
|
|
- }
|
|
|
- fclose(conf_file);
|
|
|
- free_configurations(cfg);
|
|
|
- return;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -267,11 +555,14 @@ void read_config(const char* file_name, struct configuration *cfg) {
|
|
|
* array, next time onwards used the populated array.
|
|
|
*
|
|
|
*/
|
|
|
-char * get_value(const char* key, struct configuration *cfg) {
|
|
|
+char *get_section_value(const char *key, const struct section *section) {
|
|
|
int count;
|
|
|
- for (count = 0; count < cfg->size; count++) {
|
|
|
- if (strcmp(cfg->confdetails[count]->key, key) == 0) {
|
|
|
- return strdup(cfg->confdetails[count]->value);
|
|
|
+ if (key == NULL || section == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ for (count = 0; count < section->size; count++) {
|
|
|
+ if (strcmp(section->kv_pairs[count]->key, key) == 0) {
|
|
|
+ return strdup(section->kv_pairs[count]->value);
|
|
|
}
|
|
|
}
|
|
|
return NULL;
|
|
@@ -281,61 +572,80 @@ char * get_value(const char* key, struct configuration *cfg) {
|
|
|
* Function to return an array of values for a key.
|
|
|
* Value delimiter is assumed to be a ','.
|
|
|
*/
|
|
|
-char ** get_values(const char * key, struct configuration *cfg) {
|
|
|
- char *value = get_value(key, cfg);
|
|
|
- return extract_values_delim(value, ",");
|
|
|
+char **get_section_values(const char *key, const struct section *cfg) {
|
|
|
+ return get_section_values_delimiter(key, cfg, ",");
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Function to return an array of values for a key, using the specified
|
|
|
delimiter.
|
|
|
*/
|
|
|
-char ** get_values_delim(const char * key, struct configuration *cfg,
|
|
|
- const char *delim) {
|
|
|
- char *value = get_value(key, cfg);
|
|
|
- return extract_values_delim(value, delim);
|
|
|
+char **get_section_values_delimiter(const char *key, const struct section *cfg,
|
|
|
+ const char *delim) {
|
|
|
+ if (key == NULL || cfg == NULL || delim == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ char *value = get_section_value(key, cfg);
|
|
|
+ char **split_values = split_delimiter(value, delim);
|
|
|
+
|
|
|
+ if (value) {
|
|
|
+ free(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ return split_values;
|
|
|
}
|
|
|
|
|
|
-char ** extract_values_delim(char *value, const char *delim) {
|
|
|
- char ** toPass = NULL;
|
|
|
- char *tempTok = NULL;
|
|
|
- char *tempstr = NULL;
|
|
|
- int size = 0;
|
|
|
- int toPassSize = MAX_SIZE;
|
|
|
- //first allocate any array of 10
|
|
|
- if(value != NULL) {
|
|
|
- toPass = (char **) malloc(sizeof(char *) * toPassSize);
|
|
|
- tempTok = strtok_r((char *)value, delim, &tempstr);
|
|
|
- while (tempTok != NULL) {
|
|
|
- toPass[size++] = tempTok;
|
|
|
- if(size == toPassSize) {
|
|
|
- toPassSize += MAX_SIZE;
|
|
|
- toPass = (char **) realloc(toPass,(sizeof(char *) * toPassSize));
|
|
|
- }
|
|
|
- tempTok = strtok_r(NULL, delim, &tempstr);
|
|
|
- }
|
|
|
+char *get_configuration_value(const char *key, const char *section,
|
|
|
+ const struct configuration *cfg) {
|
|
|
+ const struct section *section_ptr;
|
|
|
+ if (key == NULL || section == NULL || cfg == NULL) {
|
|
|
+ return NULL;
|
|
|
}
|
|
|
- if (toPass != NULL) {
|
|
|
- toPass[size] = NULL;
|
|
|
+ section_ptr = get_configuration_section(section, cfg);
|
|
|
+ if (section_ptr != NULL) {
|
|
|
+ return get_section_value(key, section_ptr);
|
|
|
}
|
|
|
- return toPass;
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * Extracts array of values from the '%' separated list of values.
|
|
|
- */
|
|
|
-char ** extract_values(char *value) {
|
|
|
- return extract_values_delim(value, "%");
|
|
|
+char **get_configuration_values(const char *key, const char *section,
|
|
|
+ const struct configuration *cfg) {
|
|
|
+ const struct section *section_ptr;
|
|
|
+ if (key == NULL || section == NULL || cfg == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ section_ptr = get_configuration_section(section, cfg);
|
|
|
+ if (section_ptr != NULL) {
|
|
|
+ return get_section_values(key, section_ptr);
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+char **get_configuration_values_delimiter(const char *key, const char *section,
|
|
|
+ const struct configuration *cfg, const char *delim) {
|
|
|
+ const struct section *section_ptr;
|
|
|
+ if (key == NULL || section == NULL || cfg == NULL || delim == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ section_ptr = get_configuration_section(section, cfg);
|
|
|
+ if (section_ptr != NULL) {
|
|
|
+ return get_section_values_delimiter(key, section_ptr, delim);
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
-// free an entry set of values
|
|
|
-void free_values(char** values) {
|
|
|
- if (*values != NULL) {
|
|
|
- free(*values);
|
|
|
+struct section *get_configuration_section(const char *section,
|
|
|
+ const struct configuration *cfg) {
|
|
|
+ int i = 0;
|
|
|
+ if (cfg == NULL || section == NULL) {
|
|
|
+ return NULL;
|
|
|
}
|
|
|
- if (values != NULL) {
|
|
|
- free(values);
|
|
|
+ for (i = 0; i < cfg->size; ++i) {
|
|
|
+ if (strcmp(cfg->sections[i]->name, section) == 0) {
|
|
|
+ return cfg->sections[i];
|
|
|
+ }
|
|
|
}
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -346,12 +656,12 @@ int get_kv_key(const char *input, char *out, size_t out_len) {
|
|
|
if (input == NULL)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- char *split = strchr(input, '=');
|
|
|
+ const char *split = strchr(input, '=');
|
|
|
|
|
|
if (split == NULL)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- int key_len = split - input;
|
|
|
+ unsigned long key_len = split - input;
|
|
|
|
|
|
if (out_len < (key_len + 1) || out == NULL)
|
|
|
return -ENAMETOOLONG;
|
|
@@ -370,13 +680,13 @@ int get_kv_value(const char *input, char *out, size_t out_len) {
|
|
|
if (input == NULL)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- char *split = strchr(input, '=');
|
|
|
+ const char *split = strchr(input, '=');
|
|
|
|
|
|
if (split == NULL)
|
|
|
return -EINVAL;
|
|
|
|
|
|
split++; // advance past '=' to the value
|
|
|
- int val_len = (input + strlen(input)) - split;
|
|
|
+ unsigned long val_len = (input + strlen(input)) - split;
|
|
|
|
|
|
if (out_len < (val_len + 1) || out == NULL)
|
|
|
return -ENAMETOOLONG;
|