3 // ----------------------------------------------------------------------------
6 int rrdvar_compare(void* a, void* b) {
7 if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1;
8 else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1;
9 else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name);
12 static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
13 RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
15 debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
20 static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
21 RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
23 fatal("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
28 static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
30 tmp.name = (char *)name;
31 tmp.hash = (hash)?hash:simple_hash(tmp.name);
33 return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
36 static inline RRDVAR *rrdvar_create(const char *name, uint32_t hash, int type, calculated_number *value) {
37 RRDVAR *rv = callocz(1, sizeof(RRDVAR));
39 rv->name = (char *)name;
40 rv->hash = (hash)?hash:simple_hash((rv->name));
47 static inline void rrdvar_free(RRDHOST *host, RRDVAR *rv) {
49 // FIXME: we may need some kind of locking here
50 // to have mutually exclusive access with eval()
52 for (rf = host->references; rf; rf = rf->next)
53 if (rf->rrdvar == rv) rf->rrdvar = NULL;
59 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) {
60 RRDVAR *rv = rrdvar_index_find(tree, name, hash);
62 debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", name, scope);
64 rv = rrdvar_create(name, hash, type, value);
65 RRDVAR *ret = rrdvar_index_add(tree, rv);
66 if(unlikely(ret != rv)) {
67 debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", name, scope);
68 rrdvar_free(NULL, rv);
72 debug(D_VARIABLES, "Variable '%s' created in scope '%s'", name, scope);
82 RRDVAR *ret = rrdvar_index_find(tree, name, hash);
83 if(ret != rv) fatal("oops! 1");
85 ret = rrdvar_index_del(tree, rv);
86 if(ret != rv) fatal("oops! 2");
88 ret = rrdvar_index_add(tree, rv);
89 if(ret != rv) fatal("oops! 3");
96 // ----------------------------------------------------------------------------
97 // RRDSETVAR management
99 #define RRDSETVAR_ID_MAX 1024
101 RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) {
102 debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable);
103 RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR));
105 char buffer[RRDSETVAR_ID_MAX + 1];
106 snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->id, variable);
107 rs->fullid = strdupz(buffer);
108 rs->hash_fullid = simple_hash(rs->fullid);
110 snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->name, variable);
111 rs->fullname = strdupz(buffer);
112 rs->hash_fullname = simple_hash(rs->fullname);
114 rs->variable = strdupz(variable);
115 rs->hash_variable = simple_hash(rs->variable);
119 rs->options = options;
122 rs->local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->hash_variable, rs->type, rs->value);
123 rs->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullid, rs->hash_fullid, rs->type, rs->value);
124 rs->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullid, rs->hash_fullid, rs->type, rs->value);
125 rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
126 rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
128 rs->next = st->variables;
134 void rrdsetvar_rename_all(RRDSET *st) {
135 debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name);
137 // only these 2 can change name
141 char buffer[RRDSETVAR_ID_MAX + 1];
142 RRDSETVAR *rs, *next = st->variables;
146 snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->name, rs->variable);
148 if (strcmp(buffer, rs->fullname)) {
150 if (rs->context_name) {
151 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
152 rrdvar_free(st->rrdhost, rs->context_name);
156 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
157 rrdvar_free(st->rrdhost, rs->host_name);
161 rs->fullname = strdupz(st->name);
162 rs->hash_fullname = simple_hash(rs->fullname);
163 rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
164 rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
168 rrdsetcalc_link_matching(st);
171 void rrdsetvar_free(RRDSETVAR *rs) {
172 RRDSET *st = rs->rrdset;
173 debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable);
176 rrdvar_index_del(&st->variables_root_index, rs->local);
177 rrdvar_free(st->rrdhost, rs->local);
181 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context);
182 rrdvar_free(st->rrdhost, rs->context);
186 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host);
187 rrdvar_free(st->rrdhost, rs->host);
190 if(rs->context_name) {
191 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
192 rrdvar_free(st->rrdhost, rs->context_name);
196 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
197 rrdvar_free(st->rrdhost, rs->host_name);
200 if(st->variables == rs) {
201 st->variables = rs->next;
205 for (t = st->variables; t && t->next != rs; t = t->next);
206 if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->fullname, st->id);
207 else t->next = rs->next;
216 // ----------------------------------------------------------------------------
217 // RRDDIMVAR management
219 #define RRDDIMVAR_ID_MAX 1024
221 RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) {
222 RRDSET *st = rd->rrdset;
224 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:"");
226 if(!prefix) prefix = "";
227 if(!suffix) suffix = "";
229 char buffer[RRDDIMVAR_ID_MAX + 1];
230 RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR));
232 rs->prefix = strdupz(prefix);
233 rs->suffix = strdupz(suffix);
235 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix);
236 rs->id = strdupz(buffer);
237 rs->hash = simple_hash(rs->id);
239 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
240 rs->name = strdupz(buffer);
241 rs->hash_name = simple_hash(rs->name);
243 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->id);
244 rs->fullidid = strdupz(buffer);
245 rs->hash_fullidid = simple_hash(rs->fullidid);
247 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->name);
248 rs->fullidname = strdupz(buffer);
249 rs->hash_fullidname = simple_hash(rs->fullidname);
251 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->id);
252 rs->fullnameid = strdupz(buffer);
253 rs->hash_fullnameid = simple_hash(rs->fullnameid);
255 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->name);
256 rs->fullnamename = strdupz(buffer);
257 rs->hash_fullnamename = simple_hash(rs->fullnamename);
261 rs->options = options;
264 rs->local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->id, rs->hash, rs->type, rs->value);
265 rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->hash_name, rs->type, rs->value);
267 rs->context_fullidid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullidid, rs->hash_fullidid, rs->type, rs->value);
268 rs->context_fullidname = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
269 rs->context_fullnameid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
270 rs->context_fullnamename = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
272 rs->host_fullidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidid, rs->hash_fullidid, rs->type, rs->value);
273 rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
274 rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
275 rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
277 rs->next = rd->variables;
283 void rrddimvar_rename_all(RRDDIM *rd) {
284 RRDSET *st = rd->rrdset;
285 debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
287 RRDDIMVAR *rs, *next = rd->variables;
291 if (strcmp(rd->name, rs->name)) {
292 char buffer[RRDDIMVAR_ID_MAX + 1];
296 if (rs->local_name) {
297 rrdvar_index_del(&st->variables_root_index, rs->local_name);
298 rrdvar_free(st->rrdhost, rs->local_name);
301 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
302 rs->name = strdupz(buffer);
303 rs->hash_name = simple_hash(rs->name);
304 rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->hash_name, rs->type, rs->value);
307 if (rs->context_fullidname) {
308 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
309 rrdvar_free(st->rrdhost, rs->context_fullidname);
311 if (rs->host_fullidname) {
312 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullidname);
313 rrdvar_free(st->rrdhost, rs->host_fullidname);
315 freez(rs->fullidname);
316 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->name);
317 rs->fullidname = strdupz(buffer);
318 rs->hash_fullidname = simple_hash(rs->fullidname);
319 rs->context_fullidname = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
320 rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
321 rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
322 rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
325 if (rs->context_fullnameid) {
326 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
327 rrdvar_free(st->rrdhost, rs->context_fullnameid);
329 if (rs->host_fullnameid) {
330 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnameid);
331 rrdvar_free(st->rrdhost, rs->host_fullnameid);
333 freez(rs->fullnameid);
334 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->id);
335 rs->fullnameid = strdupz(buffer);
336 rs->hash_fullnameid = simple_hash(rs->fullnameid);
337 rs->context_fullnameid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
338 rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
339 rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
340 rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
343 if (rs->context_fullnamename) {
344 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
345 rrdvar_free(st->rrdhost, rs->context_fullnamename);
347 if (rs->host_fullnamename) {
348 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnamename);
349 rrdvar_free(st->rrdhost, rs->host_fullnamename);
351 freez(rs->fullnamename);
352 snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->name);
353 rs->fullnamename = strdupz(buffer);
354 rs->hash_fullnamename = simple_hash(rs->fullnamename);
355 rs->context_fullnamename = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
356 rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
357 rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
358 rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
363 void rrddimvar_free(RRDDIMVAR *rs) {
364 RRDDIM *rd = rs->rrddim;
365 RRDSET *st = rd->rrdset;
366 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);
369 rrdvar_index_del(&st->variables_root_index, rs->local_id);
370 rrdvar_free(st->rrdhost, rs->local_id);
373 rrdvar_index_del(&st->variables_root_index, rs->local_name);
374 rrdvar_free(st->rrdhost, rs->local_name);
377 if(rs->context_fullidid) {
378 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidid);
379 rrdvar_free(st->rrdhost, rs->context_fullidid);
381 if(rs->context_fullidname) {
382 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
383 rrdvar_free(st->rrdhost, rs->context_fullidname);
385 if(rs->context_fullnameid) {
386 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
387 rrdvar_free(st->rrdhost, rs->context_fullnameid);
389 if(rs->context_fullnamename) {
390 rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
391 rrdvar_free(st->rrdhost, rs->context_fullnamename);
394 if(rs->host_fullidid) {
395 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidid);
396 rrdvar_free(st->rrdhost, rs->host_fullidid);
398 if(rs->host_fullidname) {
399 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidname);
400 rrdvar_free(st->rrdhost, rs->host_fullidname);
402 if(rs->host_fullnameid) {
403 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnameid);
404 rrdvar_free(st->rrdhost, rs->host_fullnameid);
406 if(rs->host_fullnamename) {
407 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnamename);
408 rrdvar_free(st->rrdhost, rs->host_fullnamename);
411 if(rd->variables == rs) {
412 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);
413 rd->variables = rs->next;
416 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);
418 for (t = rd->variables; t && t->next != rs; t = t->next) ;
419 if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->name, st->id, rd->id);
420 else t->next = rs->next;
428 freez(rs->fullidname);
429 freez(rs->fullnameid);
430 freez(rs->fullnamename);
434 // ----------------------------------------------------------------------------
435 // RRDCALC management
437 // this has to be called while the caller has locked
439 static inline void rrdhostcalc_linked(RRDHOST *host, RRDCALC *rc) {
440 // move it to be last
443 // we are last already
446 RRDCALC *t, *last = NULL, *prev = NULL;
447 for (t = host->calculations; t ; t = t->next) {
456 error("RRDCALC '%s' cannot be linked to the end of host '%s' list", rc->name, host->hostname);
461 prev->next = rc->next;
463 if(host->calculations == rc)
464 host->calculations = rc->next;
466 error("RRDCALC '%s' is not found in host '%s' list", rc->name, host->hostname);
475 // this has to be called while the caller has locked
477 static inline void rrdhostcalc_unlinked(RRDHOST *host, RRDCALC *rc) {
478 // move it to be first
480 if(host->calculations == rc) {
481 // ok, we are the first
485 // find the previous one
487 for (t = host->calculations; t && t->next != rc; rc = rc->next) ;
489 error("RRDCALC '%s' is not linked to host '%s'.", rc->name, host->hostname);
493 rc->next = host->calculations;
494 host->calculations = rc;
498 static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
501 rc->local = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
502 rc->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
503 rc->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
505 rrdhostcalc_linked(st->rrdhost, rc);
508 // this has to be called while the RRDHOST is locked
509 void rrdsetcalc_link_matching(RRDSET *st) {
512 for(rc = st->rrdhost->calculations; rc ; rc = rc->next) {
513 // since unlinked ones are in front and linked at the end
514 // we stop on the first linked RRDCALC
515 if(rc->rrdset != NULL) break;
517 if((rc->hash_chart == st->hash && !strcmp(rc->name, st->id)) ||
518 (rc->hash_chart == st->hash_name && !strcmp(rc->name, st->name))) {
519 rrdsetcalc_link(st, rc);
524 // this has to be called while the RRDHOST is locked
525 void rrdsetcalc_unlink(RRDCALC *rc) {
526 RRDSET *st = rc->rrdset;
529 error("Requested to unlink RRDCALC '%s' which is not linked to any RRDSET", rc->name);
533 RRDHOST *host = st->rrdhost;
537 rc->rrdset_prev->rrdset_next = rc->rrdset_next;
540 rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
542 if(st->calculations == rc)
543 st->calculations = rc->rrdset_next;
545 rc->rrdset_prev = rc->rrdset_next = NULL;
548 rrdvar_index_del(&st->variables_root_index, rc->local);
549 rrdvar_free(st->rrdhost, rc->local);
554 rrdvar_index_del(&st->rrdcontext->variables_root_index, rc->context);
559 rrdvar_index_del(&st->rrdhost->variables_root_index, rc->host);
565 // RRDCALC will remain in RRDHOST
566 // so that if the matching chart is found in the future
567 // it will be applied automatically
569 rrdhostcalc_unlinked(host, rc);
572 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) {
573 uint32_t hash = simple_hash(name);
577 // make sure it does not already exist
578 for(rc = host->calculations; rc ; rc = rc->next) {
579 if (rc->hash == hash && !strcmp(name, rc->name)) {
580 error("Attempted to create RRDCAL '%s' in host '%s', but it already exists. Ignoring it.", name, host->hostname);
585 rc = callocz(1, sizeof(RRDCALC));
587 rc->name = strdupz(name);
588 rc->hash = simple_hash(rc->name);
590 rc->chart = strdupz(chart);
591 rc->hash_chart = simple_hash(rc->chart);
594 rc->dimensions = strdupz(dimensions);
595 rc->hash_chart = simple_hash(rc->chart);
601 void rrdcalc_free(RRDCALC *rc) {
604 if(rc->rrdset) rrdsetcalc_unlink(rc);