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