8ad010696f4812bd6b0e8612603b48b21e96350f
[collectd.git] / src / rrdtool.c
1 /**
2  * collectd - src/rrdtool.c
3  * Copyright (C) 2006,2007  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "plugin.h"
24 #include "common.h"
25 #include "utils_avltree.h"
26
27 #if HAVE_PTHREAD_H
28 # include <pthread.h>
29 #endif
30
31 /*
32  * Private types
33  */
34 struct rrd_cache_s
35 {
36         int    values_num;
37         char **values;
38         time_t first_value;
39         time_t last_value;
40 };
41 typedef struct rrd_cache_s rrd_cache_t;
42
43 /*
44  * Private variables
45  */
46 static int rra_timespans[] =
47 {
48         3600,
49         86400,
50         604800,
51         2678400,
52         31622400
53 };
54 static int rra_timespans_num = STATIC_ARRAY_SIZE (rra_timespans);
55
56 static int *rra_timespans_custom = NULL;
57 static int rra_timespans_custom_num = 0;
58
59 static char *rra_types[] =
60 {
61         "AVERAGE",
62         "MIN",
63         "MAX"
64 };
65 static int rra_types_num = STATIC_ARRAY_SIZE (rra_types);
66
67 static const char *config_keys[] =
68 {
69         "CacheTimeout",
70         "CacheFlush",
71         "DataDir",
72         "StepSize",
73         "HeartBeat",
74         "RRARows",
75         "RRATimespan",
76         "XFF"
77 };
78 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
79
80 static char   *datadir   = NULL;
81 static int     stepsize  = 0;
82 static int     heartbeat = 0;
83 static int     rrarows   = 1200;
84 static double  xff       = 0.1;
85
86 static int         cache_timeout = 0;
87 static int         cache_flush_timeout = 0;
88 static time_t      cache_flush_last;
89 static avl_tree_t *cache = NULL;
90 static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
91
92 /* * * * * * * * * *
93  * WARNING:  Magic *
94  * * * * * * * * * */
95 static int rra_get (char ***ret)
96 {
97         static char **rra_def = NULL;
98         static int rra_num = 0;
99
100         int *rts;
101         int  rts_num;
102
103         int rra_max;
104
105         int span;
106
107         int cdp_num;
108         int cdp_len;
109         int i, j;
110
111         char buffer[64];
112
113         if ((rra_num != 0) && (rra_def != NULL))
114         {
115                 *ret = rra_def;
116                 return (rra_num);
117         }
118
119         /* Use the configured timespans or fall back to the built-in defaults */
120         if (rra_timespans_custom_num != 0)
121         {
122                 rts = rra_timespans_custom;
123                 rts_num = rra_timespans_custom_num;
124         }
125         else
126         {
127                 rts = rra_timespans;
128                 rts_num = rra_timespans_num;
129         }
130
131         rra_max = rts_num * rra_types_num;
132
133         if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
134                 return (-1);
135         memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
136
137         if ((stepsize <= 0) || (rrarows <= 0))
138         {
139                 *ret = NULL;
140                 return (-1);
141         }
142
143         cdp_len = 0;
144         for (i = 0; i < rts_num; i++)
145         {
146                 span = rts[i];
147
148                 if ((span / stepsize) < rrarows)
149                         continue;
150
151                 if (cdp_len == 0)
152                         cdp_len = 1;
153                 else
154                         cdp_len = (int) floor (((double) span)
155                                         / ((double) (rrarows * stepsize)));
156
157                 cdp_num = (int) ceil (((double) span)
158                                 / ((double) (cdp_len * stepsize)));
159
160                 for (j = 0; j < rra_types_num; j++)
161                 {
162                         if (rra_num >= rra_max)
163                                 break;
164
165                         if (snprintf (buffer, sizeof (buffer), "RRA:%s:%3.1f:%u:%u",
166                                                 rra_types[j], xff,
167                                                 cdp_len, cdp_num) >= sizeof (buffer))
168                         {
169                                 ERROR ("rra_get: Buffer would have been truncated.");
170                                 continue;
171                         }
172
173                         rra_def[rra_num++] = sstrdup (buffer);
174                 }
175         }
176
177 #if COLLECT_DEBUG
178         DEBUG ("rra_num = %i", rra_num);
179         for (i = 0; i < rra_num; i++)
180                 DEBUG ("  %s", rra_def[i]);
181 #endif
182
183         *ret = rra_def;
184         return (rra_num);
185 }
186
187 static void ds_free (int ds_num, char **ds_def)
188 {
189         int i;
190
191         for (i = 0; i < ds_num; i++)
192                 if (ds_def[i] != NULL)
193                         free (ds_def[i]);
194         free (ds_def);
195 }
196
197 static int ds_get (char ***ret, const data_set_t *ds)
198 {
199         char **ds_def;
200         int ds_num;
201
202         char min[32];
203         char max[32];
204         char buffer[128];
205
206         DEBUG ("ds->ds_num = %i", ds->ds_num);
207
208         ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
209         if (ds_def == NULL)
210         {
211                 char errbuf[1024];
212                 ERROR ("rrdtool plugin: malloc failed: %s",
213                                 sstrerror (errno, errbuf, sizeof (errbuf)));
214                 return (-1);
215         }
216         memset (ds_def, '\0', ds->ds_num * sizeof (char *));
217
218         for (ds_num = 0; ds_num < ds->ds_num; ds_num++)
219         {
220                 data_source_t *d = ds->ds + ds_num;
221                 char *type;
222                 int status;
223
224                 ds_def[ds_num] = NULL;
225
226                 if (d->type == DS_TYPE_COUNTER)
227                         type = "COUNTER";
228                 else if (d->type == DS_TYPE_GAUGE)
229                         type = "GAUGE";
230                 else
231                 {
232                         ERROR ("rrdtool plugin: Unknown DS type: %i",
233                                         d->type);
234                         break;
235                 }
236
237                 if (isnan (d->min))
238                 {
239                         strcpy (min, "U");
240                 }
241                 else
242                 {
243                         snprintf (min, sizeof (min), "%lf", d->min);
244                         min[sizeof (min) - 1] = '\0';
245                 }
246
247                 if (isnan (d->max))
248                 {
249                         strcpy (max, "U");
250                 }
251                 else
252                 {
253                         snprintf (max, sizeof (max), "%lf", d->max);
254                         max[sizeof (max) - 1] = '\0';
255                 }
256
257                 status = snprintf (buffer, sizeof (buffer),
258                                 "DS:%s:%s:%i:%s:%s",
259                                 d->name, type, heartbeat,
260                                 min, max);
261                 if ((status < 1) || (status >= sizeof (buffer)))
262                         break;
263
264                 ds_def[ds_num] = sstrdup (buffer);
265         } /* for ds_num = 0 .. ds->ds_num */
266
267 #if COLLECT_DEBUG
268 {
269         int i;
270         DEBUG ("ds_num = %i", ds_num);
271         for (i = 0; i < ds_num; i++)
272                 DEBUG ("  %s", ds_def[i]);
273 }
274 #endif
275
276         if (ds_num != ds->ds_num)
277         {
278                 ds_free (ds_num, ds_def);
279                 return (-1);
280         }
281
282         *ret = ds_def;
283         return (ds_num);
284 }
285
286 static int rrd_create_file (char *filename, const data_set_t *ds, const value_list_t *vl)
287 {
288         char **argv;
289         int argc;
290         char **rra_def;
291         int rra_num;
292         char **ds_def;
293         int ds_num;
294         int i, j;
295         char stepsize_str[16];
296         char begin_str[16];
297         int status = 0;
298
299         if (check_create_dir (filename))
300                 return (-1);
301
302         if ((rra_num = rra_get (&rra_def)) < 1)
303         {
304                 ERROR ("rrd_create_file failed: Could not calculate RRAs");
305                 return (-1);
306         }
307
308         if ((ds_num = ds_get (&ds_def, ds)) < 1)
309         {
310                 ERROR ("rrd_create_file failed: Could not calculate DSes");
311                 return (-1);
312         }
313
314         argc = ds_num + rra_num + 6;
315
316         if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
317         {
318                 char errbuf[1024];
319                 ERROR ("rrd_create failed: %s",
320                                 sstrerror (errno, errbuf, sizeof (errbuf)));
321                 return (-1);
322         }
323
324         status = snprintf (stepsize_str, sizeof (stepsize_str),
325                         "%i", stepsize);
326         if ((status < 1) || (status >= sizeof (stepsize_str)))
327         {
328                 ERROR ("rrdtool plugin: snprintf failed.");
329                 return (-1);
330         }
331
332         assert (vl->time > 10);
333         status = snprintf (begin_str, sizeof (begin_str),
334                         "%llu", (unsigned long long) (vl->time - 10));
335         if ((status < 1) || (status >= sizeof (begin_str)))
336         {
337                 ERROR ("rrdtool plugin: snprintf failed.");
338                 return (-1);
339         }
340
341         argv[0] = "create";
342         argv[1] = filename;
343         argv[2] = "-b";
344         argv[3] = begin_str;
345         argv[4] = "-s";
346         argv[5] = stepsize_str;
347
348         j = 6;
349         for (i = 0; i < ds_num; i++)
350                 argv[j++] = ds_def[i];
351         for (i = 0; i < rra_num; i++)
352                 argv[j++] = rra_def[i];
353         argv[j] = NULL;
354
355         optind = 0; /* bug in librrd? */
356         rrd_clear_error ();
357         if (rrd_create (argc, argv) == -1)
358         {
359                 ERROR ("rrd_create failed: %s: %s", filename, rrd_get_error ());
360                 status = -1;
361         }
362
363         free (argv);
364         ds_free (ds_num, ds_def);
365
366         return (status);
367 }
368
369 static int value_list_to_string (char *buffer, int buffer_len,
370                 const data_set_t *ds, const value_list_t *vl)
371 {
372         int offset;
373         int status;
374         int i;
375
376         memset (buffer, '\0', buffer_len);
377
378         status = snprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
379         if ((status < 1) || (status >= buffer_len))
380                 return (-1);
381         offset = status;
382
383         for (i = 0; i < ds->ds_num; i++)
384         {
385                 if ((ds->ds[i].type != DS_TYPE_COUNTER)
386                                 && (ds->ds[i].type != DS_TYPE_GAUGE))
387                         return (-1);
388
389                 if (ds->ds[i].type == DS_TYPE_COUNTER)
390                         status = snprintf (buffer + offset, buffer_len - offset,
391                                         ":%llu", vl->values[i].counter);
392                 else
393                         status = snprintf (buffer + offset, buffer_len - offset,
394                                         ":%lf", vl->values[i].gauge);
395
396                 if ((status < 1) || (status >= (buffer_len - offset)))
397                         return (-1);
398
399                 offset += status;
400         } /* for ds->ds_num */
401
402         return (0);
403 } /* int value_list_to_string */
404
405 static int value_list_to_filename (char *buffer, int buffer_len,
406                 const data_set_t *ds, const value_list_t *vl)
407 {
408         int offset = 0;
409         int status;
410
411         if (datadir != NULL)
412         {
413                 status = snprintf (buffer + offset, buffer_len - offset,
414                                 "%s/", datadir);
415                 if ((status < 1) || (status >= buffer_len - offset))
416                         return (-1);
417                 offset += status;
418         }
419
420         status = snprintf (buffer + offset, buffer_len - offset,
421                         "%s/", vl->host);
422         if ((status < 1) || (status >= buffer_len - offset))
423                 return (-1);
424         offset += status;
425
426         if (strlen (vl->plugin_instance) > 0)
427                 status = snprintf (buffer + offset, buffer_len - offset,
428                                 "%s-%s/", vl->plugin, vl->plugin_instance);
429         else
430                 status = snprintf (buffer + offset, buffer_len - offset,
431                                 "%s/", vl->plugin);
432         if ((status < 1) || (status >= buffer_len - offset))
433                 return (-1);
434         offset += status;
435
436         if (strlen (vl->type_instance) > 0)
437                 status = snprintf (buffer + offset, buffer_len - offset,
438                                 "%s-%s.rrd", ds->type, vl->type_instance);
439         else
440                 status = snprintf (buffer + offset, buffer_len - offset,
441                                 "%s.rrd", ds->type);
442         if ((status < 1) || (status >= buffer_len - offset))
443                 return (-1);
444         offset += status;
445
446         return (0);
447 } /* int value_list_to_filename */
448
449 static rrd_cache_t *rrd_cache_insert (const char *filename,
450                 const char *value, time_t value_time)
451 {
452         rrd_cache_t *rc = NULL;
453         int new_rc = 0;
454
455         if (cache != NULL)
456                 avl_get (cache, filename, (void *) &rc);
457
458         if (rc == NULL)
459         {
460                 rc = (rrd_cache_t *) malloc (sizeof (rrd_cache_t));
461                 if (rc == NULL)
462                         return (NULL);
463                 rc->values_num = 0;
464                 rc->values = NULL;
465                 rc->first_value = 0;
466                 rc->last_value = 0;
467                 new_rc = 1;
468         }
469
470         if (rc->last_value >= value_time)
471         {
472                 WARNING ("rrdtool plugin: (rc->last_value = %u) >= (value_time = %u)",
473                                 (unsigned int) rc->last_value,
474                                 (unsigned int) value_time);
475                 return (NULL);
476         }
477
478         rc->values = (char **) realloc ((void *) rc->values,
479                         (rc->values_num + 1) * sizeof (char *));
480         if (rc->values == NULL)
481         {
482                 char errbuf[1024];
483                 ERROR ("rrdtool plugin: realloc failed: %s",
484                                 sstrerror (errno, errbuf, sizeof (errbuf)));
485                 if (cache != NULL)
486                 {
487                         void *cache_key = NULL;
488                         avl_remove (cache, filename, &cache_key, NULL);
489                         sfree (cache_key);
490                 }
491                 free (rc);
492                 return (NULL);
493         }
494
495         rc->values[rc->values_num] = strdup (value);
496         if (rc->values[rc->values_num] != NULL)
497                 rc->values_num++;
498
499         if (rc->values_num == 1)
500                 rc->first_value = value_time;
501         rc->last_value = value_time;
502
503         /* Insert if this is the first value */
504         if ((cache != NULL) && (new_rc == 1))
505         {
506                 void *cache_key = strdup (filename);
507
508                 if (cache_key == NULL)
509                 {
510                         char errbuf[1024];
511                         ERROR ("rrdtool plugin: strdup failed: %s",
512                                         sstrerror (errno, errbuf, sizeof (errbuf)));
513                         sfree (rc->values[0]);
514                         sfree (rc->values);
515                         sfree (rc);
516                         return (NULL);
517                 }
518
519                 avl_insert (cache, cache_key, rc);
520         }
521
522         DEBUG ("rrd_cache_insert (%s, %s, %u) = %p", filename, value,
523                         (unsigned int) value_time, (void *) rc);
524
525         return (rc);
526 } /* rrd_cache_t *rrd_cache_insert */
527
528 static int rrd_write_cache_entry (const char *filename, rrd_cache_t *rc)
529 {
530         char **argv;
531         int    argc;
532
533         char *fn;
534         int status;
535
536         int i;
537
538         if (rc->values_num < 1)
539                 return (0);
540
541         argc = rc->values_num + 2;
542         argv = (char **) malloc ((argc + 1) * sizeof (char *));
543         if (argv == NULL)
544                 return (-1);
545
546         fn = strdup (filename);
547         if (fn == NULL)
548         {
549                 free (argv);
550                 return (-1);
551         }
552
553         argv[0] = "update";
554         argv[1] = fn;
555         memcpy (argv + 2, rc->values, rc->values_num * sizeof (char *));
556         argv[argc] = NULL;
557
558         DEBUG ("rrd_update (argc = %i, argv = %p)", argc, (void *) argv);
559
560         optind = 0; /* bug in librrd? */
561         rrd_clear_error ();
562         status = rrd_update (argc, argv);
563         if (status != 0)
564         {
565                 WARNING ("rrd_update failed: %s: %s",
566                                 filename, rrd_get_error ());
567                 status = -1;
568         }
569
570         free (argv);
571         free (fn);
572         /* Free the value list of `rc' */
573         for (i = 0; i < rc->values_num; i++)
574                 free (rc->values[i]);
575         free (rc->values);
576         rc->values = NULL;
577         rc->values_num = 0;
578
579         return (status);
580 } /* int rrd_write_cache_entry */
581
582 static void rrd_cache_flush (int timeout)
583 {
584         rrd_cache_t *rc;
585         time_t       now;
586
587         char **keys = NULL;
588         int    keys_num = 0;
589
590         char *key;
591         avl_iterator_t *iter;
592         int i;
593
594         if (cache == NULL)
595                 return;
596
597         DEBUG ("rrdtool plugin: Flushing cache, timeout = %i", timeout);
598
599         now = time (NULL);
600
601         /* Build a list of entries to be flushed */
602         iter = avl_get_iterator (cache);
603         while (avl_iterator_next (iter, (void *) &key, (void *) &rc) == 0)
604         {
605                 DEBUG ("key = %s; age = %i;", key, now - rc->first_value);
606                 if ((now - rc->first_value) >= timeout)
607                 {
608                         keys = (char **) realloc ((void *) keys,
609                                         (keys_num + 1) * sizeof (char *));
610                         if (keys == NULL)
611                         {
612                                 char errbuf[1024];
613                                 ERROR ("rrdtool plugin: "
614                                                 "realloc failed: %s",
615                                                 sstrerror (errno, errbuf,
616                                                         sizeof (errbuf)));
617                                 avl_iterator_destroy (iter);
618                                 return;
619                         }
620                         keys[keys_num] = key;
621                         keys_num++;
622                 }
623         } /* while (avl_iterator_next) */
624         avl_iterator_destroy (iter);
625         
626         for (i = 0; i < keys_num; i++)
627         {
628                 if (avl_remove (cache, keys[i], (void *) &key, (void *) &rc) != 0)
629                 {
630                         DEBUG ("rrdtool plugin: avl_remove (%s) failed.", keys[i]);
631                         continue;
632                 }
633
634                 rrd_write_cache_entry (keys[i], rc);
635                 /* rc's value-list is free's by `rrd_write_cache_entry' */
636                 sfree (rc);
637                 sfree (key);
638                 keys[i] = NULL;
639         } /* for (i = 0..keys_num) */
640
641         free (keys);
642
643         cache_flush_last = now;
644 } /* void rrd_cache_flush */
645
646 static int rrd_write (const data_set_t *ds, const value_list_t *vl)
647 {
648         struct stat  statbuf;
649         char         filename[512];
650         char         values[512];
651         rrd_cache_t *rc;
652         time_t       now;
653
654         if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
655                 return (-1);
656
657         if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
658                 return (-1);
659
660         if (stat (filename, &statbuf) == -1)
661         {
662                 if (errno == ENOENT)
663                 {
664                         if (rrd_create_file (filename, ds, vl))
665                                 return (-1);
666                 }
667                 else
668                 {
669                         char errbuf[1024];
670                         ERROR ("stat(%s) failed: %s", filename,
671                                         sstrerror (errno, errbuf,
672                                                 sizeof (errbuf)));
673                         return (-1);
674                 }
675         }
676         else if (!S_ISREG (statbuf.st_mode))
677         {
678                 ERROR ("stat(%s): Not a regular file!",
679                                 filename);
680                 return (-1);
681         }
682
683         pthread_mutex_lock (&cache_lock);
684         rc = rrd_cache_insert (filename, values, vl->time);
685         if (rc == NULL)
686         {
687                 pthread_mutex_unlock (&cache_lock);
688                 return (-1);
689         }
690
691         if (cache == NULL)
692         {
693                 rrd_write_cache_entry (filename, rc);
694                 /* rc's value-list is free's by `rrd_write_cache_entry' */
695                 sfree (rc);
696                 pthread_mutex_unlock (&cache_lock);
697                 return (0);
698         }
699
700         now = time (NULL);
701
702         DEBUG ("age (%s) = %i", filename, now - rc->first_value);
703
704         /* `rc' is not free'd here, because we'll likely reuse it. If not, then
705          * the next flush will remove this entry.  */
706         if ((now - rc->first_value) >= cache_timeout)
707                 rrd_write_cache_entry (filename, rc);
708
709         if ((now - cache_flush_last) >= cache_flush_timeout)
710                 rrd_cache_flush (cache_flush_timeout);
711
712         pthread_mutex_unlock (&cache_lock);
713         return (0);
714 } /* int rrd_write */
715
716 static int rrd_config (const char *key, const char *value)
717 {
718         if (strcasecmp ("CacheTimeout", key) == 0)
719         {
720                 int tmp = atoi (value);
721                 if (tmp < 0)
722                 {
723                         fprintf (stderr, "rrdtool: `CacheTimeout' must "
724                                         "be greater than 0.\n");
725                         return (1);
726                 }
727                 cache_timeout = tmp;
728         }
729         else if (strcasecmp ("CacheFlush", key) == 0)
730         {
731                 int tmp = atoi (value);
732                 if (tmp < 0)
733                 {
734                         fprintf (stderr, "rrdtool: `CacheFlush' must "
735                                         "be greater than 0.\n");
736                         return (1);
737                 }
738                 cache_flush_timeout = tmp;
739         }
740         else if (strcasecmp ("DataDir", key) == 0)
741         {
742                 if (datadir != NULL)
743                         free (datadir);
744                 datadir = strdup (value);
745                 if (datadir != NULL)
746                 {
747                         int len = strlen (datadir);
748                         while ((len > 0) && (datadir[len - 1] == '/'))
749                         {
750                                 len--;
751                                 datadir[len] = '\0';
752                         }
753                         if (len <= 0)
754                         {
755                                 free (datadir);
756                                 datadir = NULL;
757                         }
758                 }
759         }
760         else if (strcasecmp ("StepSize", key) == 0)
761         {
762                 int tmp = atoi (value);
763                 if (tmp <= 0)
764                 {
765                         fprintf (stderr, "rrdtool: `StepSize' must "
766                                         "be greater than 0.\n");
767                         return (1);
768                 }
769                 stepsize = tmp;
770         }
771         else if (strcasecmp ("HeartBeat", key) == 0)
772         {
773                 int tmp = atoi (value);
774                 if (tmp <= 0)
775                 {
776                         fprintf (stderr, "rrdtool: `HeartBeat' must "
777                                         "be greater than 0.\n");
778                         return (1);
779                 }
780                 heartbeat = tmp;
781         }
782         else if (strcasecmp ("RRARows", key) == 0)
783         {
784                 int tmp = atoi (value);
785                 if (tmp <= 0)
786                 {
787                         fprintf (stderr, "rrdtool: `RRARows' must "
788                                         "be greater than 0.\n");
789                         return (1);
790                 }
791                 rrarows = tmp;
792         }
793         else if (strcasecmp ("RRATimespan", key) == 0)
794         {
795                 char *saveptr = NULL;
796                 char *dummy;
797                 char *ptr;
798                 char *value_copy;
799                 int *tmp_alloc;
800
801                 value_copy = strdup (value);
802                 if (value_copy == NULL)
803                         return (1);
804
805                 dummy = value_copy;
806                 while ((ptr = strtok_r (dummy, ", \t", &saveptr)) != NULL)
807                 {
808                         dummy = NULL;
809                         
810                         tmp_alloc = realloc (rra_timespans_custom,
811                                         sizeof (int) * (rra_timespans_custom_num + 1));
812                         if (tmp_alloc == NULL)
813                         {
814                                 fprintf (stderr, "rrdtool: realloc failed.\n");
815                                 free (value_copy);
816                                 return (1);
817                         }
818                         rra_timespans_custom = tmp_alloc;
819                         rra_timespans_custom[rra_timespans_custom_num] = atoi (ptr);
820                         if (rra_timespans_custom[rra_timespans_custom_num] != 0)
821                                 rra_timespans_custom_num++;
822                 } /* while (strtok_r) */
823                 free (value_copy);
824         }
825         else if (strcasecmp ("XFF", key) == 0)
826         {
827                 double tmp = atof (value);
828                 if ((tmp < 0.0) || (tmp >= 1.0))
829                 {
830                         fprintf (stderr, "rrdtool: `XFF' must "
831                                         "be in the range 0 to 1 (exclusive).");
832                         return (1);
833                 }
834                 xff = tmp;
835         }
836         else
837         {
838                 return (-1);
839         }
840         return (0);
841 } /* int rrd_config */
842
843 static int rrd_shutdown (void)
844 {
845         pthread_mutex_lock (&cache_lock);
846         rrd_cache_flush (-1);
847         if (cache != NULL)
848                 avl_destroy (cache);
849         cache = NULL;
850         pthread_mutex_unlock (&cache_lock);
851
852         return (0);
853 } /* int rrd_shutdown */
854
855 static int rrd_init (void)
856 {
857         if (stepsize <= 0)
858                 stepsize = interval_g;
859         if (heartbeat <= 0)
860                 heartbeat = 2 * interval_g;
861
862         if (heartbeat < interval_g)
863                 WARNING ("rrdtool plugin: Your `heartbeat' is "
864                                 "smaller than your `interval'. This will "
865                                 "likely cause problems.");
866         else if (stepsize < interval_g)
867                 WARNING ("rrdtool plugin: Your `stepsize' is "
868                                 "smaller than your `interval'. This will "
869                                 "create needlessly big RRD-files.");
870
871         pthread_mutex_lock (&cache_lock);
872         if (cache_timeout < 2)
873         {
874                 cache_timeout = 0;
875                 cache_flush_timeout = 0;
876         }
877         else
878         {
879                 if (cache_flush_timeout < cache_timeout)
880                         cache_flush_timeout = 10 * cache_timeout;
881
882                 cache = avl_create ((int (*) (const void *, const void *)) strcmp);
883                 cache_flush_last = time (NULL);
884                 plugin_register_shutdown ("rrdtool", rrd_shutdown);
885         }
886         pthread_mutex_unlock (&cache_lock);
887
888         DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %i;"
889                         " heartbeat = %i; rrarows = %i; xff = %lf;",
890                         (datadir == NULL) ? "(null)" : datadir,
891                         stepsize, heartbeat, rrarows, xff);
892
893         return (0);
894 } /* int rrd_init */
895
896 void module_register (void)
897 {
898         plugin_register_config ("rrdtool", rrd_config,
899                         config_keys, config_keys_num);
900         plugin_register_init ("rrdtool", rrd_init);
901         plugin_register_write ("rrdtool", rrd_write);
902 }