152
if(likely(st->pluginsd.pos < st->pluginsd.used)) {
153
rda = st->pluginsd.rda[st->pluginsd.pos];
154
RRDDIM *rd = rrddim_acquired_to_rrddim(rda);
155
- if (likely(rd && strcmp(rrddim_id(rd), dimension) == 0)) {
155
+ if (likely(rd && string_strcmp(rd->id, dimension) == 0)) {
156
st->pluginsd.pos++;
157
return rd;
158
}
297
return PARSER_RC_OK;
298
}
299
300
+static void pluginsd_host_define_cleanup(void *user) {
301
+ PARSER_USER_OBJECT *u = user;
302
+
303
+ string_freez(u->host_define.hostname);
304
+ dictionary_destroy(u->host_define.rrdlabels);
305
+
306
+ u->host_define.hostname = NULL;
307
+ u->host_define.rrdlabels = NULL;
308
+ u->host_define.parsing_host = false;
309
+}
310
+
311
+static inline bool pluginsd_validate_machine_guid(const char *guid, uuid_t *uuid, char *output) {
312
+ if(uuid_parse(guid, *uuid))
313
+ return false;
314
+
315
+ uuid_unparse_lower(*uuid, output);
316
+
317
+ return true;
318
+}
319
+
320
+static PARSER_RC pluginsd_host_define(char **words, size_t num_words, void *user) {
321
+ PARSER_USER_OBJECT *u = user;
322
+
323
+ char *guid = get_word(words, num_words, 1);
324
+ char *hostname = get_word(words, num_words, 2);
325
+
326
+ if(unlikely(!guid || !*guid || !hostname || !*hostname))
327
+ return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST_DEFINE, "missing parameters");
328
+
329
+ if(unlikely(u->host_define.parsing_host))
330
+ return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST_DEFINE,
331
+ "another host definition is already open - did you send " PLUGINSD_KEYWORD_HOST_DEFINE_END "?");
332
+
333
+ if(!pluginsd_validate_machine_guid(guid, &u->host_define.machine_guid, u->host_define.machine_guid_str))
334
+ return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST_DEFINE, "cannot parse MACHINE_GUID - is it a valid UUID?");
335
+
336
+ u->host_define.hostname = string_strdupz(hostname);
337
+ u->host_define.rrdlabels = rrdlabels_create();
338
+ u->host_define.parsing_host = true;
339
+
340
+ return PARSER_RC_OK;
341
+}
342
+
343
+static inline PARSER_RC pluginsd_host_dictionary(char **words, size_t num_words, void *user, DICTIONARY *dict, const char *keyword) {
344
+ PARSER_USER_OBJECT *u = user;
345
+
346
+ char *name = get_word(words, num_words, 1);
347
+ char *value = get_word(words, num_words, 2);
348
+
349
+ if(!name || !*name || !value)
350
+ return PLUGINSD_DISABLE_PLUGIN(user, keyword, "missing parameters");
351
+
352
+ if(!u->host_define.parsing_host || !dict)
353
+ return PLUGINSD_DISABLE_PLUGIN(user, keyword, "host is not defined, send " PLUGINSD_KEYWORD_HOST_DEFINE " before this");
354
+
355
+ rrdlabels_add(dict, name, value, RRDLABEL_SRC_CONFIG);
356
+
357
+ return PARSER_RC_OK;
358
+}
359
+
360
+static PARSER_RC pluginsd_host_labels(char **words, size_t num_words, void *user) {
361
+ PARSER_USER_OBJECT *u = user;
362
+ return pluginsd_host_dictionary(words, num_words, user, u->host_define.rrdlabels, PLUGINSD_KEYWORD_HOST_LABEL);
363
+}
364
+
365
+static PARSER_RC pluginsd_host_define_end(char **words __maybe_unused, size_t num_words __maybe_unused, void *user) {
366
+ PARSER_USER_OBJECT *u = user;
367
+
368
+ if(!u->host_define.parsing_host)
369
+ return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST_DEFINE_END, "missing initialization, send " PLUGINSD_KEYWORD_HOST_DEFINE " before this");
370
+
371
+ RRDHOST *host = rrdhost_find_or_create(
372
+ string2str(u->host_define.hostname),
373
+ string2str(u->host_define.hostname),
374
+ u->host_define.machine_guid_str,
375
+ "Netdata Virtual Host 1.0",
376
+ netdata_configured_timezone,
377
+ netdata_configured_abbrev_timezone,
378
+ netdata_configured_utc_offset,
379
+ NULL,
380
+ program_name,
381
+ program_version,
382
+ default_rrd_update_every,
383
+ default_rrd_history_entries,
384
+ default_rrd_memory_mode,
385
+ default_health_enabled,
386
+ default_rrdpush_enabled,
387
+ default_rrdpush_destination,
388
+ default_rrdpush_api_key,
389
+ default_rrdpush_send_charts_matching,
390
+ default_rrdpush_enable_replication,
391
+ default_rrdpush_seconds_to_replicate,
392
+ default_rrdpush_replication_step,
393
+ rrdhost_labels_to_system_info(u->host_define.rrdlabels),
394
+ false
395
+ );
396
+
397
+ if(host->rrdlabels) {
398
+ rrdlabels_migrate_to_these(host->rrdlabels, u->host_define.rrdlabels);
399
+ }
400
+ else {
401
+ host->rrdlabels = u->host_define.rrdlabels;
402
+ u->host_define.rrdlabels = NULL;
403
+ }
404
+
405
+ pluginsd_host_define_cleanup(user);
406
+
407
+ u->host = host;
408
+ pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_HOST_DEFINE_END);
409
+
410
+ rrdhost_flag_clear(host, RRDHOST_FLAG_ORPHAN);
411
+ rrdcontext_host_child_connected(host);
412
+
413
+ return PARSER_RC_OK;
414
+}
415
+
416
+static PARSER_RC pluginsd_host(char **words, size_t num_words, void *user) {
417
+ PARSER_USER_OBJECT *u = user;
418
+
419
+ char *guid = get_word(words, num_words, 1);
420
+
421
+ if(!guid || !*guid || strcmp(guid, "localhost") == 0) {
422
+ u->host = localhost;
423
+ return PARSER_RC_OK;
424
+ }
425
+
426
+ uuid_t uuid;
427
+ char uuid_str[UUID_STR_LEN];
428
+ if(!pluginsd_validate_machine_guid(guid, &uuid, uuid_str))
429
+ return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST, "cannot parse MACHINE_GUID - is it a valid UUID?");
430
+
431
+ RRDHOST *host = rrdhost_find_by_guid(uuid_str);
432
+ if(unlikely(!host))
433
+ return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST, "cannot find a host with this machine guid - have you created it?");
434
+
435
+ u->host = host;
436
+
437
+ return PARSER_RC_OK;
438
+}
439
+
440
PARSER_RC pluginsd_chart(char **words, size_t num_words, void *user)
441
{
442
RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CHART);
1023
{
1024
info("PLUGINSD: plugin called DISABLE. Disabling it.");
1025
((PARSER_USER_OBJECT *) user)->enabled = 0;
886
- return PARSER_RC_ERROR;
1026
+ return PARSER_RC_STOP;
1027
}
1028
1029
PARSER_RC pluginsd_label(char **words, size_t num_words, void *user)
1794
static void pluginsd_process_thread_cleanup(void *ptr) {
1795
PARSER *parser = (PARSER *)ptr;
1796
1657
- if(parser->user_cleanup_cb)
1658
- parser->user_cleanup_cb(parser->user);
1797
+ pluginsd_cleanup_v2(parser->user);
1798
+ pluginsd_host_define_cleanup(parser->user);
1799
1800
rrd_collector_finished();
1801
parser_destroy(parser);
1835
};
1836
1837
// fp_plugin_output = our input; fp_plugin_input = our output
1698
- PARSER *parser = parser_init(host, &user, NULL, fp_plugin_output, fp_plugin_input, -1, PARSER_INPUT_SPLIT, NULL);
1838
+ PARSER *parser = parser_init(&user, fp_plugin_output, fp_plugin_input, -1,
1839
+ PARSER_INPUT_SPLIT, NULL);
1840
+
1841
+ pluginsd_keywords_init(parser, PARSER_INIT_PLUGINSD);
1842
1843
rrd_collector_started();
1844
1847
netdata_thread_cleanup_push(pluginsd_process_thread_cleanup, parser);
1848
1849
user.parser = parser;
1850
+ char buffer[PLUGINSD_LINE_MAX + 1];
1851
1708
- while (likely(!parser_next(parser))) {
1709
- if (unlikely(!service_running(SERVICE_COLLECTORS) || parser_action(parser, NULL)))
1852
+ while (likely(!parser_next(parser, buffer, PLUGINSD_LINE_MAX))) {
1853
+ if (unlikely(!service_running(SERVICE_COLLECTORS) || parser_action(parser, buffer)))
1854
break;
1855
}
1856
1869
1870
return count;
1871
}
1872
+
1873
+static void pluginsd_keywords_init_internal(PARSER *parser, PLUGINSD_KEYWORDS types, void (*add_func)(PARSER *parser, char *keyword, keyword_function func)) {
1874
+
1875
+ if (types & PARSER_INIT_PLUGINSD) {
1876
+ add_func(parser, PLUGINSD_KEYWORD_FLUSH, pluginsd_flush);
1877
+ add_func(parser, PLUGINSD_KEYWORD_DISABLE, pluginsd_disable);
1878
+
1879
+ add_func(parser, PLUGINSD_KEYWORD_HOST_DEFINE, pluginsd_host_define);
1880
+ add_func(parser, PLUGINSD_KEYWORD_HOST_DEFINE_END, pluginsd_host_define_end);
1881
+ add_func(parser, PLUGINSD_KEYWORD_HOST_LABEL, pluginsd_host_labels);
1882
+ add_func(parser, PLUGINSD_KEYWORD_HOST, pluginsd_host);
1883
+ }
1884
+
1885
+ if (types & (PARSER_INIT_PLUGINSD | PARSER_INIT_STREAMING)) {
1886
+ // plugins.d plugins and streaming
1887
+ add_func(parser, PLUGINSD_KEYWORD_CHART, pluginsd_chart);
1888
+ add_func(parser, PLUGINSD_KEYWORD_DIMENSION, pluginsd_dimension);
1889
+ add_func(parser, PLUGINSD_KEYWORD_VARIABLE, pluginsd_variable);
1890
+ add_func(parser, PLUGINSD_KEYWORD_LABEL, pluginsd_label);
1891
+ add_func(parser, PLUGINSD_KEYWORD_OVERWRITE, pluginsd_overwrite);
1892
+ add_func(parser, PLUGINSD_KEYWORD_CLABEL_COMMIT, pluginsd_clabel_commit);
1893
+ add_func(parser, PLUGINSD_KEYWORD_CLABEL, pluginsd_clabel);
1894
+ add_func(parser, PLUGINSD_KEYWORD_FUNCTION, pluginsd_function);
1895
+ add_func(parser, PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN, pluginsd_function_result_begin);
1896
+
1897
+ add_func(parser, PLUGINSD_KEYWORD_BEGIN, pluginsd_begin);
1898
+ add_func(parser, PLUGINSD_KEYWORD_SET, pluginsd_set);
1899
+ add_func(parser, PLUGINSD_KEYWORD_END, pluginsd_end);
1900
+
1901
+ inflight_functions_init(parser);
1902
+ }
1903
+
1904
+ if (types & PARSER_INIT_STREAMING) {
1905
+ add_func(parser, PLUGINSD_KEYWORD_CHART_DEFINITION_END, pluginsd_chart_definition_end);
1906
+
1907
+ // replication
1908
+ add_func(parser, PLUGINSD_KEYWORD_REPLAY_BEGIN, pluginsd_replay_begin);
1909
+ add_func(parser, PLUGINSD_KEYWORD_REPLAY_SET, pluginsd_replay_set);
1910
+ add_func(parser, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE, pluginsd_replay_rrddim_collection_state);
1911
+ add_func(parser, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE, pluginsd_replay_rrdset_collection_state);
1912
+ add_func(parser, PLUGINSD_KEYWORD_REPLAY_END, pluginsd_replay_end);
1913
+
1914
+ // streaming metrics v2
1915
+ add_func(parser, PLUGINSD_KEYWORD_BEGIN_V2, pluginsd_begin_v2);
1916
+ add_func(parser, PLUGINSD_KEYWORD_SET_V2, pluginsd_set_v2);
1917
+ add_func(parser, PLUGINSD_KEYWORD_END_V2, pluginsd_end_v2);
1918
+ }
1919
+}
1920
+
1921
+void pluginsd_keywords_init(PARSER *parser, PLUGINSD_KEYWORDS types) {
1922
+ pluginsd_keywords_init_internal(parser, types, parser_add_keyword);
1923
+}
1924
+
1925
+struct pluginsd_user_unittest {
1926
+ size_t size;
1927
+ const char **hashtable;
1928
+ uint32_t (*hash)(const char *s);
1929
+ size_t collisions;
1930
+};
1931
+
1932
+void pluginsd_keyword_collision_check(PARSER *parser, char *keyword, keyword_function func __maybe_unused) {
1933
+ struct pluginsd_user_unittest *u = parser->user;
1934
+
1935
+ uint32_t hash = u->hash(keyword);
1936
+ uint32_t slot = hash % u->size;
1937
+
1938
+ if(u->hashtable[slot])
1939
+ u->collisions++;
1940
+
1941
+ u->hashtable[slot] = keyword;
1942
+}
1943
+
1944
+static struct {
1945
+ const char *name;
1946
+ uint32_t (*hash)(const char *s);
1947
+ size_t slots_needed;
1948
+} hashers[] = {
1949
+ { .name = "djb2_hash32(s)", djb2_hash32, .slots_needed = 0, },
1950
+ { .name = "fnv1_hash32(s)", fnv1_hash32, .slots_needed = 0, },
1951
+ { .name = "fnv1a_hash32(s)", fnv1a_hash32, .slots_needed = 0, },
1952
+ { .name = "larson_hash32(s)", larson_hash32, .slots_needed = 0, },
1953
+ { .name = "pluginsd_parser_hash32(s)", pluginsd_parser_hash32, .slots_needed = 0, },
1954
+
1955
+ // terminator
1956
+ { .name = NULL, NULL, .slots_needed = 0, },
1957
+};
1958
+
1959
+int pluginsd_parser_unittest(void) {
1960
+ PARSER *p;
1961
+ size_t slots_to_check = 1000;
1962
+ size_t i, h;
1963
+
1964
+ // check for hashtable collisions
1965
+ for(h = 0; hashers[h].name ;h++) {
1966
+ hashers[h].slots_needed = slots_to_check * 1000000;
1967
+
1968
+ for (i = 10; i < slots_to_check; i++) {
1969
+ struct pluginsd_user_unittest user = {
1970
+ .hash = hashers[h].hash,
1971
+ .size = i,
1972
+ .hashtable = callocz(i, sizeof(const char *)),
1973
+ .collisions = 0,
1974
+ };
1975
+
1976
+ p = parser_init(&user, NULL, NULL, -1, PARSER_INPUT_SPLIT, NULL);
1977
+ pluginsd_keywords_init_internal(p, PARSER_INIT_PLUGINSD | PARSER_INIT_STREAMING,
1978
+ pluginsd_keyword_collision_check);
1979
+ parser_destroy(p);
1980
+
1981
+ freez(user.hashtable);
1982
+
1983
+ if (!user.collisions) {
1984
+ hashers[h].slots_needed = i;
1985
+ break;
1986
+ }
1987
+ }
1988
+ }
1989
+
1990
+ for(h = 0; hashers[h].name ;h++) {
1991
+ if(hashers[h].slots_needed > 1000)
1992
+ info("PARSER: hash function '%s' cannot be used without collisions under %zu slots", hashers[h].name, slots_to_check);
1993
+ else
1994
+ info("PARSER: hash function '%s' needs PARSER_KEYWORDS_HASHTABLE_SIZE (in parser.h) set to %zu", hashers[h].name, hashers[h].slots_needed);
1995
+ }
1996
+
1997
+ p = parser_init(NULL, NULL, NULL, -1, PARSER_INPUT_SPLIT, NULL);
1998
+ pluginsd_keywords_init(p, PARSER_INIT_PLUGINSD | PARSER_INIT_STREAMING);
1999
+ parser_destroy(p);
2000
+ return 0;
2001
+}