summaryrefslogtreecommitdiffstats
path: root/collectors/log2journal/log2journal-params.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--collectors/log2journal/log2journal-params.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/collectors/log2journal/log2journal-params.c b/collectors/log2journal/log2journal-params.c
new file mode 100644
index 00000000..a7bb3e26
--- /dev/null
+++ b/collectors/log2journal/log2journal-params.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "log2journal.h"
+
+// ----------------------------------------------------------------------------
+
+void log_job_init(LOG_JOB *jb) {
+ memset(jb, 0, sizeof(*jb));
+ simple_hashtable_init_KEY(&jb->hashtable, 32);
+ hashed_key_set(&jb->line.key, "LINE");
+}
+
+static void simple_hashtable_cleanup_allocated_keys(SIMPLE_HASHTABLE_KEY *ht) {
+ SIMPLE_HASHTABLE_FOREACH_READ_ONLY(ht, sl, _KEY) {
+ HASHED_KEY *k = SIMPLE_HASHTABLE_FOREACH_READ_ONLY_VALUE(sl);
+ if(k && k->flags & HK_HASHTABLE_ALLOCATED) {
+ // the order of these statements is important!
+ simple_hashtable_del_slot_KEY(ht, sl); // remove any references to n
+ hashed_key_cleanup(k); // cleanup the internals of n
+ freez(k); // free n
+ }
+ }
+}
+
+void log_job_cleanup(LOG_JOB *jb) {
+ hashed_key_cleanup(&jb->line.key);
+
+ if(jb->prefix) {
+ freez((void *) jb->prefix);
+ jb->prefix = NULL;
+ }
+
+ if(jb->pattern) {
+ freez((void *) jb->pattern);
+ jb->pattern = NULL;
+ }
+
+ for(size_t i = 0; i < jb->injections.used ;i++)
+ injection_cleanup(&jb->injections.keys[i]);
+
+ for(size_t i = 0; i < jb->unmatched.injections.used ;i++)
+ injection_cleanup(&jb->unmatched.injections.keys[i]);
+
+ for(size_t i = 0; i < jb->renames.used ;i++)
+ rename_cleanup(&jb->renames.array[i]);
+
+ for(size_t i = 0; i < jb->rewrites.used; i++)
+ rewrite_cleanup(&jb->rewrites.array[i]);
+
+ txt_cleanup(&jb->rewrites.tmp);
+ txt_cleanup(&jb->filename.current);
+
+ simple_hashtable_cleanup_allocated_keys(&jb->hashtable);
+ simple_hashtable_destroy_KEY(&jb->hashtable);
+
+ // remove references to everything else, to reveal them in valgrind
+ memset(jb, 0, sizeof(*jb));
+}
+
+// ----------------------------------------------------------------------------
+
+bool log_job_filename_key_set(LOG_JOB *jb, const char *key, size_t key_len) {
+ if(!key || !*key) {
+ log2stderr("filename key cannot be empty.");
+ return false;
+ }
+
+ hashed_key_len_set(&jb->filename.key, key, key_len);
+
+ return true;
+}
+
+bool log_job_key_prefix_set(LOG_JOB *jb, const char *prefix, size_t prefix_len) {
+ if(!prefix || !*prefix) {
+ log2stderr("filename key cannot be empty.");
+ return false;
+ }
+
+ if(jb->prefix)
+ freez((char*)jb->prefix);
+
+ jb->prefix = strndupz(prefix, prefix_len);
+
+ return true;
+}
+
+bool log_job_pattern_set(LOG_JOB *jb, const char *pattern, size_t pattern_len) {
+ if(!pattern || !*pattern) {
+ log2stderr("filename key cannot be empty.");
+ return false;
+ }
+
+ if(jb->pattern)
+ freez((char*)jb->pattern);
+
+ jb->pattern = strndupz(pattern, pattern_len);
+
+ return true;
+}
+
+bool log_job_include_pattern_set(LOG_JOB *jb, const char *pattern, size_t pattern_len) {
+ if(jb->filter.include.re) {
+ log2stderr("FILTER INCLUDE: there is already an include filter set");
+ return false;
+ }
+
+ if(!search_pattern_set(&jb->filter.include, pattern, pattern_len)) {
+ log2stderr("FILTER INCLUDE: failed: %s", jb->filter.include.error.txt);
+ return false;
+ }
+
+ return true;
+}
+
+bool log_job_exclude_pattern_set(LOG_JOB *jb, const char *pattern, size_t pattern_len) {
+ if(jb->filter.exclude.re) {
+ log2stderr("FILTER INCLUDE: there is already an exclude filter set");
+ return false;
+ }
+
+ if(!search_pattern_set(&jb->filter.exclude, pattern, pattern_len)) {
+ log2stderr("FILTER EXCLUDE: failed: %s", jb->filter.exclude.error.txt);
+ return false;
+ }
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+
+static bool parse_rename(LOG_JOB *jb, const char *param) {
+ // Search for '=' in param
+ const char *equal_sign = strchr(param, '=');
+ if (!equal_sign || equal_sign == param) {
+ log2stderr("Error: Invalid rename format, '=' not found in %s", param);
+ return false;
+ }
+
+ const char *new_key = param;
+ size_t new_key_len = equal_sign - new_key;
+
+ const char *old_key = equal_sign + 1;
+ size_t old_key_len = strlen(old_key);
+
+ return log_job_rename_add(jb, new_key, new_key_len, old_key, old_key_len);
+}
+
+static bool is_symbol(char c) {
+ return !isalpha(c) && !isdigit(c) && !iscntrl(c);
+}
+
+struct {
+ const char *keyword;
+ int action;
+ RW_FLAGS flag;
+} rewrite_flags[] = {
+ {"match", 1, RW_MATCH_PCRE2},
+ {"match", 0, RW_MATCH_NON_EMPTY},
+
+ {"regex", 1, RW_MATCH_PCRE2},
+ {"regex", 0, RW_MATCH_NON_EMPTY},
+
+ {"pcre2", 1, RW_MATCH_PCRE2},
+ {"pcre2", 0, RW_MATCH_NON_EMPTY},
+
+ {"non_empty", 1, RW_MATCH_NON_EMPTY},
+ {"non_empty", 0, RW_MATCH_PCRE2},
+
+ {"non-empty", 1, RW_MATCH_NON_EMPTY},
+ {"non-empty", 0, RW_MATCH_PCRE2},
+
+ {"not_empty", 1, RW_MATCH_NON_EMPTY},
+ {"not_empty", 0, RW_MATCH_PCRE2},
+
+ {"not-empty", 1, RW_MATCH_NON_EMPTY},
+ {"not-empty", 0, RW_MATCH_PCRE2},
+
+ {"stop", 0, RW_DONT_STOP},
+ {"no-stop", 1, RW_DONT_STOP},
+ {"no_stop", 1, RW_DONT_STOP},
+ {"dont-stop", 1, RW_DONT_STOP},
+ {"dont_stop", 1, RW_DONT_STOP},
+ {"continue", 1, RW_DONT_STOP},
+ {"inject", 1, RW_INJECT},
+ {"existing", 0, RW_INJECT},
+};
+
+RW_FLAGS parse_rewrite_flags(const char *options) {
+ RW_FLAGS flags = RW_MATCH_PCRE2; // Default option
+
+ // Tokenize the input options using ","
+ char *token;
+ char *optionsCopy = strdup(options); // Make a copy to avoid modifying the original
+ token = strtok(optionsCopy, ",");
+
+ while (token != NULL) {
+ // Find the keyword-action mapping
+ bool found = false;
+
+ for (size_t i = 0; i < sizeof(rewrite_flags) / sizeof(rewrite_flags[0]); i++) {
+ if (strcmp(token, rewrite_flags[i].keyword) == 0) {
+ if (rewrite_flags[i].action == 1) {
+ flags |= rewrite_flags[i].flag; // Set the flag
+ } else {
+ flags &= ~rewrite_flags[i].flag; // Unset the flag
+ }
+
+ found = true;
+ }
+ }
+
+ if(!found)
+ log2stderr("Warning: rewrite options '%s' is not understood.", token);
+
+ // Get the next token
+ token = strtok(NULL, ",");
+ }
+
+ free(optionsCopy); // Free the copied string
+
+ return flags;
+}
+
+
+static bool parse_rewrite(LOG_JOB *jb, const char *param) {
+ // Search for '=' in param
+ const char *equal_sign = strchr(param, '=');
+ if (!equal_sign || equal_sign == param) {
+ log2stderr("Error: Invalid rewrite format, '=' not found in %s", param);
+ return false;
+ }
+
+ // Get the next character as the separator
+ char separator = *(equal_sign + 1);
+ if (!separator || !is_symbol(separator)) {
+ log2stderr("Error: rewrite separator not found after '=', or is not one of /\\|-# in: %s", param);
+ return false;
+ }
+
+ // Find the next occurrence of the separator
+ const char *second_separator = strchr(equal_sign + 2, separator);
+ if (!second_separator) {
+ log2stderr("Error: rewrite second separator not found in: %s", param);
+ return false;
+ }
+
+ // Check if the search pattern is empty
+ if (equal_sign + 1 == second_separator) {
+ log2stderr("Error: rewrite search pattern is empty in: %s", param);
+ return false;
+ }
+
+ // Check if the replacement pattern is empty
+ if (*(second_separator + 1) == '\0') {
+ log2stderr("Error: rewrite replacement pattern is empty in: %s", param);
+ return false;
+ }
+
+ RW_FLAGS flags = RW_MATCH_PCRE2;
+ const char *third_separator = strchr(second_separator + 1, separator);
+ if(third_separator)
+ flags = parse_rewrite_flags(third_separator + 1);
+
+ // Extract key, search pattern, and replacement pattern
+ char *key = strndupz(param, equal_sign - param);
+ char *search_pattern = strndupz(equal_sign + 2, second_separator - (equal_sign + 2));
+ char *replace_pattern = third_separator ? strndup(second_separator + 1, third_separator - (second_separator + 1)) : strdupz(second_separator + 1);
+
+ if(!*search_pattern)
+ flags &= ~RW_MATCH_PCRE2;
+
+ bool ret = log_job_rewrite_add(jb, key, flags, search_pattern, replace_pattern);
+
+ freez(key);
+ freez(search_pattern);
+ freez(replace_pattern);
+
+ return ret;
+}
+
+static bool parse_inject(LOG_JOB *jb, const char *value, bool unmatched) {
+ const char *equal = strchr(value, '=');
+ if (!equal) {
+ log2stderr("Error: injection '%s' does not have an equal sign.", value);
+ return false;
+ }
+
+ const char *key = value;
+ const char *val = equal + 1;
+ log_job_injection_add(jb, key, equal - key, val, strlen(val), unmatched);
+
+ return true;
+}
+
+bool log_job_command_line_parse_parameters(LOG_JOB *jb, int argc, char **argv) {
+ for (int i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
+ log_job_command_line_help(argv[0]);
+ exit(0);
+ }
+#if defined(NETDATA_DEV_MODE) || defined(NETDATA_INTERNAL_CHECKS)
+ else if(strcmp(arg, "--test") == 0) {
+ // logfmt_test();
+ json_test();
+ exit(1);
+ }
+#endif
+ else if (strcmp(arg, "--show-config") == 0) {
+ jb->show_config = true;
+ }
+ else {
+ char buffer[1024];
+ char *param = NULL;
+ char *value = NULL;
+
+ char *equal_sign = strchr(arg, '=');
+ if (equal_sign) {
+ copy_to_buffer(buffer, sizeof(buffer), arg, equal_sign - arg);
+ param = buffer;
+ value = equal_sign + 1;
+ }
+ else {
+ param = arg;
+ if (i + 1 < argc) {
+ value = argv[++i];
+ }
+ else {
+ if (!jb->pattern) {
+ log_job_pattern_set(jb, arg, strlen(arg));
+ continue;
+ } else {
+ log2stderr("Error: Multiple patterns detected. Specify only one pattern. The first is '%s', the second is '%s'", jb->pattern, arg);
+ return false;
+ }
+ }
+ }
+
+ if (strcmp(param, "--filename-key") == 0) {
+ if(!log_job_filename_key_set(jb, value, value ? strlen(value) : 0))
+ return false;
+ }
+ else if (strcmp(param, "--prefix") == 0) {
+ if(!log_job_key_prefix_set(jb, value, value ? strlen(value) : 0))
+ return false;
+ }
+#ifdef HAVE_LIBYAML
+ else if (strcmp(param, "-f") == 0 || strcmp(param, "--file") == 0) {
+ if (!yaml_parse_file(value, jb))
+ return false;
+ }
+ else if (strcmp(param, "-c") == 0 || strcmp(param, "--config") == 0) {
+ if (!yaml_parse_config(value, jb))
+ return false;
+ }
+#endif
+ else if (strcmp(param, "--unmatched-key") == 0)
+ hashed_key_set(&jb->unmatched.key, value);
+ else if (strcmp(param, "--inject") == 0) {
+ if (!parse_inject(jb, value, false))
+ return false;
+ }
+ else if (strcmp(param, "--inject-unmatched") == 0) {
+ if (!parse_inject(jb, value, true))
+ return false;
+ }
+ else if (strcmp(param, "--rewrite") == 0) {
+ if (!parse_rewrite(jb, value))
+ return false;
+ }
+ else if (strcmp(param, "--rename") == 0) {
+ if (!parse_rename(jb, value))
+ return false;
+ }
+ else if (strcmp(param, "--include") == 0) {
+ if (!log_job_include_pattern_set(jb, value, strlen(value)))
+ return false;
+ }
+ else if (strcmp(param, "--exclude") == 0) {
+ if (!log_job_exclude_pattern_set(jb, value, strlen(value)))
+ return false;
+ }
+ else {
+ i--;
+ if (!jb->pattern) {
+ log_job_pattern_set(jb, arg, strlen(arg));
+ continue;
+ } else {
+ log2stderr("Error: Multiple patterns detected. Specify only one pattern. The first is '%s', the second is '%s'", jb->pattern, arg);
+ return false;
+ }
+ }
+ }
+ }
+
+ // Check if a pattern is set and exactly one pattern is specified
+ if (!jb->pattern) {
+ log2stderr("Warning: pattern not specified. Try the default config with: -c default");
+ log_job_command_line_help(argv[0]);
+ return false;
+ }
+
+ return true;
+}