Merge pull request #3339 from jkohen/patch-1
[collectd.git] / src / daemon / utils_cache.c
1 /**
2  * collectd - src/utils_cache.c
3  * Copyright (C) 2007-2010  Florian octo Forster
4  * Copyright (C) 2016       Sebastian tokkee Harl
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Authors:
25  *   Florian octo Forster <octo at collectd.org>
26  *   Sebastian tokkee Harl <sh at tokkee.org>
27  **/
28
29 #include "collectd.h"
30
31 #include "plugin.h"
32 #include "utils/avltree/avltree.h"
33 #include "utils/common/common.h"
34 #include "utils/metadata/meta_data.h"
35 #include "utils_cache.h"
36
37 #include <assert.h>
38
39 typedef struct cache_entry_s {
40   char name[6 * DATA_MAX_NAME_LEN];
41   size_t values_num;
42   gauge_t *values_gauge;
43   value_t *values_raw;
44   /* Time contained in the package
45    * (for calculating rates) */
46   cdtime_t last_time;
47   /* Time according to the local clock
48    * (for purging old entries) */
49   cdtime_t last_update;
50   /* Interval in which the data is collected
51    * (for purging old entries) */
52   cdtime_t interval;
53   int state;
54   int hits;
55
56   /*
57    * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
58    * !  0  !  1  !  2  !  3  !  4  !  5  !  6  !  7  !  8  ! ...
59    * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
60    * ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ...
61    * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
62    * !      t = 0      !      t = 1      !      t = 2      ! ...
63    * +-----------------+-----------------+-----------------+----
64    */
65   gauge_t *history;
66   size_t history_index; /* points to the next position to write to. */
67   size_t history_length;
68
69   meta_data_t *meta;
70   unsigned long callbacks_mask;
71 } cache_entry_t;
72
73 struct uc_iter_s {
74   c_avl_iterator_t *iter;
75
76   char *name;
77   cache_entry_t *entry;
78 };
79
80 static c_avl_tree_t *cache_tree;
81 static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
82
83 static int cache_compare(const cache_entry_t *a, const cache_entry_t *b) {
84 #if COLLECT_DEBUG
85   assert((a != NULL) && (b != NULL));
86 #endif
87   return strcmp(a->name, b->name);
88 } /* int cache_compare */
89
90 static cache_entry_t *cache_alloc(size_t values_num) {
91   cache_entry_t *ce;
92
93   ce = calloc(1, sizeof(*ce));
94   if (ce == NULL) {
95     ERROR("utils_cache: cache_alloc: calloc failed.");
96     return NULL;
97   }
98   ce->values_num = values_num;
99
100   ce->values_gauge = calloc(values_num, sizeof(*ce->values_gauge));
101   ce->values_raw = calloc(values_num, sizeof(*ce->values_raw));
102   if ((ce->values_gauge == NULL) || (ce->values_raw == NULL)) {
103     sfree(ce->values_gauge);
104     sfree(ce->values_raw);
105     sfree(ce);
106     ERROR("utils_cache: cache_alloc: calloc failed.");
107     return NULL;
108   }
109
110   ce->history = NULL;
111   ce->history_length = 0;
112   ce->meta = NULL;
113
114   return ce;
115 } /* cache_entry_t *cache_alloc */
116
117 static void cache_free(cache_entry_t *ce) {
118   if (ce == NULL)
119     return;
120
121   sfree(ce->values_gauge);
122   sfree(ce->values_raw);
123   sfree(ce->history);
124   if (ce->meta != NULL) {
125     meta_data_destroy(ce->meta);
126     ce->meta = NULL;
127   }
128   sfree(ce);
129 } /* void cache_free */
130
131 static void uc_check_range(const data_set_t *ds, cache_entry_t *ce) {
132   for (size_t i = 0; i < ds->ds_num; i++) {
133     if (isnan(ce->values_gauge[i]))
134       continue;
135     else if (ce->values_gauge[i] < ds->ds[i].min)
136       ce->values_gauge[i] = NAN;
137     else if (ce->values_gauge[i] > ds->ds[i].max)
138       ce->values_gauge[i] = NAN;
139   }
140 } /* void uc_check_range */
141
142 static int uc_insert(const data_set_t *ds, const value_list_t *vl,
143                      const char *key) {
144   /* `cache_lock' has been locked by `uc_update' */
145
146   char *key_copy = strdup(key);
147   if (key_copy == NULL) {
148     ERROR("uc_insert: strdup failed.");
149     return -1;
150   }
151
152   cache_entry_t *ce = cache_alloc(ds->ds_num);
153   if (ce == NULL) {
154     sfree(key_copy);
155     ERROR("uc_insert: cache_alloc (%" PRIsz ") failed.", ds->ds_num);
156     return -1;
157   }
158
159   sstrncpy(ce->name, key, sizeof(ce->name));
160
161   for (size_t i = 0; i < ds->ds_num; i++) {
162     switch (ds->ds[i].type) {
163     case DS_TYPE_COUNTER:
164       ce->values_gauge[i] = NAN;
165       ce->values_raw[i].counter = vl->values[i].counter;
166       break;
167
168     case DS_TYPE_GAUGE:
169       ce->values_gauge[i] = vl->values[i].gauge;
170       ce->values_raw[i].gauge = vl->values[i].gauge;
171       break;
172
173     case DS_TYPE_DERIVE:
174       ce->values_gauge[i] = NAN;
175       ce->values_raw[i].derive = vl->values[i].derive;
176       break;
177
178     case DS_TYPE_ABSOLUTE:
179       ce->values_gauge[i] = NAN;
180       if (vl->interval > 0)
181         ce->values_gauge[i] =
182             ((double)vl->values[i].absolute) / CDTIME_T_TO_DOUBLE(vl->interval);
183       ce->values_raw[i].absolute = vl->values[i].absolute;
184       break;
185
186     default:
187       /* This shouldn't happen. */
188       ERROR("uc_insert: Don't know how to handle data source type %i.",
189             ds->ds[i].type);
190       sfree(key_copy);
191       cache_free(ce);
192       return -1;
193     } /* switch (ds->ds[i].type) */
194   }   /* for (i) */
195
196   /* Prune invalid gauge data */
197   uc_check_range(ds, ce);
198
199   ce->last_time = vl->time;
200   ce->last_update = cdtime();
201   ce->interval = vl->interval;
202   ce->state = STATE_UNKNOWN;
203
204   if (vl->meta != NULL) {
205     ce->meta = meta_data_clone(vl->meta);
206   }
207
208   if (c_avl_insert(cache_tree, key_copy, ce) != 0) {
209     sfree(key_copy);
210     ERROR("uc_insert: c_avl_insert failed.");
211     return -1;
212   }
213
214   DEBUG("uc_insert: Added %s to the cache.", key);
215   return 0;
216 } /* int uc_insert */
217
218 int uc_init(void) {
219   if (cache_tree == NULL)
220     cache_tree =
221         c_avl_create((int (*)(const void *, const void *))cache_compare);
222
223   return 0;
224 } /* int uc_init */
225
226 int uc_check_timeout(void) {
227   struct {
228     char *key;
229     cdtime_t time;
230     cdtime_t interval;
231     unsigned long callbacks_mask;
232   } *expired = NULL;
233   size_t expired_num = 0;
234
235   pthread_mutex_lock(&cache_lock);
236   cdtime_t now = cdtime();
237
238   /* Build a list of entries to be flushed */
239   c_avl_iterator_t *iter = c_avl_get_iterator(cache_tree);
240   char *key = NULL;
241   cache_entry_t *ce = NULL;
242   while (c_avl_iterator_next(iter, (void *)&key, (void *)&ce) == 0) {
243     /* If the entry is fresh enough, continue. */
244     if ((now - ce->last_update) < (ce->interval * timeout_g))
245       continue;
246
247     void *tmp = realloc(expired, (expired_num + 1) * sizeof(*expired));
248     if (tmp == NULL) {
249       ERROR("uc_check_timeout: realloc failed.");
250       continue;
251     }
252     expired = tmp;
253
254     expired[expired_num].key = strdup(key);
255     expired[expired_num].time = ce->last_time;
256     expired[expired_num].interval = ce->interval;
257     expired[expired_num].callbacks_mask = ce->callbacks_mask;
258
259     if (expired[expired_num].key == NULL) {
260       ERROR("uc_check_timeout: strdup failed.");
261       continue;
262     }
263
264     expired_num++;
265   } /* while (c_avl_iterator_next) */
266
267   c_avl_iterator_destroy(iter);
268   pthread_mutex_unlock(&cache_lock);
269
270   if (expired_num == 0) {
271     sfree(expired);
272     return 0;
273   }
274
275   /* Call the "missing" callback for each value. Do this before removing the
276    * value from the cache, so that callbacks can still access the data stored,
277    * including plugin specific meta data, rates, history, â€¦. This must be done
278    * without holding the lock, otherwise we will run into a deadlock if a
279    * plugin calls the cache interface. */
280   for (size_t i = 0; i < expired_num; i++) {
281     value_list_t vl = {
282         .time = expired[i].time,
283         .interval = expired[i].interval,
284     };
285
286     if (parse_identifier_vl(expired[i].key, &vl) != 0) {
287       ERROR("uc_check_timeout: parse_identifier_vl (\"%s\") failed.",
288             expired[i].key);
289       continue;
290     }
291
292     plugin_dispatch_missing(&vl);
293
294     if (expired[i].callbacks_mask)
295       plugin_dispatch_cache_event(CE_VALUE_EXPIRED, expired[i].callbacks_mask,
296                                   expired[i].key, &vl);
297   } /* for (i = 0; i < expired_num; i++) */
298
299   /* Now actually remove all the values from the cache. We don't re-evaluate
300    * the timestamp again, so in theory it is possible we remove a value after
301    * it is updated here. */
302   pthread_mutex_lock(&cache_lock);
303   for (size_t i = 0; i < expired_num; i++) {
304     char *key = NULL;
305     cache_entry_t *value = NULL;
306
307     if (c_avl_remove(cache_tree, expired[i].key, (void *)&key,
308                      (void *)&value) != 0) {
309       ERROR("uc_check_timeout: c_avl_remove (\"%s\") failed.", expired[i].key);
310       sfree(expired[i].key);
311       continue;
312     }
313     sfree(key);
314     cache_free(value);
315
316     sfree(expired[i].key);
317   } /* for (i = 0; i < expired_num; i++) */
318   pthread_mutex_unlock(&cache_lock);
319
320   sfree(expired);
321   return 0;
322 } /* int uc_check_timeout */
323
324 int uc_update(const data_set_t *ds, const value_list_t *vl) {
325   char name[6 * DATA_MAX_NAME_LEN];
326
327   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
328     ERROR("uc_update: FORMAT_VL failed.");
329     return -1;
330   }
331
332   pthread_mutex_lock(&cache_lock);
333
334   cache_entry_t *ce = NULL;
335   int status = c_avl_get(cache_tree, name, (void *)&ce);
336   if (status != 0) /* entry does not yet exist */
337   {
338     status = uc_insert(ds, vl, name);
339     pthread_mutex_unlock(&cache_lock);
340
341     if (status == 0)
342       plugin_dispatch_cache_event(CE_VALUE_NEW, 0 /* mask */, name, vl);
343
344     return status;
345   }
346
347   assert(ce != NULL);
348   assert(ce->values_num == ds->ds_num);
349
350   if (ce->last_time >= vl->time) {
351     pthread_mutex_unlock(&cache_lock);
352     NOTICE("uc_update: Value too old: name = %s; value time = %.3f; "
353            "last cache update = %.3f;",
354            name, CDTIME_T_TO_DOUBLE(vl->time),
355            CDTIME_T_TO_DOUBLE(ce->last_time));
356     return -1;
357   }
358
359   for (size_t i = 0; i < ds->ds_num; i++) {
360     switch (ds->ds[i].type) {
361     case DS_TYPE_COUNTER: {
362       counter_t diff =
363           counter_diff(ce->values_raw[i].counter, vl->values[i].counter);
364       ce->values_gauge[i] =
365           ((double)diff) / (CDTIME_T_TO_DOUBLE(vl->time - ce->last_time));
366       ce->values_raw[i].counter = vl->values[i].counter;
367     } break;
368
369     case DS_TYPE_GAUGE:
370       ce->values_raw[i].gauge = vl->values[i].gauge;
371       ce->values_gauge[i] = vl->values[i].gauge;
372       break;
373
374     case DS_TYPE_DERIVE: {
375       derive_t diff = vl->values[i].derive - ce->values_raw[i].derive;
376
377       ce->values_gauge[i] =
378           ((double)diff) / (CDTIME_T_TO_DOUBLE(vl->time - ce->last_time));
379       ce->values_raw[i].derive = vl->values[i].derive;
380     } break;
381
382     case DS_TYPE_ABSOLUTE:
383       ce->values_gauge[i] = ((double)vl->values[i].absolute) /
384                             (CDTIME_T_TO_DOUBLE(vl->time - ce->last_time));
385       ce->values_raw[i].absolute = vl->values[i].absolute;
386       break;
387
388     default:
389       /* This shouldn't happen. */
390       pthread_mutex_unlock(&cache_lock);
391       ERROR("uc_update: Don't know how to handle data source type %i.",
392             ds->ds[i].type);
393       return -1;
394     } /* switch (ds->ds[i].type) */
395
396     DEBUG("uc_update: %s: ds[%" PRIsz "] = %lf", name, i, ce->values_gauge[i]);
397   } /* for (i) */
398
399   /* Update the history if it exists. */
400   if (ce->history != NULL) {
401     assert(ce->history_index < ce->history_length);
402     for (size_t i = 0; i < ce->values_num; i++) {
403       size_t hist_idx = (ce->values_num * ce->history_index) + i;
404       ce->history[hist_idx] = ce->values_gauge[i];
405     }
406
407     assert(ce->history_length > 0);
408     ce->history_index = (ce->history_index + 1) % ce->history_length;
409   }
410
411   /* Prune invalid gauge data */
412   uc_check_range(ds, ce);
413
414   ce->last_time = vl->time;
415   ce->last_update = cdtime();
416   ce->interval = vl->interval;
417
418   /* Check if cache entry has registered callbacks */
419   unsigned long callbacks_mask = ce->callbacks_mask;
420
421   pthread_mutex_unlock(&cache_lock);
422
423   if (callbacks_mask)
424     plugin_dispatch_cache_event(CE_VALUE_UPDATE, callbacks_mask, name, vl);
425
426   return 0;
427 } /* int uc_update */
428
429 int uc_set_callbacks_mask(const char *name, unsigned long mask) {
430   pthread_mutex_lock(&cache_lock);
431   cache_entry_t *ce = NULL;
432   int status = c_avl_get(cache_tree, name, (void *)&ce);
433   if (status != 0) { /* Ouch, just created entry disappeared ?! */
434     ERROR("uc_set_callbacks_mask: Couldn't find %s entry!", name);
435     pthread_mutex_unlock(&cache_lock);
436     return -1;
437   }
438   DEBUG("uc_set_callbacks_mask: set mask for \"%s\" to %lu.", name, mask);
439   ce->callbacks_mask = mask;
440   pthread_mutex_unlock(&cache_lock);
441   return 0;
442 }
443
444 int uc_get_rate_by_name(const char *name, gauge_t **ret_values,
445                         size_t *ret_values_num) {
446   gauge_t *ret = NULL;
447   size_t ret_num = 0;
448   cache_entry_t *ce = NULL;
449   int status = 0;
450
451   pthread_mutex_lock(&cache_lock);
452
453   if (c_avl_get(cache_tree, name, (void *)&ce) == 0) {
454     assert(ce != NULL);
455
456     /* remove missing values from getval */
457     if (ce->state == STATE_MISSING) {
458       DEBUG("utils_cache: uc_get_rate_by_name: requested metric \"%s\" is in "
459             "state \"missing\".",
460             name);
461       status = -1;
462     } else {
463       ret_num = ce->values_num;
464       ret = malloc(ret_num * sizeof(*ret));
465       if (ret == NULL) {
466         ERROR("utils_cache: uc_get_rate_by_name: malloc failed.");
467         status = -1;
468       } else {
469         memcpy(ret, ce->values_gauge, ret_num * sizeof(gauge_t));
470       }
471     }
472   } else {
473     DEBUG("utils_cache: uc_get_rate_by_name: No such value: %s", name);
474     status = -1;
475   }
476
477   pthread_mutex_unlock(&cache_lock);
478
479   if (status == 0) {
480     *ret_values = ret;
481     *ret_values_num = ret_num;
482   }
483
484   return status;
485 } /* gauge_t *uc_get_rate_by_name */
486
487 gauge_t *uc_get_rate(const data_set_t *ds, const value_list_t *vl) {
488   char name[6 * DATA_MAX_NAME_LEN];
489   gauge_t *ret = NULL;
490   size_t ret_num = 0;
491   int status;
492
493   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
494     ERROR("utils_cache: uc_get_rate: FORMAT_VL failed.");
495     return NULL;
496   }
497
498   status = uc_get_rate_by_name(name, &ret, &ret_num);
499   if (status != 0)
500     return NULL;
501
502   /* This is important - the caller has no other way of knowing how many
503    * values are returned. */
504   if (ret_num != ds->ds_num) {
505     ERROR("utils_cache: uc_get_rate: ds[%s] has %" PRIsz " values, "
506           "but uc_get_rate_by_name returned %" PRIsz ".",
507           ds->type, ds->ds_num, ret_num);
508     sfree(ret);
509     return NULL;
510   }
511
512   return ret;
513 } /* gauge_t *uc_get_rate */
514
515 int uc_get_value_by_name(const char *name, value_t **ret_values,
516                          size_t *ret_values_num) {
517   value_t *ret = NULL;
518   size_t ret_num = 0;
519   cache_entry_t *ce = NULL;
520   int status = 0;
521
522   pthread_mutex_lock(&cache_lock);
523
524   if (c_avl_get(cache_tree, name, (void *)&ce) == 0) {
525     assert(ce != NULL);
526
527     /* remove missing values from getval */
528     if (ce->state == STATE_MISSING) {
529       status = -1;
530     } else {
531       ret_num = ce->values_num;
532       ret = malloc(ret_num * sizeof(*ret));
533       if (ret == NULL) {
534         ERROR("utils_cache: uc_get_value_by_name: malloc failed.");
535         status = -1;
536       } else {
537         memcpy(ret, ce->values_raw, ret_num * sizeof(value_t));
538       }
539     }
540   } else {
541     DEBUG("utils_cache: uc_get_value_by_name: No such value: %s", name);
542     status = -1;
543   }
544
545   pthread_mutex_unlock(&cache_lock);
546
547   if (status == 0) {
548     *ret_values = ret;
549     *ret_values_num = ret_num;
550   }
551
552   return (status);
553 } /* int uc_get_value_by_name */
554
555 value_t *uc_get_value(const data_set_t *ds, const value_list_t *vl) {
556   char name[6 * DATA_MAX_NAME_LEN];
557   value_t *ret = NULL;
558   size_t ret_num = 0;
559   int status;
560
561   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
562     ERROR("utils_cache: uc_get_value: FORMAT_VL failed.");
563     return (NULL);
564   }
565
566   status = uc_get_value_by_name(name, &ret, &ret_num);
567   if (status != 0)
568     return (NULL);
569
570   /* This is important - the caller has no other way of knowing how many
571    * values are returned. */
572   if (ret_num != (size_t)ds->ds_num) {
573     ERROR("utils_cache: uc_get_value: ds[%s] has %" PRIsz " values, "
574           "but uc_get_value_by_name returned %" PRIsz ".",
575           ds->type, ds->ds_num, ret_num);
576     sfree(ret);
577     return (NULL);
578   }
579
580   return (ret);
581 } /* value_t *uc_get_value */
582
583 size_t uc_get_size(void) {
584   size_t size_arrays = 0;
585
586   pthread_mutex_lock(&cache_lock);
587   size_arrays = (size_t)c_avl_size(cache_tree);
588   pthread_mutex_unlock(&cache_lock);
589
590   return size_arrays;
591 }
592
593 int uc_get_names(char ***ret_names, cdtime_t **ret_times, size_t *ret_number) {
594   c_avl_iterator_t *iter;
595   char *key;
596   cache_entry_t *value;
597
598   char **names = NULL;
599   cdtime_t *times = NULL;
600   size_t number = 0;
601   size_t size_arrays = 0;
602
603   int status = 0;
604
605   if ((ret_names == NULL) || (ret_number == NULL))
606     return -1;
607
608   pthread_mutex_lock(&cache_lock);
609
610   size_arrays = (size_t)c_avl_size(cache_tree);
611   if (size_arrays < 1) {
612     /* Handle the "no values" case here, to avoid the error message when
613      * calloc() returns NULL. */
614     pthread_mutex_unlock(&cache_lock);
615     return 0;
616   }
617
618   names = calloc(size_arrays, sizeof(*names));
619   times = calloc(size_arrays, sizeof(*times));
620   if ((names == NULL) || (times == NULL)) {
621     ERROR("uc_get_names: calloc failed.");
622     sfree(names);
623     sfree(times);
624     pthread_mutex_unlock(&cache_lock);
625     return ENOMEM;
626   }
627
628   iter = c_avl_get_iterator(cache_tree);
629   while (c_avl_iterator_next(iter, (void *)&key, (void *)&value) == 0) {
630     /* remove missing values when list values */
631     if (value->state == STATE_MISSING)
632       continue;
633
634     /* c_avl_size does not return a number smaller than the number of elements
635      * returned by c_avl_iterator_next. */
636     assert(number < size_arrays);
637
638     if (ret_times != NULL)
639       times[number] = value->last_time;
640
641     names[number] = strdup(key);
642     if (names[number] == NULL) {
643       status = -1;
644       break;
645     }
646
647     number++;
648   } /* while (c_avl_iterator_next) */
649
650   c_avl_iterator_destroy(iter);
651   pthread_mutex_unlock(&cache_lock);
652
653   if (status != 0) {
654     for (size_t i = 0; i < number; i++) {
655       sfree(names[i]);
656     }
657     sfree(names);
658     sfree(times);
659
660     return -1;
661   }
662
663   *ret_names = names;
664   if (ret_times != NULL)
665     *ret_times = times;
666   else
667     sfree(times);
668   *ret_number = number;
669
670   return 0;
671 } /* int uc_get_names */
672
673 int uc_get_state(const data_set_t *ds, const value_list_t *vl) {
674   char name[6 * DATA_MAX_NAME_LEN];
675   cache_entry_t *ce = NULL;
676   int ret = STATE_ERROR;
677
678   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
679     ERROR("uc_get_state: FORMAT_VL failed.");
680     return STATE_ERROR;
681   }
682
683   pthread_mutex_lock(&cache_lock);
684
685   if (c_avl_get(cache_tree, name, (void *)&ce) == 0) {
686     assert(ce != NULL);
687     ret = ce->state;
688   }
689
690   pthread_mutex_unlock(&cache_lock);
691
692   return ret;
693 } /* int uc_get_state */
694
695 int uc_set_state(const data_set_t *ds, const value_list_t *vl, int state) {
696   char name[6 * DATA_MAX_NAME_LEN];
697   cache_entry_t *ce = NULL;
698   int ret = -1;
699
700   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
701     ERROR("uc_set_state: FORMAT_VL failed.");
702     return STATE_ERROR;
703   }
704
705   pthread_mutex_lock(&cache_lock);
706
707   if (c_avl_get(cache_tree, name, (void *)&ce) == 0) {
708     assert(ce != NULL);
709     ret = ce->state;
710     ce->state = state;
711   }
712
713   pthread_mutex_unlock(&cache_lock);
714
715   return ret;
716 } /* int uc_set_state */
717
718 int uc_get_history_by_name(const char *name, gauge_t *ret_history,
719                            size_t num_steps, size_t num_ds) {
720   cache_entry_t *ce = NULL;
721   int status = 0;
722
723   pthread_mutex_lock(&cache_lock);
724
725   status = c_avl_get(cache_tree, name, (void *)&ce);
726   if (status != 0) {
727     pthread_mutex_unlock(&cache_lock);
728     return -ENOENT;
729   }
730
731   if (((size_t)ce->values_num) != num_ds) {
732     pthread_mutex_unlock(&cache_lock);
733     return -EINVAL;
734   }
735
736   /* Check if there are enough values available. If not, increase the buffer
737    * size. */
738   if (ce->history_length < num_steps) {
739     gauge_t *tmp;
740
741     tmp =
742         realloc(ce->history, sizeof(*ce->history) * num_steps * ce->values_num);
743     if (tmp == NULL) {
744       pthread_mutex_unlock(&cache_lock);
745       return -ENOMEM;
746     }
747
748     for (size_t i = ce->history_length * ce->values_num;
749          i < (num_steps * ce->values_num); i++)
750       tmp[i] = NAN;
751
752     ce->history = tmp;
753     ce->history_length = num_steps;
754   } /* if (ce->history_length < num_steps) */
755
756   /* Copy the values to the output buffer. */
757   for (size_t i = 0; i < num_steps; i++) {
758     size_t src_index;
759     size_t dst_index;
760
761     if (i < ce->history_index)
762       src_index = ce->history_index - (i + 1);
763     else
764       src_index = ce->history_length + ce->history_index - (i + 1);
765     src_index = src_index * num_ds;
766
767     dst_index = i * num_ds;
768
769     memcpy(ret_history + dst_index, ce->history + src_index,
770            sizeof(*ret_history) * num_ds);
771   }
772
773   pthread_mutex_unlock(&cache_lock);
774
775   return 0;
776 } /* int uc_get_history_by_name */
777
778 int uc_get_history(const data_set_t *ds, const value_list_t *vl,
779                    gauge_t *ret_history, size_t num_steps, size_t num_ds) {
780   char name[6 * DATA_MAX_NAME_LEN];
781
782   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
783     ERROR("utils_cache: uc_get_history: FORMAT_VL failed.");
784     return -1;
785   }
786
787   return uc_get_history_by_name(name, ret_history, num_steps, num_ds);
788 } /* int uc_get_history */
789
790 int uc_get_hits(const data_set_t *ds, const value_list_t *vl) {
791   char name[6 * DATA_MAX_NAME_LEN];
792   cache_entry_t *ce = NULL;
793   int ret = STATE_ERROR;
794
795   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
796     ERROR("uc_get_hits: FORMAT_VL failed.");
797     return STATE_ERROR;
798   }
799
800   pthread_mutex_lock(&cache_lock);
801
802   if (c_avl_get(cache_tree, name, (void *)&ce) == 0) {
803     assert(ce != NULL);
804     ret = ce->hits;
805   }
806
807   pthread_mutex_unlock(&cache_lock);
808
809   return ret;
810 } /* int uc_get_hits */
811
812 int uc_set_hits(const data_set_t *ds, const value_list_t *vl, int hits) {
813   char name[6 * DATA_MAX_NAME_LEN];
814   cache_entry_t *ce = NULL;
815   int ret = -1;
816
817   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
818     ERROR("uc_set_hits: FORMAT_VL failed.");
819     return STATE_ERROR;
820   }
821
822   pthread_mutex_lock(&cache_lock);
823
824   if (c_avl_get(cache_tree, name, (void *)&ce) == 0) {
825     assert(ce != NULL);
826     ret = ce->hits;
827     ce->hits = hits;
828   }
829
830   pthread_mutex_unlock(&cache_lock);
831
832   return ret;
833 } /* int uc_set_hits */
834
835 int uc_inc_hits(const data_set_t *ds, const value_list_t *vl, int step) {
836   char name[6 * DATA_MAX_NAME_LEN];
837   cache_entry_t *ce = NULL;
838   int ret = -1;
839
840   if (FORMAT_VL(name, sizeof(name), vl) != 0) {
841     ERROR("uc_inc_hits: FORMAT_VL failed.");
842     return STATE_ERROR;
843   }
844
845   pthread_mutex_lock(&cache_lock);
846
847   if (c_avl_get(cache_tree, name, (void *)&ce) == 0) {
848     assert(ce != NULL);
849     ret = ce->hits;
850     ce->hits = ret + step;
851   }
852
853   pthread_mutex_unlock(&cache_lock);
854
855   return ret;
856 } /* int uc_inc_hits */
857
858 /*
859  * Iterator interface
860  */
861 uc_iter_t *uc_get_iterator(void) {
862   uc_iter_t *iter = calloc(1, sizeof(*iter));
863   if (iter == NULL)
864     return NULL;
865
866   pthread_mutex_lock(&cache_lock);
867
868   iter->iter = c_avl_get_iterator(cache_tree);
869   if (iter->iter == NULL) {
870     free(iter);
871     return NULL;
872   }
873
874   return iter;
875 } /* uc_iter_t *uc_get_iterator */
876
877 int uc_iterator_next(uc_iter_t *iter, char **ret_name) {
878   int status;
879
880   if (iter == NULL)
881     return -1;
882
883   while ((status = c_avl_iterator_next(iter->iter, (void *)&iter->name,
884                                        (void *)&iter->entry)) == 0) {
885     if (iter->entry->state == STATE_MISSING)
886       continue;
887
888     break;
889   }
890   if (status != 0) {
891     iter->name = NULL;
892     iter->entry = NULL;
893     return -1;
894   }
895
896   if (ret_name != NULL)
897     *ret_name = iter->name;
898
899   return 0;
900 } /* int uc_iterator_next */
901
902 void uc_iterator_destroy(uc_iter_t *iter) {
903   if (iter == NULL)
904     return;
905
906   c_avl_iterator_destroy(iter->iter);
907   pthread_mutex_unlock(&cache_lock);
908
909   free(iter);
910 } /* void uc_iterator_destroy */
911
912 int uc_iterator_get_time(uc_iter_t *iter, cdtime_t *ret_time) {
913   if ((iter == NULL) || (iter->entry == NULL) || (ret_time == NULL))
914     return -1;
915
916   *ret_time = iter->entry->last_time;
917   return 0;
918 } /* int uc_iterator_get_name */
919
920 int uc_iterator_get_values(uc_iter_t *iter, value_t **ret_values,
921                            size_t *ret_num) {
922   if ((iter == NULL) || (iter->entry == NULL) || (ret_values == NULL) ||
923       (ret_num == NULL))
924     return -1;
925   *ret_values =
926       calloc(iter->entry->values_num, sizeof(*iter->entry->values_raw));
927   if (*ret_values == NULL)
928     return -1;
929   for (size_t i = 0; i < iter->entry->values_num; ++i)
930     (*ret_values)[i] = iter->entry->values_raw[i];
931
932   *ret_num = iter->entry->values_num;
933
934   return 0;
935 } /* int uc_iterator_get_values */
936
937 int uc_iterator_get_interval(uc_iter_t *iter, cdtime_t *ret_interval) {
938   if ((iter == NULL) || (iter->entry == NULL) || (ret_interval == NULL))
939     return -1;
940
941   *ret_interval = iter->entry->interval;
942   return 0;
943 } /* int uc_iterator_get_name */
944
945 int uc_iterator_get_meta(uc_iter_t *iter, meta_data_t **ret_meta) {
946   if ((iter == NULL) || (iter->entry == NULL) || (ret_meta == NULL))
947     return -1;
948
949   *ret_meta = meta_data_clone(iter->entry->meta);
950
951   return 0;
952 } /* int uc_iterator_get_meta */
953
954 /*
955  * Meta data interface
956  */
957 /* XXX: This function will acquire `cache_lock' but will not free it! */
958 static meta_data_t *uc_get_meta(const value_list_t *vl) /* {{{ */
959 {
960   char name[6 * DATA_MAX_NAME_LEN];
961   cache_entry_t *ce = NULL;
962   int status;
963
964   status = FORMAT_VL(name, sizeof(name), vl);
965   if (status != 0) {
966     ERROR("utils_cache: uc_get_meta: FORMAT_VL failed.");
967     return NULL;
968   }
969
970   pthread_mutex_lock(&cache_lock);
971
972   status = c_avl_get(cache_tree, name, (void *)&ce);
973   if (status != 0) {
974     pthread_mutex_unlock(&cache_lock);
975     return NULL;
976   }
977   assert(ce != NULL);
978
979   if (ce->meta == NULL)
980     ce->meta = meta_data_create();
981
982   if (ce->meta == NULL)
983     pthread_mutex_unlock(&cache_lock);
984
985   return ce->meta;
986 } /* }}} meta_data_t *uc_get_meta */
987
988 /* Sorry about this preprocessor magic, but it really makes this file much
989  * shorter.. */
990 #define UC_WRAP(wrap_function)                                                 \
991   {                                                                            \
992     meta_data_t *meta;                                                         \
993     int status;                                                                \
994     meta = uc_get_meta(vl);                                                    \
995     if (meta == NULL)                                                          \
996       return -1;                                                               \
997     status = wrap_function(meta, key);                                         \
998     pthread_mutex_unlock(&cache_lock);                                         \
999     return status;                                                             \
1000   }
1001 int uc_meta_data_exists(const value_list_t *vl, const char *key)
1002     UC_WRAP(meta_data_exists)
1003
1004         int uc_meta_data_delete(const value_list_t *vl, const char *key)
1005             UC_WRAP(meta_data_delete)
1006
1007     /* The second argument is called `toc` in the API, but the macro expects
1008      * `key`. */
1009     int uc_meta_data_toc(const value_list_t *vl,
1010                          char ***key) UC_WRAP(meta_data_toc)
1011
1012 #undef UC_WRAP
1013
1014 /* We need a new version of this macro because the following functions take
1015  * two argumetns. */
1016 #define UC_WRAP(wrap_function)                                                 \
1017   {                                                                            \
1018     meta_data_t *meta;                                                         \
1019     int status;                                                                \
1020     meta = uc_get_meta(vl);                                                    \
1021     if (meta == NULL)                                                          \
1022       return -1;                                                               \
1023     status = wrap_function(meta, key, value);                                  \
1024     pthread_mutex_unlock(&cache_lock);                                         \
1025     return status;                                                             \
1026   }
1027         int uc_meta_data_add_string(const value_list_t *vl, const char *key,
1028                                     const char *value)
1029             UC_WRAP(meta_data_add_string) int uc_meta_data_add_signed_int(
1030                 const value_list_t *vl, const char *key, int64_t value)
1031                 UC_WRAP(meta_data_add_signed_int) int uc_meta_data_add_unsigned_int(
1032                     const value_list_t *vl, const char *key, uint64_t value)
1033                     UC_WRAP(meta_data_add_unsigned_int) int uc_meta_data_add_double(
1034                         const value_list_t *vl, const char *key, double value)
1035                         UC_WRAP(meta_data_add_double) int uc_meta_data_add_boolean(
1036                             const value_list_t *vl, const char *key,
1037                             bool value) UC_WRAP(meta_data_add_boolean)
1038
1039                             int uc_meta_data_get_string(const value_list_t *vl,
1040                                                         const char *key,
1041                                                         char **value)
1042                                 UC_WRAP(meta_data_get_string) int uc_meta_data_get_signed_int(
1043                                     const value_list_t *vl, const char *key,
1044                                     int64_t *value)
1045                                     UC_WRAP(meta_data_get_signed_int) int uc_meta_data_get_unsigned_int(
1046                                         const value_list_t *vl, const char *key,
1047                                         uint64_t *value)
1048                                         UC_WRAP(meta_data_get_unsigned_int) int uc_meta_data_get_double(
1049                                             const value_list_t *vl,
1050                                             const char *key, double *value)
1051                                             UC_WRAP(meta_data_get_double) int uc_meta_data_get_boolean(
1052                                                 const value_list_t *vl,
1053                                                 const char *key, bool *value)
1054                                                 UC_WRAP(meta_data_get_boolean)
1055 #undef UC_WRAP