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");