parser.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "parser.h"
  3. static inline int find_keyword(char *str, char *keyword, int max_size, int (*custom_isspace)(char))
  4. {
  5. char *s = str, *keyword_start;
  6. while (unlikely(custom_isspace(*s))) s++;
  7. keyword_start = s;
  8. while (likely(*s && !custom_isspace(*s)) && max_size > 0) {
  9. *keyword++ = *s++;
  10. max_size--;
  11. }
  12. *keyword = '\0';
  13. return max_size == 0 ? 0 : (int) (s - keyword_start);
  14. }
  15. /*
  16. * Initialize a parser
  17. * user : as defined by the user, will be shared across calls
  18. * input : main input stream (auto detect stream -- file, socket, pipe)
  19. * buffer : This is the buffer to be used (if null a buffer of size will be allocated)
  20. * size : buffer size either passed or will be allocated
  21. * If the buffer is auto allocated, it will auto freed when the parser is destroyed
  22. *
  23. *
  24. */
  25. PARSER *parser_init(RRDHOST *host, void *user, void *input, PARSER_INPUT_TYPE flags)
  26. {
  27. PARSER *parser;
  28. parser = callocz(1, sizeof(*parser));
  29. if (unlikely(!parser))
  30. return NULL;
  31. parser->plugins_action = callocz(1, sizeof(PLUGINSD_ACTION));
  32. if (unlikely(!parser->plugins_action)) {
  33. freez(parser);
  34. return NULL;
  35. }
  36. parser->user = user;
  37. parser->input = input;
  38. parser->flags = flags;
  39. parser->host = host;
  40. #ifdef ENABLE_HTTPS
  41. parser->bytesleft = 0;
  42. parser->readfrom = NULL;
  43. #endif
  44. if (unlikely(!(flags & PARSER_NO_PARSE_INIT))) {
  45. int rc = parser_add_keyword(parser, PLUGINSD_KEYWORD_FLUSH, pluginsd_flush);
  46. rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_CHART, pluginsd_chart);
  47. rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_DIMENSION, pluginsd_dimension);
  48. rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_DISABLE, pluginsd_disable);
  49. rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_VARIABLE, pluginsd_variable);
  50. rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_LABEL, pluginsd_label);
  51. rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_OVERWRITE, pluginsd_overwrite);
  52. rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_END, pluginsd_end);
  53. rc += parser_add_keyword(parser, "CLABEL_COMMIT", pluginsd_clabel_commit);
  54. rc += parser_add_keyword(parser, "CLABEL", pluginsd_clabel);
  55. rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_BEGIN, pluginsd_begin);
  56. rc += parser_add_keyword(parser, "SET", pluginsd_set);
  57. }
  58. return parser;
  59. }
  60. /*
  61. * Push a new line into the parsing stream
  62. *
  63. * This line will be the next one to process ie the next fetch will get this one
  64. *
  65. */
  66. int parser_push(PARSER *parser, char *line)
  67. {
  68. PARSER_DATA *tmp_parser_data;
  69. if (unlikely(!parser))
  70. return 1;
  71. if (unlikely(!line))
  72. return 0;
  73. tmp_parser_data = callocz(1, sizeof(*tmp_parser_data));
  74. tmp_parser_data->line = strdupz(line);
  75. tmp_parser_data->next = parser->data;
  76. parser->data = tmp_parser_data;
  77. return 0;
  78. }
  79. /*
  80. * Add a keyword and the corresponding function that will be called
  81. * Multiple functions may be added
  82. * Input : keyword
  83. * : callback function
  84. * : flags
  85. * Output: > 0 registered function number
  86. * : 0 Error
  87. */
  88. int parser_add_keyword(PARSER *parser, char *keyword, keyword_function func)
  89. {
  90. PARSER_KEYWORD *tmp_keyword;
  91. if (strcmp(keyword, "_read") == 0) {
  92. parser->read_function = (void *) func;
  93. return 0;
  94. }
  95. if (strcmp(keyword, "_eof") == 0) {
  96. parser->eof_function = (void *) func;
  97. return 0;
  98. }
  99. if (strcmp(keyword, "_unknown") == 0) {
  100. parser->unknown_function = (void *) func;
  101. return 0;
  102. }
  103. uint32_t keyword_hash = simple_hash(keyword);
  104. tmp_keyword = parser->keyword;
  105. while (tmp_keyword) {
  106. if (tmp_keyword->keyword_hash == keyword_hash && (!strcmp(tmp_keyword->keyword, keyword))) {
  107. if (tmp_keyword->func_no == PARSER_MAX_CALLBACKS)
  108. return 0;
  109. tmp_keyword->func[tmp_keyword->func_no++] = (void *) func;
  110. return tmp_keyword->func_no;
  111. }
  112. tmp_keyword = tmp_keyword->next;
  113. }
  114. tmp_keyword = callocz(1, sizeof(*tmp_keyword));
  115. tmp_keyword->keyword = strdupz(keyword);
  116. tmp_keyword->keyword_hash = keyword_hash;
  117. tmp_keyword->func[tmp_keyword->func_no++] = (void *) func;
  118. tmp_keyword->next = parser->keyword;
  119. parser->keyword = tmp_keyword;
  120. return tmp_keyword->func_no;
  121. }
  122. /*
  123. * Cleanup a previously allocated parser
  124. */
  125. void parser_destroy(PARSER *parser)
  126. {
  127. if (unlikely(!parser))
  128. return;
  129. PARSER_KEYWORD *tmp_keyword, *tmp_keyword_next;
  130. PARSER_DATA *tmp_parser_data, *tmp_parser_data_next;
  131. // Remove keywords
  132. tmp_keyword = parser->keyword;
  133. while (tmp_keyword) {
  134. tmp_keyword_next = tmp_keyword->next;
  135. freez(tmp_keyword->keyword);
  136. freez(tmp_keyword);
  137. tmp_keyword = tmp_keyword_next;
  138. }
  139. // Remove pushed data if any
  140. tmp_parser_data = parser->data;
  141. while (tmp_parser_data) {
  142. tmp_parser_data_next = tmp_parser_data->next;
  143. freez(tmp_parser_data->line);
  144. freez(tmp_parser_data);
  145. tmp_parser_data = tmp_parser_data_next;
  146. }
  147. freez(parser->plugins_action);
  148. freez(parser);
  149. return;
  150. }
  151. /*
  152. * Fetch the next line to process
  153. *
  154. */
  155. int parser_next(PARSER *parser)
  156. {
  157. char *tmp = NULL;
  158. if (unlikely(!parser))
  159. return 1;
  160. parser->flags &= ~(PARSER_INPUT_PROCESSED);
  161. PARSER_DATA *tmp_parser_data = parser->data;
  162. if (unlikely(tmp_parser_data)) {
  163. strncpyz(parser->buffer, tmp_parser_data->line, PLUGINSD_LINE_MAX);
  164. parser->data = tmp_parser_data->next;
  165. freez(tmp_parser_data->line);
  166. freez(tmp_parser_data);
  167. return 0;
  168. }
  169. if (unlikely(parser->read_function))
  170. tmp = parser->read_function(parser->buffer, PLUGINSD_LINE_MAX, parser->input);
  171. else
  172. tmp = fgets(parser->buffer, PLUGINSD_LINE_MAX, (FILE *)parser->input);
  173. if (unlikely(!tmp)) {
  174. if (unlikely(parser->eof_function)) {
  175. int rc = parser->eof_function(parser->input);
  176. error("read failed: user defined function returned %d", rc);
  177. }
  178. else {
  179. if (feof((FILE *)parser->input))
  180. error("read failed: end of file");
  181. else if (ferror((FILE *)parser->input))
  182. error("read failed: input error");
  183. else
  184. error("read failed: unknown error");
  185. }
  186. return 1;
  187. }
  188. return 0;
  189. }
  190. /*
  191. * Takes an initialized parser object that has an unprocessed entry (by calling parser_next)
  192. * and if it contains a valid keyword, it will execute all the callbacks
  193. *
  194. */
  195. inline int parser_action(PARSER *parser, char *input)
  196. {
  197. PARSER_RC rc = PARSER_RC_OK;
  198. char *words[PLUGINSD_MAX_WORDS] = { NULL };
  199. char command[PLUGINSD_LINE_MAX];
  200. keyword_function action_function;
  201. keyword_function *action_function_list = NULL;
  202. if (unlikely(!parser))
  203. return 1;
  204. parser->recover_location[0] = 0x0;
  205. // if not direct input check if we have reprocessed this
  206. if (unlikely(!input && parser->flags & PARSER_INPUT_PROCESSED))
  207. return 0;
  208. PARSER_KEYWORD *tmp_keyword = parser->keyword;
  209. if (unlikely(!tmp_keyword)) {
  210. return 1;
  211. }
  212. if (unlikely(!input))
  213. input = parser->buffer;
  214. if (unlikely(!find_keyword(input, command, PLUGINSD_LINE_MAX, pluginsd_space)))
  215. return 0;
  216. if ((parser->flags & PARSER_INPUT_ORIGINAL) == PARSER_INPUT_ORIGINAL)
  217. pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, parser->recover_input, parser->recover_location, PARSER_MAX_RECOVER_KEYWORDS);
  218. else
  219. pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0);
  220. uint32_t command_hash = simple_hash(command);
  221. while(tmp_keyword) {
  222. if (command_hash == tmp_keyword->keyword_hash &&
  223. (!strcmp(command, tmp_keyword->keyword))) {
  224. action_function_list = &tmp_keyword->func[0];
  225. break;
  226. }
  227. tmp_keyword = tmp_keyword->next;
  228. }
  229. if (unlikely(!action_function_list)) {
  230. if (unlikely(parser->unknown_function))
  231. rc = parser->unknown_function(words, parser->user, NULL);
  232. else
  233. rc = PARSER_RC_ERROR;
  234. #ifdef NETDATA_INTERNAL_CHECKS
  235. error("Unknown keyword [%s]", input);
  236. #endif
  237. }
  238. else {
  239. while ((action_function = *action_function_list) != NULL) {
  240. rc = action_function(words, parser->user, parser->plugins_action);
  241. if (unlikely(rc == PARSER_RC_ERROR || rc == PARSER_RC_STOP))
  242. break;
  243. action_function_list++;
  244. }
  245. }
  246. if (likely(input == parser->buffer))
  247. parser->flags |= PARSER_INPUT_PROCESSED;
  248. return (rc == PARSER_RC_ERROR);
  249. }
  250. inline int parser_recover_input(PARSER *parser)
  251. {
  252. if (unlikely(!parser))
  253. return 1;
  254. for(int i=0; i < PARSER_MAX_RECOVER_KEYWORDS && parser->recover_location[i]; i++)
  255. *(parser->recover_location[i]) = parser->recover_input[i];
  256. parser->recover_location[0] = 0x0;
  257. return 0;
  258. }