diff options
Diffstat (limited to '')
-rw-r--r-- | parser/parser.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/parser/parser.c b/parser/parser.c new file mode 100644 index 000000000..21d7fb3fc --- /dev/null +++ b/parser/parser.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "parser.h" + +static inline int find_keyword(char *str, char *keyword, int max_size, int (*custom_isspace)(char)) +{ + char *s = str, *keyword_start; + + while (unlikely(custom_isspace(*s))) s++; + keyword_start = s; + + while (likely(*s && !custom_isspace(*s)) && max_size > 0) { + *keyword++ = *s++; + max_size--; + } + *keyword = '\0'; + return max_size == 0 ? 0 : (int) (s - keyword_start); +} + +/* + * Initialize a parser + * user : as defined by the user, will be shared across calls + * input : main input stream (auto detect stream -- file, socket, pipe) + * buffer : This is the buffer to be used (if null a buffer of size will be allocated) + * size : buffer size either passed or will be allocated + * If the buffer is auto allocated, it will auto freed when the parser is destroyed + * + * + */ + +PARSER *parser_init(RRDHOST *host, void *user, void *input, PARSER_INPUT_TYPE flags) +{ + PARSER *parser; + + parser = callocz(1, sizeof(*parser)); + + if (unlikely(!parser)) + return NULL; + + parser->plugins_action = callocz(1, sizeof(PLUGINSD_ACTION)); + if (unlikely(!parser->plugins_action)) { + freez(parser); + return NULL; + } + + parser->user = user; + parser->input = input; + parser->flags = flags; + parser->host = host; +#ifdef ENABLE_HTTPS + parser->bytesleft = 0; + parser->readfrom = NULL; +#endif + + if (unlikely(!(flags & PARSER_NO_PARSE_INIT))) { + int rc = parser_add_keyword(parser, PLUGINSD_KEYWORD_FLUSH, pluginsd_flush); + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_CHART, pluginsd_chart); + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_DIMENSION, pluginsd_dimension); + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_DISABLE, pluginsd_disable); + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_VARIABLE, pluginsd_variable); + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_LABEL, pluginsd_label); + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_OVERWRITE, pluginsd_overwrite); + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_END, pluginsd_end); + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_BEGIN, pluginsd_begin); + rc += parser_add_keyword(parser, "SET", pluginsd_set); + } + + return parser; +} + + +/* + * Push a new line into the parsing stream + * + * This line will be the next one to process ie the next fetch will get this one + * + */ + +int parser_push(PARSER *parser, char *line) +{ + PARSER_DATA *tmp_parser_data; + + if (unlikely(!parser)) + return 1; + + if (unlikely(!line)) + return 0; + + tmp_parser_data = callocz(1, sizeof(*tmp_parser_data)); + tmp_parser_data->line = strdupz(line); + tmp_parser_data->next = parser->data; + parser->data = tmp_parser_data; + + return 0; +} + +/* + * Add a keyword and the corresponding function that will be called + * Multiple functions may be added + * Input : keyword + * : callback function + * : flags + * Output: > 0 registered function number + * : 0 Error + */ + +int parser_add_keyword(PARSER *parser, char *keyword, keyword_function func) +{ + PARSER_KEYWORD *tmp_keyword; + + if (strcmp(keyword, "_read") == 0) { + parser->read_function = (void *) func; + return 0; + } + + if (strcmp(keyword, "_eof") == 0) { + parser->eof_function = (void *) func; + return 0; + } + + if (strcmp(keyword, "_unknown") == 0) { + parser->unknown_function = (void *) func; + return 0; + } + + uint32_t keyword_hash = simple_hash(keyword); + + tmp_keyword = parser->keyword; + + while (tmp_keyword) { + if (tmp_keyword->keyword_hash == keyword_hash && (!strcmp(tmp_keyword->keyword, keyword))) { + if (tmp_keyword->func_no == PARSER_MAX_CALLBACKS) + return 0; + tmp_keyword->func[tmp_keyword->func_no++] = (void *) func; + return tmp_keyword->func_no; + } + tmp_keyword = tmp_keyword->next; + } + + tmp_keyword = callocz(1, sizeof(*tmp_keyword)); + + tmp_keyword->keyword = strdupz(keyword); + tmp_keyword->keyword_hash = keyword_hash; + tmp_keyword->func[tmp_keyword->func_no++] = (void *) func; + + tmp_keyword->next = parser->keyword; + parser->keyword = tmp_keyword; + return tmp_keyword->func_no; +} + +/* + * Cleanup a previously allocated parser + */ + +void parser_destroy(PARSER *parser) +{ + if (unlikely(!parser)) + return; + + PARSER_KEYWORD *tmp_keyword, *tmp_keyword_next; + PARSER_DATA *tmp_parser_data, *tmp_parser_data_next; + + // Remove keywords + tmp_keyword = parser->keyword; + while (tmp_keyword) { + tmp_keyword_next = tmp_keyword->next; + freez(tmp_keyword->keyword); + freez(tmp_keyword); + tmp_keyword = tmp_keyword_next; + } + + // Remove pushed data if any + tmp_parser_data = parser->data; + while (tmp_parser_data) { + tmp_parser_data_next = tmp_parser_data->next; + freez(tmp_parser_data->line); + freez(tmp_parser_data); + tmp_parser_data = tmp_parser_data_next; + } + + freez(parser->plugins_action); + + freez(parser); + return; +} + + +/* + * Fetch the next line to process + * + */ + +int parser_next(PARSER *parser) +{ + char *tmp = NULL; + + if (unlikely(!parser)) + return 1; + + parser->flags &= ~(PARSER_INPUT_PROCESSED); + + PARSER_DATA *tmp_parser_data = parser->data; + + if (unlikely(tmp_parser_data)) { + strncpyz(parser->buffer, tmp_parser_data->line, PLUGINSD_LINE_MAX); + parser->data = tmp_parser_data->next; + freez(tmp_parser_data->line); + freez(tmp_parser_data); + return 0; + } + + if (unlikely(parser->read_function)) + tmp = parser->read_function(parser->buffer, PLUGINSD_LINE_MAX, parser->input); + else + tmp = fgets(parser->buffer, PLUGINSD_LINE_MAX, (FILE *)parser->input); + + if (unlikely(!tmp)) { + if (unlikely(parser->eof_function)) { + int rc = parser->eof_function(parser->input); + error("read failed: user defined function returned %d", rc); + } + else { + if (feof((FILE *)parser->input)) + error("read failed: end of file"); + else if (ferror((FILE *)parser->input)) + error("read failed: input error"); + else + error("read failed: unknown error"); + } + return 1; + } + return 0; +} + + +/* +* Takes an initialized parser object that has an unprocessed entry (by calling parser_next) +* and if it contains a valid keyword, it will execute all the callbacks +* +*/ + +inline int parser_action(PARSER *parser, char *input) +{ + PARSER_RC rc = PARSER_RC_OK; + char *words[PLUGINSD_MAX_WORDS] = { NULL }; + char command[PLUGINSD_LINE_MAX]; + keyword_function action_function; + keyword_function *action_function_list = NULL; + + if (unlikely(!parser)) + return 1; + parser->recover_location[0] = 0x0; + + // if not direct input check if we have reprocessed this + if (unlikely(!input && parser->flags & PARSER_INPUT_PROCESSED)) + return 0; + + PARSER_KEYWORD *tmp_keyword = parser->keyword; + if (unlikely(!tmp_keyword)) { + return 1; + } + + if (unlikely(!input)) + input = parser->buffer; + + if (unlikely(!find_keyword(input, command, PLUGINSD_LINE_MAX, pluginsd_space))) + return 0; + + if ((parser->flags & PARSER_INPUT_ORIGINAL) == PARSER_INPUT_ORIGINAL) + pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, parser->recover_input, parser->recover_location, PARSER_MAX_RECOVER_KEYWORDS); + else + pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0); + + uint32_t command_hash = simple_hash(command); + + while(tmp_keyword) { + if (command_hash == tmp_keyword->keyword_hash && + (!strcmp(command, tmp_keyword->keyword))) { + action_function_list = &tmp_keyword->func[0]; + break; + } + tmp_keyword = tmp_keyword->next; + } + + if (unlikely(!action_function_list)) { + if (unlikely(parser->unknown_function)) + rc = parser->unknown_function(words, parser->user, NULL); + else + rc = PARSER_RC_ERROR; +#ifdef NETDATA_INTERNAL_CHECKS + error("Unknown keyword [%s]", input); +#endif + } + else { + while ((action_function = *action_function_list) != NULL) { + rc = action_function(words, parser->user, parser->plugins_action); + if (unlikely(rc == PARSER_RC_ERROR || rc == PARSER_RC_STOP)) + break; + action_function_list++; + } + } + + if (likely(input == parser->buffer)) + parser->flags |= PARSER_INPUT_PROCESSED; + + return (rc == PARSER_RC_ERROR); +} + +inline int parser_recover_input(PARSER *parser) +{ + if (unlikely(!parser)) + return 1; + + for(int i=0; i < PARSER_MAX_RECOVER_KEYWORDS && parser->recover_location[i]; i++) + *(parser->recover_location[i]) = parser->recover_input[i]; + + parser->recover_location[0] = 0x0; + + return 0; +} |