e6a2786033abd568c36cebfe1e303593f70f09c1
[collectd.git] / src / utils_rrdcreate.c
1 /**
2  * collectd - src/utils_rrdcreate.c
3  * Copyright (C) 2006-2013  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 collectd.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "utils_rrdcreate.h"
25
26 #include <pthread.h>
27 #include <rrd.h>
28
29 struct srrd_create_args_s
30 {
31   char *filename;
32   unsigned long pdp_step;
33   time_t last_up;
34   int argc;
35   char **argv;
36 };
37 typedef struct srrd_create_args_s srrd_create_args_t;
38
39 /*
40  * Private variables
41  */
42 static int rra_timespans[] =
43 {
44   3600,
45   86400,
46   604800,
47   2678400,
48   31622400
49 };
50 static int rra_timespans_num = STATIC_ARRAY_SIZE (rra_timespans);
51
52 static char *rra_types[] =
53 {
54   "AVERAGE",
55   "MIN",
56   "MAX"
57 };
58 static int rra_types_num = STATIC_ARRAY_SIZE (rra_types);
59
60 #if !defined(HAVE_THREADSAFE_LIBRRD) || !HAVE_THREADSAFE_LIBRRD
61 static pthread_mutex_t librrd_lock = PTHREAD_MUTEX_INITIALIZER;
62 #endif
63
64 /*
65  * Private functions
66  */
67 static void rra_free (int rra_num, char **rra_def) /* {{{ */
68 {
69   int i;
70
71   for (i = 0; i < rra_num; i++)
72   {
73     sfree (rra_def[i]);
74   }
75   sfree (rra_def);
76 } /* }}} void rra_free */
77
78 static void srrd_create_args_destroy (srrd_create_args_t *args)
79 {
80   if (args == NULL)
81     return;
82
83   sfree (args->filename);
84   if (args->argv != NULL)
85   {
86     int i;
87     for (i = 0; i < args->argc; i++)
88       sfree (args->argv[i]);
89     sfree (args->argv);
90   }
91 } /* void srrd_create_args_destroy */
92
93 static srrd_create_args_t *srrd_create_args_create (const char *filename,
94     unsigned long pdp_step, time_t last_up,
95     int argc, const char **argv)
96 {
97   srrd_create_args_t *args;
98
99   args = malloc (sizeof (*args));
100   if (args == NULL)
101   {
102     ERROR ("srrd_create_args_create: malloc failed.");
103     return (NULL);
104   }
105   memset (args, 0, sizeof (*args));
106   args->filename = NULL;
107   args->pdp_step = pdp_step;
108   args->last_up = last_up;
109   args->argv = NULL;
110
111   args->filename = strdup (filename);
112   if (args->filename == NULL)
113   {
114     ERROR ("srrd_create_args_create: strdup failed.");
115     srrd_create_args_destroy (args);
116     return (NULL);
117   }
118
119   args->argv = calloc ((size_t) (argc + 1), sizeof (*args->argv));
120   if (args->argv == NULL)
121   {
122     ERROR ("srrd_create_args_create: calloc failed.");
123     srrd_create_args_destroy (args);
124     return (NULL);
125   }
126
127   for (args->argc = 0; args->argc < argc; args->argc++)
128   {
129     args->argv[args->argc] = strdup (argv[args->argc]);
130     if (args->argv[args->argc] == NULL)
131     {
132       ERROR ("srrd_create_args_create: strdup failed.");
133       srrd_create_args_destroy (args);
134       return (NULL);
135     }
136   }
137   assert (args->argc == argc);
138   args->argv[args->argc] = NULL;
139
140   return (args);
141 } /* srrd_create_args_t *srrd_create_args_create */
142
143 /* * * * * * * * * *
144  * WARNING:  Magic *
145  * * * * * * * * * */
146 static int rra_get (char ***ret, const value_list_t *vl, /* {{{ */
147     const rrdcreate_config_t *cfg)
148 {
149   char **rra_def;
150   int rra_num;
151
152   int *rts;
153   int  rts_num;
154
155   int rra_max;
156
157   int span;
158
159   int cdp_num;
160   int cdp_len;
161   int i, j;
162
163   char buffer[128];
164
165   /* The stepsize we use here: If it is user-set, use it. If not, use the
166    * interval of the value-list. */
167   int ss;
168
169   if (cfg->rrarows <= 0)
170   {
171     *ret = NULL;
172     return (-1);
173   }
174
175   if ((cfg->xff < 0) || (cfg->xff >= 1.0))
176   {
177     *ret = NULL;
178     return (-1);
179   }
180
181   if (cfg->stepsize > 0)
182     ss = cfg->stepsize;
183   else
184     ss = (int) CDTIME_T_TO_TIME_T (vl->interval);
185   if (ss <= 0)
186   {
187     *ret = NULL;
188     return (-1);
189   }
190
191   /* Use the configured timespans or fall back to the built-in defaults */
192   if (cfg->timespans_num != 0)
193   {
194     rts = cfg->timespans;
195     rts_num = cfg->timespans_num;
196   }
197   else
198   {
199     rts = rra_timespans;
200     rts_num = rra_timespans_num;
201   }
202
203   rra_max = rts_num * rra_types_num;
204
205   if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
206     return (-1);
207   memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
208   rra_num = 0;
209
210   cdp_len = 0;
211   for (i = 0; i < rts_num; i++)
212   {
213     span = rts[i];
214
215     if ((span / ss) < cfg->rrarows)
216       span = ss * cfg->rrarows;
217
218     if (cdp_len == 0)
219       cdp_len = 1;
220     else
221       cdp_len = (int) floor (((double) span)
222           / ((double) (cfg->rrarows * ss)));
223
224     cdp_num = (int) ceil (((double) span)
225         / ((double) (cdp_len * ss)));
226
227     for (j = 0; j < rra_types_num; j++)
228     {
229       int status;
230
231       if (rra_num >= rra_max)
232         break;
233
234       status = ssnprintf (buffer, sizeof (buffer), "RRA:%s:%.10f:%u:%u",
235           rra_types[j], cfg->xff, cdp_len, cdp_num);
236
237       if ((status < 0) || ((size_t) status >= sizeof (buffer)))
238       {
239         ERROR ("rra_get: Buffer would have been truncated.");
240         continue;
241       }
242
243       rra_def[rra_num++] = sstrdup (buffer);
244     }
245   }
246
247   *ret = rra_def;
248   return (rra_num);
249 } /* }}} int rra_get */
250
251 static void ds_free (int ds_num, char **ds_def) /* {{{ */
252 {
253   int i;
254
255   for (i = 0; i < ds_num; i++)
256     if (ds_def[i] != NULL)
257       free (ds_def[i]);
258   free (ds_def);
259 } /* }}} void ds_free */
260
261 static int ds_get (char ***ret, /* {{{ */
262     const data_set_t *ds, const value_list_t *vl,
263     const rrdcreate_config_t *cfg)
264 {
265   char **ds_def;
266   int ds_num;
267
268   char min[32];
269   char max[32];
270   char buffer[128];
271
272   ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
273   if (ds_def == NULL)
274   {
275     char errbuf[1024];
276     ERROR ("rrdtool plugin: malloc failed: %s",
277         sstrerror (errno, errbuf, sizeof (errbuf)));
278     return (-1);
279   }
280   memset (ds_def, '\0', ds->ds_num * sizeof (char *));
281
282   for (ds_num = 0; ds_num < ds->ds_num; ds_num++)
283   {
284     data_source_t *d = ds->ds + ds_num;
285     char *type;
286     int status;
287
288     ds_def[ds_num] = NULL;
289
290     if (d->type == DS_TYPE_COUNTER)
291       type = "COUNTER";
292     else if (d->type == DS_TYPE_GAUGE)
293       type = "GAUGE";
294     else if (d->type == DS_TYPE_DERIVE)
295       type = "DERIVE";
296     else if (d->type == DS_TYPE_ABSOLUTE)
297       type = "ABSOLUTE";
298     else
299     {
300       ERROR ("rrdtool plugin: Unknown DS type: %i",
301           d->type);
302       break;
303     }
304
305     if (isnan (d->min))
306     {
307       sstrncpy (min, "U", sizeof (min));
308     }
309     else
310       ssnprintf (min, sizeof (min), "%f", d->min);
311
312     if (isnan (d->max))
313     {
314       sstrncpy (max, "U", sizeof (max));
315     }
316     else
317       ssnprintf (max, sizeof (max), "%f", d->max);
318
319     status = ssnprintf (buffer, sizeof (buffer),
320         "DS:%s:%s:%i:%s:%s",
321         d->name, type,
322         (cfg->heartbeat > 0)
323         ? cfg->heartbeat
324         : (int) CDTIME_T_TO_TIME_T (2 * vl->interval),
325         min, max);
326     if ((status < 1) || ((size_t) status >= sizeof (buffer)))
327       break;
328
329     ds_def[ds_num] = sstrdup (buffer);
330   } /* for ds_num = 0 .. ds->ds_num */
331
332   if (ds_num != ds->ds_num)
333   {
334     ds_free (ds_num, ds_def);
335     return (-1);
336   }
337
338   *ret = ds_def;
339   return (ds_num);
340 } /* }}} int ds_get */
341
342 #if HAVE_THREADSAFE_LIBRRD
343 static int srrd_create (const char *filename, /* {{{ */
344     unsigned long pdp_step, time_t last_up,
345     int argc, const char **argv)
346 {
347   int status;
348   char *filename_copy;
349
350   if ((filename == NULL) || (argv == NULL))
351     return (-EINVAL);
352
353   /* Some versions of librrd don't have the `const' qualifier for the first
354    * argument, so we have to copy the pointer here to avoid warnings. It sucks,
355    * but what else can we do? :(  -octo */
356   filename_copy = strdup (filename);
357   if (filename_copy == NULL)
358   {
359     ERROR ("srrd_create: strdup failed.");
360     return (-ENOMEM);
361   }
362
363   optind = 0; /* bug in librrd? */
364   rrd_clear_error ();
365
366   status = rrd_create_r (filename_copy, pdp_step, last_up,
367       argc, (void *) argv);
368
369   if (status != 0)
370   {
371     WARNING ("rrdtool plugin: rrd_create_r (%s) failed: %s",
372         filename, rrd_get_error ());
373   }
374
375   sfree (filename_copy);
376
377   return (status);
378 } /* }}} int srrd_create */
379 /* #endif HAVE_THREADSAFE_LIBRRD */
380
381 #else /* !HAVE_THREADSAFE_LIBRRD */
382 static int srrd_create (const char *filename, /* {{{ */
383     unsigned long pdp_step, time_t last_up,
384     int argc, const char **argv)
385 {
386   int status;
387
388   int new_argc;
389   char **new_argv;
390
391   char pdp_step_str[16];
392   char last_up_str[16];
393
394   new_argc = 6 + argc;
395   new_argv = (char **) malloc ((new_argc + 1) * sizeof (char *));
396   if (new_argv == NULL)
397   {
398     ERROR ("rrdtool plugin: malloc failed.");
399     return (-1);
400   }
401
402   if (last_up == 0)
403     last_up = time (NULL) - 10;
404
405   ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step);
406   ssnprintf (last_up_str, sizeof (last_up_str), "%lu", (unsigned long) last_up);
407
408   new_argv[0] = "create";
409   new_argv[1] = (void *) filename;
410   new_argv[2] = "-s";
411   new_argv[3] = pdp_step_str;
412   new_argv[4] = "-b";
413   new_argv[5] = last_up_str;
414
415   memcpy (new_argv + 6, argv, argc * sizeof (char *));
416   new_argv[new_argc] = NULL;
417
418   pthread_mutex_lock (&librrd_lock);
419   optind = 0; /* bug in librrd? */
420   rrd_clear_error ();
421
422   status = rrd_create (new_argc, new_argv);
423   pthread_mutex_unlock (&librrd_lock);
424
425   if (status != 0)
426   {
427     WARNING ("rrdtool plugin: rrd_create (%s) failed: %s",
428         filename, rrd_get_error ());
429   }
430
431   sfree (new_argv);
432
433   return (status);
434 } /* }}} int srrd_create */
435 #endif /* !HAVE_THREADSAFE_LIBRRD */
436
437 static void *srrd_create_thread (void *targs) /* {{{ */
438 {
439   srrd_create_args_t *args = targs;
440   int status;
441
442   status = srrd_create (args->filename, args->pdp_step, args->last_up,
443       args->argc, (void *) args->argv);
444   if (status != 0)
445   {
446     WARNING ("srrd_create_thread: srrd_create (%s) returned status %i.",
447         args->filename, status);
448   }
449   else
450   {
451     DEBUG ("srrd_create_thread: Successfully created RRD file \"%s\".",
452         args->filename);
453   }
454
455   srrd_create_args_destroy (args);
456
457   return (0);
458 } /* }}} void *srrd_create_thread */
459
460 static int srrd_create_async (const char *filename, /* {{{ */
461     unsigned long pdp_step, time_t last_up,
462     int argc, const char **argv)
463 {
464   srrd_create_args_t *args;
465   pthread_t thread;
466   pthread_attr_t attr;
467   int status;
468
469   args = srrd_create_args_create (filename, pdp_step, last_up, argc, argv);
470   if (args == NULL)
471     return (-1);
472
473   status = pthread_attr_init (&attr);
474   if (status != 0)
475   {
476     srrd_create_args_destroy (args);
477     return (-1);
478   }
479
480   status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
481   if (status != 0)
482   {
483     pthread_attr_destroy (&attr);
484     srrd_create_args_destroy (args);
485     return (-1);
486   }
487
488   status = pthread_create (&thread, &attr, srrd_create_thread, args);
489   if (status != 0)
490   {
491     char errbuf[1024];
492     ERROR ("srrd_create_async: pthread_create failed: %s",
493         sstrerror (status, errbuf, sizeof (errbuf)));
494     pthread_attr_destroy (&attr);
495     srrd_create_args_destroy (args);
496     return (status);
497   }
498
499   pthread_attr_destroy (&attr);
500   /* args is freed in srrd_create_thread(). */
501   return (0);
502 } /* }}} int srrd_create_async */
503
504 /*
505  * Public functions
506  */
507 int cu_rrd_create_file (const char *filename, /* {{{ */
508     const data_set_t *ds, const value_list_t *vl,
509     const rrdcreate_config_t *cfg)
510 {
511   char **argv;
512   int argc;
513   char **rra_def;
514   int rra_num;
515   char **ds_def;
516   int ds_num;
517   int status = 0;
518   time_t last_up;
519   unsigned long stepsize;
520
521   if (check_create_dir (filename))
522     return (-1);
523
524   if ((rra_num = rra_get (&rra_def, vl, cfg)) < 1)
525   {
526     ERROR ("cu_rrd_create_file failed: Could not calculate RRAs");
527     return (-1);
528   }
529
530   if ((ds_num = ds_get (&ds_def, ds, vl, cfg)) < 1)
531   {
532     ERROR ("cu_rrd_create_file failed: Could not calculate DSes");
533     return (-1);
534   }
535
536   argc = ds_num + rra_num;
537
538   if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
539   {
540     char errbuf[1024];
541     ERROR ("cu_rrd_create_file failed: %s",
542         sstrerror (errno, errbuf, sizeof (errbuf)));
543     return (-1);
544   }
545
546   memcpy (argv, ds_def, ds_num * sizeof (char *));
547   memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *));
548   argv[ds_num + rra_num] = NULL;
549
550   last_up = CDTIME_T_TO_TIME_T (vl->time);
551   if (last_up <= 0)
552     last_up = time (NULL);
553   last_up -= 1;
554
555   if (cfg->stepsize > 0)
556     stepsize = cfg->stepsize;
557   else
558     stepsize = (unsigned long) CDTIME_T_TO_TIME_T (vl->interval);
559
560   if (cfg->async)
561   {
562     status = srrd_create_async (filename, stepsize, last_up,
563         argc, (const char **) argv);
564     if (status != 0)
565       WARNING ("cu_rrd_create_file: srrd_create_async (%s) "
566           "returned status %i.",
567           filename, status);
568   }
569   else /* synchronous */
570   {
571     status = srrd_create (filename, stepsize, last_up,
572         argc, (const char **) argv);
573
574     if (status != 0)
575     {
576       WARNING ("cu_rrd_create_file: srrd_create (%s) returned status %i.",
577           filename, status);
578     }
579     else
580     {
581       DEBUG ("cu_rrd_create_file: Successfully created RRD file \"%s\".",
582           filename);
583     }
584   }
585
586   free (argv);
587   ds_free (ds_num, ds_def);
588   rra_free (rra_num, rra_def);
589
590   return (status);
591 } /* }}} int cu_rrd_create_file */
592
593 /* vim: set sw=2 sts=2 et fdm=marker : */