Merge branch 'ff/entropy' into collectd-4
[collectd.git] / src / rrdtool.c
1 /**
2  * collectd - src/rrdtool.c
3  * Copyright (C) 2006  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_llist.h"
26 #include "utils_debug.h"
27
28 /*
29  * This weird macro cascade forces the glibc to define `NAN'. I don't know
30  * another way to solve this, so more intelligent solutions are welcome. -octo
31  */
32 #ifndef __USE_ISOC99
33 # define DISABLE__USE_ISOC99 1
34 # define __USE_ISOC99 1
35 #endif
36 #include <math.h>
37 #ifdef DISABLE__USE_ISOC99
38 # undef DISABLE__USE_ISOC99
39 # undef __USE_ISOC99
40 #endif
41
42 /*
43  * Private types
44  */
45 struct rrd_cache_s
46 {
47         int    values_num;
48         char **values;
49         time_t first_value;
50 };
51 typedef struct rrd_cache_s rrd_cache_t;
52
53 /*
54  * Private variables
55  */
56 static int rra_timespans[] =
57 {
58         3600,
59         86400,
60         604800,
61         2678400,
62         31622400,
63         0
64 };
65 static int rra_timespans_num = 5;
66
67 static char *rra_types[] =
68 {
69         "AVERAGE",
70         "MIN",
71         "MAX",
72         NULL
73 };
74 static int rra_types_num = 3;
75
76 static const char *config_keys[] =
77 {
78         "CacheTimeout",
79         NULL
80 };
81 static int config_keys_num = 1;
82
83 static int      cache_timeout = 0;
84 static time_t   cache_flush;
85 static llist_t *cache = NULL;
86
87 /* * * * * * * * * *
88  * WARNING:  Magic *
89  * * * * * * * * * */
90 static int rra_get (char ***ret)
91 {
92         static char **rra_def = NULL;
93         static int rra_num = 0;
94
95         int rra_max = rra_timespans_num * rra_types_num;
96
97         int step;
98         int rows;
99         int span;
100
101         int cdp_num;
102         int cdp_len;
103         int i, j;
104
105         char buffer[64];
106
107         if ((rra_num != 0) && (rra_def != NULL))
108         {
109                 *ret = rra_def;
110                 return (rra_num);
111         }
112
113         if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
114                 return (-1);
115         memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
116
117         step = atoi (COLLECTD_STEP);
118         rows = atoi (COLLECTD_ROWS);
119
120         if ((step <= 0) || (rows <= 0))
121         {
122                 *ret = NULL;
123                 return (-1);
124         }
125
126         cdp_len = 0;
127         for (i = 0; i < rra_timespans_num; i++)
128         {
129                 span = rra_timespans[i];
130
131                 if ((span / step) < rows)
132                         continue;
133
134                 if (cdp_len == 0)
135                         cdp_len = 1;
136                 else
137                         cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
138
139                 cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
140
141                 for (j = 0; j < rra_types_num; j++)
142                 {
143                         if (rra_num >= rra_max)
144                                 break;
145
146                         if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
147                                                 rra_types[j], COLLECTD_XFF,
148                                                 cdp_len, cdp_num) >= sizeof (buffer))
149                         {
150                                 syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
151                                 continue;
152                         }
153
154                         rra_def[rra_num++] = sstrdup (buffer);
155                 }
156         }
157
158 #if COLLECT_DEBUG
159         DBG ("rra_num = %i", rra_num);
160         for (i = 0; i < rra_num; i++)
161                 DBG ("  %s", rra_def[i]);
162 #endif
163
164         *ret = rra_def;
165         return (rra_num);
166 }
167
168 static void ds_free (int ds_num, char **ds_def)
169 {
170         int i;
171
172         for (i = 0; i < ds_num; i++)
173                 if (ds_def[i] != NULL)
174                         free (ds_def[i]);
175         free (ds_def);
176 }
177
178 static int ds_get (char ***ret, const data_set_t *ds)
179 {
180         char **ds_def;
181         int ds_num;
182
183         char min[32];
184         char max[32];
185         char buffer[128];
186
187         DBG ("ds->ds_num = %i", ds->ds_num);
188
189         ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
190         if (ds_def == NULL)
191         {
192                 syslog (LOG_ERR, "rrdtool plugin: malloc failed: %s",
193                                 strerror (errno));
194                 return (-1);
195         }
196         memset (ds_def, '\0', ds->ds_num * sizeof (char *));
197
198         for (ds_num = 0; ds_num < ds->ds_num; ds_num++)
199         {
200                 data_source_t *d = ds->ds + ds_num;
201                 char *type;
202                 int status;
203
204                 ds_def[ds_num] = NULL;
205
206                 if (d->type == DS_TYPE_COUNTER)
207                         type = "COUNTER";
208                 else if (d->type == DS_TYPE_GAUGE)
209                         type = "GAUGE";
210                 else
211                 {
212                         syslog (LOG_ERR, "rrdtool plugin: Unknown DS type: %i",
213                                         d->type);
214                         break;
215                 }
216
217                 if (d->min == NAN)
218                 {
219                         strcpy (min, "U");
220                 }
221                 else
222                 {
223                         snprintf (min, sizeof (min), "%lf", d->min);
224                         min[sizeof (min) - 1] = '\0';
225                 }
226
227                 if (d->max == NAN)
228                 {
229                         strcpy (max, "U");
230                 }
231                 else
232                 {
233                         snprintf (max, sizeof (max), "%lf", d->max);
234                         max[sizeof (max) - 1] = '\0';
235                 }
236
237                 status = snprintf (buffer, sizeof (buffer),
238                                 "DS:%s:%s:%s:%s:%s",
239                                 d->name, type, COLLECTD_HEARTBEAT,
240                                 min, max);
241                 if ((status < 1) || (status >= sizeof (buffer)))
242                         break;
243
244                 ds_def[ds_num] = sstrdup (buffer);
245         } /* for ds_num = 0 .. ds->ds_num */
246
247 #if COLLECT_DEBUG
248 {
249         int i;
250         DBG ("ds_num = %i", ds_num);
251         for (i = 0; i < ds_num; i++)
252                 DBG ("  %s", ds_def[i]);
253 }
254 #endif
255
256         if (ds_num != ds->ds_num)
257         {
258                 ds_free (ds_num, ds_def);
259                 return (-1);
260         }
261
262         *ret = ds_def;
263         return (ds_num);
264 }
265
266 static int rrd_create_file (char *filename, const data_set_t *ds)
267 {
268         char **argv;
269         int argc;
270         char **rra_def;
271         int rra_num;
272         char **ds_def;
273         int ds_num;
274         int i, j;
275         int status = 0;
276
277         if (check_create_dir (filename))
278                 return (-1);
279
280         if ((rra_num = rra_get (&rra_def)) < 1)
281         {
282                 syslog (LOG_ERR, "rrd_create_file failed: Could not calculate RRAs");
283                 return (-1);
284         }
285
286         if ((ds_num = ds_get (&ds_def, ds)) < 1)
287         {
288                 syslog (LOG_ERR, "rrd_create_file failed: Could not calculate DSes");
289                 return (-1);
290         }
291
292         argc = ds_num + rra_num + 4;
293
294         if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
295         {
296                 syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
297                 return (-1);
298         }
299
300         argv[0] = "create";
301         argv[1] = filename;
302         argv[2] = "-s";
303         argv[3] = COLLECTD_STEP;
304
305         j = 4;
306         for (i = 0; i < ds_num; i++)
307                 argv[j++] = ds_def[i];
308         for (i = 0; i < rra_num; i++)
309                 argv[j++] = rra_def[i];
310         argv[j] = NULL;
311
312         optind = 0; /* bug in librrd? */
313         rrd_clear_error ();
314         if (rrd_create (argc, argv) == -1)
315         {
316                 syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
317                 status = -1;
318         }
319
320         free (argv);
321         ds_free (ds_num, ds_def);
322
323         return (status);
324 }
325
326 static int value_list_to_string (char *buffer, int buffer_len,
327                 const data_set_t *ds, const value_list_t *vl)
328 {
329         int offset;
330         int status;
331         int i;
332
333         memset (buffer, '\0', sizeof (buffer_len));
334
335         status = snprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
336         if ((status < 1) || (status >= buffer_len))
337                 return (-1);
338         offset = status;
339
340         for (i = 0; i < ds->ds_num; i++)
341         {
342                 if ((ds->ds[i].type != DS_TYPE_COUNTER)
343                                 && (ds->ds[i].type != DS_TYPE_GAUGE))
344                         return (-1);
345
346                 if (ds->ds[i].type == DS_TYPE_COUNTER)
347                         status = snprintf (buffer + offset, buffer_len - offset,
348                                         ":%llu", vl->values[i].counter);
349                 else
350                         status = snprintf (buffer + offset, buffer_len - offset,
351                                         ":%lf", vl->values[i].gauge);
352
353                 if ((status < 1) || (status >= (buffer_len - offset)))
354                         return (-1);
355
356                 offset += status;
357         } /* for ds->ds_num */
358
359         return (0);
360 } /* int value_list_to_string */
361
362 static int value_list_to_filename (char *buffer, int buffer_len,
363                 const data_set_t *ds, const value_list_t *vl)
364 {
365         int offset = 0;
366         int status;
367
368         status = snprintf (buffer + offset, buffer_len - offset,
369                         "%s/", vl->host);
370         if ((status < 1) || (status >= buffer_len - offset))
371                 return (-1);
372         offset += status;
373
374         if (strlen (vl->plugin_instance) > 0)
375                 status = snprintf (buffer + offset, buffer_len - offset,
376                                 "%s-%s/", vl->plugin, vl->plugin_instance);
377         else
378                 status = snprintf (buffer + offset, buffer_len - offset,
379                                 "%s/", vl->plugin);
380         if ((status < 1) || (status >= buffer_len - offset))
381                 return (-1);
382         offset += status;
383
384         if (strlen (vl->type_instance) > 0)
385                 status = snprintf (buffer + offset, buffer_len - offset,
386                                 "%s-%s.rrd", ds->type, vl->type_instance);
387         else
388                 status = snprintf (buffer + offset, buffer_len - offset,
389                                 "%s.rrd", ds->type);
390         if ((status < 1) || (status >= buffer_len - offset))
391                 return (-1);
392         offset += status;
393
394         return (0);
395 } /* int value_list_to_filename */
396
397 static rrd_cache_t *rrd_cache_insert (const char *filename,
398                 const char *value)
399 {
400         rrd_cache_t *rc = NULL;
401         llentry_t   *le = NULL;
402
403         if (cache != NULL)
404         {
405                 le = llist_search (cache, filename);
406                 if (le != NULL)
407                         rc = (rrd_cache_t *) le->value;
408         }
409
410         if (rc == NULL)
411         {
412                 rc = (rrd_cache_t *) malloc (sizeof (rrd_cache_t));
413                 if (rc == NULL)
414                         return (NULL);
415                 rc->values_num = 0;
416                 rc->values = NULL;
417                 rc->first_value = 0;
418         }
419
420         rc->values = (char **) realloc ((void *) rc->values,
421                         (rc->values_num + 1) * sizeof (char *));
422         if (rc->values == NULL)
423         {
424                 syslog (LOG_ERR, "rrdtool plugin: realloc failed: %s",
425                                 strerror (errno));
426                 free (rc);
427                 if (le != NULL)
428                 {
429                         llist_remove (cache, le);
430                         llentry_destroy (le);
431                 }
432                 return (NULL);
433         }
434
435         rc->values[rc->values_num] = strdup (value);
436         if (rc->values[rc->values_num] != NULL)
437                 rc->values_num++;
438
439         if (rc->values_num == 1)
440                 rc->first_value = time (NULL);
441
442         if ((cache != NULL) && (le == NULL))
443         {
444                 le = llentry_create (filename, (void *) rc);
445                 if (le != NULL)
446                         llist_prepend (cache, le);
447         }
448
449         DBG ("rrd_cache_insert (%s, %s) = %p", filename, value, (void *) rc);
450
451         return (rc);
452 } /* rrd_cache_t *rrd_cache_insert */
453
454 static int rrd_write_cache_entry (const char *filename, rrd_cache_t *rc)
455 {
456         char **argv;
457         int    argc;
458
459         char *fn;
460         int status;
461
462         argc = rc->values_num + 2;
463         argv = (char **) malloc ((argc + 1) * sizeof (char *));
464         if (argv == NULL)
465                 return (-1);
466
467         fn = strdup (filename);
468         if (fn == NULL)
469         {
470                 free (argv);
471                 return (-1);
472         }
473
474         argv[0] = "update";
475         argv[1] = fn;
476         memcpy (argv + 2, rc->values, rc->values_num * sizeof (char *));
477         argv[argc] = NULL;
478
479         DBG ("rrd_update (argc = %i, argv = %p)", argc, (void *) argv);
480
481         optind = 0; /* bug in librrd? */
482         rrd_clear_error ();
483         status = rrd_update (argc, argv);
484
485         free (argv);
486         free (fn);
487
488         free (rc->values);
489         rc->values = NULL;
490         rc->values_num = 0;
491
492         if (status != 0)
493         {
494                 syslog (LOG_WARNING, "rrd_update failed: %s: %s",
495                                 filename, rrd_get_error ());
496                 return (-1);
497         }
498
499         return (0);
500 } /* int rrd_update_file */
501
502 static void rrd_cache_flush (int timeout)
503 {
504         llentry_t   *le;
505         rrd_cache_t *rc;
506         time_t       now;
507
508         if (cache == NULL)
509                 return;
510
511         DBG ("Flushing cache, timeout = %i", timeout);
512
513         now = time (NULL);
514
515         /* Remove empty entries */
516         le = llist_head (cache);
517         while (le != NULL)
518         {
519                 llentry_t *next = le->next;
520                 rc = (rrd_cache_t *) le->value;
521                 if (rc->values_num == 0)
522                 {
523                         DBG ("Removing cache entry for `%s'", le->key);
524                         free (rc->values);
525                         free (rc);
526                         llist_remove (cache, le);
527                 }
528                 le = next;
529         }
530
531         /* Write timed out entries */
532         le = llist_head (cache);
533         while (le != NULL)
534         {
535                 rc = (rrd_cache_t *) le->value;
536                 if ((now - rc->first_value) >= timeout)
537                         rrd_write_cache_entry (le->key, rc);
538
539                 le = le->next;
540         }
541
542         cache_flush = now;
543 } /* void rrd_cache_flush */
544
545 static int rrd_write (const data_set_t *ds, const value_list_t *vl)
546 {
547         struct stat  statbuf;
548         char         filename[512];
549         char         values[512];
550         rrd_cache_t *rc;
551         time_t       now;
552
553         if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
554                 return (-1);
555
556         if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
557                 return (-1);
558
559         if (stat (filename, &statbuf) == -1)
560         {
561                 if (errno == ENOENT)
562                 {
563                         if (rrd_create_file (filename, ds))
564                                 return (-1);
565                 }
566                 else
567                 {
568                         syslog (LOG_ERR, "stat(%s) failed: %s",
569                                         filename, strerror (errno));
570                         return (-1);
571                 }
572         }
573         else if (!S_ISREG (statbuf.st_mode))
574         {
575                 syslog (LOG_ERR, "stat(%s): Not a regular file!",
576                                 filename);
577                 return (-1);
578         }
579
580         rc = rrd_cache_insert (filename, values);
581         if (rc == NULL)
582                 return (-1);
583
584         if (cache == NULL)
585         {
586                 rrd_write_cache_entry (filename, rc);
587                 free (rc->values);
588                 free (rc);
589                 return (0);
590         }
591
592         now = time (NULL);
593
594         DBG ("age (%s) = %i", filename, now - rc->first_value);
595
596         if ((now - rc->first_value) >= cache_timeout)
597                 rrd_write_cache_entry (filename, rc);
598
599         if ((time (NULL) - cache_flush) >= cache_timeout)
600         {
601                 rrd_cache_flush (cache_timeout);
602         }
603
604         return (0);
605 } /* int rrd_dispatch */
606
607 static int rrd_config (const char *key, const char *val)
608 {
609         if (strcasecmp ("CacheTimeout", key) == 0)
610         {
611                 int tmp = atoi (val);
612                 if (tmp < 0)
613                 {
614                         fprintf (stderr, "rrdtool: `CacheTimeout' must "
615                                         "be greater than 0.\n");
616                         return (1);
617                 }
618                 cache_timeout = tmp;
619         }
620         else
621         {
622                 return (-1);
623         }
624         return (0);
625 } /* int rrd_config */
626
627 static int rrd_shutdown (void)
628 {
629         rrd_cache_flush (-1);
630
631         return (0);
632 } /* int rrd_shutdown */
633
634 static int rrd_init (void)
635 {
636         if (cache_timeout < 2)
637         {
638                 cache_timeout = 0;
639         }
640         else
641         {
642                 cache = llist_create ();
643                 cache_flush = time (NULL);
644                 plugin_register_shutdown ("rrdtool", rrd_shutdown);
645         }
646         return (0);
647 } /* int rrd_init */
648
649 void module_register (void)
650 {
651         plugin_register_config ("rrdtool", rrd_config,
652                         config_keys, config_keys_num);
653         plugin_register_init ("rrdtool", rrd_init);
654         plugin_register_write ("rrdtool", rrd_write);
655 }