2 * collectd - src/rrdtool.c
3 * Copyright (C) 2006 Florian octo Forster
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.
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.
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
19 * Florian octo Forster <octo at verplant.org>
25 #include "utils_llist.h"
26 #include "utils_debug.h"
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
33 # define DISABLE__USE_ISOC99 1
34 # define __USE_ISOC99 1
37 #ifdef DISABLE__USE_ISOC99
38 # undef DISABLE__USE_ISOC99
51 typedef struct rrd_cache_s rrd_cache_t;
56 static int rra_timespans[] =
65 static int rra_timespans_num = 5;
67 static char *rra_types[] =
74 static int rra_types_num = 3;
76 static const char *config_keys[] =
82 static int config_keys_num = 2;
84 static char *datadir = NULL;
86 static int cache_timeout = 0;
87 static time_t cache_flush;
88 static llist_t *cache = NULL;
93 static int rra_get (char ***ret)
95 static char **rra_def = NULL;
96 static int rra_num = 0;
98 int rra_max = rra_timespans_num * rra_types_num;
110 if ((rra_num != 0) && (rra_def != NULL))
116 if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
118 memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
120 step = atoi (COLLECTD_STEP);
121 rows = atoi (COLLECTD_ROWS);
123 if ((step <= 0) || (rows <= 0))
130 for (i = 0; i < rra_timespans_num; i++)
132 span = rra_timespans[i];
134 if ((span / step) < rows)
140 cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
142 cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
144 for (j = 0; j < rra_types_num; j++)
146 if (rra_num >= rra_max)
149 if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
150 rra_types[j], COLLECTD_XFF,
151 cdp_len, cdp_num) >= sizeof (buffer))
153 syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
157 rra_def[rra_num++] = sstrdup (buffer);
162 DBG ("rra_num = %i", rra_num);
163 for (i = 0; i < rra_num; i++)
164 DBG (" %s", rra_def[i]);
171 static void ds_free (int ds_num, char **ds_def)
175 for (i = 0; i < ds_num; i++)
176 if (ds_def[i] != NULL)
181 static int ds_get (char ***ret, const data_set_t *ds)
190 DBG ("ds->ds_num = %i", ds->ds_num);
192 ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
195 syslog (LOG_ERR, "rrdtool plugin: malloc failed: %s",
199 memset (ds_def, '\0', ds->ds_num * sizeof (char *));
201 for (ds_num = 0; ds_num < ds->ds_num; ds_num++)
203 data_source_t *d = ds->ds + ds_num;
207 ds_def[ds_num] = NULL;
209 if (d->type == DS_TYPE_COUNTER)
211 else if (d->type == DS_TYPE_GAUGE)
215 syslog (LOG_ERR, "rrdtool plugin: Unknown DS type: %i",
226 snprintf (min, sizeof (min), "%lf", d->min);
227 min[sizeof (min) - 1] = '\0';
236 snprintf (max, sizeof (max), "%lf", d->max);
237 max[sizeof (max) - 1] = '\0';
240 status = snprintf (buffer, sizeof (buffer),
242 d->name, type, COLLECTD_HEARTBEAT,
244 if ((status < 1) || (status >= sizeof (buffer)))
247 ds_def[ds_num] = sstrdup (buffer);
248 } /* for ds_num = 0 .. ds->ds_num */
253 DBG ("ds_num = %i", ds_num);
254 for (i = 0; i < ds_num; i++)
255 DBG (" %s", ds_def[i]);
259 if (ds_num != ds->ds_num)
261 ds_free (ds_num, ds_def);
269 static int rrd_create_file (char *filename, const data_set_t *ds)
280 if (check_create_dir (filename))
283 if ((rra_num = rra_get (&rra_def)) < 1)
285 syslog (LOG_ERR, "rrd_create_file failed: Could not calculate RRAs");
289 if ((ds_num = ds_get (&ds_def, ds)) < 1)
291 syslog (LOG_ERR, "rrd_create_file failed: Could not calculate DSes");
295 argc = ds_num + rra_num + 4;
297 if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
299 syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
306 argv[3] = COLLECTD_STEP;
309 for (i = 0; i < ds_num; i++)
310 argv[j++] = ds_def[i];
311 for (i = 0; i < rra_num; i++)
312 argv[j++] = rra_def[i];
315 optind = 0; /* bug in librrd? */
317 if (rrd_create (argc, argv) == -1)
319 syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
324 ds_free (ds_num, ds_def);
329 static int value_list_to_string (char *buffer, int buffer_len,
330 const data_set_t *ds, const value_list_t *vl)
336 memset (buffer, '\0', sizeof (buffer_len));
338 status = snprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
339 if ((status < 1) || (status >= buffer_len))
343 for (i = 0; i < ds->ds_num; i++)
345 if ((ds->ds[i].type != DS_TYPE_COUNTER)
346 && (ds->ds[i].type != DS_TYPE_GAUGE))
349 if (ds->ds[i].type == DS_TYPE_COUNTER)
350 status = snprintf (buffer + offset, buffer_len - offset,
351 ":%llu", vl->values[i].counter);
353 status = snprintf (buffer + offset, buffer_len - offset,
354 ":%lf", vl->values[i].gauge);
356 if ((status < 1) || (status >= (buffer_len - offset)))
360 } /* for ds->ds_num */
363 } /* int value_list_to_string */
365 static int value_list_to_filename (char *buffer, int buffer_len,
366 const data_set_t *ds, const value_list_t *vl)
373 status = snprintf (buffer + offset, buffer_len - offset,
375 if ((status < 1) || (status >= buffer_len - offset))
380 status = snprintf (buffer + offset, buffer_len - offset,
382 if ((status < 1) || (status >= buffer_len - offset))
386 if (strlen (vl->plugin_instance) > 0)
387 status = snprintf (buffer + offset, buffer_len - offset,
388 "%s-%s/", vl->plugin, vl->plugin_instance);
390 status = snprintf (buffer + offset, buffer_len - offset,
392 if ((status < 1) || (status >= buffer_len - offset))
396 if (strlen (vl->type_instance) > 0)
397 status = snprintf (buffer + offset, buffer_len - offset,
398 "%s-%s.rrd", ds->type, vl->type_instance);
400 status = snprintf (buffer + offset, buffer_len - offset,
402 if ((status < 1) || (status >= buffer_len - offset))
407 } /* int value_list_to_filename */
409 static rrd_cache_t *rrd_cache_insert (const char *filename,
412 rrd_cache_t *rc = NULL;
413 llentry_t *le = NULL;
417 le = llist_search (cache, filename);
419 rc = (rrd_cache_t *) le->value;
424 rc = (rrd_cache_t *) malloc (sizeof (rrd_cache_t));
432 rc->values = (char **) realloc ((void *) rc->values,
433 (rc->values_num + 1) * sizeof (char *));
434 if (rc->values == NULL)
436 syslog (LOG_ERR, "rrdtool plugin: realloc failed: %s",
441 llist_remove (cache, le);
442 llentry_destroy (le);
447 rc->values[rc->values_num] = strdup (value);
448 if (rc->values[rc->values_num] != NULL)
451 if (rc->values_num == 1)
452 rc->first_value = time (NULL);
454 if ((cache != NULL) && (le == NULL))
456 le = llentry_create (filename, (void *) rc);
458 llist_prepend (cache, le);
461 DBG ("rrd_cache_insert (%s, %s) = %p", filename, value, (void *) rc);
464 } /* rrd_cache_t *rrd_cache_insert */
466 static int rrd_write_cache_entry (const char *filename, rrd_cache_t *rc)
474 argc = rc->values_num + 2;
475 argv = (char **) malloc ((argc + 1) * sizeof (char *));
479 fn = strdup (filename);
488 memcpy (argv + 2, rc->values, rc->values_num * sizeof (char *));
491 DBG ("rrd_update (argc = %i, argv = %p)", argc, (void *) argv);
493 optind = 0; /* bug in librrd? */
495 status = rrd_update (argc, argv);
506 syslog (LOG_WARNING, "rrd_update failed: %s: %s",
507 filename, rrd_get_error ());
512 } /* int rrd_update_file */
514 static void rrd_cache_flush (int timeout)
523 DBG ("Flushing cache, timeout = %i", timeout);
527 /* Remove empty entries */
528 le = llist_head (cache);
531 llentry_t *next = le->next;
532 rc = (rrd_cache_t *) le->value;
533 if (rc->values_num == 0)
535 DBG ("Removing cache entry for `%s'", le->key);
538 llist_remove (cache, le);
543 /* Write timed out entries */
544 le = llist_head (cache);
547 rc = (rrd_cache_t *) le->value;
548 if ((now - rc->first_value) >= timeout)
549 rrd_write_cache_entry (le->key, rc);
555 } /* void rrd_cache_flush */
557 static int rrd_write (const data_set_t *ds, const value_list_t *vl)
565 if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
568 if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
571 if (stat (filename, &statbuf) == -1)
575 if (rrd_create_file (filename, ds))
580 syslog (LOG_ERR, "stat(%s) failed: %s",
581 filename, strerror (errno));
585 else if (!S_ISREG (statbuf.st_mode))
587 syslog (LOG_ERR, "stat(%s): Not a regular file!",
592 rc = rrd_cache_insert (filename, values);
598 rrd_write_cache_entry (filename, rc);
606 DBG ("age (%s) = %i", filename, now - rc->first_value);
608 if ((now - rc->first_value) >= cache_timeout)
609 rrd_write_cache_entry (filename, rc);
611 if ((time (NULL) - cache_flush) >= cache_timeout)
613 rrd_cache_flush (cache_timeout);
617 } /* int rrd_dispatch */
619 static int rrd_config (const char *key, const char *val)
621 if (strcasecmp ("CacheTimeout", key) == 0)
623 int tmp = atoi (val);
626 fprintf (stderr, "rrdtool: `CacheTimeout' must "
627 "be greater than 0.\n");
632 else if (strcasecmp ("DataDir", key) == 0)
636 datadir = strdup (val);
639 int len = strlen (datadir);
640 while ((len > 0) && (datadir[len - 1] == '/'))
657 } /* int rrd_config */
659 static int rrd_shutdown (void)
661 rrd_cache_flush (-1);
664 } /* int rrd_shutdown */
666 static int rrd_init (void)
668 if (cache_timeout < 2)
674 cache = llist_create ();
675 cache_flush = time (NULL);
676 plugin_register_shutdown ("rrdtool", rrd_shutdown);
681 void module_register (void)
683 plugin_register_config ("rrdtool", rrd_config,
684 config_keys, config_keys_num);
685 plugin_register_init ("rrdtool", rrd_init);
686 plugin_register_write ("rrdtool", rrd_write);