3 static const char *health_default_exec = PLUGINS_DIR "/alarm.sh";
5 // ----------------------------------------------------------------------------
8 int rrdvar_compare(void* a, void* b) {
9 if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1;
10 else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1;
11 else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name);
14 static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
15 RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
17 debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
22 static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
23 RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
25 fatal("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
30 static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
32 tmp.name = (char *)name;
33 tmp.hash = (hash)?hash:simple_hash(tmp.name);
35 return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
38 static inline RRDVAR *rrdvar_create(const char *name, uint32_t hash, int type, calculated_number *value) {
39 RRDVAR *rv = callocz(1, sizeof(RRDVAR));
41 rv->name = (char *)name;
42 rv->hash = (hash)?hash:simple_hash((rv->name));
49 static inline void rrdvar_free(RRDHOST *host, RRDVAR *rv) {
51 // FIXME: we may need some kind of locking here
53 for (rf = host->references; rf; rf = rf->next)
54 if (rf->rrdvar == rv) rf->rrdvar = NULL;
60 static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, uint32_t hash, int type, calculated_number *value) {
61 RRDVAR *rv = rrdvar_index_find(tree, name, hash);
63 debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", name, scope);
65 rv = rrdvar_create(name, hash, type, value);
66 RRDVAR *ret = rrdvar_index_add(tree, rv);
67 if(unlikely(ret != rv)) {
68 debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", name, scope);
69 rrdvar_free(NULL, rv);
73 debug(D_VARIABLES, "Variable '%s' created in scope '%s'", name, scope);
83 RRDVAR *ret = rrdvar_index_find(tree, name, hash);
84 if(ret != rv) fatal("oops! 1");
86 ret = rrdvar_index_del(tree, rv);
87 if(ret != rv) fatal("oops! 2");
89 ret = rrdvar_index_add(tree, rv);
90 if(ret != rv) fatal("oops! 3");
97 // ----------------------------------------------------------------------------
98 // RRDSETVAR management
100 #define RRDSETVAR_ID_MAX 1024
102 RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) {
103 debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable);
104 RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR));
106 char buffer[RRDSETVAR_ID_MAX + 1];
107 snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->id, variable);
108 rs->fullid = strdupz(buffer);
109 rs->hash_fullid = simple_hash(rs->fullid);
111 snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->name, variable);
112 rs->fullname = strdupz(buffer);
113 rs->hash_fullname = simple_hash(rs->fullname);
115 rs->variable = strdupz(variable);
116 rs->hash_variable = simple_hash(rs->variable);
120 rs->options = options;
123 rs->local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->hash_variable, rs->type, rs->value);
124 rs->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullid, rs->hash_fullid, rs->type, rs->value);
125 rs->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullid, rs->hash_fullid, rs->type, rs->value);
126 rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
127 rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
129 rs->next = st->variables;
135 void rrdsetvar_rename_all(RRDSET *st) {
136 debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name);
138 // only these 2 can change name
142 char buffer[RRDSETVAR_ID_MAX + 1];
143 RRDSETVAR *rs, *next = st->variables;
147 snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->name, rs->variable);
149 if (strcmp(buffer, rs->fullname)) {
151 if (rs->context_name) {
152 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
153 rrdvar_free(st->rrdhost, rs->context_name);
157 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
158 rrdvar_free(st->rrdhost, rs->host_name);
162 rs->fullname = strdupz(st->name);
163 rs->hash_fullname = simple_hash(rs->fullname);
164 rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
165 rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
169 rrdsetcalc_link_matching(st);
172 void rrdsetvar_free(RRDSETVAR *rs) {
173 RRDSET *st = rs->rrdset;
174 debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable);
177 rrdvar_index_del(&st->variables_root_index, rs->local);
178 rrdvar_free(st->rrdhost, rs->local);
182 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context);
183 rrdvar_free(st->rrdhost, rs->context);
187 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host);
188 rrdvar_free(st->rrdhost, rs->host);
191 if(rs->context_name) {
192 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
193 rrdvar_free(st->rrdhost, rs->context_name);
197 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
198 rrdvar_free(st->rrdhost, rs->host_name);
201 if(st->variables == rs) {
202 st->variables = rs->next;
206 for (t = st->variables; t && t->next != rs; t = t->next);
207 if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->fullname, st->id);
208 else t->next = rs->next;
217 // ----------------------------------------------------------------------------
218 // RRDDIMVAR management
220 #define RRDDIMVAR_ID_MAX 1024
222 RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) {
223 RRDSET *st = rd->rrdset;
225 debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:"");
227 if(!prefix) prefix = "";
228 if(!suffix) suffix = "";
230 char buffer[RRDDIMVAR_ID_MAX + 1];
231 RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR));
233 rs->prefix = strdupz(prefix);
234 rs->suffix = strdupz(suffix);
236 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix);
237 rs->id = strdupz(buffer);
238 rs->hash = simple_hash(rs->id);
240 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
241 rs->name = strdupz(buffer);
242 rs->hash_name = simple_hash(rs->name);
244 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->id);
245 rs->fullidid = strdupz(buffer);
246 rs->hash_fullidid = simple_hash(rs->fullidid);
248 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->name);
249 rs->fullidname = strdupz(buffer);
250 rs->hash_fullidname = simple_hash(rs->fullidname);
252 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->id);
253 rs->fullnameid = strdupz(buffer);
254 rs->hash_fullnameid = simple_hash(rs->fullnameid);
256 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->name);
257 rs->fullnamename = strdupz(buffer);
258 rs->hash_fullnamename = simple_hash(rs->fullnamename);
262 rs->options = options;
265 rs->local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->id, rs->hash, rs->type, rs->value);
266 rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->hash_name, rs->type, rs->value);
268 rs->context_fullidid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullidid, rs->hash_fullidid, rs->type, rs->value);
269 rs->context_fullidname = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
270 rs->context_fullnameid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
271 rs->context_fullnamename = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
273 rs->host_fullidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidid, rs->hash_fullidid, rs->type, rs->value);
274 rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
275 rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
276 rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
278 rs->next = rd->variables;
284 void rrddimvar_rename_all(RRDDIM *rd) {
285 RRDSET *st = rd->rrdset;
286 debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
288 RRDDIMVAR *rs, *next = rd->variables;
292 if (strcmp(rd->name, rs->name)) {
293 char buffer[RRDDIMVAR_ID_MAX + 1];
297 if (rs->local_name) {
298 rrdvar_index_del(&st->variables_root_index, rs->local_name);
299 rrdvar_free(st->rrdhost, rs->local_name);
302 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
303 rs->name = strdupz(buffer);
304 rs->hash_name = simple_hash(rs->name);
305 rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->hash_name, rs->type, rs->value);
308 if (rs->context_fullidname) {
309 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
310 rrdvar_free(st->rrdhost, rs->context_fullidname);
312 if (rs->host_fullidname) {
313 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullidname);
314 rrdvar_free(st->rrdhost, rs->host_fullidname);
316 freez(rs->fullidname);
317 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->name);
318 rs->fullidname = strdupz(buffer);
319 rs->hash_fullidname = simple_hash(rs->fullidname);
320 rs->context_fullidname = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
321 rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
322 rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
323 rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
326 if (rs->context_fullnameid) {
327 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
328 rrdvar_free(st->rrdhost, rs->context_fullnameid);
330 if (rs->host_fullnameid) {
331 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnameid);
332 rrdvar_free(st->rrdhost, rs->host_fullnameid);
334 freez(rs->fullnameid);
335 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->id);
336 rs->fullnameid = strdupz(buffer);
337 rs->hash_fullnameid = simple_hash(rs->fullnameid);
338 rs->context_fullnameid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
339 rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
340 rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
341 rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
344 if (rs->context_fullnamename) {
345 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
346 rrdvar_free(st->rrdhost, rs->context_fullnamename);
348 if (rs->host_fullnamename) {
349 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnamename);
350 rrdvar_free(st->rrdhost, rs->host_fullnamename);
352 freez(rs->fullnamename);
353 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->name);
354 rs->fullnamename = strdupz(buffer);
355 rs->hash_fullnamename = simple_hash(rs->fullnamename);
356 rs->context_fullnamename = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
357 rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
358 rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
359 rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
364 void rrddimvar_free(RRDDIMVAR *rs) {
365 RRDDIM *rd = rs->rrddim;
366 RRDSET *st = rd->rrdset;
367 debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix);
370 rrdvar_index_del(&st->variables_root_index, rs->local_id);
371 rrdvar_free(st->rrdhost, rs->local_id);
374 rrdvar_index_del(&st->variables_root_index, rs->local_name);
375 rrdvar_free(st->rrdhost, rs->local_name);
378 if(rs->context_fullidid) {
379 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidid);
380 rrdvar_free(st->rrdhost, rs->context_fullidid);
382 if(rs->context_fullidname) {
383 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
384 rrdvar_free(st->rrdhost, rs->context_fullidname);
386 if(rs->context_fullnameid) {
387 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
388 rrdvar_free(st->rrdhost, rs->context_fullnameid);
390 if(rs->context_fullnamename) {
391 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
392 rrdvar_free(st->rrdhost, rs->context_fullnamename);
395 if(rs->host_fullidid) {
396 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidid);
397 rrdvar_free(st->rrdhost, rs->host_fullidid);
399 if(rs->host_fullidname) {
400 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidname);
401 rrdvar_free(st->rrdhost, rs->host_fullidname);
403 if(rs->host_fullnameid) {
404 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnameid);
405 rrdvar_free(st->rrdhost, rs->host_fullnameid);
407 if(rs->host_fullnamename) {
408 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnamename);
409 rrdvar_free(st->rrdhost, rs->host_fullnamename);
412 if(rd->variables == rs) {
413 debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
414 rd->variables = rs->next;
417 debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
419 for (t = rd->variables; t && t->next != rs; t = t->next) ;
420 if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->name, st->id, rd->id);
421 else t->next = rs->next;
429 freez(rs->fullidname);
430 freez(rs->fullnameid);
431 freez(rs->fullnamename);
435 // ----------------------------------------------------------------------------
436 // RRDCALC management
438 // this has to be called while the caller has locked
440 static inline void rrdset_linked_optimize_rrdhost(RRDHOST *host, RRDCALC *rc) {
441 rrdhost_check_wrlock(host);
443 // move it to be last
446 // we are last already
449 RRDCALC *t, *last = NULL, *prev = NULL;
450 for (t = host->calculations; t ; t = t->next) {
459 error("RRDCALC '%s' cannot be linked to the end of host '%s' list", rc->name, host->hostname);
464 prev->next = rc->next;
466 if(host->calculations == rc)
467 host->calculations = rc->next;
469 error("RRDCALC '%s' is not found in host '%s' list", rc->name, host->hostname);
478 // this has to be called while the caller has locked
480 static inline void rrdcalc_unlinked_optimize_rrdhost(RRDHOST *host, RRDCALC *rc) {
481 rrdhost_check_wrlock(host);
483 // move it to be first
485 if(host->calculations == rc) {
486 // ok, we are the first
490 // find the previous one
492 for (t = host->calculations; t && t->next != rc; rc = rc->next) ;
494 error("RRDCALC '%s' is not linked to host '%s'.", rc->name, host->hostname);
498 rc->next = host->calculations;
499 host->calculations = rc;
503 static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
507 st->green = rc->green;
512 rc->local = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
513 rc->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
514 rc->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
516 rrdset_linked_optimize_rrdhost(st->rrdhost, rc);
519 static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) {
520 if((rc->hash_chart == st->hash && !strcmp(rc->name, st->id)) ||
521 (rc->hash_chart == st->hash_name && !strcmp(rc->name, st->name)))
527 // this has to be called while the RRDHOST is locked
528 void rrdsetcalc_link_matching(RRDSET *st) {
531 for(rc = st->rrdhost->calculations; rc ; rc = rc->next) {
532 // since unlinked ones are in front and linked at the end
533 // we stop on the first linked RRDCALC
534 if(rc->rrdset != NULL) break;
536 if(rrdcalc_is_matching_this_rrdset(rc, st))
537 rrdsetcalc_link(st, rc);
541 // this has to be called while the RRDHOST is locked
542 void rrdsetcalc_unlink(RRDCALC *rc) {
543 RRDSET *st = rc->rrdset;
546 error("Requested to unlink RRDCALC '%s' which is not linked to any RRDSET", rc->name);
550 RRDHOST *host = st->rrdhost;
554 rc->rrdset_prev->rrdset_next = rc->rrdset_next;
557 rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
559 if(st->calculations == rc)
560 st->calculations = rc->rrdset_next;
562 rc->rrdset_prev = rc->rrdset_next = NULL;
565 rrdvar_index_del(&st->variables_root_index, rc->local);
566 rrdvar_free(st->rrdhost, rc->local);
571 rrdvar_index_del(&st->rrdcontext->variables_root_index, rc->context);
576 rrdvar_index_del(&st->rrdhost->variables_root_index, rc->host);
582 // RRDCALC will remain in RRDHOST
583 // so that if the matching chart is found in the future
584 // it will be applied automatically
586 rrdcalc_unlinked_optimize_rrdhost(host, rc);
589 static inline int rrdcalc_exists(RRDHOST *host, const char *name, uint32_t hash) {
592 // make sure it does not already exist
593 for(rc = host->calculations; rc ; rc = rc->next) {
594 if (rc->hash == hash && !strcmp(name, rc->name)) {
595 error("Attempted to create RRDCAL '%s' in host '%s', but it already exists.", name, host->hostname);
603 void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
604 // link it to the host
605 rc->next = host->calculations;
606 host->calculations = rc;
608 // link it to its chart
610 for(st = host->rrdset_root; st ; st = st->next) {
611 if(rrdcalc_is_matching_this_rrdset(rc, st)) {
612 rrdsetcalc_link(st, rc);
618 RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, const char *dimensions, int group_method, uint32_t after, uint32_t before, int update_every, uint32_t options) {
619 uint32_t hash = simple_hash(name);
621 if(rrdcalc_exists(host, name, hash))
624 RRDCALC *rc = callocz(1, sizeof(RRDCALC));
626 rc->name = strdupz(name);
627 rc->hash = simple_hash(rc->name);
629 rc->chart = strdupz(chart);
630 rc->hash_chart = simple_hash(rc->chart);
632 if(dimensions) rc->dimensions = strdupz(dimensions);
634 rc->group = group_method;
637 rc->update_every = update_every;
638 rc->options = options;
640 rrdcalc_create_part2(host, rc);
644 void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
647 // unlink it from RRDSET
648 if(rc->rrdset) rrdsetcalc_unlink(rc);
650 // unlink it from RRDHOST
651 if(rc == host->calculations)
652 host->calculations = rc->next;
654 else if(host->calculations) {
655 RRDCALC *t, *last = host->calculations;
657 for(t = last->next; t && t != rc; last = t, t = t->next) ;
658 if(last && last->next == rc)
659 last->next = rc->next;
661 error("Cannot unlink RRDCALC '%s' from RRDHOST '%s': not found", rc->name, host->hostname);
664 error("Cannot unlink RRDCALC '%s' from RRDHOST '%s': RRDHOST does not have any calculations", rc->name, host->hostname);
666 if(rc->warning) expression_free(rc->warning);
667 if(rc->critical) expression_free(rc->critical);
672 freez(rc->dimensions);
678 // ----------------------------------------------------------------------------
679 // RRDCALCTEMPLATE management
681 static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
682 if(host->templates) {
683 if(host->templates == rt) {
684 host->templates = rt->next;
687 RRDCALCTEMPLATE *t, *last = host->templates;
688 for (t = last->next; t && t != rt; last = t, t = t->next ) ;
689 if(last && last->next == rt) {
690 last->next = rt->next;
694 error("Cannot find RRDCALCTEMPLATE '%s' linked in host '%s'", rt->name, host->hostname);
698 if(rt->warning) expression_free(rt->warning);
699 if(rt->critical) expression_free(rt->critical);
701 freez(rt->dimensions);
709 // ----------------------------------------------------------------------------
710 // load health configuration
712 #define HEALTH_CONF_MAX_LINE 4096
714 #define HEALTH_ALARM_KEY "alarm"
715 #define HEALTH_TEMPLATE_KEY "template"
716 #define HEALTH_ON_KEY "on"
717 #define HEALTH_CALC_KEY "calc"
718 #define HEALTH_GREEN_KEY "green"
719 #define HEALTH_RED_KEY "red"
720 #define HEALTH_WARN_KEY "warn"
721 #define HEALTH_CRIT_KEY "crit"
722 #define HEALTH_EXEC_KEY "exec"
724 static inline int rrdcalc_add(RRDHOST *host, RRDCALC *rc) {
725 info("Health configuration examining alarm '%s': chart '%s', exec '%s', green %Lf, red %Lf, calculation: group %d, after %d, before %d, options %u, update every %d, dimensions '%s', warning '%s', critical '%s', source '%s",
727 (rc->chart)?rc->chart:"NONE",
728 (rc->exec)?rc->exec:"DEFAULT",
736 (rc->dimensions)?rc->dimensions:"NONE",
737 (rc->warning)?rc->warning->parsed_as:"NONE",
738 (rc->critical)?rc->critical->parsed_as:"NONE",
742 if(rrdcalc_exists(host, rc->name, rc->hash))
746 error("Health configuration for alarm '%s' does not have a chart", rc->name);
750 if(!RRDCALC_HAS_CALCULATION(rc) && !rc->warning && !rc->critical) {
751 error("Health configuration for alarm '%s' is useless (no calculation, no warning and no critical evaluation)", rc->name);
755 rrdcalc_create_part2(host, rc);
759 static inline int rrdcalctemplate_add(RRDHOST *host, RRDCALCTEMPLATE *rt) {
760 info("Health configuration examining template '%s': context '%s', exec '%s', green %Lf, red %Lf, calculation: group %d, after %d, before %d, options %u, update every %d, dimensions '%s', warning '%s', critical '%s', source '%s'",
762 (rt->context)?rt->context:"NONE",
763 (rt->exec)?rt->exec:"DEFAULT",
771 (rt->dimensions)?rt->dimensions:"NONE",
772 (rt->warning)?rt->warning->parsed_as:"NONE",
773 (rt->critical)?rt->critical->parsed_as:"NONE",
777 if(!RRDCALCTEMPLATE_HAS_CALCULATION(rt) && !rt->warning && !rt->critical) {
778 error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rt->name);
783 for (t = host->templates; t ; t = t->next) {
784 if(t->hash_name == rt->hash_name && !strcmp(t->name, rt->name)) {
785 error("Health configuration template '%s' already exists for host '%s'.", rt->name, host->hostname);
790 rt->next = host->templates;
791 host->templates = rt;
795 static inline int health_parse_time(char *string, int *result) {
796 // make sure it is a number
797 if(!*string || !(isdigit(*string) || *string == '+' || *string == '-')) {
803 calculated_number n = strtold(string, &e);
807 *result = (int) (n * 86400 * 365);
810 *result = (int) (n * 86400 * 30);
813 *result = (int) (n * 86400 * 7);
816 *result = (int) (n * 86400);
819 *result = (int) (n * 3600);
822 *result = (int) (n * 60);
837 static inline int health_parse_chart_calc(
838 size_t line, const char *path, const char *file, char *string,
839 int *group_method, int *after, int *before, int *every,
840 uint32_t *options, char **dimensions
842 if(*dimensions) freez(*dimensions);
849 char *s = string, *key;
851 // first is the group method
853 while(*s && !isspace(*s)) s++;
854 while(*s && isspace(*s)) *s++ = '\0';
856 error("Health configuration invalid chart calculation at line %zu of file '%s/%s': expected group method followed by the 'after' time, but got '%s'",
857 line, path, file, key);
861 if((*group_method = web_client_api_request_v1_data_group(key, -1)) == -1) {
862 error("Health configuration at line %zu of file '%s/%s': invalid group method '%s'",
863 line, path, file, key);
867 // then is the 'after' time
869 while(*s && !isspace(*s)) s++;
870 while(*s && isspace(*s)) *s++ = '\0';
872 if(!health_parse_time(key, after)) {
873 error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' after group method",
874 line, path, file, key);
879 *every = abs(*after);
881 // now we may have optional parameters
884 while(*s && !isspace(*s)) s++;
885 while(*s && isspace(*s)) *s++ = '\0';
888 if(!strcasecmp(key, "at")) {
890 while(*s && !isspace(*s)) s++;
891 while(*s && isspace(*s)) *s++ = '\0';
893 if (!health_parse_time(value, before)) {
894 error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
895 line, path, file, value, key);
898 else if(!strcasecmp(key, "every")) {
900 while(*s && !isspace(*s)) s++;
901 while(*s && isspace(*s)) *s++ = '\0';
903 if (!health_parse_time(value, every)) {
904 error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
905 line, path, file, value, key);
908 else if(!strcasecmp(key, "absolute") || !strcasecmp(key, "abs") || !strcasecmp(key, "absolute_sum")) {
909 *options |= RRDR_OPTION_ABSOLUTE;
911 else if(!strcasecmp(key, "min2max")) {
912 *options |= RRDR_OPTION_MIN2MAX;
914 else if(!strcasecmp(key, "null2zero")) {
915 *options |= RRDR_OPTION_NULL2ZERO;
917 else if(!strcasecmp(key, "percentage")) {
918 *options |= RRDR_OPTION_PERCENTAGE;
920 else if(!strcasecmp(key, "unaligned")) {
921 *options |= RRDR_OPTION_NOT_ALIGNED;
923 else if(!strcasecmp(key, "of")) {
924 if(*s && strcasecmp(s, "all"))
925 *dimensions = strdupz(s);
933 static inline char *health_source_file(int line, const char *path, const char *filename) {
934 char buffer[FILENAME_MAX + 1];
935 snprintfz(buffer, FILENAME_MAX, "%d@%s/%s", line, path, filename);
936 return strdupz(buffer);
939 int health_readfile(const char *path, const char *filename) {
940 static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 0, hash_calc = 0, hash_green = 0, hash_red = 0, hash_warn = 0, hash_crit = 0, hash_exec = 0;
941 char buffer[HEALTH_CONF_MAX_LINE + 1];
943 if(unlikely(!hash_alarm)) {
944 hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
945 hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
946 hash_on = simple_uhash(HEALTH_ON_KEY);
947 hash_calc = simple_uhash(HEALTH_CALC_KEY);
948 hash_green = simple_uhash(HEALTH_GREEN_KEY);
949 hash_red = simple_uhash(HEALTH_RED_KEY);
950 hash_warn = simple_uhash(HEALTH_WARN_KEY);
951 hash_crit = simple_uhash(HEALTH_CRIT_KEY);
952 hash_exec = simple_uhash(HEALTH_EXEC_KEY);
955 // info("Reading file '%s/%s'", path, filename);
957 snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
958 FILE *fp = fopen(buffer, "r");
960 error("Health configuration cannot read file '%s'.", buffer);
965 RRDCALCTEMPLATE *rt = NULL;
967 size_t line = 0, append = 0;
969 while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) {
970 int stop_appending = !s;
972 // info("Line %zu of file '%s/%s': '%s'", line, path, filename, s);
975 // info("Trimmed line %zu of file '%s/%s': '%s'", line, path, filename, s);
978 if(!stop_appending && s[append - 1] == '\\') {
980 append = &s[append] - buffer;
981 if(append < HEALTH_CONF_MAX_LINE)
988 while(*s && *s != ':') s++;
990 error("Health configuration has invalid line %zu of file '%s/%s'. It does not contain a ':'. Ignoring it.", line, path, filename);
1001 error("Health configuration has invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename);
1006 error("Health configuration has invalid line %zu of file '%s/%s'. value is empty. Ignoring it.", line, path, filename);
1010 // info("Health file '%s/%s', key '%s', value '%s'", path, filename, key, value);
1011 uint32_t hash = simple_uhash(key);
1013 if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
1014 if(rc && !rrdcalc_add(&localhost, rc))
1015 rrdcalc_free(&localhost, rc);
1018 if (!rrdcalctemplate_add(&localhost, rt))
1019 rrdcalctemplate_free(&localhost, rt);
1023 rc = callocz(1, sizeof(RRDCALC));
1024 rc->name = strdupz(value);
1025 rc->hash = simple_hash(rc->name);
1026 rc->source = health_source_file(line, path, filename);
1028 else if(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) {
1030 if(!rrdcalc_add(&localhost, rc))
1031 rrdcalc_free(&localhost, rc);
1035 if(rt && !rrdcalctemplate_add(&localhost, rt))
1036 rrdcalctemplate_free(&localhost, rt);
1038 rt = callocz(1, sizeof(RRDCALCTEMPLATE));
1039 rt->name = strdupz(value);
1040 rt->hash_name = simple_hash(rt->name);
1041 rt->source = health_source_file(line, path, filename);
1044 if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
1046 if(strcmp(rc->chart, value))
1047 info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
1048 line, path, filename, rc->name, key, rc->chart, value, value);
1052 rc->chart = strdupz(value);
1053 rc->hash_chart = simple_hash(rc->chart);
1055 else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
1056 health_parse_chart_calc(line, path, filename, value, &rc->group, &rc->after, &rc->before, &rc->update_every, &rc->options, &rc->dimensions);
1058 else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
1060 rc->green = strtold(value, &e);
1062 info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
1063 line, path, filename, rc->name, key, e);
1066 else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
1068 rc->red = strtold(value, &e);
1070 info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
1071 line, path, filename, rc->name, key, e);
1074 else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
1075 const char *failed_at = NULL;
1077 rc->warning = expression_parse(value, &failed_at, &error);
1079 error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1080 line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
1083 else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
1084 const char *failed_at = NULL;
1086 rc->critical = expression_parse(value, &failed_at, &error);
1088 error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1089 line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
1092 else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
1094 if(strcmp(rc->exec, value))
1095 info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
1096 line, path, filename, rc->name, key, rc->exec, value, value);
1100 rc->exec = strdupz(value);
1103 error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has unknown key '%s'.",
1104 line, path, filename, rc->name, key);
1108 if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
1110 if(strcmp(rt->context, value))
1111 info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
1112 line, path, filename, rt->name, key, rt->context, value, value);
1116 rt->context = strdupz(value);
1117 rt->hash_context = simple_hash(rt->context);
1119 else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
1120 health_parse_chart_calc(line, path, filename, value, &rt->group, &rt->after, &rt->before, &rt->update_every, &rt->options, &rt->dimensions);
1122 else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
1124 rt->green = strtold(value, &e);
1126 info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
1127 line, path, filename, rt->name, key, e);
1130 else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
1132 rt->red = strtold(value, &e);
1134 info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
1135 line, path, filename, rt->name, key, e);
1138 else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
1139 const char *failed_at = NULL;
1141 rt->warning = expression_parse(value, &failed_at, &error);
1143 error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1144 line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
1147 else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
1148 const char *failed_at = NULL;
1150 rt->critical = expression_parse(value, &failed_at, &error);
1152 error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1153 line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
1156 else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
1158 if(strcmp(rt->exec, value))
1159 info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
1160 line, path, filename, rt->name, key, rt->exec, value, value);
1164 rt->exec = strdupz(value);
1167 error("Health configuration at line %zu of file '%s/%s' for template '%s' has unknown key '%s'.",
1168 line, path, filename, rt->name, key);
1172 error("Health configuration at line %zu of file '%s/%s' has unknown key '%s'. Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.",
1173 line, path, filename, key);
1177 if(rc && !rrdcalc_add(&localhost, rc))
1178 rrdcalc_free(&localhost, rc);
1180 if(rt && !rrdcalctemplate_add(&localhost, rt))
1181 rrdcalctemplate_free(&localhost, rt);
1187 void health_readdir(const char *path) {
1188 size_t pathlen = strlen(path);
1190 info("Reading directory '%s'", path);
1192 DIR *dir = opendir(path);
1194 error("Health configuration cannot open directory '%s'.", path);
1198 struct dirent *de = NULL;
1199 while ((de = readdir(dir))) {
1200 size_t len = strlen(de->d_name);
1202 if(de->d_type == DT_DIR
1204 (de->d_name[0] == '.' && de->d_name[1] == '\0')
1205 || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
1209 else if(de->d_type == DT_DIR) {
1210 char *s = mallocz(pathlen + strlen(de->d_name) + 2);
1213 strcat(s, de->d_name);
1219 else if((de->d_type == DT_LNK || de->d_type == DT_REG) &&
1220 len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
1221 health_readfile(path, de->d_name);
1226 void health_init(void) {
1230 char buffer[FILENAME_MAX + 1];
1231 snprintfz(buffer, FILENAME_MAX, "%s/health.d", config_get("global", "config directory", CONFIG_DIR));
1232 path = config_get("health", "configuration files in directory", buffer);
1234 snprintfz(buffer, FILENAME_MAX, "%s/alarm.sh", config_get("global", "plugins directory", PLUGINS_DIR));
1235 health_default_exec = config_get("health", "script to execute on alarm", buffer);
1238 health_readdir(path);