2 * collectd - src/utils_rrdcreate.c
3 * Copyright (C) 2006-2013 Florian octo Forster
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Florian octo Forster <octo at collectd.org>
30 #include "utils_rrdcreate.h"
35 struct srrd_create_args_s
38 unsigned long pdp_step;
43 typedef struct srrd_create_args_s srrd_create_args_t;
45 struct async_create_file_s;
46 typedef struct async_create_file_s async_create_file_t;
47 struct async_create_file_s
50 async_create_file_t *next;
56 static int rra_timespans[] =
64 static int rra_timespans_num = STATIC_ARRAY_SIZE (rra_timespans);
66 static const char *const rra_types[] =
72 static int rra_types_num = STATIC_ARRAY_SIZE (rra_types);
74 #if !defined(HAVE_THREADSAFE_LIBRRD)
75 static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER;
78 static async_create_file_t *async_creation_list = NULL;
79 static pthread_mutex_t async_creation_lock = PTHREAD_MUTEX_INITIALIZER;
84 static void rra_free (int rra_num, char **rra_def) /* {{{ */
88 for (i = 0; i < rra_num; i++)
93 } /* }}} void rra_free */
95 static void srrd_create_args_destroy (srrd_create_args_t *args)
100 sfree (args->filename);
101 if (args->argv != NULL)
104 for (i = 0; i < args->argc; i++)
105 sfree (args->argv[i]);
109 } /* void srrd_create_args_destroy */
111 static srrd_create_args_t *srrd_create_args_create (const char *filename,
112 unsigned long pdp_step, time_t last_up,
113 int argc, const char **argv)
115 srrd_create_args_t *args;
117 args = calloc (1, sizeof (*args));
120 ERROR ("srrd_create_args_create: calloc failed.");
123 args->filename = NULL;
124 args->pdp_step = pdp_step;
125 args->last_up = last_up;
128 args->filename = strdup (filename);
129 if (args->filename == NULL)
131 ERROR ("srrd_create_args_create: strdup failed.");
132 srrd_create_args_destroy (args);
136 args->argv = calloc ((size_t) (argc + 1), sizeof (*args->argv));
137 if (args->argv == NULL)
139 ERROR ("srrd_create_args_create: calloc failed.");
140 srrd_create_args_destroy (args);
144 for (args->argc = 0; args->argc < argc; args->argc++)
146 args->argv[args->argc] = strdup (argv[args->argc]);
147 if (args->argv[args->argc] == NULL)
149 ERROR ("srrd_create_args_create: strdup failed.");
150 srrd_create_args_destroy (args);
154 assert (args->argc == argc);
155 args->argv[args->argc] = NULL;
158 } /* srrd_create_args_t *srrd_create_args_create */
163 static int rra_get (char ***ret, const value_list_t *vl, /* {{{ */
164 const rrdcreate_config_t *cfg)
178 /* The stepsize we use here: If it is user-set, use it. If not, use the
179 * interval of the value-list. */
182 if (cfg->rrarows <= 0)
188 if ((cfg->xff < 0) || (cfg->xff >= 1.0))
194 if (cfg->stepsize > 0)
197 ss = (int) CDTIME_T_TO_TIME_T (vl->interval);
204 /* Use the configured timespans or fall back to the built-in defaults */
205 if (cfg->timespans_num != 0)
207 rts = cfg->timespans;
208 rts_num = cfg->timespans_num;
213 rts_num = rra_timespans_num;
216 rra_max = rts_num * rra_types_num;
217 assert (rra_max > 0);
219 if ((rra_def = calloc (rra_max + 1, sizeof (*rra_def))) == NULL)
224 for (i = 0; i < rts_num; i++)
228 if ((span / ss) < cfg->rrarows)
229 span = ss * cfg->rrarows;
234 cdp_len = (int) floor (((double) span)
235 / ((double) (cfg->rrarows * ss)));
237 cdp_num = (int) ceil (((double) span)
238 / ((double) (cdp_len * ss)));
240 for (j = 0; j < rra_types_num; j++)
245 if (rra_num >= rra_max)
248 status = ssnprintf (buffer, sizeof (buffer), "RRA:%s:%.10f:%u:%u",
249 rra_types[j], cfg->xff, cdp_len, cdp_num);
251 if ((status < 0) || ((size_t) status >= sizeof (buffer)))
253 ERROR ("rra_get: Buffer would have been truncated.");
257 rra_def[rra_num++] = sstrdup (buffer);
269 } /* }}} int rra_get */
271 static void ds_free (int ds_num, char **ds_def) /* {{{ */
275 for (i = 0; i < ds_num; i++)
276 if (ds_def[i] != NULL)
279 } /* }}} void ds_free */
281 static int ds_get (char ***ret, /* {{{ */
282 const data_set_t *ds, const value_list_t *vl,
283 const rrdcreate_config_t *cfg)
292 assert (ds->ds_num > 0);
294 ds_def = calloc (ds->ds_num, sizeof (*ds_def));
298 ERROR ("rrdtool plugin: calloc failed: %s",
299 sstrerror (errno, errbuf, sizeof (errbuf)));
303 for (ds_num = 0; ds_num < ds->ds_num; ds_num++)
305 data_source_t *d = ds->ds + ds_num;
309 ds_def[ds_num] = NULL;
311 if (d->type == DS_TYPE_COUNTER)
313 else if (d->type == DS_TYPE_GAUGE)
315 else if (d->type == DS_TYPE_DERIVE)
317 else if (d->type == DS_TYPE_ABSOLUTE)
321 ERROR ("rrdtool plugin: Unknown DS type: %i",
328 sstrncpy (min, "U", sizeof (min));
331 ssnprintf (min, sizeof (min), "%f", d->min);
335 sstrncpy (max, "U", sizeof (max));
338 ssnprintf (max, sizeof (max), "%f", d->max);
340 status = ssnprintf (buffer, sizeof (buffer),
345 : (int) CDTIME_T_TO_TIME_T (2 * vl->interval),
347 if ((status < 1) || ((size_t) status >= sizeof (buffer)))
350 ds_def[ds_num] = sstrdup (buffer);
351 } /* for ds_num = 0 .. ds->ds_num */
353 if (ds_num != ds->ds_num)
355 ds_free (ds_num, ds_def);
367 } /* }}} int ds_get */
369 #if HAVE_THREADSAFE_LIBRRD
370 static int srrd_create (const char *filename, /* {{{ */
371 unsigned long pdp_step, time_t last_up,
372 int argc, const char **argv)
377 if ((filename == NULL) || (argv == NULL))
380 /* Some versions of librrd don't have the `const' qualifier for the first
381 * argument, so we have to copy the pointer here to avoid warnings. It sucks,
382 * but what else can we do? :( -octo */
383 filename_copy = strdup (filename);
384 if (filename_copy == NULL)
386 ERROR ("srrd_create: strdup failed.");
390 optind = 0; /* bug in librrd? */
393 status = rrd_create_r (filename_copy, pdp_step, last_up,
394 argc, (void *) argv);
398 WARNING ("rrdtool plugin: rrd_create_r (%s) failed: %s",
399 filename, rrd_get_error ());
402 sfree (filename_copy);
405 } /* }}} int srrd_create */
406 /* #endif HAVE_THREADSAFE_LIBRRD */
408 #else /* !HAVE_THREADSAFE_LIBRRD */
409 static int srrd_create (const char *filename, /* {{{ */
410 unsigned long pdp_step, time_t last_up,
411 int argc, const char **argv)
418 char pdp_step_str[16];
419 char last_up_str[16];
422 new_argv = malloc ((new_argc + 1) * sizeof (*new_argv));
423 if (new_argv == NULL)
425 ERROR ("rrdtool plugin: malloc failed.");
430 last_up = time (NULL) - 10;
432 ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step);
433 ssnprintf (last_up_str, sizeof (last_up_str), "%lu", (unsigned long) last_up);
435 new_argv[0] = "create";
436 new_argv[1] = (void *) filename;
438 new_argv[3] = pdp_step_str;
440 new_argv[5] = last_up_str;
442 memcpy (new_argv + 6, argv, argc * sizeof (char *));
443 new_argv[new_argc] = NULL;
445 pthread_mutex_lock (&librrd_lock);
446 optind = 0; /* bug in librrd? */
449 status = rrd_create (new_argc, new_argv);
450 pthread_mutex_unlock (&librrd_lock);
454 WARNING ("rrdtool plugin: rrd_create (%s) failed: %s",
455 filename, rrd_get_error ());
461 } /* }}} int srrd_create */
462 #endif /* !HAVE_THREADSAFE_LIBRRD */
464 static int lock_file (char const *filename) /* {{{ */
466 async_create_file_t *ptr;
470 pthread_mutex_lock (&async_creation_lock);
472 for (ptr = async_creation_list; ptr != NULL; ptr = ptr->next)
473 if (strcmp (filename, ptr->filename) == 0)
478 pthread_mutex_unlock (&async_creation_lock);
482 status = stat (filename, &sb);
483 if ((status == 0) || (errno != ENOENT))
485 pthread_mutex_unlock (&async_creation_lock);
489 ptr = malloc (sizeof (*ptr));
492 pthread_mutex_unlock (&async_creation_lock);
496 ptr->filename = strdup (filename);
497 if (ptr->filename == NULL)
499 pthread_mutex_unlock (&async_creation_lock);
504 ptr->next = async_creation_list;
505 async_creation_list = ptr;
507 pthread_mutex_unlock (&async_creation_lock);
510 } /* }}} int lock_file */
512 static int unlock_file (char const *filename) /* {{{ */
514 async_create_file_t *this;
515 async_create_file_t *prev;
518 pthread_mutex_lock (&async_creation_lock);
521 for (this = async_creation_list; this != NULL; this = this->next)
523 if (strcmp (filename, this->filename) == 0)
530 pthread_mutex_unlock (&async_creation_lock);
536 assert (this == async_creation_list);
537 async_creation_list = this->next;
541 assert (this == prev->next);
542 prev->next = this->next;
546 pthread_mutex_unlock (&async_creation_lock);
548 sfree (this->filename);
552 } /* }}} int unlock_file */
554 static void *srrd_create_thread (void *targs) /* {{{ */
556 srrd_create_args_t *args = targs;
557 char tmpfile[PATH_MAX];
560 status = lock_file (args->filename);
563 if (status == EEXIST)
564 NOTICE ("srrd_create_thread: File \"%s\" is already being created.",
567 ERROR ("srrd_create_thread: Unable to lock file \"%s\".",
569 srrd_create_args_destroy (args);
573 ssnprintf (tmpfile, sizeof (tmpfile), "%s.async", args->filename);
575 status = srrd_create (tmpfile, args->pdp_step, args->last_up,
576 args->argc, (void *) args->argv);
579 WARNING ("srrd_create_thread: srrd_create (%s) returned status %i.",
580 args->filename, status);
582 unlock_file (args->filename);
583 srrd_create_args_destroy (args);
587 status = rename (tmpfile, args->filename);
591 ERROR ("srrd_create_thread: rename (\"%s\", \"%s\") failed: %s",
592 tmpfile, args->filename,
593 sstrerror (errno, errbuf, sizeof (errbuf)));
595 unlock_file (args->filename);
596 srrd_create_args_destroy (args);
600 DEBUG ("srrd_create_thread: Successfully created RRD file \"%s\".",
603 unlock_file (args->filename);
604 srrd_create_args_destroy (args);
607 } /* }}} void *srrd_create_thread */
609 static int srrd_create_async (const char *filename, /* {{{ */
610 unsigned long pdp_step, time_t last_up,
611 int argc, const char **argv)
613 srrd_create_args_t *args;
618 DEBUG ("srrd_create_async: Creating \"%s\" in the background.", filename);
620 args = srrd_create_args_create (filename, pdp_step, last_up, argc, argv);
624 status = pthread_attr_init (&attr);
627 srrd_create_args_destroy (args);
631 status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
634 pthread_attr_destroy (&attr);
635 srrd_create_args_destroy (args);
639 status = pthread_create (&thread, &attr, srrd_create_thread, args);
643 ERROR ("srrd_create_async: pthread_create failed: %s",
644 sstrerror (status, errbuf, sizeof (errbuf)));
645 pthread_attr_destroy (&attr);
646 srrd_create_args_destroy (args);
650 pthread_attr_destroy (&attr);
651 /* args is freed in srrd_create_thread(). */
653 } /* }}} int srrd_create_async */
658 int cu_rrd_create_file (const char *filename, /* {{{ */
659 const data_set_t *ds, const value_list_t *vl,
660 const rrdcreate_config_t *cfg)
664 char **rra_def = NULL;
666 char **ds_def = NULL;
670 unsigned long stepsize;
672 if (check_create_dir (filename))
675 if ((rra_num = rra_get (&rra_def, vl, cfg)) < 1)
677 ERROR ("cu_rrd_create_file failed: Could not calculate RRAs");
681 if ((ds_num = ds_get (&ds_def, ds, vl, cfg)) < 1)
683 ERROR ("cu_rrd_create_file failed: Could not calculate DSes");
684 rra_free (rra_num, rra_def);
688 argc = ds_num + rra_num;
690 if ((argv = malloc (sizeof (*argv) * (argc + 1))) == NULL)
693 ERROR ("cu_rrd_create_file failed: %s",
694 sstrerror (errno, errbuf, sizeof (errbuf)));
695 rra_free (rra_num, rra_def);
696 ds_free (ds_num, ds_def);
700 memcpy (argv, ds_def, ds_num * sizeof (char *));
701 memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *));
702 argv[ds_num + rra_num] = NULL;
704 last_up = CDTIME_T_TO_TIME_T (vl->time);
706 last_up = time (NULL);
709 if (cfg->stepsize > 0)
710 stepsize = cfg->stepsize;
712 stepsize = (unsigned long) CDTIME_T_TO_TIME_T (vl->interval);
716 status = srrd_create_async (filename, stepsize, last_up,
717 argc, (const char **) argv);
719 WARNING ("cu_rrd_create_file: srrd_create_async (%s) "
720 "returned status %i.",
723 else /* synchronous */
725 status = lock_file (filename);
728 if (status == EEXIST)
729 NOTICE ("cu_rrd_create_file: File \"%s\" is already being created.",
732 ERROR ("cu_rrd_create_file: Unable to lock file \"%s\".",
737 status = srrd_create (filename, stepsize, last_up,
738 argc, (const char **) argv);
742 WARNING ("cu_rrd_create_file: srrd_create (%s) returned status %i.",
747 DEBUG ("cu_rrd_create_file: Successfully created RRD file \"%s\".",
750 unlock_file (filename);
755 ds_free (ds_num, ds_def);
756 rra_free (rra_num, rra_def);
759 } /* }}} int cu_rrd_create_file */
761 /* vim: set sw=2 sts=2 et fdm=marker : */