]> arthur.barton.de Git - netdata.git/blob - src/health.c
Merge remote-tracking branch 'upstream/master' into health
[netdata.git] / src / health.c
1 #include "common.h"
2
3 // ----------------------------------------------------------------------------
4 // RRDVAR management
5
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);
10 }
11
12 static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
13     RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
14     if(ret != rv)
15         debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
16
17     return ret;
18 }
19
20 static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
21     RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
22     if(!ret)
23         fatal("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
24
25     return ret;
26 }
27
28 static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
29     RRDVAR tmp;
30     tmp.name = (char *)name;
31     tmp.hash = (hash)?hash:simple_hash(tmp.name);
32
33     return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
34 }
35
36 static inline RRDVAR *rrdvar_create(const char *name, uint32_t hash, int type, calculated_number *value) {
37     RRDVAR *rv = callocz(1, sizeof(RRDVAR));
38
39     rv->name = (char *)name;
40     rv->hash = (hash)?hash:simple_hash((rv->name));
41     rv->type = type;
42     rv->value = value;
43
44     return rv;
45 }
46
47 static inline void rrdvar_free(RRDHOST *host, RRDVAR *rv) {
48     if(host) {
49         // FIXME: we may need some kind of locking here
50         // to have mutually exclusive access with eval()
51         EVAL_VARIABLE *rf;
52         for (rf = host->references; rf; rf = rf->next)
53             if (rf->rrdvar == rv) rf->rrdvar = NULL;
54     }
55
56     freez(rv);
57 }
58
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);
61     if(unlikely(!rv)) {
62         debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", name, scope);
63
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);
69             rv = NULL;
70         }
71         else
72             debug(D_VARIABLES, "Variable '%s' created in scope '%s'", name, scope);
73     }
74     else {
75         // already exists
76         rv = NULL;
77     }
78
79     /*
80      * check
81     if(rv) {
82         RRDVAR *ret = rrdvar_index_find(tree, name, hash);
83         if(ret != rv) fatal("oops! 1");
84
85         ret = rrdvar_index_del(tree, rv);
86         if(ret != rv) fatal("oops! 2");
87
88         ret = rrdvar_index_add(tree, rv);
89         if(ret != rv) fatal("oops! 3");
90     }
91     */
92
93     return rv;
94 }
95
96 // ----------------------------------------------------------------------------
97 // RRDSETVAR management
98
99 #define RRDSETVAR_ID_MAX 1024
100
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));
104
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);
109
110     snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->name, variable);
111     rs->fullname = strdupz(buffer);
112     rs->hash_fullname = simple_hash(rs->fullname);
113
114     rs->variable = strdupz(variable);
115     rs->hash_variable = simple_hash(rs->variable);
116
117     rs->type = type;
118     rs->value = value;
119     rs->options = options;
120     rs->rrdset = st;
121
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);
127
128     rs->next = st->variables;
129     st->variables = rs;
130
131     return rs;
132 }
133
134 void rrdsetvar_rename_all(RRDSET *st) {
135     debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name);
136
137     // only these 2 can change name
138     // rs->context_name
139     // rs->host_name
140
141     char buffer[RRDSETVAR_ID_MAX + 1];
142     RRDSETVAR *rs, *next = st->variables;
143     while((rs = next)) {
144         next = rs->next;
145
146         snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->name, rs->variable);
147
148         if (strcmp(buffer, rs->fullname)) {
149             // name changed
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);
153             }
154
155             if (rs->host_name) {
156                 rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
157                 rrdvar_free(st->rrdhost, rs->host_name);
158             }
159
160             freez(rs->fullname);
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);
165         }
166     }
167
168     rrdsetcalc_link_matching(st);
169 }
170
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);
174
175     if(rs->local) {
176         rrdvar_index_del(&st->variables_root_index, rs->local);
177         rrdvar_free(st->rrdhost, rs->local);
178     }
179
180     if(rs->context) {
181         rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context);
182         rrdvar_free(st->rrdhost, rs->context);
183     }
184
185     if(rs->host) {
186         rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host);
187         rrdvar_free(st->rrdhost, rs->host);
188     }
189
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);
193     }
194
195     if(rs->host_name) {
196         rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
197         rrdvar_free(st->rrdhost, rs->host_name);
198     }
199
200     if(st->variables == rs) {
201         st->variables = rs->next;
202     }
203     else {
204         RRDSETVAR *t;
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;
208     }
209
210     freez(rs->fullid);
211     freez(rs->fullname);
212     freez(rs->variable);
213     freez(rs);
214 }
215
216 // ----------------------------------------------------------------------------
217 // RRDDIMVAR management
218
219 #define RRDDIMVAR_ID_MAX 1024
220
221 RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) {
222     RRDSET *st = rd->rrdset;
223
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:"");
225
226     if(!prefix) prefix = "";
227     if(!suffix) suffix = "";
228
229     char buffer[RRDDIMVAR_ID_MAX + 1];
230     RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR));
231
232     rs->prefix = strdupz(prefix);
233     rs->suffix = strdupz(suffix);
234
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);
238
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);
242
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);
246
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);
250
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);
254
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);
258
259     rs->type = type;
260     rs->value = value;
261     rs->options = options;
262     rs->rrddim = rd;
263
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);
266
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);
271
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);
276
277     rs->next = rd->variables;
278     rd->variables = rs;
279
280     return rs;
281 }
282
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);
286
287     RRDDIMVAR *rs, *next = rd->variables;
288     while((rs = next)) {
289         next = rs->next;
290
291         if (strcmp(rd->name, rs->name)) {
292             char buffer[RRDDIMVAR_ID_MAX + 1];
293             // name changed
294
295             // name
296             if (rs->local_name) {
297                 rrdvar_index_del(&st->variables_root_index, rs->local_name);
298                 rrdvar_free(st->rrdhost, rs->local_name);
299             }
300             freez(rs->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);
305
306             // fullidname
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);
310             }
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);
314             }
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);
323
324             // fullnameid
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);
328             }
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);
332             }
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);
341
342             // fullnamename
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);
346             }
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);
350             }
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);
359         }
360     }
361 }
362
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);
367
368     if(rs->local_id) {
369         rrdvar_index_del(&st->variables_root_index, rs->local_id);
370         rrdvar_free(st->rrdhost, rs->local_id);
371     }
372     if(rs->local_name) {
373         rrdvar_index_del(&st->variables_root_index, rs->local_name);
374         rrdvar_free(st->rrdhost, rs->local_name);
375     }
376
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);
380     }
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);
384     }
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);
388     }
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);
392     }
393
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);
397     }
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);
401     }
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);
405     }
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);
409     }
410
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;
414     }
415     else {
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);
417         RRDDIMVAR *t;
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;
421     }
422
423     freez(rs->prefix);
424     freez(rs->suffix);
425     freez(rs->id);
426     freez(rs->name);
427     freez(rs->fullidid);
428     freez(rs->fullidname);
429     freez(rs->fullnameid);
430     freez(rs->fullnamename);
431     freez(rs);
432 }
433
434 // ----------------------------------------------------------------------------
435 // RRDCALC management
436
437 // this has to be called while the caller has locked
438 // the RRDHOST
439 static inline void rrdhostcalc_linked(RRDHOST *host, RRDCALC *rc) {
440     // move it to be last
441
442     if(!rc->next)
443         // we are last already
444         return;
445
446     RRDCALC *t, *last = NULL, *prev = NULL;
447     for (t = host->calculations; t ; t = t->next) {
448         if(t->next == rc)
449             prev = t;
450
451         if(!t->next)
452             last = t;
453     }
454
455     if(!last) {
456         error("RRDCALC '%s' cannot be linked to the end of host '%s' list", rc->name, host->hostname);
457         return;
458     }
459
460     if(prev)
461         prev->next = rc->next;
462     else {
463         if(host->calculations == rc)
464             host->calculations = rc->next;
465         else {
466             error("RRDCALC '%s' is not found in host '%s' list", rc->name, host->hostname);
467             return;
468         }
469     }
470
471     last->next = rc;
472     rc->next = NULL;
473 }
474
475 // this has to be called while the caller has locked
476 // the RRDHOST
477 static inline void rrdhostcalc_unlinked(RRDHOST *host, RRDCALC *rc) {
478     // move it to be first
479
480     if(host->calculations == rc) {
481         // ok, we are the first
482         return;
483     }
484     else {
485         // find the previous one
486         RRDCALC *t;
487         for (t = host->calculations; t && t->next != rc; rc = rc->next) ;
488         if(unlikely(!t)) {
489             error("RRDCALC '%s' is not linked to host '%s'.", rc->name, host->hostname);
490             return;
491         }
492         t->next = rc->next;
493         rc->next = host->calculations;
494         host->calculations = rc;
495     }
496 }
497
498 static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
499     rc->rrdset = st;
500
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);
504
505     rrdhostcalc_linked(st->rrdhost, rc);
506 }
507
508 // this has to be called while the RRDHOST is locked
509 void rrdsetcalc_link_matching(RRDSET *st) {
510     RRDCALC *rc;
511
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;
516
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);
520         }
521     }
522 }
523
524 // this has to be called while the RRDHOST is locked
525 void rrdsetcalc_unlink(RRDCALC *rc) {
526     RRDSET *st = rc->rrdset;
527
528     if(!st) {
529         error("Requested to unlink RRDCALC '%s' which is not linked to any RRDSET", rc->name);
530         return;
531     }
532
533     RRDHOST *host = st->rrdhost;
534
535     // unlink it
536     if(rc->rrdset_prev)
537         rc->rrdset_prev->rrdset_next = rc->rrdset_next;
538
539     if(rc->rrdset_next)
540         rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
541
542     if(st->calculations == rc)
543         st->calculations = rc->rrdset_next;
544
545     rc->rrdset_prev = rc->rrdset_next = NULL;
546
547     if(rc->local) {
548         rrdvar_index_del(&st->variables_root_index, rc->local);
549         rrdvar_free(st->rrdhost, rc->local);
550         rc->local = NULL;
551     }
552
553     if(rc->context) {
554         rrdvar_index_del(&st->rrdcontext->variables_root_index, rc->context);
555         rc->context = NULL;
556     }
557
558     if(rc->host) {
559         rrdvar_index_del(&st->rrdhost->variables_root_index, rc->host);
560         rc->host = NULL;
561     }
562
563     rc->rrdset = NULL;
564
565     // RRDCALC will remain in RRDHOST
566     // so that if the matching chart is found in the future
567     // it will be applied automatically
568
569     rrdhostcalc_unlinked(host, rc);
570 }
571
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);
574
575     RRDCALC *rc;
576
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);
581             return NULL;
582         }
583     }
584
585     rc = callocz(1, sizeof(RRDCALC));
586
587     rc->name = strdupz(name);
588     rc->hash = simple_hash(rc->name);
589
590     rc->chart = strdupz(chart);
591     rc->hash_chart = simple_hash(rc->chart);
592
593     if(dimensions) {
594         rc->dimensions = strdupz(dimensions);
595         rc->hash_chart = simple_hash(rc->chart);
596     }
597
598     return NULL;
599 }
600
601 void rrdcalc_free(RRDCALC *rc) {
602     if(!rc) return;
603
604     if(rc->rrdset) rrdsetcalc_unlink(rc);
605
606     freez(rc);
607 }