1
// SPDX-License-Identifier: GPL-3.0-or-later
2
3
-#include "pluginsd_parser.h"
4
-
5
-#define LOG_FUNCTIONS false
6
-
7
-#define SERVING_STREAMING(parser) ((parser)->repertoire == PARSER_INIT_STREAMING)
8
-#define SERVING_PLUGINSD(parser) ((parser)->repertoire == PARSER_INIT_PLUGINSD)
9
-
10
-static ssize_t send_to_plugin(const char *txt, void *data) {
11
- PARSER *parser = data;
12
-
13
- if(!txt || !*txt)
14
- return 0;
15
-
16
-#ifdef ENABLE_H2O
17
- if(parser->h2o_ctx)
18
- return h2o_stream_write(parser->h2o_ctx, txt, strlen(txt));
19
-#endif
20
-
21
- errno = 0;
22
- spinlock_lock(&parser->writer.spinlock);
23
- ssize_t bytes = -1;
24
-
25
-#ifdef ENABLE_HTTPS
26
- NETDATA_SSL *ssl = parser->ssl_output;
27
- if(ssl) {
28
-
29
- if(SSL_connection(ssl))
30
- bytes = netdata_ssl_write(ssl, (void *) txt, strlen(txt));
31
-
32
- else
33
- netdata_log_error("PLUGINSD: cannot send command (SSL)");
34
-
35
- spinlock_unlock(&parser->writer.spinlock);
36
- return bytes;
37
- }
38
-#endif
39
-
40
- if(parser->fp_output) {
41
-
42
- bytes = fprintf(parser->fp_output, "%s", txt);
43
- if(bytes <= 0) {
44
- netdata_log_error("PLUGINSD: cannot send command (FILE)");
45
- bytes = -2;
46
- }
47
- else
48
- fflush(parser->fp_output);
49
-
50
- spinlock_unlock(&parser->writer.spinlock);
51
- return bytes;
52
- }
53
-
54
- if(parser->fd != -1) {
55
- bytes = 0;
56
- ssize_t total = (ssize_t)strlen(txt);
57
- ssize_t sent;
58
-
59
- do {
60
- sent = write(parser->fd, &txt[bytes], total - bytes);
61
- if(sent <= 0) {
62
- netdata_log_error("PLUGINSD: cannot send command (fd)");
63
- spinlock_unlock(&parser->writer.spinlock);
64
- return -3;
65
- }
66
- bytes += sent;
67
- }
68
- while(bytes < total);
69
-
70
- spinlock_unlock(&parser->writer.spinlock);
71
- return (int)bytes;
72
- }
73
-
74
- spinlock_unlock(&parser->writer.spinlock);
75
- netdata_log_error("PLUGINSD: cannot send command (no output socket/pipe/file given to plugins.d parser)");
76
- return -4;
77
-}
78
-
79
-static inline RRDHOST *pluginsd_require_scope_host(PARSER *parser, const char *cmd) {
80
- RRDHOST *host = parser->user.host;
81
-
82
- if(unlikely(!host))
83
- netdata_log_error("PLUGINSD: command %s requires a host, but is not set.", cmd);
84
-
85
- return host;
86
-}
87
-
88
-static inline RRDSET *pluginsd_require_scope_chart(PARSER *parser, const char *cmd, const char *parent_cmd) {
89
- RRDSET *st = parser->user.st;
90
-
91
- if(unlikely(!st))
92
- netdata_log_error("PLUGINSD: command %s requires a chart defined via command %s, but is not set.", cmd, parent_cmd);
93
-
94
- return st;
95
-}
96
-
97
-static inline RRDSET *pluginsd_get_scope_chart(PARSER *parser) {
98
- return parser->user.st;
99
-}
100
-
101
-static inline void pluginsd_lock_rrdset_data_collection(PARSER *parser) {
102
- if(parser->user.st && !parser->user.v2.locked_data_collection) {
103
- spinlock_lock(&parser->user.st->data_collection_lock);
104
- parser->user.v2.locked_data_collection = true;
105
- }
106
-}
107
-
108
-static inline bool pluginsd_unlock_rrdset_data_collection(PARSER *parser) {
109
- if(parser->user.st && parser->user.v2.locked_data_collection) {
110
- spinlock_unlock(&parser->user.st->data_collection_lock);
111
- parser->user.v2.locked_data_collection = false;
112
- return true;
113
- }
114
-
115
- return false;
116
-}
117
-
118
-static inline void pluginsd_unlock_previous_scope_chart(PARSER *parser, const char *keyword, bool stale) {
119
- if(unlikely(pluginsd_unlock_rrdset_data_collection(parser))) {
120
- if(stale)
121
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s/' stale data collection lock found during %s; it has been unlocked",
122
- rrdhost_hostname(parser->user.st->rrdhost),
123
- rrdset_id(parser->user.st),
124
- keyword);
125
- }
126
-
127
- if(unlikely(parser->user.v2.ml_locked)) {
128
- ml_chart_update_end(parser->user.st);
129
- parser->user.v2.ml_locked = false;
130
-
131
- if(stale)
132
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s/' stale ML lock found during %s, it has been unlocked",
133
- rrdhost_hostname(parser->user.st->rrdhost),
134
- rrdset_id(parser->user.st),
135
- keyword);
136
- }
137
-}
138
-
139
-static inline void pluginsd_clear_scope_chart(PARSER *parser, const char *keyword) {
140
- pluginsd_unlock_previous_scope_chart(parser, keyword, true);
141
-
142
- if(parser->user.cleanup_slots && parser->user.st)
143
- rrdset_pluginsd_receive_unslot(parser->user.st);
144
-
145
- parser->user.st = NULL;
146
- parser->user.cleanup_slots = false;
147
-}
148
-
149
-static inline bool pluginsd_set_scope_chart(PARSER *parser, RRDSET *st, const char *keyword) {
150
- RRDSET *old_st = parser->user.st;
151
- pid_t old_collector_tid = (old_st) ? old_st->pluginsd.collector_tid : 0;
152
- pid_t my_collector_tid = gettid();
153
-
154
- if(unlikely(old_collector_tid)) {
155
- if(old_collector_tid != my_collector_tid) {
156
- nd_log_limit_static_global_var(erl, 1, 0);
157
- nd_log_limit(&erl, NDLS_COLLECTORS, NDLP_WARNING,
158
- "PLUGINSD: keyword %s: 'host:%s/chart:%s' is collected twice (my tid %d, other collector tid %d)",
159
- keyword ? keyword : "UNKNOWN",
160
- rrdhost_hostname(st->rrdhost), rrdset_id(st),
161
- my_collector_tid, old_collector_tid);
162
-
163
- return false;
164
- }
165
-
166
- old_st->pluginsd.collector_tid = 0;
167
- }
168
-
169
- st->pluginsd.collector_tid = my_collector_tid;
170
-
171
- pluginsd_clear_scope_chart(parser, keyword);
172
-
173
- st->pluginsd.pos = 0;
174
- parser->user.st = st;
175
- parser->user.cleanup_slots = false;
176
-
177
- return true;
178
-}
179
-
180
-static inline void pluginsd_rrddim_put_to_slot(PARSER *parser, RRDSET *st, RRDDIM *rd, ssize_t slot, bool obsolete) {
181
- size_t wanted_size = st->pluginsd.size;
182
-
183
- if(slot >= 1) {
184
- st->pluginsd.dims_with_slots = true;
185
- wanted_size = slot;
186
- }
187
- else {
188
- st->pluginsd.dims_with_slots = false;
189
- wanted_size = dictionary_entries(st->rrddim_root_index);
190
- }
191
-
192
- if(wanted_size > st->pluginsd.size) {
193
- st->pluginsd.prd_array = reallocz(st->pluginsd.prd_array, wanted_size * sizeof(struct pluginsd_rrddim));
194
-
195
- // initialize the empty slots
196
- for(ssize_t i = (ssize_t) wanted_size - 1; i >= (ssize_t) st->pluginsd.size; i--) {
197
- st->pluginsd.prd_array[i].rda = NULL;
198
- st->pluginsd.prd_array[i].rd = NULL;
199
- st->pluginsd.prd_array[i].id = NULL;
200
- }
201
-
202
- st->pluginsd.size = wanted_size;
203
- }
204
-
205
- if(st->pluginsd.dims_with_slots) {
206
- struct pluginsd_rrddim *prd = &st->pluginsd.prd_array[slot - 1];
207
-
208
- if(prd->rd != rd) {
209
- prd->rda = rrddim_find_and_acquire(st, string2str(rd->id));
210
- prd->rd = rrddim_acquired_to_rrddim(prd->rda);
211
- prd->id = string2str(prd->rd->id);
212
- }
213
-
214
- if(obsolete)
215
- parser->user.cleanup_slots = true;
216
- }
217
-}
218
-
219
-static inline RRDDIM *pluginsd_acquire_dimension(RRDHOST *host, RRDSET *st, const char *dimension, ssize_t slot, const char *cmd) {
220
- if (unlikely(!dimension || !*dimension)) {
221
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s' got a %s, without a dimension.",
222
- rrdhost_hostname(host), rrdset_id(st), cmd);
223
- return NULL;
224
- }
225
-
226
- if (unlikely(!st->pluginsd.size)) {
227
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s' got a %s, but the chart has no dimensions.",
228
- rrdhost_hostname(host), rrdset_id(st), cmd);
229
- return NULL;
230
- }
231
-
232
- struct pluginsd_rrddim *prd;
233
- RRDDIM *rd;
234
-
235
- if(likely(st->pluginsd.dims_with_slots)) {
236
- // caching with slots
237
-
238
- if(unlikely(slot < 1 || slot > st->pluginsd.size)) {
239
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s' got a %s with slot %zd, but slots in the range [1 - %u] are expected.",
240
- rrdhost_hostname(host), rrdset_id(st), cmd, slot, st->pluginsd.size);
241
- return NULL;
242
- }
243
-
244
- prd = &st->pluginsd.prd_array[slot - 1];
245
-
246
- rd = prd->rd;
247
- if(likely(rd)) {
248
-#ifdef NETDATA_INTERNAL_CHECKS
249
- if(strcmp(prd->id, dimension) != 0) {
250
- ssize_t t;
251
- for(t = 0; t < st->pluginsd.size ;t++) {
252
- if (strcmp(st->pluginsd.prd_array[t].id, dimension) == 0)
253
- break;
254
- }
255
- if(t >= st->pluginsd.size)
256
- t = -1;
257
-
258
- internal_fatal(true,
259
- "PLUGINSD: expected to find dimension '%s' on slot %zd, but found '%s', "
260
- "the right slot is %zd",
261
- dimension, slot, prd->id, t);
262
- }
263
-#endif
264
- return rd;
265
- }
266
- }
267
- else {
268
- // caching without slots
269
-
270
- if(unlikely(st->pluginsd.pos >= st->pluginsd.size))
271
- st->pluginsd.pos = 0;
272
-
273
- prd = &st->pluginsd.prd_array[st->pluginsd.pos++];
274
-
275
- rd = prd->rd;
276
- if(likely(rd)) {
277
- const char *id = prd->id;
278
-
279
- if(strcmp(id, dimension) == 0) {
280
- // we found it cached
281
- return rd;
282
- }
283
- else {
284
- // the cached one is not good for us
285
- rrddim_acquired_release(prd->rda);
286
- prd->rda = NULL;
287
- prd->rd = NULL;
288
- prd->id = NULL;
289
- }
290
- }
291
- }
292
-
293
- // we need to find the dimension and set it to prd
294
-
295
- RRDDIM_ACQUIRED *rda = rrddim_find_and_acquire(st, dimension);
296
- if (unlikely(!rda)) {
297
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a %s but dimension does not exist.",
298
- rrdhost_hostname(host), rrdset_id(st), dimension, cmd);
299
-
300
- return NULL;
301
- }
302
-
303
- prd->rda = rda;
304
- prd->rd = rd = rrddim_acquired_to_rrddim(rda);
305
- prd->id = string2str(rd->id);
306
-
307
- return rd;
308
-}
309
-
310
-static inline RRDSET *pluginsd_find_chart(RRDHOST *host, const char *chart, const char *cmd) {
311
- if (unlikely(!chart || !*chart)) {
312
- netdata_log_error("PLUGINSD: 'host:%s' got a %s without a chart id.",
313
- rrdhost_hostname(host), cmd);
314
- return NULL;
315
- }
316
-
317
- RRDSET *st = rrdset_find(host, chart);
318
- if (unlikely(!st))
319
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s' got a %s but chart does not exist.",
320
- rrdhost_hostname(host), chart, cmd);
321
-
322
- return st;
323
-}
324
-
325
-static inline ssize_t pluginsd_parse_rrd_slot(char **words, size_t num_words) {
326
- ssize_t slot = -1;
327
- char *id = get_word(words, num_words, 1);
328
- if(id && id[0] == PLUGINSD_KEYWORD_SLOT[0] && id[1] == PLUGINSD_KEYWORD_SLOT[1] &&
329
- id[2] == PLUGINSD_KEYWORD_SLOT[2] && id[3] == PLUGINSD_KEYWORD_SLOT[3] && id[4] == ':') {
330
- slot = (ssize_t) str2ull_encoded(&id[5]);
331
- if(slot < 0) slot = 0; // to make the caller increment its idx of the words
332
- }
333
-
334
- return slot;
335
-}
336
-
337
-static inline void pluginsd_rrdset_cache_put_to_slot(PARSER *parser, RRDSET *st, ssize_t slot, bool obsolete) {
338
- // clean possible old cached data
339
- rrdset_pluginsd_receive_unslot(st);
340
-
341
- if(unlikely(slot < 1 || slot >= INT32_MAX))
342
- return;
343
-
344
- RRDHOST *host = st->rrdhost;
345
-
346
- if(unlikely((size_t)slot > host->rrdpush.receive.pluginsd_chart_slots.size)) {
347
- spinlock_lock(&host->rrdpush.receive.pluginsd_chart_slots.spinlock);
348
- size_t old_slots = host->rrdpush.receive.pluginsd_chart_slots.size;
349
- size_t new_slots = (old_slots < PLUGINSD_MIN_RRDSET_POINTERS_CACHE) ? PLUGINSD_MIN_RRDSET_POINTERS_CACHE : old_slots * 2;
350
-
351
- if(new_slots < (size_t)slot)
352
- new_slots = slot;
353
-
354
- host->rrdpush.receive.pluginsd_chart_slots.array =
355
- reallocz(host->rrdpush.receive.pluginsd_chart_slots.array, new_slots * sizeof(RRDSET *));
356
-
357
- for(size_t i = old_slots; i < new_slots ;i++)
358
- host->rrdpush.receive.pluginsd_chart_slots.array[i] = NULL;
359
-
360
- host->rrdpush.receive.pluginsd_chart_slots.size = new_slots;
361
- spinlock_unlock(&host->rrdpush.receive.pluginsd_chart_slots.spinlock);
362
- }
363
-
364
- host->rrdpush.receive.pluginsd_chart_slots.array[slot - 1] = st;
365
- st->pluginsd.last_slot = (int32_t)slot - 1;
366
- parser->user.cleanup_slots = obsolete;
367
-}
368
-
369
-static inline RRDSET *pluginsd_rrdset_cache_get_from_slot(PARSER *parser, RRDHOST *host, const char *id, ssize_t slot, const char *keyword) {
370
- if(unlikely(slot < 1 || (size_t)slot > host->rrdpush.receive.pluginsd_chart_slots.size))
371
- return pluginsd_find_chart(host, id, keyword);
372
-
373
- RRDSET *st = host->rrdpush.receive.pluginsd_chart_slots.array[slot - 1];
374
-
375
- if(!st) {
376
- st = pluginsd_find_chart(host, id, keyword);
377
- if(st)
378
- pluginsd_rrdset_cache_put_to_slot(parser, st, slot, rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE));
379
- }
380
- else {
381
- internal_fatal(string_strcmp(st->id, id) != 0,
382
- "PLUGINSD: wrong chart in slot %zd, expected '%s', found '%s'",
383
- slot - 1, id, string2str(st->id));
384
- }
385
-
386
- return st;
387
-}
388
-
389
-static inline PARSER_RC PLUGINSD_DISABLE_PLUGIN(PARSER *parser, const char *keyword, const char *msg) {
390
- parser->user.enabled = 0;
391
-
392
- if(keyword && msg) {
393
- nd_log_limit_static_global_var(erl, 1, 0);
394
- nd_log_limit(&erl, NDLS_COLLECTORS, NDLP_INFO,
395
- "PLUGINSD: keyword %s: %s", keyword, msg);
396
- }
397
-
398
- return PARSER_RC_ERROR;
399
-}
3
+#include "pluginsd_internals.h"
4
5
static inline PARSER_RC pluginsd_set(char **words, size_t num_words, PARSER *parser) {
6
int idx = 1;
504
return PARSER_RC_OK;
505
}
506
903
-// ----------------------------------------------------------------------------
904
-// execution of functions
905
-
906
-struct inflight_function {
907
- int code;
908
- int timeout;
909
- STRING *function;
910
- BUFFER *result_body_wb;
911
- rrd_function_result_callback_t result_cb;
912
- void *result_cb_data;
913
- usec_t timeout_ut;
914
- usec_t started_ut;
915
- usec_t sent_ut;
916
- const char *payload;
917
- PARSER *parser;
918
- bool virtual;
919
-};
920
-
921
-static void inflight_functions_insert_callback(const DICTIONARY_ITEM *item, void *func, void *parser_ptr) {
922
- struct inflight_function *pf = func;
923
-
924
- PARSER *parser = parser_ptr;
925
-
926
- // leave this code as default, so that when the dictionary is destroyed this will be sent back to the caller
927
- pf->code = HTTP_RESP_GATEWAY_TIMEOUT;
928
-
929
- const char *transaction = dictionary_acquired_item_name(item);
930
-
931
- char buffer[2048 + 1];
932
- snprintfz(buffer, sizeof(buffer) - 1, "%s %s %d \"%s\"\n",
933
- pf->payload ? "FUNCTION_PAYLOAD" : "FUNCTION",
934
- transaction,
935
- pf->timeout,
936
- string2str(pf->function));
937
-
938
- // send the command to the plugin
939
- ssize_t ret = send_to_plugin(buffer, parser);
940
-
941
- pf->sent_ut = now_realtime_usec();
942
-
943
- if(ret < 0) {
944
- netdata_log_error("FUNCTION '%s': failed to send it to the plugin, error %zd", string2str(pf->function), ret);
945
- rrd_call_function_error(pf->result_body_wb, "Failed to communicate with collector", HTTP_RESP_SERVICE_UNAVAILABLE);
946
- }
947
- else {
948
- internal_error(LOG_FUNCTIONS,
949
- "FUNCTION '%s' with transaction '%s' sent to collector (%zd bytes, in %"PRIu64" usec)",
950
- string2str(pf->function), dictionary_acquired_item_name(item), ret,
951
- pf->sent_ut - pf->started_ut);
952
- }
953
-
954
- if (!pf->payload)
955
- return;
956
-
957
- // send the payload to the plugin
958
- ret = send_to_plugin(pf->payload, parser);
959
-
960
- if(ret < 0) {
961
- netdata_log_error("FUNCTION_PAYLOAD '%s': failed to send function to plugin, error %zd", string2str(pf->function), ret);
962
- rrd_call_function_error(pf->result_body_wb, "Failed to communicate with collector", HTTP_RESP_SERVICE_UNAVAILABLE);
963
- }
964
- else {
965
- internal_error(LOG_FUNCTIONS,
966
- "FUNCTION_PAYLOAD '%s' with transaction '%s' sent to collector (%zd bytes, in %"PRIu64" usec)",
967
- string2str(pf->function), dictionary_acquired_item_name(item), ret,
968
- pf->sent_ut - pf->started_ut);
969
- }
970
-
971
- send_to_plugin("\nFUNCTION_PAYLOAD_END\n", parser);
972
-}
973
-
974
-static bool inflight_functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func __maybe_unused, void *new_func, void *parser_ptr __maybe_unused) {
975
- struct inflight_function *pf = new_func;
976
-
977
- netdata_log_error("PLUGINSD_PARSER: duplicate UUID on pending function '%s' detected. Ignoring the second one.", string2str(pf->function));
978
- pf->code = rrd_call_function_error(pf->result_body_wb, "This request is already in progress", HTTP_RESP_BAD_REQUEST);
979
- pf->result_cb(pf->result_body_wb, pf->code, pf->result_cb_data);
980
- string_freez(pf->function);
981
-
982
- return false;
983
-}
984
-
985
-void delete_job_finalize(struct parser *parser __maybe_unused, struct configurable_plugin *plug, const char *fnc_sig, int code) {
986
- if (code != DYNCFG_VFNC_RET_CFG_ACCEPTED)
987
- return;
988
-
989
- char *params_local = strdupz(fnc_sig);
990
- char *words[DYNCFG_MAX_WORDS];
991
- size_t words_c = quoted_strings_splitter(params_local, words, DYNCFG_MAX_WORDS, isspace_map_pluginsd);
992
-
993
- if (words_c != 3) {
994
- netdata_log_error("PLUGINSD_PARSER: invalid number of parameters for delete_job");
995
- freez(params_local);
996
- return;
997
- }
998
-
999
- const char *module = words[1];
1000
- const char *job = words[2];
1001
-
1002
- delete_job(plug, module, job);
1003
-
1004
- unlink_job(plug->name, module, job);
1005
-
1006
- rrdpush_send_job_deleted(localhost, plug->name, module, job);
1007
-
1008
- freez(params_local);
1009
-}
1010
-
1011
-void set_job_finalize(struct parser *parser __maybe_unused, struct configurable_plugin *plug __maybe_unused, const char *fnc_sig, int code) {
1012
- if (code != DYNCFG_VFNC_RET_CFG_ACCEPTED)
1013
- return;
1014
-
1015
- char *params_local = strdupz(fnc_sig);
1016
- char *words[DYNCFG_MAX_WORDS];
1017
- size_t words_c = quoted_strings_splitter(params_local, words, DYNCFG_MAX_WORDS, isspace_map_pluginsd);
1018
-
1019
- if (words_c != 3) {
1020
- netdata_log_error("PLUGINSD_PARSER: invalid number of parameters for set_job_config");
1021
- freez(params_local);
1022
- return;
1023
- }
1024
-
1025
- const char *module_name = get_word(words, words_c, 1);
1026
- const char *job_name = get_word(words, words_c, 2);
1027
-
1028
- if (register_job(parser->user.host->configurable_plugins, parser->user.cd->configuration->name, module_name, job_name, JOB_TYPE_USER, JOB_FLG_USER_CREATED, 1)) {
1029
- freez(params_local);
1030
- return;
1031
- }
1032
-
1033
- // only send this if it is not existing already (register_job cares for that)
1034
- rrdpush_send_dyncfg_reg_job(localhost, parser->user.cd->configuration->name, module_name, job_name, JOB_TYPE_USER, JOB_FLG_USER_CREATED);
1035
-
1036
- freez(params_local);
1037
-}
1038
-
1039
-static void inflight_functions_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func, void *parser_ptr) {
1040
- struct inflight_function *pf = func;
1041
- struct parser *parser = (struct parser *)parser_ptr;
1042
-
1043
- internal_error(LOG_FUNCTIONS,
1044
- "FUNCTION '%s' result of transaction '%s' received from collector (%zu bytes, request %"PRIu64" usec, response %"PRIu64" usec)",
1045
- string2str(pf->function), dictionary_acquired_item_name(item),
1046
- buffer_strlen(pf->result_body_wb), pf->sent_ut - pf->started_ut, now_realtime_usec() - pf->sent_ut);
1047
-
1048
- if (pf->virtual && SERVING_PLUGINSD(parser)) {
1049
- if (pf->payload) {
1050
- if (strncmp(string2str(pf->function), FUNCTION_NAME_SET_JOB_CONFIG, strlen(FUNCTION_NAME_SET_JOB_CONFIG)) == 0)
1051
- set_job_finalize(parser, parser->user.cd->configuration, string2str(pf->function), pf->code);
1052
- dyn_conf_store_config(string2str(pf->function), pf->payload, parser->user.cd->configuration);
1053
- } else if (strncmp(string2str(pf->function), FUNCTION_NAME_DELETE_JOB, strlen(FUNCTION_NAME_DELETE_JOB)) == 0) {
1054
- delete_job_finalize(parser, parser->user.cd->configuration, string2str(pf->function), pf->code);
1055
- }
1056
- }
1057
-
1058
- pf->result_cb(pf->result_body_wb, pf->code, pf->result_cb_data);
1059
-
1060
- string_freez(pf->function);
1061
- freez((void *)pf->payload);
1062
-}
1063
-
1064
-void inflight_functions_init(PARSER *parser) {
1065
- parser->inflight.functions = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE, &dictionary_stats_category_functions, 0);
1066
- dictionary_register_insert_callback(parser->inflight.functions, inflight_functions_insert_callback, parser);
1067
- dictionary_register_delete_callback(parser->inflight.functions, inflight_functions_delete_callback, parser);
1068
- dictionary_register_conflict_callback(parser->inflight.functions, inflight_functions_conflict_callback, parser);
1069
-}
1070
-
1071
-static void inflight_functions_garbage_collect(PARSER *parser, usec_t now) {
1072
- parser->inflight.smaller_timeout = 0;
1073
- struct inflight_function *pf;
1074
- dfe_start_write(parser->inflight.functions, pf) {
1075
- if (pf->timeout_ut < now) {
1076
- internal_error(true,
1077
- "FUNCTION '%s' removing expired transaction '%s', after %"PRIu64" usec.",
1078
- string2str(pf->function), pf_dfe.name, now - pf->started_ut);
1079
-
1080
- if(!buffer_strlen(pf->result_body_wb) || pf->code == HTTP_RESP_OK)
1081
- pf->code = rrd_call_function_error(pf->result_body_wb,
1082
- "Timeout waiting for collector response.",
1083
- HTTP_RESP_GATEWAY_TIMEOUT);
1084
-
1085
- dictionary_del(parser->inflight.functions, pf_dfe.name);
1086
- }
1087
-
1088
- else if(!parser->inflight.smaller_timeout || pf->timeout_ut < parser->inflight.smaller_timeout)
1089
- parser->inflight.smaller_timeout = pf->timeout_ut;
1090
- }
1091
- dfe_done(pf);
1092
-}
1093
-
1094
-void pluginsd_function_cancel(void *data) {
1095
- struct inflight_function *look_for = data, *t;
1096
-
1097
- bool sent = false;
1098
- dfe_start_read(look_for->parser->inflight.functions, t) {
1099
- if(look_for == t) {
1100
- const char *transaction = t_dfe.name;
1101
-
1102
- internal_error(true, "PLUGINSD: sending function cancellation to plugin for transaction '%s'", transaction);
1103
-
1104
- char buffer[2048 + 1];
1105
- snprintfz(buffer, sizeof(buffer) - 1, "%s %s\n",
1106
- PLUGINSD_KEYWORD_FUNCTION_CANCEL,
1107
- transaction);
1108
-
1109
- // send the command to the plugin
1110
- ssize_t ret = send_to_plugin(buffer, t->parser);
1111
- if(ret < 0)
1112
- sent = true;
1113
-
1114
- break;
1115
- }
1116
- }
1117
- dfe_done(t);
1118
-
1119
- if(sent <= 0)
1120
- nd_log(NDLS_DAEMON, NDLP_DEBUG,
1121
- "PLUGINSD: FUNCTION_CANCEL request didn't match any pending function requests in pluginsd.d.");
1122
-}
1123
-
1124
-// this is the function that is called from
1125
-// rrd_call_function_and_wait() and rrd_call_function_async()
1126
-static int pluginsd_function_execute_cb(BUFFER *result_body_wb, int timeout, const char *function,
1127
- void *execute_cb_data,
1128
- rrd_function_result_callback_t result_cb, void *result_cb_data,
1129
- rrd_function_is_cancelled_cb_t is_cancelled_cb __maybe_unused,
1130
- void *is_cancelled_cb_data __maybe_unused,
1131
- rrd_function_register_canceller_cb_t register_canceller_cb,
1132
- void *register_canceller_db_data) {
1133
- PARSER *parser = execute_cb_data;
1134
-
1135
- usec_t now = now_realtime_usec();
1136
-
1137
- struct inflight_function tmp = {
1138
- .started_ut = now,
1139
- .timeout_ut = now + timeout * USEC_PER_SEC + RRDFUNCTIONS_TIMEOUT_EXTENSION_UT,
1140
- .result_body_wb = result_body_wb,
1141
- .timeout = timeout,
1142
- .function = string_strdupz(function),
1143
- .result_cb = result_cb,
1144
- .result_cb_data = result_cb_data,
1145
- .payload = NULL,
1146
- .parser = parser,
1147
- };
1148
-
1149
- uuid_t uuid;
1150
- uuid_generate_random(uuid);
1151
-
1152
- char transaction[UUID_STR_LEN];
1153
- uuid_unparse_lower(uuid, transaction);
1154
-
1155
- dictionary_write_lock(parser->inflight.functions);
1156
-
1157
- // if there is any error, our dictionary callbacks will call the caller callback to notify
1158
- // the caller about the error - no need for error handling here.
1159
- void *t = dictionary_set(parser->inflight.functions, transaction, &tmp, sizeof(struct inflight_function));
1160
- if(register_canceller_cb)
1161
- register_canceller_cb(register_canceller_db_data, pluginsd_function_cancel, t);
1162
-
1163
- if(!parser->inflight.smaller_timeout || tmp.timeout_ut < parser->inflight.smaller_timeout)
1164
- parser->inflight.smaller_timeout = tmp.timeout_ut;
1165
-
1166
- // garbage collect stale inflight functions
1167
- if(parser->inflight.smaller_timeout < now)
1168
- inflight_functions_garbage_collect(parser, now);
1169
-
1170
- dictionary_write_unlock(parser->inflight.functions);
1171
-
1172
- return HTTP_RESP_OK;
1173
-}
1174
-
1175
-static inline PARSER_RC pluginsd_function(char **words, size_t num_words, PARSER *parser) {
1176
- // a plugin or a child is registering a function
1177
-
1178
- bool global = false;
1179
- size_t i = 1;
1180
- if(num_words >= 2 && strcmp(get_word(words, num_words, 1), "GLOBAL") == 0) {
1181
- i++;
1182
- global = true;
1183
- }
1184
-
1185
- char *name = get_word(words, num_words, i++);
1186
- char *timeout_s = get_word(words, num_words, i++);
1187
- char *help = get_word(words, num_words, i++);
1188
-
1189
- RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_FUNCTION);
1190
- if(!host) return PARSER_RC_ERROR;
1191
-
1192
- RRDSET *st = (global)? NULL: pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_FUNCTION, PLUGINSD_KEYWORD_CHART);
1193
- if(!st) global = true;
1194
-
1195
- if (unlikely(!timeout_s || !name || !help || (!global && !st))) {
1196
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s' got a FUNCTION, without providing the required data (global = '%s', name = '%s', timeout = '%s', help = '%s'). Ignoring it.",
1197
- rrdhost_hostname(host),
1198
- st?rrdset_id(st):"(unset)",
1199
- global?"yes":"no",
1200
- name?name:"(unset)",
1201
- timeout_s?timeout_s:"(unset)",
1202
- help?help:"(unset)"
1203
- );
1204
- return PARSER_RC_ERROR;
1205
- }
1206
-
1207
- int timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT;
1208
- if (timeout_s && *timeout_s) {
1209
- timeout = str2i(timeout_s);
1210
- if (unlikely(timeout <= 0))
1211
- timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT;
1212
- }
1213
-
1214
- rrd_function_add(host, st, name, timeout, help, false, pluginsd_function_execute_cb, parser);
1215
-
1216
- parser->user.data_collections_count++;
1217
-
1218
- return PARSER_RC_OK;
1219
-}
1220
-
1221
-static void pluginsd_function_result_end(struct parser *parser, void *action_data) {
1222
- STRING *key = action_data;
1223
- if(key)
1224
- dictionary_del(parser->inflight.functions, string2str(key));
1225
- string_freez(key);
1226
-
1227
- parser->user.data_collections_count++;
1228
-}
1229
-
1230
-static inline PARSER_RC pluginsd_function_result_begin(char **words, size_t num_words, PARSER *parser) {
1231
- char *key = get_word(words, num_words, 1);
1232
- char *status = get_word(words, num_words, 2);
1233
- char *format = get_word(words, num_words, 3);
1234
- char *expires = get_word(words, num_words, 4);
1235
-
1236
- if (unlikely(!key || !*key || !status || !*status || !format || !*format || !expires || !*expires)) {
1237
- netdata_log_error("got a " PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN " without providing the required data (key = '%s', status = '%s', format = '%s', expires = '%s')."
1238
- , key ? key : "(unset)"
1239
- , status ? status : "(unset)"
1240
- , format ? format : "(unset)"
1241
- , expires ? expires : "(unset)"
1242
- );
1243
- }
1244
-
1245
- int code = (status && *status) ? str2i(status) : 0;
1246
- if (code <= 0)
1247
- code = HTTP_RESP_BACKEND_RESPONSE_INVALID;
1248
-
1249
- time_t expiration = (expires && *expires) ? str2l(expires) : 0;
1250
-
1251
- struct inflight_function *pf = NULL;
1252
-
1253
- if(key && *key)
1254
- pf = (struct inflight_function *)dictionary_get(parser->inflight.functions, key);
1255
-
1256
- if(!pf) {
1257
- netdata_log_error("got a " PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN " for transaction '%s', but the transaction is not found.", key?key:"(unset)");
1258
- }
1259
- else {
1260
- if(format && *format)
1261
- pf->result_body_wb->content_type = functions_format_to_content_type(format);
1262
-
1263
- pf->code = code;
1264
-
1265
- pf->result_body_wb->expires = expiration;
1266
- if(expiration <= now_realtime_sec())
1267
- buffer_no_cacheable(pf->result_body_wb);
1268
- else
1269
- buffer_cacheable(pf->result_body_wb);
1270
- }
1271
-
1272
- parser->defer.response = (pf) ? pf->result_body_wb : NULL;
1273
- parser->defer.end_keyword = PLUGINSD_KEYWORD_FUNCTION_RESULT_END;
1274
- parser->defer.action = pluginsd_function_result_end;
1275
- parser->defer.action_data = string_strdupz(key); // it is ok is key is NULL
1276
- parser->flags |= PARSER_DEFER_UNTIL_KEYWORD;
1277
-
1278
- return PARSER_RC_OK;
1279
-}
1280
-
507
// ----------------------------------------------------------------------------
508
509
static inline PARSER_RC pluginsd_variable(char **words, size_t num_words, PARSER *parser) {
698
699
rrdlabels_add(parser->user.chart_rrdlabels_linked_temporarily, name, value, str2l(label_source));
700
1475
- return PARSER_RC_OK;
1476
-}
1477
-
1478
-static inline PARSER_RC pluginsd_clabel_commit(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser) {
1479
- RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_CLABEL_COMMIT);
1480
- if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1481
-
1482
- RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_CLABEL_COMMIT, PLUGINSD_KEYWORD_BEGIN);
1483
- if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1484
-
1485
- netdata_log_debug(D_PLUGINSD, "requested to commit chart labels");
1486
-
1487
- if(!parser->user.chart_rrdlabels_linked_temporarily) {
1488
- netdata_log_error("PLUGINSD: 'host:%s' got CLABEL_COMMIT, without a CHART or BEGIN. Ignoring it.", rrdhost_hostname(host));
1489
- return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1490
- }
1491
-
1492
- rrdlabels_remove_all_unmarked(parser->user.chart_rrdlabels_linked_temporarily);
1493
-
1494
- rrdset_flag_set(st, RRDSET_FLAG_METADATA_UPDATE);
1495
- rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
1496
- rrdset_metadata_updated(st);
1497
-
1498
- parser->user.chart_rrdlabels_linked_temporarily = NULL;
1499
- return PARSER_RC_OK;
1500
-}
1501
-
1502
-static inline PARSER_RC pluginsd_replay_begin(char **words, size_t num_words, PARSER *parser) {
1503
- int idx = 1;
1504
- ssize_t slot = pluginsd_parse_rrd_slot(words, num_words);
1505
- if(slot >= 0) idx++;
1506
-
1507
- char *id = get_word(words, num_words, idx++);
1508
- char *start_time_str = get_word(words, num_words, idx++);
1509
- char *end_time_str = get_word(words, num_words, idx++);
1510
- char *child_now_str = get_word(words, num_words, idx++);
1511
-
1512
- RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1513
- if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1514
-
1515
- RRDSET *st;
1516
- if (likely(!id || !*id))
1517
- st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_BEGIN, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1518
- else
1519
- st = pluginsd_rrdset_cache_get_from_slot(parser, host, id, slot, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1520
-
1521
- if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1522
-
1523
- if(!pluginsd_set_scope_chart(parser, st, PLUGINSD_KEYWORD_REPLAY_BEGIN))
1524
- return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1525
-
1526
- if(start_time_str && end_time_str) {
1527
- time_t start_time = (time_t) str2ull_encoded(start_time_str);
1528
- time_t end_time = (time_t) str2ull_encoded(end_time_str);
1529
-
1530
- time_t wall_clock_time = 0, tolerance;
1531
- bool wall_clock_comes_from_child; (void)wall_clock_comes_from_child;
1532
- if(child_now_str) {
1533
- wall_clock_time = (time_t) str2ull_encoded(child_now_str);
1534
- tolerance = st->update_every + 1;
1535
- wall_clock_comes_from_child = true;
1536
- }
1537
-
1538
- if(wall_clock_time <= 0) {
1539
- wall_clock_time = now_realtime_sec();
1540
- tolerance = st->update_every + 5;
1541
- wall_clock_comes_from_child = false;
1542
- }
1543
-
1544
-#ifdef NETDATA_LOG_REPLICATION_REQUESTS
1545
- internal_error(
1546
- (!st->replay.start_streaming && (end_time < st->replay.after || start_time > st->replay.before)),
1547
- "REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN " from %ld to %ld, which does not match our request (%ld to %ld).",
1548
- rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time, st->replay.after, st->replay.before);
1549
-
1550
- internal_error(
1551
- true,
1552
- "REPLAY: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN " from %ld to %ld, child wall clock is %ld (%s), had requested %ld to %ld",
1553
- rrdhost_hostname(st->rrdhost), rrdset_id(st),
1554
- start_time, end_time, wall_clock_time, wall_clock_comes_from_child ? "from child" : "parent time",
1555
- st->replay.after, st->replay.before);
1556
-#endif
1557
-
1558
- if(start_time && end_time && start_time < wall_clock_time + tolerance && end_time < wall_clock_time + tolerance && start_time < end_time) {
1559
- if (unlikely(end_time - start_time != st->update_every))
1560
- rrdset_set_update_every_s(st, end_time - start_time);
1561
-
1562
- st->last_collected_time.tv_sec = end_time;
1563
- st->last_collected_time.tv_usec = 0;
1564
-
1565
- st->last_updated.tv_sec = end_time;
1566
- st->last_updated.tv_usec = 0;
1567
-
1568
- st->counter++;
1569
- st->counter_done++;
1570
-
1571
- // these are only needed for db mode RAM, SAVE, MAP, ALLOC
1572
- st->db.current_entry++;
1573
- if(st->db.current_entry >= st->db.entries)
1574
- st->db.current_entry -= st->db.entries;
1575
-
1576
- parser->user.replay.start_time = start_time;
1577
- parser->user.replay.end_time = end_time;
1578
- parser->user.replay.start_time_ut = (usec_t) start_time * USEC_PER_SEC;
1579
- parser->user.replay.end_time_ut = (usec_t) end_time * USEC_PER_SEC;
1580
- parser->user.replay.wall_clock_time = wall_clock_time;
1581
- parser->user.replay.rset_enabled = true;
1582
-
1583
- return PARSER_RC_OK;
1584
- }
1585
-
1586
- netdata_log_error("PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN
1587
- " from %ld to %ld, but timestamps are invalid "
1588
- "(now is %ld [%s], tolerance %ld). Ignoring " PLUGINSD_KEYWORD_REPLAY_SET,
1589
- rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time,
1590
- wall_clock_time, wall_clock_comes_from_child ? "child wall clock" : "parent wall clock",
1591
- tolerance);
1592
- }
1593
-
1594
- // the child sends an RBEGIN without any parameters initially
1595
- // setting rset_enabled to false, means the RSET should not store any metrics
1596
- // to store metrics, the RBEGIN needs to have timestamps
1597
- parser->user.replay.start_time = 0;
1598
- parser->user.replay.end_time = 0;
1599
- parser->user.replay.start_time_ut = 0;
1600
- parser->user.replay.end_time_ut = 0;
1601
- parser->user.replay.wall_clock_time = 0;
1602
- parser->user.replay.rset_enabled = false;
1603
- return PARSER_RC_OK;
1604
-}
1605
-
1606
-static inline SN_FLAGS pluginsd_parse_storage_number_flags(const char *flags_str) {
1607
- SN_FLAGS flags = SN_FLAG_NONE;
1608
-
1609
- char c;
1610
- while ((c = *flags_str++)) {
1611
- switch (c) {
1612
- case 'A':
1613
- flags |= SN_FLAG_NOT_ANOMALOUS;
1614
- break;
1615
-
1616
- case 'R':
1617
- flags |= SN_FLAG_RESET;
1618
- break;
1619
-
1620
- case 'E':
1621
- flags = SN_EMPTY_SLOT;
1622
- return flags;
1623
-
1624
- default:
1625
- internal_error(true, "Unknown SN_FLAGS flag '%c'", c);
1626
- break;
1627
- }
1628
- }
1629
-
1630
- return flags;
1631
-}
1632
-
1633
-static inline PARSER_RC pluginsd_replay_set(char **words, size_t num_words, PARSER *parser) {
1634
- int idx = 1;
1635
- ssize_t slot = pluginsd_parse_rrd_slot(words, num_words);
1636
- if(slot >= 0) idx++;
1637
-
1638
- char *dimension = get_word(words, num_words, idx++);
1639
- char *value_str = get_word(words, num_words, idx++);
1640
- char *flags_str = get_word(words, num_words, idx++);
1641
-
1642
- RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_REPLAY_SET);
1643
- if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1644
-
1645
- RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_SET, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1646
- if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1647
-
1648
- if(!parser->user.replay.rset_enabled) {
1649
- nd_log_limit_static_thread_var(erl, 1, 0);
1650
- nd_log_limit(&erl, NDLS_COLLECTORS, NDLP_ERR,
1651
- "PLUGINSD: 'host:%s/chart:%s' got a %s but it is disabled by %s errors",
1652
- rrdhost_hostname(host), rrdset_id(st), PLUGINSD_KEYWORD_REPLAY_SET, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1653
-
1654
- // we have to return OK here
1655
- return PARSER_RC_OK;
1656
- }
1657
-
1658
- RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, slot, PLUGINSD_KEYWORD_REPLAY_SET);
1659
- if(!rd) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1660
-
1661
- st->pluginsd.set = true;
1662
-
1663
- if (unlikely(!parser->user.replay.start_time || !parser->user.replay.end_time)) {
1664
- netdata_log_error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a %s with invalid timestamps %ld to %ld from a %s. Disabling it.",
1665
- rrdhost_hostname(host),
1666
- rrdset_id(st),
1667
- dimension,
1668
- PLUGINSD_KEYWORD_REPLAY_SET,
1669
- parser->user.replay.start_time,
1670
- parser->user.replay.end_time,
1671
- PLUGINSD_KEYWORD_REPLAY_BEGIN);
1672
- return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1673
- }
1674
-
1675
- if (unlikely(!value_str || !*value_str))
1676
- value_str = "NAN";
1677
-
1678
- if(unlikely(!flags_str))
1679
- flags_str = "";
1680
-
1681
- if (likely(value_str)) {
1682
- RRDDIM_FLAGS rd_flags = rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE | RRDDIM_FLAG_ARCHIVED);
1683
-
1684
- if(!(rd_flags & RRDDIM_FLAG_ARCHIVED)) {
1685
- NETDATA_DOUBLE value = str2ndd_encoded(value_str, NULL);
1686
- SN_FLAGS flags = pluginsd_parse_storage_number_flags(flags_str);
1687
-
1688
- if (!netdata_double_isnumber(value) || (flags == SN_EMPTY_SLOT)) {
1689
- value = NAN;
1690
- flags = SN_EMPTY_SLOT;
1691
- }
1692
-
1693
- rrddim_store_metric(rd, parser->user.replay.end_time_ut, value, flags);
1694
- rd->collector.last_collected_time.tv_sec = parser->user.replay.end_time;
1695
- rd->collector.last_collected_time.tv_usec = 0;
1696
- rd->collector.counter++;
1697
- }
1698
- else {
1699
- nd_log_limit_static_global_var(erl, 1, 0);
1700
- nd_log_limit(&erl, NDLS_COLLECTORS, NDLP_WARNING,
1701
- "PLUGINSD: 'host:%s/chart:%s/dim:%s' has the ARCHIVED flag set, but it is replicated. "
1702
- "Ignoring data.",
1703
- rrdhost_hostname(st->rrdhost), rrdset_id(st), rrddim_name(rd));
1704
- }
1705
- }
1706
-
1707
- return PARSER_RC_OK;
1708
-}
1709
-
1710
-static inline PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words, PARSER *parser) {
1711
- if(parser->user.replay.rset_enabled == false)
1712
- return PARSER_RC_OK;
1713
-
1714
- int idx = 1;
1715
- ssize_t slot = pluginsd_parse_rrd_slot(words, num_words);
1716
- if(slot >= 0) idx++;
1717
-
1718
- char *dimension = get_word(words, num_words, idx++);
1719
- char *last_collected_ut_str = get_word(words, num_words, idx++);
1720
- char *last_collected_value_str = get_word(words, num_words, idx++);
1721
- char *last_calculated_value_str = get_word(words, num_words, idx++);
1722
- char *last_stored_value_str = get_word(words, num_words, idx++);
1723
-
1724
- RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE);
1725
- if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1726
-
1727
- RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1728
- if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1729
-
1730
- if(st->pluginsd.set) {
1731
- // reset pos to reuse the same RDAs
1732
- st->pluginsd.pos = 0;
1733
- st->pluginsd.set = false;
1734
- }
1735
-
1736
- RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, slot, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE);
1737
- if(!rd) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1738
-
1739
- usec_t dim_last_collected_ut = (usec_t)rd->collector.last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)rd->collector.last_collected_time.tv_usec;
1740
- usec_t last_collected_ut = last_collected_ut_str ? str2ull_encoded(last_collected_ut_str) : 0;
1741
- if(last_collected_ut > dim_last_collected_ut) {
1742
- rd->collector.last_collected_time.tv_sec = (time_t)(last_collected_ut / USEC_PER_SEC);
1743
- rd->collector.last_collected_time.tv_usec = (last_collected_ut % USEC_PER_SEC);
1744
- }
1745
-
1746
- rd->collector.last_collected_value = last_collected_value_str ? str2ll_encoded(last_collected_value_str) : 0;
1747
- rd->collector.last_calculated_value = last_calculated_value_str ? str2ndd_encoded(last_calculated_value_str, NULL) : 0;
1748
- rd->collector.last_stored_value = last_stored_value_str ? str2ndd_encoded(last_stored_value_str, NULL) : 0.0;
1749
-
1750
- return PARSER_RC_OK;
1751
-}
1752
-
1753
-static inline PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words, PARSER *parser) {
1754
- if(parser->user.replay.rset_enabled == false)
1755
- return PARSER_RC_OK;
1756
-
1757
- char *last_collected_ut_str = get_word(words, num_words, 1);
1758
- char *last_updated_ut_str = get_word(words, num_words, 2);
1759
-
1760
- RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE);
1761
- if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1762
-
1763
- RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE,
1764
- PLUGINSD_KEYWORD_REPLAY_BEGIN);
1765
- if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1766
-
1767
- usec_t chart_last_collected_ut = (usec_t)st->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)st->last_collected_time.tv_usec;
1768
- usec_t last_collected_ut = last_collected_ut_str ? str2ull_encoded(last_collected_ut_str) : 0;
1769
- if(last_collected_ut > chart_last_collected_ut) {
1770
- st->last_collected_time.tv_sec = (time_t)(last_collected_ut / USEC_PER_SEC);
1771
- st->last_collected_time.tv_usec = (last_collected_ut % USEC_PER_SEC);
1772
- }
1773
-
1774
- usec_t chart_last_updated_ut = (usec_t)st->last_updated.tv_sec * USEC_PER_SEC + (usec_t)st->last_updated.tv_usec;
1775
- usec_t last_updated_ut = last_updated_ut_str ? str2ull_encoded(last_updated_ut_str) : 0;
1776
- if(last_updated_ut > chart_last_updated_ut) {
1777
- st->last_updated.tv_sec = (time_t)(last_updated_ut / USEC_PER_SEC);
1778
- st->last_updated.tv_usec = (last_updated_ut % USEC_PER_SEC);
1779
- }
1780
-
1781
- st->counter++;
1782
- st->counter_done++;
1783
-
1784
- return PARSER_RC_OK;
1785
-}
1786
-
1787
-static inline PARSER_RC pluginsd_replay_end(char **words, size_t num_words, PARSER *parser) {
1788
- if (num_words < 7) { // accepts 7, but the 7th is optional
1789
- netdata_log_error("REPLAY: malformed " PLUGINSD_KEYWORD_REPLAY_END " command");
1790
- return PARSER_RC_ERROR;
1791
- }
1792
-
1793
- const char *update_every_child_txt = get_word(words, num_words, 1);
1794
- const char *first_entry_child_txt = get_word(words, num_words, 2);
1795
- const char *last_entry_child_txt = get_word(words, num_words, 3);
1796
- const char *start_streaming_txt = get_word(words, num_words, 4);
1797
- const char *first_entry_requested_txt = get_word(words, num_words, 5);
1798
- const char *last_entry_requested_txt = get_word(words, num_words, 6);
1799
- const char *child_world_time_txt = get_word(words, num_words, 7); // optional
1800
-
1801
- time_t update_every_child = (time_t) str2ull_encoded(update_every_child_txt);
1802
- time_t first_entry_child = (time_t) str2ull_encoded(first_entry_child_txt);
1803
- time_t last_entry_child = (time_t) str2ull_encoded(last_entry_child_txt);
1804
-
1805
- bool start_streaming = (strcmp(start_streaming_txt, "true") == 0);
1806
- time_t first_entry_requested = (time_t) str2ull_encoded(first_entry_requested_txt);
1807
- time_t last_entry_requested = (time_t) str2ull_encoded(last_entry_requested_txt);
1808
-
1809
- // the optional child world time
1810
- time_t child_world_time = (child_world_time_txt && *child_world_time_txt) ? (time_t) str2ull_encoded(
1811
- child_world_time_txt) : now_realtime_sec();
1812
-
1813
- RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_REPLAY_END);
1814
- if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1815
-
1816
- RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_END, PLUGINSD_KEYWORD_REPLAY_BEGIN);
1817
- if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
1818
-
1819
-#ifdef NETDATA_LOG_REPLICATION_REQUESTS
1820
- internal_error(true,
1821
- "PLUGINSD REPLAY: 'host:%s/chart:%s': got a " PLUGINSD_KEYWORD_REPLAY_END " child db from %llu to %llu, start_streaming %s, had requested from %llu to %llu, wall clock %llu",
1822
- rrdhost_hostname(host), rrdset_id(st),
1823
- (unsigned long long)first_entry_child, (unsigned long long)last_entry_child,
1824
- start_streaming?"true":"false",
1825
- (unsigned long long)first_entry_requested, (unsigned long long)last_entry_requested,
1826
- (unsigned long long)child_world_time
1827
- );
1828
-#endif
1829
-
1830
- parser->user.data_collections_count++;
1831
-
1832
- if(parser->user.replay.rset_enabled && st->rrdhost->receiver) {
1833
- time_t now = now_realtime_sec();
1834
- time_t started = st->rrdhost->receiver->replication_first_time_t;
1835
- time_t current = parser->user.replay.end_time;
1836
-
1837
- if(started && current > started) {
1838
- host->rrdpush_receiver_replication_percent = (NETDATA_DOUBLE) (current - started) * 100.0 / (NETDATA_DOUBLE) (now - started);
1839
- worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION,
1840
- host->rrdpush_receiver_replication_percent);
1841
- }
1842
- }
1843
-
1844
- parser->user.replay.start_time = 0;
1845
- parser->user.replay.end_time = 0;
1846
- parser->user.replay.start_time_ut = 0;
1847
- parser->user.replay.end_time_ut = 0;
1848
- parser->user.replay.wall_clock_time = 0;
1849
- parser->user.replay.rset_enabled = false;
1850
-
1851
- st->counter++;
1852
- st->counter_done++;
1853
- store_metric_collection_completed();
1854
-
1855
-#ifdef NETDATA_LOG_REPLICATION_REQUESTS
1856
- st->replay.start_streaming = false;
1857
- st->replay.after = 0;
1858
- st->replay.before = 0;
1859
- if(start_streaming)
1860
- st->replay.log_next_data_collection = true;
1861
-#endif
1862
-
1863
- if (start_streaming) {
1864
- if (st->update_every != update_every_child)
1865
- rrdset_set_update_every_s(st, update_every_child);
701
+ return PARSER_RC_OK;
702
+}
703
1867
- if(rrdset_flag_check(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS)) {
1868
- rrdset_flag_set(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED);
1869
- rrdset_flag_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS);
1870
- rrdset_flag_clear(st, RRDSET_FLAG_SYNC_CLOCK);
1871
- rrdhost_receiver_replicating_charts_minus_one(st->rrdhost);
1872
- }
1873
-#ifdef NETDATA_LOG_REPLICATION_REQUESTS
1874
- else
1875
- internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_END " with enable_streaming = true, but there is no replication in progress for this chart.",
1876
- rrdhost_hostname(host), rrdset_id(st));
1877
-#endif
704
+static inline PARSER_RC pluginsd_clabel_commit(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser) {
705
+ RRDHOST *host = pluginsd_require_scope_host(parser, PLUGINSD_KEYWORD_CLABEL_COMMIT);
706
+ if(!host) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
707
1879
- pluginsd_clear_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_END);
708
+ RRDSET *st = pluginsd_require_scope_chart(parser, PLUGINSD_KEYWORD_CLABEL_COMMIT, PLUGINSD_KEYWORD_BEGIN);
709
+ if(!st) return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
710
1881
- host->rrdpush_receiver_replication_percent = 100.0;
1882
- worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION, host->rrdpush_receiver_replication_percent);
711
+ netdata_log_debug(D_PLUGINSD, "requested to commit chart labels");
712
1884
- return PARSER_RC_OK;
713
+ if(!parser->user.chart_rrdlabels_linked_temporarily) {
714
+ netdata_log_error("PLUGINSD: 'host:%s' got CLABEL_COMMIT, without a CHART or BEGIN. Ignoring it.", rrdhost_hostname(host));
715
+ return PLUGINSD_DISABLE_PLUGIN(parser, NULL, NULL);
716
}
717
1887
- pluginsd_clear_scope_chart(parser, PLUGINSD_KEYWORD_REPLAY_END);
718
+ rrdlabels_remove_all_unmarked(parser->user.chart_rrdlabels_linked_temporarily);
719
1889
- rrdcontext_updated_retention_rrdset(st);
720
+ rrdset_flag_set(st, RRDSET_FLAG_METADATA_UPDATE);
721
+ rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
722
+ rrdset_metadata_updated(st);
723
1891
- bool ok = replicate_chart_request(send_to_plugin, parser, host, st,
1892
- first_entry_child, last_entry_child, child_world_time,
1893
- first_entry_requested, last_entry_requested);
1894
- return ok ? PARSER_RC_OK : PARSER_RC_ERROR;
724
+ parser->user.chart_rrdlabels_linked_temporarily = NULL;
725
+ return PARSER_RC_OK;
726
}
727
728
static inline PARSER_RC pluginsd_begin_v2(char **words, size_t num_words, PARSER *parser) {
991
return PARSER_RC_OK;
992
}
993
2163
-void pluginsd_cleanup_v2(PARSER *parser) {
2164
- // this is called when the thread is stopped while processing
2165
- pluginsd_clear_scope_chart(parser, "THREAD CLEANUP");
2166
-}
2167
-
994
static inline PARSER_RC pluginsd_end_v2(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser) {
995
timing_init();
996
1069
return PARSER_RC_STOP;
1070
}
1071
2246
-struct mutex_cond {
2247
- pthread_mutex_t lock;
2248
- pthread_cond_t cond;
2249
- int rc;
2250
-};
2251
-
2252
-static void virt_fnc_got_data_cb(BUFFER *wb __maybe_unused, int code, void *callback_data)
2253
-{
2254
- struct mutex_cond *ctx = callback_data;
2255
- pthread_mutex_lock(&ctx->lock);
2256
- ctx->rc = code;
2257
- pthread_cond_broadcast(&ctx->cond);
2258
- pthread_mutex_unlock(&ctx->lock);
2259
-}
2260
-
2261
-#define VIRT_FNC_TIMEOUT 1
2262
-#define VIRT_FNC_BUF_SIZE (4096)
2263
-void call_virtual_function_async(BUFFER *wb, RRDHOST *host, const char *name, const char *payload, rrd_function_result_callback_t callback, void *callback_data) {
2264
- PARSER *parser = NULL;
2265
-
2266
- //TODO simplify (as we really need only first parameter to get plugin name maybe we can avoid parsing all)
2267
- char *words[PLUGINSD_MAX_WORDS];
2268
- char *function_with_params = strdupz(name);
2269
- size_t num_words = quoted_strings_splitter(function_with_params, words, PLUGINSD_MAX_WORDS, isspace_map_pluginsd);
2270
-
2271
- if (num_words < 2) {
2272
- netdata_log_error("PLUGINSD: virtual function name is empty.");
2273
- freez(function_with_params);
2274
- return;
2275
- }
2276
-
2277
- const DICTIONARY_ITEM *cpi = dictionary_get_and_acquire_item(host->configurable_plugins, get_word(words, num_words, 1));
2278
- if (unlikely(cpi == NULL)) {
2279
- netdata_log_error("PLUGINSD: virtual function plugin '%s' not found.", name);
2280
- freez(function_with_params);
2281
- return;
2282
- }
2283
- struct configurable_plugin *cp = dictionary_acquired_item_value(cpi);
2284
- parser = (PARSER *)cp->cb_usr_ctx;
2285
-
2286
- BUFFER *function_out = buffer_create(VIRT_FNC_BUF_SIZE, NULL);
2287
- // if we are forwarding this to a plugin (as opposed to streaming/child) we have to remove the first parameter (plugin_name)
2288
- buffer_strcat(function_out, get_word(words, num_words, 0));
2289
- for (size_t i = 1; i < num_words; i++) {
2290
- if (i == 1 && SERVING_PLUGINSD(parser))
2291
- continue;
2292
- buffer_sprintf(function_out, " %s", get_word(words, num_words, i));
2293
- }
2294
- freez(function_with_params);
2295
-
2296
- usec_t now = now_realtime_usec();
2297
-
2298
- struct inflight_function tmp = {
2299
- .started_ut = now,
2300
- .timeout_ut = now + VIRT_FNC_TIMEOUT + USEC_PER_SEC,
2301
- .result_body_wb = wb,
2302
- .timeout = VIRT_FNC_TIMEOUT * 10,
2303
- .function = string_strdupz(buffer_tostring(function_out)),
2304
- .result_cb = callback,
2305
- .result_cb_data = callback_data,
2306
- .payload = payload != NULL ? strdupz(payload) : NULL,
2307
- .virtual = true,
2308
- };
2309
- buffer_free(function_out);
2310
-
2311
- uuid_t uuid;
2312
- uuid_generate_time(uuid);
2313
-
2314
- char key[UUID_STR_LEN];
2315
- uuid_unparse_lower(uuid, key);
2316
-
2317
- dictionary_write_lock(parser->inflight.functions);
2318
-
2319
- // if there is any error, our dictionary callbacks will call the caller callback to notify
2320
- // the caller about the error - no need for error handling here.
2321
- dictionary_set(parser->inflight.functions, key, &tmp, sizeof(struct inflight_function));
2322
-
2323
- if(!parser->inflight.smaller_timeout || tmp.timeout_ut < parser->inflight.smaller_timeout)
2324
- parser->inflight.smaller_timeout = tmp.timeout_ut;
2325
-
2326
- // garbage collect stale inflight functions
2327
- if(parser->inflight.smaller_timeout < now)
2328
- inflight_functions_garbage_collect(parser, now);
2329
-
2330
- dictionary_write_unlock(parser->inflight.functions);
2331
-}
2332
-
2333
-
2334
-dyncfg_config_t call_virtual_function_blocking(PARSER *parser, const char *name, int *rc, const char *payload) {
2335
- usec_t now = now_realtime_usec();
2336
- BUFFER *wb = buffer_create(VIRT_FNC_BUF_SIZE, NULL);
2337
-
2338
- struct mutex_cond cond = {
2339
- .lock = PTHREAD_MUTEX_INITIALIZER,
2340
- .cond = PTHREAD_COND_INITIALIZER
2341
- };
2342
-
2343
- struct inflight_function tmp = {
2344
- .started_ut = now,
2345
- .timeout_ut = now + VIRT_FNC_TIMEOUT + USEC_PER_SEC,
2346
- .result_body_wb = wb,
2347
- .timeout = VIRT_FNC_TIMEOUT,
2348
- .function = string_strdupz(name),
2349
- .result_cb = virt_fnc_got_data_cb,
2350
- .result_cb_data = &cond,
2351
- .payload = payload != NULL ? strdupz(payload) : NULL,
2352
- .virtual = true,
2353
- };
2354
-
2355
- uuid_t uuid;
2356
- uuid_generate_time(uuid);
2357
-
2358
- char key[UUID_STR_LEN];
2359
- uuid_unparse_lower(uuid, key);
2360
-
2361
- dictionary_write_lock(parser->inflight.functions);
2362
-
2363
- // if there is any error, our dictionary callbacks will call the caller callback to notify
2364
- // the caller about the error - no need for error handling here.
2365
- dictionary_set(parser->inflight.functions, key, &tmp, sizeof(struct inflight_function));
2366
-
2367
- if(!parser->inflight.smaller_timeout || tmp.timeout_ut < parser->inflight.smaller_timeout)
2368
- parser->inflight.smaller_timeout = tmp.timeout_ut;
2369
-
2370
- // garbage collect stale inflight functions
2371
- if(parser->inflight.smaller_timeout < now)
2372
- inflight_functions_garbage_collect(parser, now);
2373
-
2374
- dictionary_write_unlock(parser->inflight.functions);
2375
-
2376
- struct timespec tp;
2377
- clock_gettime(CLOCK_REALTIME, &tp);
2378
- tp.tv_sec += (time_t)VIRT_FNC_TIMEOUT;
2379
-
2380
- pthread_mutex_lock(&cond.lock);
2381
-
2382
- int ret = pthread_cond_timedwait(&cond.cond, &cond.lock, &tp);
2383
- if (ret == ETIMEDOUT)
2384
- netdata_log_error("PLUGINSD: DYNCFG virtual function %s timed out", name);
2385
-
2386
- pthread_mutex_unlock(&cond.lock);
2387
-
2388
- dyncfg_config_t cfg;
2389
- cfg.data = strdupz(buffer_tostring(wb));
2390
- cfg.data_size = buffer_strlen(wb);
2391
-
2392
- if (rc != NULL)
2393
- *rc = cond.rc;
2394
-
2395
- buffer_free(wb);
2396
- return cfg;
2397
-}
2398
-
2399
-#define CVF_MAX_LEN (1024)
2400
-static dyncfg_config_t get_plugin_config_cb(void *usr_ctx, const char *plugin_name)
2401
-{
2402
- PARSER *parser = usr_ctx;
2403
-
2404
- if (SERVING_STREAMING(parser)) {
2405
- char buf[CVF_MAX_LEN + 1];
2406
- snprintfz(buf, CVF_MAX_LEN, FUNCTION_NAME_GET_PLUGIN_CONFIG " %s", plugin_name);
2407
- return call_virtual_function_blocking(parser, buf, NULL, NULL);
2408
- }
2409
-
2410
- return call_virtual_function_blocking(parser, FUNCTION_NAME_GET_PLUGIN_CONFIG, NULL, NULL);
2411
-}
2412
-
2413
-static dyncfg_config_t get_plugin_config_schema_cb(void *usr_ctx, const char *plugin_name)
2414
-{
2415
- PARSER *parser = usr_ctx;
2416
-
2417
- if (SERVING_STREAMING(parser)) {
2418
- char buf[CVF_MAX_LEN + 1];
2419
- snprintfz(buf, CVF_MAX_LEN, FUNCTION_NAME_GET_PLUGIN_CONFIG_SCHEMA " %s", plugin_name);
2420
- return call_virtual_function_blocking(parser, buf, NULL, NULL);
2421
- }
2422
-
2423
- return call_virtual_function_blocking(parser, "get_plugin_config_schema", NULL, NULL);
2424
-}
2425
-
2426
-static dyncfg_config_t get_module_config_cb(void *usr_ctx, const char *plugin_name, const char *module_name)
2427
-{
2428
- PARSER *parser = usr_ctx;
2429
- BUFFER *wb = buffer_create(CVF_MAX_LEN, NULL);
2430
-
2431
- buffer_strcat(wb, FUNCTION_NAME_GET_MODULE_CONFIG);
2432
- if (SERVING_STREAMING(parser))
2433
- buffer_sprintf(wb, " %s", plugin_name);
2434
-
2435
- buffer_sprintf(wb, " %s", module_name);
2436
-
2437
- dyncfg_config_t ret = call_virtual_function_blocking(parser, buffer_tostring(wb), NULL, NULL);
2438
-
2439
- buffer_free(wb);
2440
-
2441
- return ret;
2442
-}
2443
-
2444
-static dyncfg_config_t get_module_config_schema_cb(void *usr_ctx, const char *plugin_name, const char *module_name)
2445
-{
2446
- PARSER *parser = usr_ctx;
2447
- BUFFER *wb = buffer_create(CVF_MAX_LEN, NULL);
2448
-
2449
- buffer_strcat(wb, FUNCTION_NAME_GET_MODULE_CONFIG_SCHEMA);
2450
- if (SERVING_STREAMING(parser))
2451
- buffer_sprintf(wb, " %s", plugin_name);
2452
-
2453
- buffer_sprintf(wb, " %s", module_name);
2454
-
2455
- dyncfg_config_t ret = call_virtual_function_blocking(parser, buffer_tostring(wb), NULL, NULL);
2456
-
2457
- buffer_free(wb);
2458
-
2459
- return ret;
2460
-}
2461
-
2462
-static dyncfg_config_t get_job_config_schema_cb(void *usr_ctx, const char *plugin_name, const char *module_name)
2463
-{
2464
- PARSER *parser = usr_ctx;
2465
- BUFFER *wb = buffer_create(CVF_MAX_LEN, NULL);
2466
-
2467
- buffer_strcat(wb, FUNCTION_NAME_GET_JOB_CONFIG_SCHEMA);
2468
-
2469
- if (SERVING_STREAMING(parser))
2470
- buffer_sprintf(wb, " %s", plugin_name);
2471
-
2472
- buffer_sprintf(wb, " %s", module_name);
2473
-
2474
- dyncfg_config_t ret = call_virtual_function_blocking(parser, buffer_tostring(wb), NULL, NULL);
2475
-
2476
- buffer_free(wb);
2477
-
2478
- return ret;
2479
-}
2480
-
2481
-static dyncfg_config_t get_job_config_cb(void *usr_ctx, const char *plugin_name, const char *module_name, const char* job_name)
2482
-{
2483
- PARSER *parser = usr_ctx;
2484
- BUFFER *wb = buffer_create(CVF_MAX_LEN, NULL);
2485
-
2486
- buffer_strcat(wb, FUNCTION_NAME_GET_JOB_CONFIG);
2487
-
2488
- if (SERVING_STREAMING(parser))
2489
- buffer_sprintf(wb, " %s", plugin_name);
2490
-
2491
- buffer_sprintf(wb, " %s %s", module_name, job_name);
2492
-
2493
- dyncfg_config_t ret = call_virtual_function_blocking(parser, buffer_tostring(wb), NULL, NULL);
2494
-
2495
- buffer_free(wb);
2496
-
2497
- return ret;
2498
-}
2499
-
2500
-enum set_config_result set_plugin_config_cb(void *usr_ctx, const char *plugin_name, dyncfg_config_t *cfg)
2501
-{
2502
- PARSER *parser = usr_ctx;
2503
- BUFFER *wb = buffer_create(CVF_MAX_LEN, NULL);
2504
-
2505
- buffer_strcat(wb, FUNCTION_NAME_SET_PLUGIN_CONFIG);
2506
-
2507
- if (SERVING_STREAMING(parser))
2508
- buffer_sprintf(wb, " %s", plugin_name);
2509
-
2510
- int rc;
2511
- call_virtual_function_blocking(parser, buffer_tostring(wb), &rc, cfg->data);
2512
-
2513
- buffer_free(wb);
2514
- if(rc != DYNCFG_VFNC_RET_CFG_ACCEPTED)
2515
- return SET_CONFIG_REJECTED;
2516
- return SET_CONFIG_ACCEPTED;
2517
-}
2518
-
2519
-enum set_config_result set_module_config_cb(void *usr_ctx, const char *plugin_name, const char *module_name, dyncfg_config_t *cfg)
2520
-{
2521
- PARSER *parser = usr_ctx;
2522
- BUFFER *wb = buffer_create(CVF_MAX_LEN, NULL);
2523
-
2524
- buffer_strcat(wb, FUNCTION_NAME_SET_MODULE_CONFIG);
2525
-
2526
- if (SERVING_STREAMING(parser))
2527
- buffer_sprintf(wb, " %s", plugin_name);
2528
-
2529
- buffer_sprintf(wb, " %s", module_name);
2530
-
2531
- int rc;
2532
- call_virtual_function_blocking(parser, buffer_tostring(wb), &rc, cfg->data);
2533
-
2534
- buffer_free(wb);
2535
-
2536
- if(rc != DYNCFG_VFNC_RET_CFG_ACCEPTED)
2537
- return SET_CONFIG_REJECTED;
2538
- return SET_CONFIG_ACCEPTED;
2539
-}
2540
-
2541
-enum set_config_result set_job_config_cb(void *usr_ctx, const char *plugin_name, const char *module_name, const char *job_name, dyncfg_config_t *cfg)
2542
-{
2543
- PARSER *parser = usr_ctx;
2544
- BUFFER *wb = buffer_create(CVF_MAX_LEN, NULL);
2545
-
2546
- buffer_strcat(wb, FUNCTION_NAME_SET_JOB_CONFIG);
2547
-
2548
- if (SERVING_STREAMING(parser))
2549
- buffer_sprintf(wb, " %s", plugin_name);
2550
-
2551
- buffer_sprintf(wb, " %s %s", module_name, job_name);
2552
-
2553
- int rc;
2554
- call_virtual_function_blocking(parser, buffer_tostring(wb), &rc, cfg->data);
2555
-
2556
- buffer_free(wb);
2557
-
2558
- if(rc != DYNCFG_VFNC_RET_CFG_ACCEPTED)
2559
- return SET_CONFIG_REJECTED;
2560
- return SET_CONFIG_ACCEPTED;
2561
-}
2562
-
2563
-enum set_config_result delete_job_cb(void *usr_ctx, const char *plugin_name ,const char *module_name, const char *job_name)
2564
-{
2565
- PARSER *parser = usr_ctx;
2566
- BUFFER *wb = buffer_create(CVF_MAX_LEN, NULL);
2567
-
2568
- buffer_strcat(wb, FUNCTION_NAME_DELETE_JOB);
2569
-
2570
- if (SERVING_STREAMING(parser))
2571
- buffer_sprintf(wb, " %s", plugin_name);
2572
-
2573
- buffer_sprintf(wb, " %s %s", module_name, job_name);
2574
-
2575
- int rc;
2576
- call_virtual_function_blocking(parser, buffer_tostring(wb), &rc, NULL);
2577
-
2578
- buffer_free(wb);
2579
-
2580
- if(rc != DYNCFG_VFNC_RET_CFG_ACCEPTED)
2581
- return SET_CONFIG_REJECTED;
2582
- return SET_CONFIG_ACCEPTED;
2583
-}
2584
-
2585
-
2586
-static inline PARSER_RC pluginsd_register_plugin(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser __maybe_unused) {
2587
- netdata_log_info("PLUGINSD: DYNCFG_ENABLE");
2588
-
2589
- if (unlikely (num_words != 2))
2590
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_ENABLE, "missing name parameter");
2591
-
2592
- struct configurable_plugin *cfg = callocz(1, sizeof(struct configurable_plugin));
2593
-
2594
- cfg->name = strdupz(words[1]);
2595
- cfg->set_config_cb = set_plugin_config_cb;
2596
- cfg->get_config_cb = get_plugin_config_cb;
2597
- cfg->get_config_schema_cb = get_plugin_config_schema_cb;
2598
- cfg->cb_usr_ctx = parser;
2599
-
2600
- const DICTIONARY_ITEM *di = register_plugin(parser->user.host->configurable_plugins, cfg, SERVING_PLUGINSD(parser));
2601
- if (unlikely(di == NULL)) {
2602
- freez(cfg->name);
2603
- freez(cfg);
2604
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_ENABLE, "error registering plugin");
2605
- }
2606
-
2607
- if (SERVING_PLUGINSD(parser)) {
2608
- // this is optimization for pluginsd to avoid extra dictionary lookup
2609
- // as we know which plugin is comunicating with us
2610
- parser->user.cd->cfg_dict_item = di;
2611
- parser->user.cd->configuration = cfg;
2612
- } else {
2613
- // register_plugin keeps the item acquired, so we need to release it
2614
- dictionary_acquired_item_release(parser->user.host->configurable_plugins, di);
2615
- }
2616
-
2617
- rrdpush_send_dyncfg_enable(parser->user.host, cfg->name);
2618
-
2619
- return PARSER_RC_OK;
2620
-}
2621
-
2622
-#define LOG_MSG_SIZE (1024)
2623
-#define MODULE_NAME_IDX (SERVING_PLUGINSD(parser) ? 1 : 2)
2624
-#define MODULE_TYPE_IDX (SERVING_PLUGINSD(parser) ? 2 : 3)
2625
-static inline PARSER_RC pluginsd_register_module(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser __maybe_unused) {
2626
- netdata_log_info("PLUGINSD: DYNCFG_REG_MODULE");
2627
-
2628
- size_t expected_num_words = SERVING_PLUGINSD(parser) ? 3 : 4;
2629
-
2630
- if (unlikely(num_words != expected_num_words)) {
2631
- char log[LOG_MSG_SIZE + 1];
2632
- snprintfz(log, LOG_MSG_SIZE, "expected %zu (got %zu) parameters: %smodule_name module_type", expected_num_words - 1, num_words - 1, SERVING_PLUGINSD(parser) ? "" : "plugin_name ");
2633
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_MODULE, log);
2634
- }
2635
-
2636
- struct configurable_plugin *plug_cfg;
2637
- const DICTIONARY_ITEM *di = NULL;
2638
- if (SERVING_PLUGINSD(parser)) {
2639
- plug_cfg = parser->user.cd->configuration;
2640
- if (unlikely(plug_cfg == NULL))
2641
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_MODULE, "you have to enable dynamic configuration first using " PLUGINSD_KEYWORD_DYNCFG_ENABLE);
2642
- } else {
2643
- di = dictionary_get_and_acquire_item(parser->user.host->configurable_plugins, words[1]);
2644
- if (unlikely(di == NULL))
2645
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_MODULE, "plugin not found");
2646
-
2647
- plug_cfg = (struct configurable_plugin *)dictionary_acquired_item_value(di);
2648
- }
2649
-
2650
- struct module *mod = callocz(1, sizeof(struct module));
2651
-
2652
- mod->type = str2_module_type(words[MODULE_TYPE_IDX]);
2653
- if (unlikely(mod->type == MOD_TYPE_UNKNOWN)) {
2654
- freez(mod);
2655
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_MODULE, "unknown module type (allowed: job_array, single)");
2656
- }
2657
-
2658
- mod->name = strdupz(words[MODULE_NAME_IDX]);
2659
-
2660
- mod->set_config_cb = set_module_config_cb;
2661
- mod->get_config_cb = get_module_config_cb;
2662
- mod->get_config_schema_cb = get_module_config_schema_cb;
2663
- mod->config_cb_usr_ctx = parser;
2664
-
2665
- mod->get_job_config_cb = get_job_config_cb;
2666
- mod->get_job_config_schema_cb = get_job_config_schema_cb;
2667
- mod->set_job_config_cb = set_job_config_cb;
2668
- mod->delete_job_cb = delete_job_cb;
2669
- mod->job_config_cb_usr_ctx = parser;
2670
-
2671
- register_module(parser->user.host->configurable_plugins, plug_cfg, mod, SERVING_PLUGINSD(parser));
2672
-
2673
- if (di != NULL)
2674
- dictionary_acquired_item_release(parser->user.host->configurable_plugins, di);
2675
-
2676
- rrdpush_send_dyncfg_reg_module(parser->user.host, plug_cfg->name, mod->name, mod->type);
2677
-
2678
- return PARSER_RC_OK;
2679
-}
2680
-
2681
-static inline PARSER_RC pluginsd_register_job_common(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser __maybe_unused, const char *plugin_name) {
2682
- const char *module_name = words[0];
2683
- const char *job_name = words[1];
2684
- const char *job_type_str = words[2];
2685
- const char *flags_str = words[3];
2686
-
2687
- long f = str2l(flags_str);
2688
-
2689
- if (f < 0)
2690
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_JOB, "invalid flags received");
2691
-
2692
- dyncfg_job_flg_t flags = f;
2693
-
2694
- if (SERVING_PLUGINSD(parser))
2695
- flags |= JOB_FLG_PLUGIN_PUSHED;
2696
- else
2697
- flags |= JOB_FLG_STREAMING_PUSHED;
2698
-
2699
- enum job_type job_type = dyncfg_str2job_type(job_type_str);
2700
- if (job_type == JOB_TYPE_UNKNOWN)
2701
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_JOB, "unknown job type");
2702
-
2703
- if (SERVING_PLUGINSD(parser) && job_type == JOB_TYPE_USER)
2704
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_JOB, "plugins cannot push jobs of type \"user\" (this is allowed only in streaming)");
2705
-
2706
- if (register_job(parser->user.host->configurable_plugins, plugin_name, module_name, job_name, job_type, flags, 0)) // ignore existing is off as this is explicitly called register job
2707
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_JOB, "error registering job");
2708
-
2709
- rrdpush_send_dyncfg_reg_job(parser->user.host, plugin_name, module_name, job_name, job_type, flags);
2710
-
2711
- return PARSER_RC_OK;
2712
-}
2713
-
2714
-static inline PARSER_RC pluginsd_register_job(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser __maybe_unused) {
2715
- size_t expected_num_words = SERVING_PLUGINSD(parser) ? 5 : 6;
2716
-
2717
- if (unlikely(num_words != expected_num_words)) {
2718
- char log[LOG_MSG_SIZE + 1];
2719
- snprintfz(log, LOG_MSG_SIZE, "expected %zu (got %zu) parameters: %smodule_name job_name job_type", expected_num_words - 1, num_words - 1, SERVING_PLUGINSD(parser) ? "" : "plugin_name ");
2720
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_REGISTER_JOB, log);
2721
- }
2722
-
2723
- if (SERVING_PLUGINSD(parser)) {
2724
- return pluginsd_register_job_common(&words[1], num_words - 1, parser, parser->user.cd->configuration->name);
2725
- }
2726
- return pluginsd_register_job_common(&words[2], num_words - 2, parser, words[1]);
2727
-}
2728
-
2729
-static inline PARSER_RC pluginsd_dyncfg_reset(char **words __maybe_unused, size_t num_words __maybe_unused, PARSER *parser __maybe_unused) {
2730
- if (unlikely(num_words != (SERVING_PLUGINSD(parser) ? 1 : 2)))
2731
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_RESET, SERVING_PLUGINSD(parser) ? "expected 0 parameters" : "expected 1 parameter: plugin_name");
2732
-
2733
- if (SERVING_PLUGINSD(parser)) {
2734
- unregister_plugin(parser->user.host->configurable_plugins, parser->user.cd->cfg_dict_item);
2735
- rrdpush_send_dyncfg_reset(parser->user.host, parser->user.cd->configuration->name);
2736
- parser->user.cd->configuration = NULL;
2737
- } else {
2738
- const DICTIONARY_ITEM *di = dictionary_get_and_acquire_item(parser->user.host->configurable_plugins, words[1]);
2739
- if (unlikely(di == NULL))
2740
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DYNCFG_RESET, "plugin not found");
2741
- unregister_plugin(parser->user.host->configurable_plugins, di);
2742
- rrdpush_send_dyncfg_reset(parser->user.host, words[1]);
2743
- }
2744
-
2745
- return PARSER_RC_OK;
2746
-}
2747
-
2748
-static inline PARSER_RC pluginsd_job_status_common(char **words, size_t num_words, PARSER *parser, const char *plugin_name) {
2749
- int state = str2i(words[3]);
2750
-
2751
- enum job_status status = str2job_state(words[2]);
2752
- if (unlikely(SERVING_PLUGINSD(parser) && status == JOB_STATUS_UNKNOWN))
2753
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_REPORT_JOB_STATUS, "unknown job status");
2754
-
2755
- char *message = NULL;
2756
- if (num_words == 5 && strlen(words[4]) > 0)
2757
- message = words[4];
2758
-
2759
- const DICTIONARY_ITEM *plugin_item;
2760
- DICTIONARY *job_dict;
2761
- const DICTIONARY_ITEM *job_item = report_job_status_acq_lock(parser->user.host->configurable_plugins, &plugin_item, &job_dict, plugin_name, words[0], words[1], status, state, message);
2762
-
2763
- if (job_item != NULL) {
2764
- struct job *job = dictionary_acquired_item_value(job_item);
2765
- rrdpush_send_job_status_update(parser->user.host, plugin_name, words[0], job);
2766
-
2767
- pthread_mutex_unlock(&job->lock);
2768
- dictionary_acquired_item_release(job_dict, job_item);
2769
- dictionary_acquired_item_release(parser->user.host->configurable_plugins, plugin_item);
2770
- }
2771
-
2772
- return PARSER_RC_OK;
2773
-}
2774
-
2775
-// job_status [plugin_name if streaming] <module_name> <job_name> <status_code> <state> [message]
2776
-static PARSER_RC pluginsd_job_status(char **words, size_t num_words, PARSER *parser) {
2777
- if (SERVING_PLUGINSD(parser)) {
2778
- if (unlikely(num_words != 5 && num_words != 6))
2779
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_REPORT_JOB_STATUS, "expected 4 or 5 parameters: module_name, job_name, status_code, state, [optional: message]");
2780
- } else {
2781
- if (unlikely(num_words != 6 && num_words != 7))
2782
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_REPORT_JOB_STATUS, "expected 5 or 6 parameters: plugin_name, module_name, job_name, status_code, state, [optional: message]");
2783
- }
2784
-
2785
- if (SERVING_PLUGINSD(parser)) {
2786
- return pluginsd_job_status_common(&words[1], num_words - 1, parser, parser->user.cd->configuration->name);
2787
- }
2788
- return pluginsd_job_status_common(&words[2], num_words - 2, parser, words[1]);
2789
-}
2790
-
2791
-static PARSER_RC pluginsd_delete_job(char **words, size_t num_words, PARSER *parser) {
2792
- // this can confuse a bit but there is a diference between KEYWORD_DELETE_JOB and actual delete_job function
2793
- // they are of opossite direction
2794
- if (num_words != 4)
2795
- return PLUGINSD_DISABLE_PLUGIN(parser, PLUGINSD_KEYWORD_DELETE_JOB, "expected 2 parameters: plugin_name, module_name, job_name");
2796
-
2797
- const char *plugin_name = get_word(words, num_words, 1);
2798
- const char *module_name = get_word(words, num_words, 2);
2799
- const char *job_name = get_word(words, num_words, 3);
2800
-
2801
- if (SERVING_STREAMING(parser))
2802
- delete_job_pname(parser->user.host->configurable_plugins, plugin_name, module_name, job_name);
2803
-
2804
- // forward to parent if any
2805
- rrdpush_send_job_deleted(parser->user.host, plugin_name, module_name, job_name);
2806
- return PARSER_RC_OK;
2807
-}
2808
-
1072
static inline PARSER_RC streaming_claimed_id(char **words, size_t num_words, PARSER *parser)
1073
{
1074
const char *host_uuid_str = get_word(words, num_words, 1);
1118
1119
// ----------------------------------------------------------------------------
1120
1121
+void pluginsd_cleanup_v2(PARSER *parser) {
1122
+ // this is called when the thread is stopped while processing
1123
+ pluginsd_clear_scope_chart(parser, "THREAD CLEANUP");
1124
+}
1125
+
1126
void pluginsd_process_thread_cleanup(void *ptr) {
1127
PARSER *parser = (PARSER *)ptr;
1128
1264
return count;
1265
}
1266
2999
-void pluginsd_keywords_init(PARSER *parser, PARSER_REPERTOIRE repertoire) {
3000
- parser_init_repertoire(parser, repertoire);
3001
-
3002
- if (repertoire & (PARSER_INIT_PLUGINSD | PARSER_INIT_STREAMING))
3003
- inflight_functions_init(parser);
3004
-}
3005
-
3006
-PARSER *parser_init(struct parser_user_object *user, FILE *fp_input, FILE *fp_output, int fd,
3007
- PARSER_INPUT_TYPE flags, void *ssl __maybe_unused) {
3008
- PARSER *parser;
3009
-
3010
- parser = callocz(1, sizeof(*parser));
3011
- if(user)
3012
- parser->user = *user;
3013
- parser->fd = fd;
3014
- parser->fp_input = fp_input;
3015
- parser->fp_output = fp_output;
3016
-#ifdef ENABLE_HTTPS
3017
- parser->ssl_output = ssl;
3018
-#endif
3019
- parser->flags = flags;
3020
-
3021
- spinlock_init(&parser->writer.spinlock);
3022
- return parser;
3023
-}
3024
-
1267
PARSER_RC parser_execute(PARSER *parser, PARSER_KEYWORD *keyword, char **words, size_t num_words) {
1268
switch(keyword->id) {
1269
case 1:
1320
case 42:
1321
return pluginsd_function_result_begin(words, num_words, parser);
1322
1323
+ case 43:
1324
+ return pluginsd_function_progress(words, num_words, parser);
1325
+
1326
case 51:
1327
return pluginsd_label(words, num_words, parser);
1328
1390
}
1391
}
1392
3148
-static void parser_destroy_dyncfg(PARSER *parser) {
3149
- if (parser->user.cd != NULL && parser->user.cd->configuration != NULL) {
3150
- unregister_plugin(parser->user.host->configurable_plugins, parser->user.cd->cfg_dict_item);
3151
- parser->user.cd->configuration = NULL;
3152
- } else if (parser->user.host != NULL && SERVING_STREAMING(parser) && parser->user.host != localhost){
3153
- dictionary_flush(parser->user.host->configurable_plugins);
3154
- }
3155
-}
3156
-
3157
-void parser_destroy(PARSER *parser) {
3158
- if (unlikely(!parser))
3159
- return;
3160
-
3161
- parser_destroy_dyncfg(parser);
3162
-
3163
- dictionary_destroy(parser->inflight.functions);
3164
- freez(parser);
3165
-}
3166
-
1393
int pluginsd_parser_unittest(void) {
1394
PARSER *p = parser_init(NULL, NULL, NULL, -1, PARSER_INPUT_SPLIT, NULL);
1395
pluginsd_keywords_init(p, PARSER_INIT_PLUGINSD | PARSER_INIT_STREAMING);