summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/monkey/mk_core/mk_rconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'fluent-bit/lib/monkey/mk_core/mk_rconf.c')
-rw-r--r--fluent-bit/lib/monkey/mk_core/mk_rconf.c779
1 files changed, 779 insertions, 0 deletions
diff --git a/fluent-bit/lib/monkey/mk_core/mk_rconf.c b/fluent-bit/lib/monkey/mk_core/mk_rconf.c
new file mode 100644
index 000000000..bf169d0e8
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_core/mk_rconf.c
@@ -0,0 +1,779 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef _MSC_VER
+#include <glob.h>
+#endif
+
+#include <mk_core/mk_rconf.h>
+#include <mk_core/mk_utils.h>
+#include <mk_core/mk_string.h>
+#include <mk_core/mk_list.h>
+
+#ifdef _WIN32
+#include <Windows.h>
+#include <strsafe.h>
+#define PATH_MAX MAX_PATH
+#endif
+
+/* Raise a configuration schema error */
+static void mk_config_error(const char *path, int line, const char *msg)
+{
+ mk_err("File %s", path);
+ mk_err("Error in line %i: %s", line, msg);
+}
+
+/* Raise a warning */
+static void mk_rconf_warning(const char *path, int line, const char *msg)
+{
+ mk_warn("Config file warning '%s':\n"
+ "\t\t\t\tat line %i: %s",
+ path, line, msg);
+}
+
+
+/* Returns a configuration section by [section name] */
+struct mk_rconf_section *mk_rconf_section_get(struct mk_rconf *conf,
+ const char *name)
+{
+ struct mk_list *head;
+ struct mk_rconf_section *section;
+
+ mk_list_foreach(head, &conf->sections) {
+ section = mk_list_entry(head, struct mk_rconf_section, _head);
+ if (strcasecmp(section->name, name) == 0) {
+ return section;
+ }
+ }
+
+ return NULL;
+}
+
+/* Register a key/value entry in the last section available of the struct */
+static void mk_rconf_section_entry_add(struct mk_rconf *conf,
+ const char *key, const char *val)
+{
+ struct mk_rconf_section *section;
+ struct mk_rconf_entry *new;
+ struct mk_list *head = &conf->sections;
+
+ if (mk_list_is_empty(&conf->sections) == 0) {
+ mk_err("Error: there are not sections available on %s!", conf->file);
+ return;
+ }
+
+ /* Last section */
+ section = mk_list_entry_last(head, struct mk_rconf_section, _head);
+
+ /* Alloc new entry */
+ new = mk_mem_alloc(sizeof(struct mk_rconf_entry));
+ new->key = mk_string_dup(key);
+ new->val = mk_string_dup(val);
+
+ mk_list_add(&new->_head, &section->entries);
+}
+
+/* Create a configuration schema */
+struct mk_rconf *mk_rconf_create(const char *name)
+{
+ struct mk_rconf *conf = NULL;
+
+ /* Alloc configuration node */
+ conf = mk_mem_alloc_z(sizeof(struct mk_rconf));
+ if (!conf) {
+ perror("malloc");
+ return NULL;
+ }
+ conf->created = time(NULL);
+ conf->file = mk_string_dup(name);
+ mk_list_init(&conf->sections);
+
+ return conf;
+}
+
+static int is_file_included(struct mk_rconf *conf, const char *path)
+{
+ struct mk_list *head;
+ struct mk_rconf_file *file;
+
+ mk_list_foreach(head, &conf->includes) {
+ file = mk_list_entry(head, struct mk_rconf_file, _head);
+ if (strcmp(file->path, path) == 0) {
+ return MK_TRUE;
+ }
+ }
+
+ return MK_FALSE;
+}
+
+char *mk_rconf_meta_get(struct mk_rconf *conf, char *key)
+{
+ struct mk_list *head;
+ struct mk_rconf_entry *meta;
+
+ mk_list_foreach(head, &conf->metas) {
+ meta = mk_list_entry(head, struct mk_rconf_entry, _head);
+ if (strcmp(meta->key, key) == 0) {
+ return meta->val;
+ }
+ }
+
+ return NULL;
+}
+
+static int mk_rconf_meta_add(struct mk_rconf *conf, char *buf, int len)
+{
+ int xlen;
+ char *p;
+ char *tmp;
+ struct mk_rconf_entry *meta;
+
+ if (buf[0] != '@') {
+ return -1;
+ }
+
+ meta = mk_mem_alloc(sizeof(struct mk_rconf_entry));
+ if (!meta) {
+ return -1;
+ }
+
+ p = buf;
+ tmp = strchr(p, ' ');
+ xlen = (tmp - p);
+ meta->key = mk_string_copy_substr(buf, 1, xlen);
+ mk_string_trim(&meta->key);
+
+ meta->val = mk_string_copy_substr(buf, xlen + 1, len);
+ mk_string_trim(&meta->val);
+
+ mk_list_add(&meta->_head, &conf->metas);
+ return 0;
+}
+
+static int check_indent(const char *line, const char *indent)
+{
+ while (*line == *indent && *indent) {
+ line++;
+ indent++;
+ }
+
+ if (*indent != '\0') {
+ if (isblank(*line)) {
+ mk_err("[config] Inconsistent use of tab and space");
+ }
+ else {
+ mk_err("[config] Indentation level is too low");
+ }
+ return -1;
+ }
+
+ if (isblank(*line)) {
+ mk_err("[config] Extra indentation level found");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* To call this function from mk_rconf_read */
+static int mk_rconf_read_glob(struct mk_rconf *conf, const char * path);
+
+static int mk_rconf_read(struct mk_rconf *conf, const char *path)
+{
+ int i;
+ int len;
+ int ret;
+ int line = 0;
+ int indent_len = -1;
+ int n_keys = 0;
+ char *buf;
+ char tmp[PATH_MAX];
+ char *section = NULL;
+ char *indent = NULL;
+ char *key, *val;
+ char *cfg_file = (char *) path;
+ struct stat st;
+ struct mk_rconf_file *file;
+ struct mk_rconf_section *current = NULL;
+ FILE *f;
+
+ /* Check if the path exists (relative cases for included files) */
+ if (conf->level >= 0) {
+ ret = stat(path, &st);
+ if (ret == -1 && errno == ENOENT) {
+ /* Try to resolve the real path (if exists) */
+ if (path[0] == '/') {
+ return -1;
+ }
+
+ if (conf->root_path) {
+ snprintf(tmp, PATH_MAX, "%s/%s", conf->root_path, path);
+ cfg_file = tmp;
+ }
+ }
+ }
+
+ /* Check this file have not been included before */
+ ret = is_file_included(conf, cfg_file);
+ if (ret == MK_TRUE) {
+ mk_err("[config] file already included %s", cfg_file);
+ return -1;
+ }
+
+ conf->level++;
+
+ /* Open configuration file */
+ if ((f = fopen(cfg_file, "r")) == NULL) {
+ mk_warn("[config] I cannot open %s file", cfg_file);
+ return -1;
+ }
+
+ /* Allocate temporal buffer to read file content */
+ buf = mk_mem_alloc(MK_RCONF_KV_SIZE);
+ if (!buf) {
+ fclose(f);
+ perror("malloc");
+ return -1;
+ }
+
+ /* looking for configuration directives */
+ while (fgets(buf, MK_RCONF_KV_SIZE, f)) {
+ len = strlen(buf);
+ if (len > 0 && buf[len - 1] == '\n') {
+ buf[--len] = 0;
+ if (len && buf[len - 1] == '\r') {
+ buf[--len] = 0;
+ }
+ }
+ else {
+ /*
+ * If we don't find a break line, validate if we got an EOF or not. No EOF
+ * means that the incoming string is not finished so we must raise an
+ * exception.
+ */
+ if (!feof(f)) {
+ mk_config_error(path, line, "Length of content has exceeded limit");
+ fclose(f);
+ mk_mem_free(buf);
+ return -1;
+ }
+ }
+
+ /* Line number */
+ line++;
+
+ if (!buf[0]) {
+ continue;
+ }
+
+ /* Skip commented lines */
+ if (buf[0] == '#') {
+ continue;
+ }
+
+ if (len > 9 && strncasecmp(buf, "@INCLUDE ", 9) == 0) {
+ if (strchr(buf + 9, '*') != NULL) {
+ ret = mk_rconf_read_glob(conf, buf + 9);
+ }
+ else {
+ ret = mk_rconf_read(conf, buf + 9);
+ }
+ if (ret == -1) {
+ conf->level--;
+ fclose(f);
+ if (indent) {
+ mk_mem_free(indent);
+ }
+ mk_mem_free(buf);
+ return -1;
+ }
+ continue;
+ }
+ else if (buf[0] == '@' && len > 3) {
+ ret = mk_rconf_meta_add(conf, buf, len);
+ if (ret == -1) {
+ fclose(f);
+ if (indent) {
+ mk_mem_free(indent);
+ }
+ mk_mem_free(buf);
+ return -1;
+ }
+ continue;
+ }
+
+ /* Section definition */
+ if (buf[0] == '[') {
+ int end = -1;
+ end = mk_string_char_search(buf, ']', len);
+ if (end > 0) {
+ /*
+ * Before to add a new section, lets check the previous
+ * one have at least one key set
+ */
+ if (current && n_keys == 0) {
+ mk_rconf_warning(path, line, "Previous section did not have keys");
+ }
+
+ /* Create new section */
+ section = mk_string_copy_substr(buf, 1, end);
+ current = mk_rconf_section_add(conf, section);
+ if (!current) {
+ fclose(f);
+ if (indent) {
+ mk_mem_free(indent);
+ }
+ mk_mem_free(buf);
+ mk_mem_free(section);
+ return -1;
+ }
+ mk_mem_free(section);
+ n_keys = 0;
+ continue;
+ }
+ else {
+ mk_config_error(path, line, "Bad header definition");
+ fclose(f);
+ mk_mem_free(buf);
+ return -1;
+ }
+ }
+
+ /* No separator defined */
+ if (!indent) {
+ i = 0;
+
+ do { i++; } while (i < len && isblank(buf[i]));
+
+ indent = mk_string_copy_substr(buf, 0, i);
+ indent_len = strlen(indent);
+
+ /* Blank indented line */
+ if (i == len) {
+ continue;
+ }
+ }
+
+ /* Validate indentation level */
+ if (check_indent(buf, indent) < 0) {
+ mk_config_error(path, line, "Invalid indentation level");
+ fclose(f);
+ return -1;
+ }
+
+ if (buf[indent_len] == '#' || indent_len == len) {
+ continue;
+ }
+
+ if (len - indent_len >= 3 && strncmp(buf + indent_len, "---", 3) == 0) {
+ continue;
+ }
+
+ /* Get key and val */
+ i = mk_string_char_search(buf + indent_len, ' ', len - indent_len);
+ key = mk_string_copy_substr(buf + indent_len, 0, i);
+ val = mk_string_copy_substr(buf + indent_len + i, 1, len - indent_len - i);
+
+ if (!key || !val || i < 0) {
+ mk_config_error(path, line, "Each key must have a value");
+ fclose(f);
+ mk_mem_free(key);
+ mk_mem_free(val);
+ return -1;
+ }
+
+ /* Trim strings */
+ mk_string_trim(&key);
+ mk_string_trim(&val);
+
+ if (strlen(val) == 0) {
+ mk_config_error(path, line, "Key has an empty value");
+ fclose(f);
+ mk_mem_free(key);
+ mk_mem_free(val);
+ return -1;
+ }
+
+ /* Register entry: key and val are copied as duplicated */
+ mk_rconf_section_entry_add(conf, key, val);
+
+ /* Free temporal key and val */
+ mk_mem_free(key);
+ mk_mem_free(val);
+
+ n_keys++;
+ }
+
+ if (section && n_keys == 0) {
+ /* No key, no warning */
+ }
+
+ /*
+ struct mk_config_section *s;
+ struct mk_rconf_entry *e;
+
+ s = conf->section;
+ while(s) {
+ printf("\n[%s]", s->name);
+ e = s->entry;
+ while(e) {
+ printf("\n %s = %s", e->key, e->val);
+ e = e->next;
+ }
+ s = s->next;
+ }
+ fflush(stdout);
+ */
+ fclose(f);
+ if (indent) {
+ mk_mem_free(indent);
+ }
+ mk_mem_free(buf);
+
+ /* Append this file to the list */
+ file = mk_mem_alloc(sizeof(struct mk_rconf_file));
+ if (!file) {
+ conf->level--;
+ return -1;
+ }
+
+ file->path = mk_string_dup(path);
+ mk_list_add(&file->_head, &conf->includes);
+ conf->level--;
+ return 0;
+}
+
+#ifndef _WIN32
+static int mk_rconf_read_glob(struct mk_rconf *conf, const char * path)
+{
+ int ret = -1;
+ glob_t glb;
+ char tmp[PATH_MAX];
+
+ const char *glb_path;
+ size_t i;
+ int ret_glb = -1;
+
+ if (conf->root_path && path[0] != '/') {
+ snprintf(tmp, PATH_MAX, "%s/%s", conf->root_path, path);
+ glb_path = tmp;
+ }
+ else {
+ glb_path = path;
+ }
+
+ ret_glb = glob(glb_path, GLOB_NOSORT, NULL, &glb);
+ if (ret_glb != 0) {
+ switch(ret_glb){
+ case GLOB_NOSPACE:
+ mk_warn("[%s] glob: [%s] no space", __FUNCTION__, glb_path);
+ break;
+ case GLOB_NOMATCH:
+ mk_warn("[%s] glob: [%s] no match", __FUNCTION__, glb_path);
+ break;
+ case GLOB_ABORTED:
+ mk_warn("[%s] glob: [%s] aborted", __FUNCTION__, glb_path);
+ break;
+ default:
+ mk_warn("[%s] glob: [%s] other error", __FUNCTION__, glb_path);
+ }
+ return ret;
+ }
+
+ for (i = 0; i < glb.gl_pathc; i++) {
+ ret = mk_rconf_read(conf, glb.gl_pathv[i]);
+ if (ret < 0) {
+ break;
+ }
+ }
+
+ globfree(&glb);
+ return ret;
+}
+#else
+static int mk_rconf_read_glob(struct mk_rconf *conf, const char *path)
+{
+ char *star, *p0, *p1;
+ char pattern[MAX_PATH];
+ char buf[MAX_PATH];
+ int ret;
+ struct stat st;
+ HANDLE h;
+ WIN32_FIND_DATA data;
+
+ if (strlen(path) > MAX_PATH - 1) {
+ return -1;
+ }
+
+ star = strchr(path, '*');
+ if (star == NULL) {
+ return -1;
+ }
+
+ /*
+ * C:\data\tmp\input_*.conf
+ * 0<-----|
+ */
+ p0 = star;
+ while (path <= p0 && *p0 != '\\') {
+ p0--;
+ }
+
+ /*
+ * C:\data\tmp\input_*.conf
+ * |---->1
+ */
+ p1 = star;
+ while (*p1 && *p1 != '\\') {
+ p1++;
+ }
+
+ memcpy(pattern, path, (p1 - path));
+ pattern[p1 - path] = '\0';
+
+ h = FindFirstFileA(pattern, &data);
+ if (h == INVALID_HANDLE_VALUE) {
+ return 0;
+ }
+
+ do {
+ /* Ignore the current and parent dirs */
+ if (!strcmp(".", data.cFileName) || !strcmp("..", data.cFileName)) {
+ continue;
+ }
+
+ /* Avoid an infinite loop */
+ if (strchr(data.cFileName, '*')) {
+ continue;
+ }
+
+ /* Create a path (prefix + filename + suffix) */
+ memcpy(buf, path, p0 - path + 1);
+ buf[p0 - path + 1] = '\0';
+
+ if (FAILED(StringCchCatA(buf, MAX_PATH, data.cFileName))) {
+ continue;
+ }
+ if (FAILED(StringCchCatA(buf, MAX_PATH, p1))) {
+ continue;
+ }
+
+ if (strchr(p1, '*')) {
+ mk_rconf_read_glob(conf, buf); /* recursive */
+ continue;
+ }
+
+ ret = stat(buf, &st);
+ if (ret == 0 && (st.st_mode & S_IFMT) == S_IFREG) {
+ if (mk_rconf_read(conf, buf) < 0) {
+ return -1;
+ }
+ }
+ } while (FindNextFileA(h, &data) != 0);
+
+ FindClose(h);
+ return 0;
+}
+#endif
+
+static int mk_rconf_path_set(struct mk_rconf *conf, char *file)
+{
+ char *p;
+ char *end;
+ char path[PATH_MAX + 1];
+
+#ifdef _MSC_VER
+ p = _fullpath(path, file, PATH_MAX + 1);
+#else
+ p = realpath(file, path);
+#endif
+ if (!p) {
+ return -1;
+ }
+
+ /* lookup path ending and truncate */
+ end = strrchr(path, '/');
+ if (!end) {
+ return -1;
+ }
+
+ end++;
+ *end = '\0';
+ conf->root_path = mk_string_dup(path);
+
+ return 0;
+}
+
+struct mk_rconf *mk_rconf_open(const char *path)
+{
+ int ret;
+ struct mk_rconf *conf = NULL;
+
+ if (!path) {
+ mk_warn("[config] invalid path file %s", path);
+ return NULL;
+ }
+
+ /* Alloc configuration node */
+ conf = mk_mem_alloc_z(sizeof(struct mk_rconf));
+ if (!conf) {
+ perror("malloc");
+ return NULL;
+ }
+ conf->created = time(NULL);
+ conf->file = mk_string_dup(path);
+ conf->level = -1;
+ mk_list_init(&conf->sections);
+ mk_list_init(&conf->includes);
+ mk_list_init(&conf->metas);
+
+ /* Set the absolute path for the entrypoint file */
+ mk_rconf_path_set(conf, (char *) path);
+
+ /* Read entrypoint */
+ ret = mk_rconf_read(conf, path);
+ if (ret == -1) {
+ mk_rconf_free(conf);
+ return NULL;
+ }
+
+ return conf;
+}
+
+void mk_rconf_free(struct mk_rconf *conf)
+{
+ struct mk_list *head, *tmp;
+ struct mk_rconf_section *section;
+ struct mk_rconf_entry *entry;
+ struct mk_rconf_file *file;
+
+ /* Remove included files */
+ mk_list_foreach_safe(head, tmp, &conf->includes) {
+ file = mk_list_entry(head, struct mk_rconf_file, _head);
+ mk_list_del(&file->_head);
+ mk_mem_free(file->path);
+ mk_mem_free(file);
+ }
+
+ /* Remove metas */
+ mk_list_foreach_safe(head, tmp, &conf->metas) {
+ entry = mk_list_entry(head, struct mk_rconf_entry, _head);
+ mk_list_del(&entry->_head);
+ mk_mem_free(entry->key);
+ mk_mem_free(entry->val);
+ mk_mem_free(entry);
+ }
+
+ /* Free sections */
+ mk_list_foreach_safe(head, tmp, &conf->sections) {
+ section = mk_list_entry(head, struct mk_rconf_section, _head);
+ mk_list_del(&section->_head);
+
+ /* Free section entries */
+ mk_rconf_free_entries(section);
+
+ /* Free section node */
+ mk_mem_free(section->name);
+ mk_mem_free(section);
+ }
+ if (conf->file) {
+ mk_mem_free(conf->file);
+ }
+
+ mk_mem_free(conf->root_path);
+ mk_mem_free(conf);
+}
+
+void mk_rconf_free_entries(struct mk_rconf_section *section)
+{
+ struct mk_rconf_entry *entry;
+ struct mk_list *head, *tmp;
+
+ mk_list_foreach_safe(head, tmp, &section->entries) {
+ entry = mk_list_entry(head, struct mk_rconf_entry, _head);
+ mk_list_del(&entry->_head);
+
+ /* Free memory assigned */
+ mk_mem_free(entry->key);
+ mk_mem_free(entry->val);
+ mk_mem_free(entry);
+ }
+}
+
+/* Register a new section into the configuration struct */
+struct mk_rconf_section *mk_rconf_section_add(struct mk_rconf *conf,
+ char *name)
+{
+ struct mk_rconf_section *new;
+
+ /* Alloc section node */
+ new = mk_mem_alloc(sizeof(struct mk_rconf_section));
+ if (!new) {
+ return NULL;
+ }
+ new->name = mk_string_dup(name);
+ mk_list_init(&new->entries);
+ mk_list_add(&new->_head, &conf->sections);
+
+ return new;
+}
+
+/* Return the value of a key of a specific section */
+void *mk_rconf_section_get_key(struct mk_rconf_section *section,
+ char *key, int mode)
+{
+ int on, off;
+ struct mk_rconf_entry *entry;
+ struct mk_list *head;
+
+ mk_list_foreach(head, &section->entries) {
+ entry = mk_list_entry(head, struct mk_rconf_entry, _head);
+
+ if (strcasecmp(entry->key, key) == 0) {
+ switch (mode) {
+ case MK_RCONF_STR:
+ return (void *) mk_string_dup(entry->val);
+ case MK_RCONF_NUM:
+ return (void *) strtol(entry->val, (char **) NULL, 10);
+ case MK_RCONF_BOOL:
+ on = strcasecmp(entry->val, MK_RCONF_ON);
+ off = strcasecmp(entry->val, MK_RCONF_OFF);
+
+ if (on != 0 && off != 0) {
+ return (void *) -1;
+ }
+ else if (on >= 0) {
+ return (void *) MK_TRUE;
+ }
+ else {
+ return (void *) MK_FALSE;
+ }
+ case MK_RCONF_LIST:
+ return (void *)mk_string_split_line(entry->val);
+ }
+ }
+ }
+ return NULL;
+}