@cryptotaxi247 / netdata-1 / commits / 570d5253f

Implement new incremental parser (#9074)

Implemented a new parser for the pluginsd language

Stelios Fragkakis committed May 25, 2020 at 20:25 UTC 570d5253f53b46ce59b3fd93a53b15f56f946038
12 files changed +1315 -498
Makefile.am
+9
@@ -105,6 +105,7 @@ SUBDIRS += \
105 streaming \
106 web \
107 claim \
108 + parser \
109 aclk \
110 spawn \
111 $(NULL)
@@ -325,6 +326,8 @@ MACOS_PLUGIN_FILES = \
326 PLUGINSD_PLUGIN_FILES = \
327 collectors/plugins.d/plugins_d.c \
328 collectors/plugins.d/plugins_d.h \
329 + collectors/plugins.d/pluginsd_parser.c \
330 + collectors/plugins.d/pluginsd_parser.h \
331 $(NULL)
332
333 RRD_PLUGIN_FILES = \
@@ -472,6 +475,11 @@ CLAIM_FILES = \
475 claim/claim.h \
476 $(NULL)
477
478 +PARSER_FILES = \
479 + parser/parser.c \
480 + parser/parser.h \
481 + $(NULL)
482 +
483 ACLK_FILES = \
484 aclk/aclk_common.c \
485 aclk/aclk_common.h \
@@ -597,6 +605,7 @@ NETDATA_FILES = \
605 $(STATSD_PLUGIN_FILES) \
606 $(WEB_PLUGIN_FILES) \
607 $(CLAIM_FILES) \
608 + $(PARSER_FILES) \
609 $(ACLK_FILES) \
610 $(SPAWN_PLUGIN_FILES) \
611 $(NULL)
collectors/plugins.d/plugins_d.c
+28 -495
@@ -1,22 +1,22 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "plugins_d.h"
4 +#include "pluginsd_parser.h"
5
6 char *plugin_directories[PLUGINSD_MAX_DIRECTORIES] = { NULL };
7 struct plugind *pluginsd_root = NULL;
8
8 -static inline int pluginsd_space(char c)
9 -{
10 - switch (c) {
11 - case ' ':
12 - case '\t':
13 - case '\r':
14 - case '\n':
15 - case '=':
16 - return 1;
9 +inline int pluginsd_space(char c) {
10 + switch(c) {
11 + case ' ':
12 + case '\t':
13 + case '\r':
14 + case '\n':
15 + case '=':
16 + return 1;
17
18 - default:
19 - return 0;
18 + default:
19 + return 0;
20 }
21 }
22
@@ -36,10 +36,11 @@ inline int config_isspace(char c)
36 }
37
38 // split a text into words, respecting quotes
39 -static inline int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char))
39 +static inline int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover)
40 {
41 char *s = str, quote = 0;
42 - int i = 0, j;
42 + int i = 0, j, rec = 0;
43 + char *recover = recover_input;
44
45 // skip all white space
46 while (unlikely(custom_isspace(*s)))
@@ -65,6 +66,10 @@ static inline int quoted_strings_splitter(char *str, char **words, int max_words
66 // if it is quote
67 else if (unlikely(*s == quote)) {
68 quote = 0;
69 + if (recover && rec < max_recover) {
70 + recover_location[rec++] = s;
71 + *recover++ = *s;
72 + }
73 *s = ' ';
74 continue;
75 }
@@ -72,6 +77,12 @@ static inline int quoted_strings_splitter(char *str, char **words, int max_words
77 // if it is a space
78 else if (unlikely(quote == 0 && custom_isspace(*s))) {
79 // terminate the word
80 + if (recover && rec < max_recover) {
81 + if (!rec || (rec && recover_location[rec-1] != s)) {
82 + recover_location[rec++] = s;
83 + *recover++ = *s;
84 + }
85 + }
86 *s++ = '\0';
87
88 // skip all white space
@@ -120,12 +131,12 @@ inline int pluginsd_initialize_plugin_directories()
131 }
132
133 // Parse it and store it to plugin directories
123 - return quoted_strings_splitter(plugins_dir_list, plugin_directories, PLUGINSD_MAX_DIRECTORIES, config_isspace);
134 + return quoted_strings_splitter(plugins_dir_list, plugin_directories, PLUGINSD_MAX_DIRECTORIES, config_isspace, NULL, NULL, 0);
135 }
136
126 -inline int pluginsd_split_words(char *str, char **words, int max_words)
137 +inline int pluginsd_split_words(char *str, char **words, int max_words, char *recover_input, char **recover_location, int max_recover)
138 {
128 - return quoted_strings_splitter(str, words, max_words, pluginsd_space);
139 + return quoted_strings_splitter(str, words, max_words, pluginsd_space, recover_input, recover_location, max_recover);
140 }
141
142 #ifdef ENABLE_HTTPS
@@ -226,482 +237,6 @@ char *pluginsd_get_from_buffer(char *output, int *bytesread, char *input, SSL *s
237 }
238 #endif
239
229 -inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations)
230 -{
231 - int enabled = cd->enabled;
232 -
233 - if (!fp || !enabled) {
234 - cd->enabled = 0;
235 - return 0;
236 - }
237 -
238 - size_t count = 0;
239 -
240 - char line[PLUGINSD_LINE_MAX + 1];
241 -
242 - char *words[PLUGINSD_MAX_WORDS] = { NULL };
243 - uint32_t BEGIN_HASH = simple_hash(PLUGINSD_KEYWORD_BEGIN);
244 - uint32_t END_HASH = simple_hash(PLUGINSD_KEYWORD_END);
245 - uint32_t FLUSH_HASH = simple_hash(PLUGINSD_KEYWORD_FLUSH);
246 - uint32_t CHART_HASH = simple_hash(PLUGINSD_KEYWORD_CHART);
247 - uint32_t DIMENSION_HASH = simple_hash(PLUGINSD_KEYWORD_DIMENSION);
248 - uint32_t DISABLE_HASH = simple_hash(PLUGINSD_KEYWORD_DISABLE);
249 - uint32_t VARIABLE_HASH = simple_hash(PLUGINSD_KEYWORD_VARIABLE);
250 - uint32_t LABEL_HASH = simple_hash(PLUGINSD_KEYWORD_LABEL);
251 - uint32_t OVERWRITE_HASH = simple_hash(PLUGINSD_KEYWORD_OVERWRITE);
252 -
253 - RRDSET *st = NULL;
254 - uint32_t hash;
255 - struct label *new_labels = NULL;
256 -
257 - errno = 0;
258 - clearerr(fp);
259 -
260 - if (unlikely(fileno(fp) == -1)) {
261 - error("file descriptor given is not a valid stream");
262 - goto cleanup;
263 - }
264 -
265 -#ifdef ENABLE_HTTPS
266 - int bytesleft = 0;
267 - char tmpbuffer[PLUGINSD_LINE_MAX];
268 - char *readfrom = NULL;
269 -#endif
270 - char *r = NULL;
271 - while (!ferror(fp)) {
272 - if (unlikely(netdata_exit))
273 - break;
274 -
275 -#ifdef ENABLE_HTTPS
276 - int normalread = 1;
277 - if (netdata_srv_ctx) {
278 - if (host->stream_ssl.conn && !host->stream_ssl.flags) {
279 - if (!bytesleft) {
280 - r = line;
281 - readfrom = tmpbuffer;
282 - bytesleft = pluginsd_update_buffer(readfrom, host->stream_ssl.conn);
283 - if (bytesleft <= 0) {
284 - break;
285 - }
286 - }
287 -
288 - readfrom = pluginsd_get_from_buffer(line, &bytesleft, readfrom, host->stream_ssl.conn, tmpbuffer);
289 - if (!readfrom) {
290 - r = NULL;
291 - }
292 -
293 - normalread = 0;
294 - }
295 - }
296 -
297 - if (normalread) {
298 - r = fgets(line, PLUGINSD_LINE_MAX, fp);
299 - }
300 -#else
301 - r = fgets(line, PLUGINSD_LINE_MAX, fp);
302 -#endif
303 -
304 - if (unlikely(!r)) {
305 - if (feof(fp))
306 - error("read failed: end of file");
307 - else if (ferror(fp))
308 - error("read failed: input error");
309 - else
310 - error("read failed: unknown error");
311 - break;
312 - }
313 -
314 - if (unlikely(netdata_exit))
315 - break;
316 -
317 - line[PLUGINSD_LINE_MAX] = '\0';
318 -
319 - int w = pluginsd_split_words(line, words, PLUGINSD_MAX_WORDS);
320 - char *s = words[0];
321 - if (unlikely(!s || !*s || !w)) {
322 - continue;
323 - }
324 -
325 - // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
326 -
327 - if (likely(!simple_hash_strcmp(s, "SET", &hash))) {
328 - char *dimension = words[1];
329 - char *value = words[2];
330 -
331 - if (unlikely(!dimension || !*dimension)) {
332 - error(
333 - "requested a SET on chart '%s' of host '%s', without a dimension. Disabling it.", st->id,
334 - host->hostname);
335 - enabled = 0;
336 - break;
337 - }
338 -
339 - if (unlikely(!value || !*value))
340 - value = NULL;
341 -
342 - if (unlikely(!st)) {
343 - error(
344 - "requested a SET on dimension %s with value %s on host '%s', without a BEGIN. Disabling it.",
345 - dimension, value ? value : "<nothing>", host->hostname);
346 - enabled = 0;
347 - break;
348 - }
349 -
350 - if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
351 - debug(D_PLUGINSD, "is setting dimension %s/%s to %s", st->id, dimension, value ? value : "<nothing>");
352 -
353 - if (value) {
354 - RRDDIM *rd = rrddim_find(st, dimension);
355 - if (unlikely(!rd)) {
356 - error(
357 - "requested a SET to dimension with id '%s' on stats '%s' (%s) on host '%s', which does not exist. Disabling it.",
358 - dimension, st->name, st->id, st->rrdhost->hostname);
359 - enabled = 0;
360 - break;
361 - } else
362 - rrddim_set_by_pointer(st, rd, strtoll(value, NULL, 0));
363 - }
364 - } else if (likely(hash == BEGIN_HASH && !strcmp(s, PLUGINSD_KEYWORD_BEGIN))) {
365 - char *id = words[1];
366 - char *microseconds_txt = words[2];
367 -
368 - if (unlikely(!id)) {
369 - error("requested a BEGIN without a chart id for host '%s'. Disabling it.", host->hostname);
370 - enabled = 0;
371 - break;
372 - }
373 -
374 - st = rrdset_find(host, id);
375 - if (unlikely(!st)) {
376 - error(
377 - "requested a BEGIN on chart '%s', which does not exist on host '%s'. Disabling it.", id,
378 - host->hostname);
379 - enabled = 0;
380 - break;
381 - }
382 -
383 - if (likely(st->counter_done)) {
384 - usec_t microseconds = 0;
385 - if (microseconds_txt && *microseconds_txt)
386 - microseconds = str2ull(microseconds_txt);
387 -
388 - if (likely(microseconds)) {
389 - if (trust_durations)
390 - rrdset_next_usec_unfiltered(st, microseconds);
391 - else
392 - rrdset_next_usec(st, microseconds);
393 - } else
394 - rrdset_next(st);
395 - }
396 - } else if (likely(hash == END_HASH && !strcmp(s, PLUGINSD_KEYWORD_END))) {
397 - if (unlikely(!st)) {
398 - error("requested an END, without a BEGIN on host '%s'. Disabling it.", host->hostname);
399 - enabled = 0;
400 - break;
401 - }
402 -
403 - if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
404 - debug(D_PLUGINSD, "requested an END on chart %s", st->id);
405 -
406 - rrdset_done(st);
407 - st = NULL;
408 -
409 - count++;
410 - } else if (likely(hash == CHART_HASH && !strcmp(s, PLUGINSD_KEYWORD_CHART))) {
411 - st = NULL;
412 -
413 - char *type = words[1];
414 - char *name = words[2];
415 - char *title = words[3];
416 - char *units = words[4];
417 - char *family = words[5];
418 - char *context = words[6];
419 - char *chart = words[7];
420 - char *priority_s = words[8];
421 - char *update_every_s = words[9];
422 - char *options = words[10];
423 - char *plugin = words[11];
424 - char *module = words[12];
425 -
426 - // parse the id from type
427 - char *id = NULL;
428 - if (likely(type && (id = strchr(type, '.')))) {
429 - *id = '\0';
430 - id++;
431 - }
432 -
433 - // make sure we have the required variables
434 - if (unlikely(!type || !*type || !id || !*id)) {
435 - error("requested a CHART, without a type.id, on host '%s'. Disabling it.", host->hostname);
436 - enabled = 0;
437 - break;
438 - }
439 -
440 - // parse the name, and make sure it does not include 'type.'
441 - if (unlikely(name && *name)) {
442 - // when data are coming from slaves
443 - // name will be type.name
444 - // so we have to remove 'type.' from name too
445 - size_t len = strlen(type);
446 - if (strncmp(type, name, len) == 0 && name[len] == '.')
447 - name = &name[len + 1];
448 -
449 - // if the name is the same with the id,
450 - // or is just 'NULL', clear it.
451 - if (unlikely(strcmp(name, id) == 0 || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0))
452 - name = NULL;
453 - }
454 -
455 - int priority = 1000;
456 - if (likely(priority_s && *priority_s))
457 - priority = str2i(priority_s);
458 -
459 - int update_every = cd->update_every;
460 - if (likely(update_every_s && *update_every_s))
461 - update_every = str2i(update_every_s);
462 - if (unlikely(!update_every))
463 - update_every = cd->update_every;
464 -
465 - RRDSET_TYPE chart_type = RRDSET_TYPE_LINE;
466 - if (unlikely(chart))
467 - chart_type = rrdset_type_id(chart);
468 -
469 - if (unlikely(name && !*name))
470 - name = NULL;
471 - if (unlikely(family && !*family))
472 - family = NULL;
473 - if (unlikely(context && !*context))
474 - context = NULL;
475 - if (unlikely(!title))
476 - title = "";
477 - if (unlikely(!units))
478 - units = "unknown";
479 -
480 - debug(
481 - D_PLUGINSD,
482 - "creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d",
483 - type, id, name ? name : "", family ? family : "", context ? context : "", rrdset_type_name(chart_type),
484 - priority, update_every);
485 -
486 - st = rrdset_create(
487 - host, type, id, name, family, context, title, units, (plugin && *plugin) ? plugin : cd->filename,
488 - module, priority, update_every, chart_type);
489 -
490 - if (options && *options) {
491 - if (strstr(options, "obsolete"))
492 - rrdset_is_obsolete(st);
493 - else
494 - rrdset_isnot_obsolete(st);
495 -
496 - if (strstr(options, "detail"))
497 - rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
498 - else
499 - rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
500 -
501 - if (strstr(options, "hidden"))
502 - rrdset_flag_set(st, RRDSET_FLAG_HIDDEN);
503 - else
504 - rrdset_flag_clear(st, RRDSET_FLAG_HIDDEN);
505 -
506 - if (strstr(options, "store_first"))
507 - rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST);
508 - else
509 - rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
510 - } else {
511 - rrdset_isnot_obsolete(st);
512 - rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
513 - rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
514 - }
515 - } else if (likely(hash == DIMENSION_HASH && !strcmp(s, PLUGINSD_KEYWORD_DIMENSION))) {
516 - char *id = words[1];
517 - char *name = words[2];
518 - char *algorithm = words[3];
519 - char *multiplier_s = words[4];
520 - char *divisor_s = words[5];
521 - char *options = words[6];
522 -
523 - if (unlikely(!id || !*id)) {
524 - error(
525 - "requested a DIMENSION, without an id, host '%s' and chart '%s'. Disabling it.", host->hostname,
526 - st ? st->id : "UNSET");
527 - enabled = 0;
528 - break;
529 - }
530 -
531 - if (unlikely(!st)) {
532 - error("requested a DIMENSION, without a CHART, on host '%s'. Disabling it.", host->hostname);
533 - enabled = 0;
534 - break;
535 - }
536 -
537 - long multiplier = 1;
538 - if (multiplier_s && *multiplier_s)
539 - multiplier = strtol(multiplier_s, NULL, 0);
540 - if (unlikely(!multiplier))
541 - multiplier = 1;
542 -
543 - long divisor = 1;
544 - if (likely(divisor_s && *divisor_s))
545 - divisor = strtol(divisor_s, NULL, 0);
546 - if (unlikely(!divisor))
547 - divisor = 1;
548 -
549 - if (unlikely(!algorithm || !*algorithm))
550 - algorithm = "absolute";
551 -
552 - if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
553 - debug(
554 - D_PLUGINSD,
555 - "creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'",
556 - st->id, id, name ? name : "", rrd_algorithm_name(rrd_algorithm_id(algorithm)), multiplier, divisor,
557 - options ? options : "");
558 -
559 - RRDDIM *rd = rrddim_add(st, id, name, multiplier, divisor, rrd_algorithm_id(algorithm));
560 - rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN);
561 - rrddim_flag_clear(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
562 - if (options && *options) {
563 - if (strstr(options, "obsolete") != NULL)
564 - rrddim_is_obsolete(st, rd);
565 - else
566 - rrddim_isnot_obsolete(st, rd);
567 - if (strstr(options, "hidden") != NULL)
568 - rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN);
569 - if (strstr(options, "noreset") != NULL)
570 - rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
571 - if (strstr(options, "nooverflow") != NULL)
572 - rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
573 - } else {
574 - rrddim_isnot_obsolete(st, rd);
575 - }
576 - } else if (likely(hash == VARIABLE_HASH && !strcmp(s, PLUGINSD_KEYWORD_VARIABLE))) {
577 - char *name = words[1];
578 - char *value = words[2];
579 - int global = (st) ? 0 : 1;
580 -
581 - if (name && *name) {
582 - if ((strcmp(name, "GLOBAL") == 0 || strcmp(name, "HOST") == 0)) {
583 - global = 1;
584 - name = words[2];
585 - value = words[3];
586 - } else if ((strcmp(name, "LOCAL") == 0 || strcmp(name, "CHART") == 0)) {
587 - global = 0;
588 - name = words[2];
589 - value = words[3];
590 - }
591 - }
592 -
593 - if (unlikely(!name || !*name)) {
594 - error("requested a VARIABLE on host '%s', without a variable name. Disabling it.", host->hostname);
595 - enabled = 0;
596 - break;
597 - }
598 -
599 - if (unlikely(!value || !*value))
600 - value = NULL;
601 -
602 - if (value) {
603 - char *endptr = NULL;
604 - calculated_number v = (calculated_number)str2ld(value, &endptr);
605 -
606 - if (unlikely(endptr && *endptr)) {
607 - if (endptr == value)
608 - error(
609 - "the value '%s' of VARIABLE '%s' on host '%s' cannot be parsed as a number", value, name,
610 - host->hostname);
611 - else
612 - error(
613 - "the value '%s' of VARIABLE '%s' on host '%s' has leftovers: '%s'", value, name,
614 - host->hostname, endptr);
615 - }
616 -
617 - if (global) {
618 - RRDVAR *rv = rrdvar_custom_host_variable_create(host, name);
619 - if (rv)
620 - rrdvar_custom_host_variable_set(host, rv, v);
621 - else
622 - error("cannot find/create HOST VARIABLE '%s' on host '%s'", name, host->hostname);
623 - } else if (st) {
624 - RRDSETVAR *rs = rrdsetvar_custom_chart_variable_create(st, name);
625 - if (rs)
626 - rrdsetvar_custom_chart_variable_set(rs, v);
627 - else
628 - error(
629 - "cannot find/create CHART VARIABLE '%s' on host '%s', chart '%s'", name, host->hostname,
630 - st->id);
631 - } else
632 - error("cannot find/create CHART VARIABLE '%s' on host '%s' without a chart", name, host->hostname);
633 - } else
634 - error(
635 - "cannot set %s VARIABLE '%s' on host '%s' to an empty value", (global) ? "HOST" : "CHART", name,
636 - host->hostname);
637 - } else if (likely(hash == FLUSH_HASH && !strcmp(s, PLUGINSD_KEYWORD_FLUSH))) {
638 - debug(D_PLUGINSD, "requested a FLUSH");
639 - st = NULL;
640 - } else if (unlikely(hash == DISABLE_HASH && !strcmp(s, PLUGINSD_KEYWORD_DISABLE))) {
641 - info("called DISABLE. Disabling it.");
642 - enabled = 0;
643 - break;
644 - } else if (likely(hash == LABEL_HASH && !strcmp(s, PLUGINSD_KEYWORD_LABEL))) {
645 - debug(D_PLUGINSD, "requested a LABEL CHANGE");
646 - char *store;
647 - if (!words[4])
648 - store = words[3];
649 - else {
650 - store = callocz(PLUGINSD_LINE_MAX + 1, sizeof(char));
651 - size_t remaining = PLUGINSD_LINE_MAX;
652 - char *move = store;
653 - int i = 3;
654 - while (i < w) {
655 - size_t length = strlen(words[i]);
656 - if ((length + 1) >= remaining)
657 - break;
658 -
659 - remaining -= (length + 1);
660 - memcpy(move, words[i], length);
661 - move += length;
662 - *move++ = ' ';
663 -
664 - i++;
665 - if (!words[i])
666 - break;
667 - }
668 - }
669 -
670 - new_labels = add_label_to_list(new_labels, words[1], store, strtol(words[2], NULL, 10));
671 - if (store != words[3])
672 - freez(store);
673 - } else if (likely(hash == OVERWRITE_HASH && !strcmp(s, PLUGINSD_KEYWORD_OVERWRITE))) {
674 - debug(D_PLUGINSD, "requested a OVERWITE a variable");
675 - if (!host->labels) {
676 - host->labels = new_labels;
677 - } else {
678 - rrdhost_rdlock(host);
679 - replace_label_list(host, new_labels);
680 - rrdhost_unlock(host);
681 - }
682 -
683 - new_labels = NULL;
684 - } else {
685 - error("sent command '%s' which is not known by netdata, for host '%s'. Disabling it.", s, host->hostname);
686 - enabled = 0;
687 - break;
688 - }
689 - }
690 -
691 -cleanup:
692 - cd->enabled = enabled;
693 -
694 - if (new_labels)
695 - free_host_labels(new_labels);
696 -
697 - if (likely(count)) {
698 - cd->successful_collections += count;
699 - cd->serial_failures = 0;
700 - } else
701 - cd->serial_failures++;
702 -
703 - return count;
704 -}
240
241 static void pluginsd_worker_thread_cleanup(void *arg)
242 {
@@ -809,9 +344,7 @@ void *pluginsd_worker_thread(void *arg)
344
345 info("connected to '%s' running on pid %d", cd->fullfilename, cd->pid);
346 count = pluginsd_process(localhost, cd, fp, 0);
812 - error(
813 - "'%s' (pid %d) disconnected after %zu successful data collections (ENDs).", cd->fullfilename, cd->pid,
814 - count);
347 + error("'%s' (pid %d) disconnected after %zu successful data collections (ENDs).", cd->fullfilename, cd->pid, count);
348 killpid(cd->pid);
349
350 int worker_ret_code = mypclose(fp, cd->pid);
collectors/plugins.d/plugins_d.h
+10 -2
@@ -31,6 +31,11 @@
31 #define PLUGINSD_KEYWORD_VARIABLE "VARIABLE"
32 #define PLUGINSD_KEYWORD_LABEL "LABEL"
33 #define PLUGINSD_KEYWORD_OVERWRITE "OVERWRITE"
34 +#define PLUGINSD_KEYWORD_CONTEXT "CONTEXT"
35 +#define PLUGINSD_KEYWORD_GUID "GUID"
36 +#define PLUGINSD_KEYWORD_HOST "HOST"
37 +#define PLUGINSD_KEYWORD_TOMBSTONE "TOMBSTONE"
38 +
39
40 #define PLUGINSD_LINE_MAX 1024
41 #define PLUGINSD_LINE_MAX_SSL_READ 512
@@ -60,7 +65,7 @@ struct plugind {
65 volatile sig_atomic_t enabled; // if this is enabled or not
66
67 time_t started_t;
63 -
68 + uint32_t version;
69 struct plugind *next;
70 };
71
@@ -69,10 +74,13 @@ extern struct plugind *pluginsd_root;
74 extern void *pluginsd_main(void *ptr);
75
76 extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations);
72 -extern int pluginsd_split_words(char *str, char **words, int max_words);
77 +extern int pluginsd_split_words(char *str, char **words, int max_words, char *recover_string, char **recover_location, int max_recover);
78
79 extern int pluginsd_initialize_plugin_directories();
80
81 extern int config_isspace(char c);
82 +extern int pluginsd_space(char c);
83 +extern int pluginsd_update_buffer(char *output, SSL *ssl);
84 +extern char * pluginsd_get_from_buffer(char *output, int *bytesread, char *input, SSL *ssl, char *src);
85
86 #endif /* NETDATA_PLUGINS_D_H */
collectors/plugins.d/pluginsd_parser.c new
+630
@@ -0,0 +1,630 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "pluginsd_parser.h"
4 +
5 +/*
6 + * This is the action defined for the FLUSH command
7 + */
8 +PARSER_RC pluginsd_set_action(void *user, RRDSET *st, RRDDIM *rd, long long int value)
9 +{
10 + UNUSED(user);
11 +
12 + rrddim_set_by_pointer(st, rd, value);
13 + return PARSER_RC_OK;
14 +}
15 +
16 +PARSER_RC pluginsd_flush_action(void *user, RRDSET *st)
17 +{
18 + UNUSED(user);
19 + UNUSED(st);
20 + return PARSER_RC_OK;
21 +}
22 +
23 +PARSER_RC pluginsd_begin_action(void *user, RRDSET *st, usec_t microseconds, int trust_durations)
24 +{
25 + UNUSED(user);
26 + if (likely(st->counter_done)) {
27 + if (likely(microseconds)) {
28 + if (trust_durations)
29 + rrdset_next_usec_unfiltered(st, microseconds);
30 + else
31 + rrdset_next_usec(st, microseconds);
32 + } else
33 + rrdset_next(st);
34 + }
35 + return PARSER_RC_OK;
36 +}
37 +
38 +
39 +PARSER_RC pluginsd_end_action(void *user, RRDSET *st)
40 +{
41 + UNUSED(user);
42 +
43 + rrdset_done(st);
44 + return PARSER_RC_OK;
45 +}
46 +
47 +PARSER_RC pluginsd_chart_action(void *user, char *type, char *id, char *name, char *family, char *context, char *title, char *units, char *plugin,
48 + char *module, int priority, int update_every, RRDSET_TYPE chart_type, char *options)
49 +{
50 + RRDSET *st = NULL;
51 + RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
52 +
53 + st = rrdset_create(
54 + host, type, id, name, family, context, title, units,
55 + plugin, module, priority, update_every,
56 + chart_type);
57 +
58 + if (options && *options) {
59 + if (strstr(options, "obsolete"))
60 + rrdset_is_obsolete(st);
61 + else
62 + rrdset_isnot_obsolete(st);
63 +
64 + if (strstr(options, "detail"))
65 + rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
66 + else
67 + rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
68 +
69 + if (strstr(options, "hidden"))
70 + rrdset_flag_set(st, RRDSET_FLAG_HIDDEN);
71 + else
72 + rrdset_flag_clear(st, RRDSET_FLAG_HIDDEN);
73 +
74 + if (strstr(options, "store_first"))
75 + rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST);
76 + else
77 + rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
78 + } else {
79 + rrdset_isnot_obsolete(st);
80 + rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
81 + rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
82 + }
83 + ((PARSER_USER_OBJECT *)user)->st = st;
84 +
85 + return PARSER_RC_OK;
86 +}
87 +
88 +
89 +PARSER_RC pluginsd_disable_action(void *user)
90 +{
91 + UNUSED(user);
92 +
93 + info("called DISABLE. Disabling it.");
94 + ((PARSER_USER_OBJECT *) user)->enabled = 0;
95 + return PARSER_RC_ERROR;
96 +}
97 +
98 +
99 +PARSER_RC pluginsd_variable_action(void *user, RRDHOST *host, RRDSET *st, char *name, int global, calculated_number value)
100 +{
101 + UNUSED(user);
102 +
103 + if (global) {
104 + RRDVAR *rv = rrdvar_custom_host_variable_create(host, name);
105 + if (rv)
106 + rrdvar_custom_host_variable_set(host, rv, value);
107 + else
108 + error("cannot find/create HOST VARIABLE '%s' on host '%s'", name, host->hostname);
109 + } else {
110 + RRDSETVAR *rs = rrdsetvar_custom_chart_variable_create(st, name);
111 + if (rs)
112 + rrdsetvar_custom_chart_variable_set(rs, value);
113 + else
114 + error("cannot find/create CHART VARIABLE '%s' on host '%s', chart '%s'", name, host->hostname, st->id);
115 + }
116 + return PARSER_RC_OK;
117 +}
118 +
119 +
120 +
121 +PARSER_RC pluginsd_dimension_action(void *user, RRDSET *st, char *id, char *name, char *algorithm, long multiplier, long divisor, char *options,
122 + RRD_ALGORITHM algorithm_type)
123 +{
124 + UNUSED(user);
125 + UNUSED(algorithm);
126 +
127 + RRDDIM *rd = rrddim_add(st, id, name, multiplier, divisor, algorithm_type);
128 + rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN);
129 + rrddim_flag_clear(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
130 + if (options && *options) {
131 + if (strstr(options, "obsolete") != NULL)
132 + rrddim_is_obsolete(st, rd);
133 + else
134 + rrddim_isnot_obsolete(st, rd);
135 + if (strstr(options, "hidden") != NULL)
136 + rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN);
137 + if (strstr(options, "noreset") != NULL)
138 + rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
139 + if (strstr(options, "nooverflow") != NULL)
140 + rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
141 + } else {
142 + rrddim_isnot_obsolete(st, rd);
143 + }
144 + return PARSER_RC_OK;
145 +}
146 +
147 +PARSER_RC pluginsd_label_action(void *user, char *key, char *value, LABEL_SOURCE source)
148 +{
149 +
150 + ((PARSER_USER_OBJECT *) user)->new_labels = add_label_to_list(((PARSER_USER_OBJECT *) user)->new_labels, key, value, source);
151 +
152 + return PARSER_RC_OK;
153 +}
154 +
155 +
156 +PARSER_RC pluginsd_overwrite_action(void *user, RRDHOST *host, struct label *new_labels)
157 +{
158 + UNUSED(user);
159 +
160 + if (!host->labels) {
161 + host->labels = new_labels;
162 + } else {
163 + rrdhost_rdlock(host);
164 + replace_label_list(host, new_labels);
165 + rrdhost_unlock(host);
166 + }
167 + return PARSER_RC_OK;
168 +}
169 +
170 +PARSER_RC pluginsd_set(char **words, void *user, PLUGINSD_ACTION *plugins_action)
171 +{
172 + char *dimension = words[1];
173 + char *value = words[2];
174 +
175 + RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
176 + RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
177 +
178 + if (unlikely(!dimension || !*dimension)) {
179 + error("requested a SET on chart '%s' of host '%s', without a dimension. Disabling it.", st->id, host->hostname);
180 + goto disable;
181 + }
182 +
183 + if (unlikely(!value || !*value))
184 + value = NULL;
185 +
186 + if (unlikely(!st)) {
187 + error(
188 + "requested a SET on dimension %s with value %s on host '%s', without a BEGIN. Disabling it.", dimension,
189 + value ? value : "<nothing>", host->hostname);
190 + goto disable;
191 + }
192 +
193 + if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
194 + debug(D_PLUGINSD, "is setting dimension %s/%s to %s", st->id, dimension, value ? value : "<nothing>");
195 +
196 + if (value) {
197 + RRDDIM *rd = rrddim_find(st, dimension);
198 + if (unlikely(!rd)) {
199 + error(
200 + "requested a SET to dimension with id '%s' on stats '%s' (%s) on host '%s', which does not exist. Disabling it.",
201 + dimension, st->name, st->id, st->rrdhost->hostname);
202 + goto disable;
203 + } else {
204 + if (plugins_action->set_action) {
205 + return plugins_action->set_action(
206 + user, st, rd, strtoll(value, NULL, 0));
207 + }
208 + }
209 + }
210 + return PARSER_RC_OK;
211 +
212 +disable:
213 + ((PARSER_USER_OBJECT *) user)->enabled = 0;
214 + return PARSER_RC_ERROR;
215 +}
216 +
217 +PARSER_RC pluginsd_begin(char **words, void *user, PLUGINSD_ACTION *plugins_action)
218 +{
219 + char *id = words[1];
220 + char *microseconds_txt = words[2];
221 +
222 + RRDSET *st = NULL;
223 + RRDHOST *host = ((PARSER_USER_OBJECT *)user)->host;
224 +
225 + if (unlikely(!id)) {
226 + error("requested a BEGIN without a chart id for host '%s'. Disabling it.", host->hostname);
227 + goto disable;
228 + }
229 +
230 + st = rrdset_find(host, id);
231 + if (unlikely(!st)) {
232 + error("requested a BEGIN on chart '%s', which does not exist on host '%s'. Disabling it.", id, host->hostname);
233 + goto disable;
234 + }
235 + ((PARSER_USER_OBJECT *)user)->st = st;
236 +
237 + usec_t microseconds = 0;
238 + if (microseconds_txt && *microseconds_txt)
239 + microseconds = str2ull(microseconds_txt);
240 +
241 + if (plugins_action->begin_action) {
242 + return plugins_action->begin_action(user, st, microseconds, ((PARSER_USER_OBJECT *)user)->trust_durations);
243 + }
244 + return PARSER_RC_OK;
245 +disable:
246 + ((PARSER_USER_OBJECT *)user)->enabled = 0;
247 + return PARSER_RC_ERROR;
248 +}
249 +
250 +PARSER_RC pluginsd_end(char **words, void *user, PLUGINSD_ACTION *plugins_action)
251 +{
252 + UNUSED(words);
253 + RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
254 + RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
255 +
256 + if (unlikely(!st)) {
257 + error("requested an END, without a BEGIN on host '%s'. Disabling it.", host->hostname);
258 + ((PARSER_USER_OBJECT *) user)->enabled = 0;
259 + return PARSER_RC_ERROR;
260 + }
261 +
262 + if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
263 + debug(D_PLUGINSD, "requested an END on chart %s", st->id);
264 +
265 + ((PARSER_USER_OBJECT *) user)->st = NULL;
266 + ((PARSER_USER_OBJECT *) user)->count++;
267 + if (plugins_action->end_action) {
268 + return plugins_action->end_action(user, st);
269 + }
270 + return PARSER_RC_OK;
271 +}
272 +
273 +PARSER_RC pluginsd_chart(char **words, void *user, PLUGINSD_ACTION *plugins_action)
274 +{
275 + RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
276 +
277 + char *type = words[1];
278 + char *name = words[2];
279 + char *title = words[3];
280 + char *units = words[4];
281 + char *family = words[5];
282 + char *context = words[6];
283 + char *chart = words[7];
284 + char *priority_s = words[8];
285 + char *update_every_s = words[9];
286 + char *options = words[10];
287 + char *plugin = words[11];
288 + char *module = words[12];
289 +
290 + int have_action = ((plugins_action->chart_action) != NULL);
291 +
292 + // parse the id from type
293 + char *id = NULL;
294 + if (likely(type && (id = strchr(type, '.')))) {
295 + *id = '\0';
296 + id++;
297 + }
298 +
299 + // make sure we have the required variables
300 + if (unlikely((!type || !*type || !id || !*id) && !have_action)) {
301 + error("requested a CHART, without a type.id, on host '%s'. Disabling it.", host->hostname);
302 + ((PARSER_USER_OBJECT *) user)->enabled = 0;
303 + return PARSER_RC_ERROR;
304 + }
305 +
306 + // parse the name, and make sure it does not include 'type.'
307 + if (unlikely(name && *name)) {
308 + // when data are coming from slaves
309 + // name will be type.name
310 + // so we have to remove 'type.' from name too
311 + size_t len = strlen(type);
312 + if (strncmp(type, name, len) == 0 && name[len] == '.')
313 + name = &name[len + 1];
314 +
315 + // if the name is the same with the id,
316 + // or is just 'NULL', clear it.
317 + if (unlikely(strcmp(name, id) == 0 || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0))
318 + name = NULL;
319 + }
320 +
321 + int priority = 1000;
322 + if (likely(priority_s && *priority_s))
323 + priority = str2i(priority_s);
324 +
325 + int update_every = ((PARSER_USER_OBJECT *) user)->cd->update_every;
326 + if (likely(update_every_s && *update_every_s))
327 + update_every = str2i(update_every_s);
328 + if (unlikely(!update_every))
329 + update_every = ((PARSER_USER_OBJECT *) user)->cd->update_every;
330 +
331 + RRDSET_TYPE chart_type = RRDSET_TYPE_LINE;
332 + if (unlikely(chart))
333 + chart_type = rrdset_type_id(chart);
334 +
335 + if (unlikely(name && !*name))
336 + name = NULL;
337 + if (unlikely(family && !*family))
338 + family = NULL;
339 + if (unlikely(context && !*context))
340 + context = NULL;
341 + if (unlikely(!title))
342 + title = "";
343 + if (unlikely(!units))
344 + units = "unknown";
345 +
346 + debug(
347 + D_PLUGINSD,
348 + "creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d",
349 + type, id, name ? name : "", family ? family : "", context ? context : "", rrdset_type_name(chart_type),
350 + priority, update_every);
351 +
352 + if (have_action) {
353 + return plugins_action->chart_action(
354 + user, type, id, name, family, context, title, units,
355 + (plugin && *plugin) ? plugin : ((PARSER_USER_OBJECT *)user)->cd->filename, module, priority, update_every,
356 + chart_type, options);
357 + }
358 +
359 + return PARSER_RC_OK;
360 +}
361 +
362 +PARSER_RC pluginsd_dimension(char **words, void *user, PLUGINSD_ACTION *plugins_action)
363 +{
364 + char *id = words[1];
365 + char *name = words[2];
366 + char *algorithm = words[3];
367 + char *multiplier_s = words[4];
368 + char *divisor_s = words[5];
369 + char *options = words[6];
370 +
371 + RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
372 + RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
373 +
374 + if (unlikely(!id)) {
375 + error(
376 + "requested a DIMENSION, without an id, host '%s' and chart '%s'. Disabling it.", host->hostname,
377 + st ? st->id : "UNSET");
378 + goto disable;
379 + }
380 +
381 + if (unlikely(!st)) {
382 + error("requested a DIMENSION, without a CHART, on host '%s'. Disabling it.", host->hostname);
383 + goto disable;
384 + }
385 +
386 + long multiplier = 1;
387 + if (multiplier_s && *multiplier_s) {
388 + multiplier = strtol(multiplier_s, NULL, 0);
389 + if (unlikely(!multiplier))
390 + multiplier = 1;
391 + }
392 +
393 + long divisor = 1;
394 + if (likely(divisor_s && *divisor_s)) {
395 + divisor = strtol(divisor_s, NULL, 0);
396 + if (unlikely(!divisor))
397 + divisor = 1;
398 + }
399 +
400 + if (unlikely(!algorithm || !*algorithm))
401 + algorithm = "absolute";
402 +
403 + if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
404 + debug(
405 + D_PLUGINSD,
406 + "creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'",
407 + st->id, id, name ? name : "", rrd_algorithm_name(rrd_algorithm_id(algorithm)), multiplier, divisor,
408 + options ? options : "");
409 +
410 + if (plugins_action->dimension_action) {
411 + return plugins_action->dimension_action(
412 + user, st, id, name, algorithm,
413 + multiplier, divisor, (options && *options)?options:NULL, rrd_algorithm_id(algorithm));
414 + }
415 +
416 + return PARSER_RC_OK;
417 +disable:
418 + ((PARSER_USER_OBJECT *)user)->enabled = 0;
419 + return PARSER_RC_ERROR;
420 +}
421 +
422 +PARSER_RC pluginsd_variable(char **words, void *user, PLUGINSD_ACTION *plugins_action)
423 +{
424 + char *name = words[1];
425 + char *value = words[2];
426 + calculated_number v;
427 +
428 + RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
429 + RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
430 +
431 + int global = (st) ? 0 : 1;
432 +
433 + if (name && *name) {
434 + if ((strcmp(name, "GLOBAL") == 0 || strcmp(name, "HOST") == 0)) {
435 + global = 1;
436 + name = words[2];
437 + value = words[3];
438 + } else if ((strcmp(name, "LOCAL") == 0 || strcmp(name, "CHART") == 0)) {
439 + global = 0;
440 + name = words[2];
441 + value = words[3];
442 + }
443 + }
444 +
445 + if (unlikely(!name || !*name)) {
446 + error("requested a VARIABLE on host '%s', without a variable name. Disabling it.", host->hostname);
447 + ((PARSER_USER_OBJECT *)user)->enabled = 0;
448 + return PARSER_RC_ERROR;
449 + }
450 +
451 + if (unlikely(!value || !*value))
452 + value = NULL;
453 +
454 + if (unlikely(!value)) {
455 + error("cannot set %s VARIABLE '%s' on host '%s' to an empty value", (global) ? "HOST" : "CHART", name,
456 + host->hostname);
457 + return PARSER_RC_OK;
458 + }
459 +
460 + if (!global && !st) {
461 + error("cannot find/create CHART VARIABLE '%s' on host '%s' without a chart", name, host->hostname);
462 + return PARSER_RC_OK;
463 + }
464 +
465 + char *endptr = NULL;
466 + v = (calculated_number)str2ld(value, &endptr);
467 + if (unlikely(endptr && *endptr)) {
468 + if (endptr == value)
469 + error(
470 + "the value '%s' of VARIABLE '%s' on host '%s' cannot be parsed as a number", value, name,
471 + host->hostname);
472 + else
473 + error(
474 + "the value '%s' of VARIABLE '%s' on host '%s' has leftovers: '%s'", value, name, host->hostname,
475 + endptr);
476 + }
477 +
478 + if (plugins_action->variable_action) {
479 + return plugins_action->variable_action(user, host, st, name, global, v);
480 + }
481 +
482 + return PARSER_RC_OK;
483 +}
484 +
485 +PARSER_RC pluginsd_flush(char **words, void *user, PLUGINSD_ACTION *plugins_action)
486 +{
487 + UNUSED(words);
488 + debug(D_PLUGINSD, "requested a FLUSH");
489 + RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
490 + ((PARSER_USER_OBJECT *) user)->st = NULL;
491 + if (plugins_action->flush_action) {
492 + return plugins_action->flush_action(user, st);
493 + }
494 + return PARSER_RC_OK;
495 +}
496 +
497 +PARSER_RC pluginsd_disable(char **words, void *user, PLUGINSD_ACTION *plugins_action)
498 +{
499 + UNUSED(user);
500 + UNUSED(words);
501 +
502 + if (plugins_action->disable_action) {
503 + return plugins_action->disable_action(user);
504 + }
505 + return PARSER_RC_ERROR;
506 +}
507 +
508 +PARSER_RC pluginsd_label(char **words, void *user, PLUGINSD_ACTION *plugins_action)
509 +{
510 + char *store;
511 + if (!words[4])
512 + store = words[3];
513 + else {
514 + store = callocz(PLUGINSD_LINE_MAX + 1, sizeof(char));
515 + size_t remaining = PLUGINSD_LINE_MAX;
516 + char *move = store;
517 + int i = 3;
518 + while (i < PLUGINSD_MAX_WORDS) {
519 + size_t length = strlen(words[i]);
520 + if ((length + 1) >= remaining)
521 + break;
522 +
523 + remaining -= (length + 1);
524 + memcpy(move, words[i], length);
525 + move += length;
526 + *move++ = ' ';
527 +
528 + i++;
529 + if (!words[i])
530 + break;
531 + }
532 + }
533 +
534 + if (plugins_action->label_action) {
535 + PARSER_RC rc = plugins_action->label_action(user, words[1], store, strtol(words[2], NULL, 10));
536 + if (store != words[3])
537 + freez(store);
538 + return rc;
539 + }
540 +
541 + if (store != words[3])
542 + freez(store);
543 + return PARSER_RC_OK;
544 +}
545 +
546 +PARSER_RC pluginsd_overwrite(char **words, void *user, PLUGINSD_ACTION *plugins_action)
547 +{
548 + UNUSED(words);
549 +
550 + RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
551 + debug(D_PLUGINSD, "requested a OVERWITE a variable");
552 +
553 + struct label *new_labels = ((PARSER_USER_OBJECT *)user)->new_labels;
554 + ((PARSER_USER_OBJECT *)user)->new_labels = NULL;
555 +
556 + if (plugins_action->overwrite_action) {
557 + return plugins_action->overwrite_action(user, host, new_labels);
558 + }
559 +
560 + return PARSER_RC_OK;
561 +}
562 +
563 +
564 +// New plugins.d parser
565 +
566 +inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations)
567 +{
568 + int enabled = cd->enabled;
569 +
570 + if (!fp || !enabled) {
571 + cd->enabled = 0;
572 + return 0;
573 + }
574 +
575 + if (unlikely(fileno(fp) == -1)) {
576 + error("file descriptor given is not a valid stream");
577 + cd->serial_failures++;
578 + return 0;
579 + }
580 + clearerr(fp);
581 +
582 + PARSER_USER_OBJECT *user = callocz(1, sizeof(*user));
583 + ((PARSER_USER_OBJECT *) user)->enabled = cd->enabled;
584 + ((PARSER_USER_OBJECT *) user)->host = host;
585 + ((PARSER_USER_OBJECT *) user)->cd = cd;
586 + ((PARSER_USER_OBJECT *) user)->trust_durations = trust_durations;
587 +
588 + PARSER *parser = parser_init(host, user, fp, PARSER_INPUT_SPLIT);
589 +
590 + if (unlikely(!parser)) {
591 + error("Failed to initialize parser");
592 + cd->serial_failures++;
593 + return 0;
594 + }
595 +
596 + parser->plugins_action->begin_action = &pluginsd_begin_action;
597 + parser->plugins_action->flush_action = &pluginsd_flush_action;
598 + parser->plugins_action->end_action = &pluginsd_end_action;
599 + parser->plugins_action->disable_action = &pluginsd_disable_action;
600 + parser->plugins_action->variable_action = &pluginsd_variable_action;
601 + parser->plugins_action->dimension_action = &pluginsd_dimension_action;
602 + parser->plugins_action->label_action = &pluginsd_label_action;
603 + parser->plugins_action->overwrite_action = &pluginsd_overwrite_action;
604 + parser->plugins_action->chart_action = &pluginsd_chart_action;
605 + parser->plugins_action->set_action = &pluginsd_set_action;
606 +
607 + user->parser = parser;
608 +
609 + while (likely(!parser_next(parser))) {
610 + if (unlikely(netdata_exit || parser_action(parser, NULL)))
611 + break;
612 + }
613 + info("PARSER ended");
614 +
615 + parser_destroy(parser);
616 +
617 + cd->enabled = ((PARSER_USER_OBJECT *) user)->enabled;
618 + size_t count = ((PARSER_USER_OBJECT *) user)->count;
619 +
620 + freez(user);
621 +
622 + if (likely(count)) {
623 + cd->successful_collections += count;
624 + cd->serial_failures = 0;
625 + } else
626 + cd->serial_failures++;
627 +
628 + return count;
629 +}
630 +
collectors/plugins.d/pluginsd_parser.h new
+20
@@ -0,0 +1,20 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_PLUGINSD_PARSER_H
4 +#define NETDATA_PLUGINSD_PARSER_H
5 +
6 +#include "../../parser/parser.h"
7 +
8 +
9 +typedef struct parser_user_object {
10 + PARSER *parser;
11 + RRDSET *st;
12 + RRDHOST *host;
13 + struct plugind *cd;
14 + int trust_durations;
15 + struct label *new_labels;
16 + size_t count;
17 + int enabled;
18 +} PARSER_USER_OBJECT;
19 +
20 +#endif //NETDATA_PLUGINSD_PARSER_H
collectors/statsd.plugin/statsd.c
+1 -1
@@ -1324,7 +1324,7 @@ static int statsd_readfile(const char *filename, STATSD_APP *app, STATSD_APP_CHA
1324 else if (!strcmp(name, "dimension")) {
1325 // metric [name [type [multiplier [divisor]]]]
1326 char *words[10];
1327 - pluginsd_split_words(value, words, 10);
1327 + pluginsd_split_words(value, words, 10, NULL, NULL, 0);
1328
1329 int pattern = 0;
1330 size_t i = 0;
configure.ac
+1
@@ -1491,6 +1491,7 @@ AC_CONFIG_FILES([
1491 claim/Makefile
1492 aclk/Makefile
1493 spawn/Makefile
1494 + parser/Makefile
1495 ])
1496 AC_OUTPUT
1497
parser/Makefile.am new
+9
@@ -0,0 +1,9 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
9 +
parser/README.md new
+147
@@ -0,0 +1,147 @@
1 +#### Introduction
2 +
3 +The parser will be used to process streaming and plugins input as well as metadata
4 +
5 +Usage
6 +
7 +1. Define a structure that will be used to share user state across calls
8 +1. Initialize the parser using `parser_init`
9 +2. Register keywords and assosiated callback function using `parser_add_keyword`
10 +3. Register actions on the keywords
11 +4. Start a loop until EOF
12 + 1. Fetch the next line using `parser_next`
13 + 2. Process the line using `parser_action`
14 + 1. The registered callbacks are executed to parse the input
15 + 2. The registered action for the callback is called for processing
16 +4. Release the parser using `parser_destroy`
17 +5. Release the user structure
18 +
19 +#### Functions
20 +
21 +----
22 +##### parse_init(RRDHOST *host, void *user, void *input, int flags)
23 +
24 +Initialize an internal parser with the specified user defined data structure that will be shared across calls.
25 +
26 +Input
27 +- Host
28 + - The host this parser will be dealing with. For streaming with SSL enabled for this host
29 +- user
30 + - User defined structure that is passed in all the calls
31 +- input
32 + - Where the parser will get the input from
33 +- flags
34 + - flags to define processing on the input
35 +
36 +Output
37 +- A parser structure
38 +
39 +
40 +
41 +----
42 +##### parse_push(PARSER *parser, char *line)
43 +
44 +Push a new line for processing
45 +
46 +Input
47 +
48 +- parser
49 + - The parser object as returned by the `parser_init`
50 +- line
51 + - The new line to process
52 +
53 +
54 +Output
55 +- The line will be injected into the stream and will be the next one to be processed
56 +
57 +Returns
58 +- 0 line added
59 +- 1 error detected
60 +
61 +----
62 +##### parse_add_keyword(PARSER *parser, char *keyword, keyword_function callback_function)
63 +
64 +The function will add callbacks for keywords. The callback function is defined as
65 +
66 +`typedef PARSER_RC (*keyword_function)(char **, void *);`
67 +
68 +Input
69 +
70 +- parser
71 + - The parser object as returned by the `parser_init`
72 +- keyword
73 + - The keyword to register
74 +- keyword_function
75 + - The callback that will handle the keyword processing
76 + * The callback function should return one of the following
77 + * PARSER_RC_OK -- Callback was successful (continue with other callbacks)
78 + * PARSER_RC_STOP -- Stop processing callbacks (return OK)
79 + * PARSER_RC_ERROR -- Callback failed, exit
80 +
81 +Output
82 +- The correspoding keyword and callback will be registered
83 +
84 +Returns
85 +- 0 maximum callbacks already registered for this keyword
86 +- > 0 which is the number of callbacks assosiated with this keyword.
87 +
88 +
89 +----
90 +##### parser_next(PARSER *parser)
91 +Return the next item to parse
92 +
93 +Input
94 +- parser
95 + - The parser object as returned by the `parser_init`
96 +
97 +Output
98 +- The parser will store internally the next item to parse
99 +
100 +Returns
101 +- 0 Next item fetched successfully
102 +- 1 No more items to parse
103 +
104 +----
105 +##### parser_action(PARSER *parser, char *input)
106 +Return the next item to parse
107 +
108 +Input
109 +- parser
110 + - The parser object as returned by the `parser_init`
111 +- input
112 + - Process the input specified instead of using the internal buffer
113 +
114 +Output
115 +- The current keyword will be processed by calling all the registered callbacks
116 +
117 +Returns
118 +- 0 Callbacks called successfully
119 +- 1 Failed
120 +
121 +----
122 +##### parser_destroy(PARSER *parser)
123 +Cleanup a previously allocated parser
124 +
125 +Input
126 +- parser
127 + - The parser object as returned by the `parser_init`
128 +
129 +Output
130 +- The parser is deallocated
131 +
132 +Returns
133 +- none
134 +
135 +----
136 +##### parser_recover_input(PARSER *parser)
137 +Cleanup a previously allocated parser
138 +
139 +Input
140 +- parser
141 + - The parser object as returned by the `parser_init`
142 +
143 +Output
144 +- The parser is deallocated
145 +
146 +Returns
147 +- none
\ No newline at end of file
parser/parser.c new
+352
@@ -0,0 +1,352 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "parser.h"
4 +
5 +static inline int find_keyword(char *str, char *keyword, int max_size, int (*custom_isspace)(char))
6 +{
7 + char *s = str, *keyword_start;
8 +
9 + while (unlikely(custom_isspace(*s))) s++;
10 + keyword_start = s;
11 +
12 + while (likely(*s && !custom_isspace(*s)) && max_size > 0) {
13 + *keyword++ = *s++;
14 + max_size--;
15 + }
16 + *keyword = '\0';
17 + return max_size == 0 ? 0 : (int) (s - keyword_start);
18 +}
19 +
20 +/*
21 + * Initialize a parser
22 + * user : as defined by the user, will be shared across calls
23 + * input : main input stream (auto detect stream -- file, socket, pipe)
24 + * buffer : This is the buffer to be used (if null a buffer of size will be allocated)
25 + * size : buffer size either passed or will be allocated
26 + * If the buffer is auto allocated, it will auto freed when the parser is destroyed
27 + *
28 + *
29 + */
30 +
31 +PARSER *parser_init(RRDHOST *host, void *user, void *input, PARSER_INPUT_TYPE flags)
32 +{
33 + PARSER *parser;
34 +
35 + parser = callocz(1, sizeof(*parser));
36 +
37 + if (unlikely(!parser))
38 + return NULL;
39 +
40 + parser->plugins_action = callocz(1, sizeof(PLUGINSD_ACTION));
41 + if (unlikely(!parser->plugins_action)) {
42 + freez(parser);
43 + return NULL;
44 + }
45 +
46 + parser->user = user;
47 + parser->input = input;
48 + parser->flags = flags;
49 + parser->host = host;
50 +#ifdef ENABLE_HTTPS
51 + parser->bytesleft = 0;
52 + parser->readfrom = NULL;
53 +#endif
54 +
55 + if (unlikely(!(flags & PARSER_NO_PARSE_INIT))) {
56 + int rc = parser_add_keyword(parser, PLUGINSD_KEYWORD_FLUSH, pluginsd_flush);
57 + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_CHART, pluginsd_chart);
58 + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_DIMENSION, pluginsd_dimension);
59 + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_DISABLE, pluginsd_disable);
60 + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_VARIABLE, pluginsd_variable);
61 + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_LABEL, pluginsd_label);
62 + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_OVERWRITE, pluginsd_overwrite);
63 + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_END, pluginsd_end);
64 + rc += parser_add_keyword(parser, PLUGINSD_KEYWORD_BEGIN, pluginsd_begin);
65 + rc += parser_add_keyword(parser, "SET", pluginsd_set);
66 + }
67 +
68 + return parser;
69 +}
70 +
71 +
72 +/*
73 + * Push a new line into the parsing stream
74 + *
75 + * This line will be the next one to process ie the next fetch will get this one
76 + *
77 + */
78 +
79 +int parser_push(PARSER *parser, char *line)
80 +{
81 + PARSER_DATA *tmp_parser_data;
82 +
83 + if (unlikely(!parser))
84 + return 1;
85 +
86 + if (unlikely(!line))
87 + return 0;
88 +
89 + tmp_parser_data = callocz(1, sizeof(*tmp_parser_data));
90 + tmp_parser_data->line = strdupz(line);
91 + tmp_parser_data->next = parser->data;
92 + parser->data = tmp_parser_data;
93 +
94 + return 0;
95 +}
96 +
97 +/*
98 + * Add a keyword and the corresponding function that will be called
99 + * Multiple functions may be added
100 + * Input : keyword
101 + * : callback function
102 + * : flags
103 + * Output: > 0 registered function number
104 + * : 0 Error
105 + */
106 +
107 +int parser_add_keyword(PARSER *parser, char *keyword, keyword_function func)
108 +{
109 + PARSER_KEYWORD *tmp_keyword;
110 +
111 + if (strcmp(keyword, "_read") == 0) {
112 + parser->read_function = (void *) func;
113 + return 0;
114 + }
115 +
116 + if (strcmp(keyword, "_eof") == 0) {
117 + parser->eof_function = (void *) func;
118 + return 0;
119 + }
120 +
121 + if (strcmp(keyword, "_unknown") == 0) {
122 + parser->unknown_function = (void *) func;
123 + return 0;
124 + }
125 +
126 + uint32_t keyword_hash = simple_hash(keyword);
127 +
128 + tmp_keyword = parser->keyword;
129 +
130 + while (tmp_keyword) {
131 + if (tmp_keyword->keyword_hash == keyword_hash && (!strcmp(tmp_keyword->keyword, keyword))) {
132 + if (tmp_keyword->func_no == PARSER_MAX_CALLBACKS)
133 + return 0;
134 + tmp_keyword->func[tmp_keyword->func_no++] = (void *) func;
135 + return tmp_keyword->func_no;
136 + }
137 + tmp_keyword = tmp_keyword->next;
138 + }
139 +
140 + tmp_keyword = callocz(1, sizeof(*tmp_keyword));
141 +
142 + tmp_keyword->keyword = strdupz(keyword);
143 + tmp_keyword->keyword_hash = keyword_hash;
144 + tmp_keyword->func[tmp_keyword->func_no++] = (void *) func;
145 +
146 + tmp_keyword->next = parser->keyword;
147 + parser->keyword = tmp_keyword;
148 + return tmp_keyword->func_no;
149 +}
150 +
151 +/*
152 + * Cleanup a previously allocated parser
153 + */
154 +
155 +void parser_destroy(PARSER *parser)
156 +{
157 + if (unlikely(!parser))
158 + return;
159 +
160 + PARSER_KEYWORD *tmp_keyword, *tmp_keyword_next;
161 + PARSER_DATA *tmp_parser_data, *tmp_parser_data_next;
162 +
163 + // Remove keywords
164 + tmp_keyword = parser->keyword;
165 + while (tmp_keyword) {
166 + tmp_keyword_next = tmp_keyword->next;
167 + freez(tmp_keyword->keyword);
168 + freez(tmp_keyword);
169 + tmp_keyword = tmp_keyword_next;
170 + }
171 +
172 + // Remove pushed data if any
173 + tmp_parser_data = parser->data;
174 + while (tmp_parser_data) {
175 + tmp_parser_data_next = tmp_parser_data->next;
176 + freez(tmp_parser_data->line);
177 + freez(tmp_parser_data);
178 + tmp_parser_data = tmp_parser_data_next;
179 + }
180 +
181 + freez(parser->plugins_action);
182 +
183 + freez(parser);
184 + return;
185 +}
186 +
187 +
188 +/*
189 + * Fetch the next line to process
190 + *
191 + */
192 +
193 +int parser_next(PARSER *parser)
194 +{
195 + char *tmp = NULL;
196 +
197 + if (unlikely(!parser))
198 + return 1;
199 +
200 + parser->flags &= ~(PARSER_INPUT_PROCESSED);
201 +
202 + PARSER_DATA *tmp_parser_data = parser->data;
203 +
204 + if (unlikely(tmp_parser_data)) {
205 + strncpyz(parser->buffer, tmp_parser_data->line, PLUGINSD_LINE_MAX);
206 + parser->data = tmp_parser_data->next;
207 + freez(tmp_parser_data->line);
208 + freez(tmp_parser_data);
209 + return 0;
210 + }
211 +
212 +#ifdef ENABLE_HTTPS
213 + int normalread = 1;
214 + if (netdata_srv_ctx) {
215 + if (parser->host->stream_ssl.conn && !parser->host->stream_ssl.flags) {
216 + tmp = parser->buffer;
217 + if (!parser->bytesleft) {
218 + parser->readfrom = parser->tmpbuffer;
219 + parser->bytesleft =
220 + pluginsd_update_buffer(parser->readfrom, parser->host->stream_ssl.conn);
221 + if (parser->bytesleft <= 0) {
222 + return 1;
223 + }
224 + }
225 +
226 + parser->readfrom = pluginsd_get_from_buffer(
227 + parser->buffer, &parser->bytesleft, parser->readfrom,
228 + parser->host->stream_ssl.conn, parser->tmpbuffer);
229 +
230 + if (!parser->readfrom)
231 + tmp = NULL;
232 +
233 + normalread = 0;
234 + }
235 + }
236 + if (normalread) {
237 + if (unlikely(parser->read_function))
238 + tmp = parser->read_function(parser->buffer, PLUGINSD_LINE_MAX, parser->input);
239 + else
240 + tmp = fgets(parser->buffer, PLUGINSD_LINE_MAX, (FILE *)parser->input);
241 + }
242 +#else
243 + if (unlikely(parser->read_function))
244 + tmp = parser->read_function(parser->buffer, PLUGINSD_LINE_MAX, parser->input);
245 + else
246 + tmp = fgets(parser->buffer, PLUGINSD_LINE_MAX, (FILE *)parser->input);
247 +#endif
248 +
249 + if (unlikely(!tmp)) {
250 + if (unlikely(parser->eof_function)) {
251 + int rc = parser->eof_function(parser->input);
252 + error("read failed: user defined function returned %d", rc);
253 + }
254 + else {
255 + if (feof((FILE *)parser->input))
256 + error("read failed: end of file");
257 + else if (ferror((FILE *)parser->input))
258 + error("read failed: input error");
259 + else
260 + error("read failed: unknown error");
261 + }
262 + return 1;
263 + }
264 + return 0;
265 +}
266 +
267 +
268 +/*
269 +* Takes an initialized parser object that has an unprocessed entry (by calling parser_next)
270 +* and if it contains a valid keyword, it will execute all the callbacks
271 +*
272 +*/
273 +
274 +inline int parser_action(PARSER *parser, char *input)
275 +{
276 + PARSER_RC rc = PARSER_RC_OK;
277 + char *words[PLUGINSD_MAX_WORDS] = { NULL };
278 + char command[PLUGINSD_LINE_MAX];
279 + keyword_function action_function;
280 + keyword_function *action_function_list = NULL;
281 +
282 + if (unlikely(!parser))
283 + return 1;
284 + parser->recover_location[0] = 0x0;
285 +
286 + // if not direct input check if we have reprocessed this
287 + if (unlikely(!input && parser->flags & PARSER_INPUT_PROCESSED))
288 + return 0;
289 +
290 + PARSER_KEYWORD *tmp_keyword = parser->keyword;
291 + if (unlikely(!tmp_keyword)) {
292 + return 1;
293 + }
294 +
295 + if (unlikely(!input))
296 + input = parser->buffer;
297 +
298 + if (unlikely(!find_keyword(input, command, PLUGINSD_LINE_MAX, pluginsd_space)))
299 + return 1;
300 +
301 + if ((parser->flags & PARSER_INPUT_ORIGINAL) == PARSER_INPUT_ORIGINAL)
302 + pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, parser->recover_input, parser->recover_location, PARSER_MAX_RECOVER_KEYWORDS);
303 + else
304 + pluginsd_split_words(input, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0);
305 +
306 + uint32_t command_hash = simple_hash(command);
307 +
308 + while(tmp_keyword) {
309 + if (command_hash == tmp_keyword->keyword_hash &&
310 + (!strcmp(command, tmp_keyword->keyword))) {
311 + action_function_list = &tmp_keyword->func[0];
312 + break;
313 + }
314 + tmp_keyword = tmp_keyword->next;
315 + }
316 +
317 + if (unlikely(!action_function_list)) {
318 + if (unlikely(parser->unknown_function))
319 + rc = parser->unknown_function(words, parser->user, NULL);
320 + else
321 + rc = PARSER_RC_ERROR;
322 +#ifdef NETDATA_INTERNAL_CHECKS
323 + error("Unknown keyword [%s]", words[0]);
324 +#endif
325 + }
326 + else {
327 + while ((action_function = *action_function_list) != NULL) {
328 + rc = action_function(words, parser->user, parser->plugins_action);
329 + if (unlikely(rc == PARSER_RC_ERROR || rc == PARSER_RC_STOP))
330 + break;
331 + action_function_list++;
332 + }
333 + }
334 +
335 + if (likely(input == parser->buffer))
336 + parser->flags |= PARSER_INPUT_PROCESSED;
337 +
338 + return (rc == PARSER_RC_ERROR);
339 +}
340 +
341 +inline int parser_recover_input(PARSER *parser)
342 +{
343 + if (unlikely(!parser))
344 + return 1;
345 +
346 + for(int i=0; i < PARSER_MAX_RECOVER_KEYWORDS && parser->recover_location[i]; i++)
347 + *(parser->recover_location[i]) = parser->recover_input[i];
348 +
349 + parser->recover_location[0] = 0x0;
350 +
351 + return 0;
352 +}
parser/parser.h new
+105
@@ -0,0 +1,105 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_INCREMENTAL_PARSER_H
4 +#define NETDATA_INCREMENTAL_PARSER_H 1
5 +
6 +#include "../daemon/common.h"
7 +
8 +#define PARSER_MAX_CALLBACKS 20
9 +#define PARSER_MAX_RECOVER_KEYWORDS 128
10 +
11 +// PARSER return codes
12 +typedef enum parser_rc {
13 + PARSER_RC_OK, // Callback was successful, go on
14 + PARSER_RC_STOP, // Callback says STOP
15 + PARSER_RC_ERROR // Callback failed (abort rest of callbacks)
16 +} PARSER_RC;
17 +
18 +typedef struct pluginsd_action {
19 + PARSER_RC (*set_action)(void *user, RRDSET *st, RRDDIM *rd, long long int value);
20 + PARSER_RC (*begin_action)(void *user, RRDSET *st, usec_t microseconds, int trust_durations);
21 + PARSER_RC (*end_action)(void *user, RRDSET *st);
22 + PARSER_RC (*chart_action)
23 + (void *user, char *type, char *id, char *name, char *family, char *context, char *title, char *units, char *plugin,
24 + char *module, int priority, int update_every, RRDSET_TYPE chart_type, char *options);
25 + PARSER_RC (*dimension_action)
26 + (void *user, RRDSET *st, char *id, char *name, char *algorithm, long multiplier, long divisor, char *options,
27 + RRD_ALGORITHM algorithm_type);
28 +
29 + PARSER_RC (*flush_action)(void *user, RRDSET *st);
30 + PARSER_RC (*disable_action)(void *user);
31 + PARSER_RC (*variable_action)(void *user, RRDHOST *host, RRDSET *st, char *name, int global, calculated_number value);
32 + PARSER_RC (*label_action)(void *user, char *key, char *value, LABEL_SOURCE source);
33 + PARSER_RC (*overwrite_action)(void *user, RRDHOST *host, struct label *new_labels);
34 +} PLUGINSD_ACTION;
35 +
36 +typedef enum parser_input_type {
37 + PARSER_INPUT_SPLIT = 1 << 1,
38 + PARSER_INPUT_ORIGINAL = 1 << 2,
39 + PARSER_INPUT_PROCESSED = 1 << 3,
40 + PARSER_NO_PARSE_INIT = 1 << 4,
41 + PARSER_NO_ACTION_INIT = 1 << 5,
42 +} PARSER_INPUT_TYPE;
43 +
44 +#define PARSER_INPUT_FULL (PARSER_INPUT_SPLIT|PARSER_INPUT_ORIGINAL)
45 +
46 +typedef PARSER_RC (*keyword_function)(char **, void *, PLUGINSD_ACTION *plugins_action);
47 +
48 +typedef struct parser_keyword {
49 + char *keyword;
50 + uint32_t keyword_hash;
51 + int func_no;
52 + keyword_function func[PARSER_MAX_CALLBACKS+1];
53 + struct parser_keyword *next;
54 +} PARSER_KEYWORD;
55 +
56 +typedef struct parser_data {
57 + char *line;
58 + struct parser_data *next;
59 +} PARSER_DATA;
60 +
61 +typedef struct parser {
62 + uint8_t version; // Parser version
63 + RRDHOST *host;
64 + void *input; // Input source e.g. stream
65 + PARSER_DATA *data; // extra input
66 + PARSER_KEYWORD *keyword; // List of parse keywords and functions
67 + PLUGINSD_ACTION *plugins_action;
68 + void *user; // User defined structure to hold extra state between calls
69 + uint32_t flags;
70 +
71 + char *(*read_function)(char *buffer, long unsigned int, void *input);
72 + int (*eof_function)(void *input);
73 + keyword_function unknown_function;
74 + char buffer[PLUGINSD_LINE_MAX];
75 + char *recover_location[PARSER_MAX_RECOVER_KEYWORDS+1];
76 + char recover_input[PARSER_MAX_RECOVER_KEYWORDS];
77 +#ifdef ENABLE_HTTPS
78 + int bytesleft;
79 + char tmpbuffer[PLUGINSD_LINE_MAX];
80 + char *readfrom;
81 +#endif
82 +} PARSER;
83 +
84 +PARSER *parser_init(RRDHOST *host, void *user, void *input, PARSER_INPUT_TYPE flags);
85 +int parser_add_keyword(PARSER *working_parser, char *keyword, keyword_function func);
86 +int parser_next(PARSER *working_parser);
87 +int parser_action(PARSER *working_parser, char *input);
88 +int parser_push(PARSER *working_parser, char *line);
89 +void parser_destroy(PARSER *working_parser);
90 +int parser_recover_input(PARSER *working_parser);
91 +
92 +extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations);
93 +
94 +extern PARSER_RC pluginsd_set(char **words, void *user, PLUGINSD_ACTION *plugins_action);
95 +extern PARSER_RC pluginsd_begin(char **words, void *user, PLUGINSD_ACTION *plugins_action);
96 +extern PARSER_RC pluginsd_end(char **words, void *user, PLUGINSD_ACTION *plugins_action);
97 +extern PARSER_RC pluginsd_chart(char **words, void *user, PLUGINSD_ACTION *plugins_action);
98 +extern PARSER_RC pluginsd_dimension(char **words, void *user, PLUGINSD_ACTION *plugins_action);
99 +extern PARSER_RC pluginsd_variable(char **words, void *user, PLUGINSD_ACTION *plugins_action);
100 +extern PARSER_RC pluginsd_flush(char **words, void *user, PLUGINSD_ACTION *plugins_action);
101 +extern PARSER_RC pluginsd_disable(char **words, void *user, PLUGINSD_ACTION *plugins_action);
102 +extern PARSER_RC pluginsd_label(char **words, void *user, PLUGINSD_ACTION *plugins_action);
103 +extern PARSER_RC pluginsd_overwrite(char **words, void *user, PLUGINSD_ACTION *plugins_action);
104 +
105 +#endif
streaming/rrdpush.c
+3
@@ -1,6 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "rrdpush.h"
4 +#include "../parser/parser.h"
5
6 /*
7 * rrdpush
@@ -1214,6 +1215,7 @@ static int rrdpush_receive(int fd
1215 .obsolete = 0,
1216 .started_t = now_realtime_sec(),
1217 .next = NULL,
1218 + .version = 0,
1219 };
1220
1221 // put the client IP and port into the buffers used by plugins.d
@@ -1289,6 +1291,7 @@ static int rrdpush_receive(int fd
1291 info("STREAM %s [receive from [%s]:%s]: receiving metrics...", host->hostname, client_ip, client_port);
1292 log_stream_connection(client_ip, client_port, key, host->machine_guid, host->hostname, "CONNECTED");
1293
1294 + cd.version = stream_version;
1295 size_t count = pluginsd_process(host, &cd, fp, 1);
1296
1297 log_stream_connection(client_ip, client_port, key, host->machine_guid, host->hostname, "DISCONNECTED");