123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- // 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 > 1) {
- *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));
- parser->plugins_action = callocz(1, sizeof(PLUGINSD_ACTION));
- 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))) {
- parser_add_keyword(parser, PLUGINSD_KEYWORD_FLUSH, pluginsd_flush);
- parser_add_keyword(parser, PLUGINSD_KEYWORD_CHART, pluginsd_chart);
- parser_add_keyword(parser, PLUGINSD_KEYWORD_DIMENSION, pluginsd_dimension);
- parser_add_keyword(parser, PLUGINSD_KEYWORD_DISABLE, pluginsd_disable);
- parser_add_keyword(parser, PLUGINSD_KEYWORD_VARIABLE, pluginsd_variable);
- parser_add_keyword(parser, PLUGINSD_KEYWORD_LABEL, pluginsd_label);
- parser_add_keyword(parser, PLUGINSD_KEYWORD_OVERWRITE, pluginsd_overwrite);
- parser_add_keyword(parser, PLUGINSD_KEYWORD_END, pluginsd_end);
- parser_add_keyword(parser, "CLABEL_COMMIT", pluginsd_clabel_commit);
- parser_add_keyword(parser, "CLABEL", pluginsd_clabel);
- parser_add_keyword(parser, PLUGINSD_KEYWORD_BEGIN, pluginsd_begin);
- 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->worker_job_id = parser->worker_job_ids++;
- tmp_keyword->keyword = strdupz(keyword);
- tmp_keyword->keyword_hash = keyword_hash;
- tmp_keyword->func[tmp_keyword->func_no++] = (void *) func;
- worker_register_job_name(tmp_keyword->worker_job_id, tmp_keyword->keyword);
- 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);
- }
- /*
- * 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 + 1];
- 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);
- size_t worker_job_id = 0;
- while(tmp_keyword) {
- if (command_hash == tmp_keyword->keyword_hash &&
- (!strcmp(command, tmp_keyword->keyword))) {
- action_function_list = &tmp_keyword->func[0];
- worker_job_id = tmp_keyword->worker_job_id;
- 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 {
- worker_is_busy(worker_job_id);
- 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++;
- }
- worker_is_idle();
- }
- 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;
- }
|