96eee579c2972430b52f62369b640d6ea98ab034
[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_debug.h"
26
27 /*
28  * This weird macro cascade forces the glibc to define `NAN'. I don't know
29  * another way to solve this, so more intelligent solutions are welcome. -octo
30  */
31 #ifndef __USE_ISOC99
32 # define DISABLE__USE_ISOC99 1
33 # define __USE_ISOC99 1
34 #endif
35 #include <math.h>
36 #ifdef DISABLE__USE_ISOC99
37 # undef DISABLE__USE_ISOC99
38 # undef __USE_ISOC99
39 #endif
40
41 static int rra_timespans[] =
42 {
43         3600,
44         86400,
45         604800,
46         2678400,
47         31622400,
48         0
49 };
50 static int rra_timespans_num = 5;
51
52 static char *rra_types[] =
53 {
54         "AVERAGE",
55         "MIN",
56         "MAX",
57         NULL
58 };
59 static int rra_types_num = 3;
60
61 /* * * * * * * * * *
62  * WARNING:  Magic *
63  * * * * * * * * * */
64 static int rra_get (char ***ret)
65 {
66         static char **rra_def = NULL;
67         static int rra_num = 0;
68
69         int rra_max = rra_timespans_num * rra_types_num;
70
71         int step;
72         int rows;
73         int span;
74
75         int cdp_num;
76         int cdp_len;
77         int i, j;
78
79         char buffer[64];
80
81         if ((rra_num != 0) && (rra_def != NULL))
82         {
83                 *ret = rra_def;
84                 return (rra_num);
85         }
86
87         if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
88                 return (-1);
89         memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
90
91         step = atoi (COLLECTD_STEP);
92         rows = atoi (COLLECTD_ROWS);
93
94         if ((step <= 0) || (rows <= 0))
95         {
96                 *ret = NULL;
97                 return (-1);
98         }
99
100         cdp_len = 0;
101         for (i = 0; i < rra_timespans_num; i++)
102         {
103                 span = rra_timespans[i];
104
105                 if ((span / step) < rows)
106                         continue;
107
108                 if (cdp_len == 0)
109                         cdp_len = 1;
110                 else
111                         cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
112
113                 cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
114
115                 for (j = 0; j < rra_types_num; j++)
116                 {
117                         if (rra_num >= rra_max)
118                                 break;
119
120                         if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
121                                                 rra_types[j], COLLECTD_XFF,
122                                                 cdp_len, cdp_num) >= sizeof (buffer))
123                         {
124                                 syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
125                                 continue;
126                         }
127
128                         rra_def[rra_num++] = sstrdup (buffer);
129                 }
130         }
131
132 #if COLLECT_DEBUG
133         DBG ("rra_num = %i", rra_num);
134         for (i = 0; i < rra_num; i++)
135                 DBG ("  %s", rra_def[i]);
136 #endif
137
138         *ret = rra_def;
139         return (rra_num);
140 }
141
142 static void ds_free (int ds_num, char **ds_def)
143 {
144         int i;
145
146         for (i = 0; i < ds_num; i++)
147                 if (ds_def[i] != NULL)
148                         free (ds_def[i]);
149         free (ds_def);
150 }
151
152 static int ds_get (char ***ret, const data_set_t *ds)
153 {
154         char **ds_def;
155         int ds_num;
156
157         char min[32];
158         char max[32];
159         char buffer[128];
160
161         DBG ("ds->ds_num = %i", ds->ds_num);
162
163         ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
164         if (ds_def == NULL)
165         {
166                 syslog (LOG_ERR, "rrdtool plugin: malloc failed: %s",
167                                 strerror (errno));
168                 return (-1);
169         }
170         memset (ds_def, '\0', ds->ds_num * sizeof (char *));
171
172         for (ds_num = 0; ds_num < ds->ds_num; ds_num++)
173         {
174                 data_source_t *d = ds->ds + ds_num;
175                 char *type;
176                 int status;
177
178                 ds_def[ds_num] = NULL;
179
180                 if (d->type == DS_TYPE_COUNTER)
181                         type = "COUNTER";
182                 else if (d->type == DS_TYPE_GAUGE)
183                         type = "GAUGE";
184                 else
185                 {
186                         syslog (LOG_ERR, "rrdtool plugin: Unknown DS type: %i",
187                                         d->type);
188                         break;
189                 }
190
191                 if (d->min == NAN)
192                 {
193                         strcpy (min, "U");
194                 }
195                 else
196                 {
197                         snprintf (min, sizeof (min), "%lf", d->min);
198                         min[sizeof (min) - 1] = '\0';
199                 }
200
201                 if (d->max == NAN)
202                 {
203                         strcpy (max, "U");
204                 }
205                 else
206                 {
207                         snprintf (max, sizeof (max), "%lf", d->max);
208                         max[sizeof (max) - 1] = '\0';
209                 }
210
211                 status = snprintf (buffer, sizeof (buffer),
212                                 "DS:%s:%s:%s:%s:%s",
213                                 d->name, type, COLLECTD_HEARTBEAT,
214                                 min, max);
215                 if ((status < 1) || (status >= sizeof (buffer)))
216                         break;
217
218                 ds_def[ds_num] = sstrdup (buffer);
219         } /* for ds_num = 0 .. ds->ds_num */
220
221 #if COLLECT_DEBUG
222 {
223         int i;
224         DBG ("ds_num = %i", ds_num);
225         for (i = 0; i < ds_num; i++)
226                 DBG ("  %s", ds_def[i]);
227 }
228 #endif
229
230         if (ds_num != ds->ds_num)
231         {
232                 ds_free (ds_num, ds_def);
233                 return (-1);
234         }
235
236         *ret = ds_def;
237         return (ds_num);
238 }
239
240 static int rrd_create_file (char *filename, const data_set_t *ds)
241 {
242         char **argv;
243         int argc;
244         char **rra_def;
245         int rra_num;
246         char **ds_def;
247         int ds_num;
248         int i, j;
249         int status = 0;
250
251         if (check_create_dir (filename))
252                 return (-1);
253
254         if ((rra_num = rra_get (&rra_def)) < 1)
255         {
256                 syslog (LOG_ERR, "rrd_create_file failed: Could not calculate RRAs");
257                 return (-1);
258         }
259
260         if ((ds_num = ds_get (&ds_def, ds)) < 1)
261         {
262                 syslog (LOG_ERR, "rrd_create_file failed: Could not calculate DSes");
263                 return (-1);
264         }
265
266         argc = ds_num + rra_num + 4;
267
268         if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
269         {
270                 syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
271                 return (-1);
272         }
273
274         argv[0] = "create";
275         argv[1] = filename;
276         argv[2] = "-s";
277         argv[3] = COLLECTD_STEP;
278
279         j = 4;
280         for (i = 0; i < ds_num; i++)
281                 argv[j++] = ds_def[i];
282         for (i = 0; i < rra_num; i++)
283                 argv[j++] = rra_def[i];
284         argv[j] = NULL;
285
286         optind = 0; /* bug in librrd? */
287         rrd_clear_error ();
288         if (rrd_create (argc, argv) == -1)
289         {
290                 syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
291                 status = -1;
292         }
293
294         free (argv);
295         ds_free (ds_num, ds_def);
296
297         return (status);
298 }
299
300 static int value_list_to_string (char *buffer, int buffer_len,
301                 const data_set_t *ds, const value_list_t *vl)
302 {
303         int offset;
304         int status;
305         int i;
306
307         memset (buffer, '\0', sizeof (buffer_len));
308         buffer[0] = 'N';
309         offset = 1;
310
311         for (i = 0; i < ds->ds_num; i++)
312         {
313                 if ((ds->ds[i].type != DS_TYPE_COUNTER)
314                                 && (ds->ds[i].type != DS_TYPE_GAUGE))
315                         return (-1);
316
317                 if (ds->ds[i].type == DS_TYPE_COUNTER)
318                         status = snprintf (buffer + offset, buffer_len - offset,
319                                         ":%llu", vl->values[i].counter);
320                 else
321                         status = snprintf (buffer + offset, buffer_len - offset,
322                                         ":%lf", vl->values[i].gauge);
323
324                 if ((status < 1) || (status >= (buffer_len - offset)))
325                         return (-1);
326
327                 offset += status;
328         } /* for ds->ds_num */
329
330         return (0);
331 } /* int value_list_to_string */
332
333 static int value_list_to_filename (char *buffer, int buffer_len,
334                 const data_set_t *ds, const value_list_t *vl)
335 {
336         int offset = 0;
337         int status;
338
339         status = snprintf (buffer + offset, buffer_len - offset,
340                         "%s/", vl->host);
341         if ((status < 1) || (status >= buffer_len - offset))
342                 return (-1);
343         offset += status;
344
345         if (strlen (vl->plugin_instance) > 0)
346                 status = snprintf (buffer + offset, buffer_len - offset,
347                                 "%s-%s/", vl->plugin, vl->plugin_instance);
348         else
349                 status = snprintf (buffer + offset, buffer_len - offset,
350                                 "%s/", vl->plugin);
351         if ((status < 1) || (status >= buffer_len - offset))
352                 return (-1);
353         offset += status;
354
355         if (strlen (vl->type_instance) > 0)
356                 status = snprintf (buffer + offset, buffer_len - offset,
357                                 "%s-%s.rrd", ds->type, vl->type_instance);
358         else
359                 status = snprintf (buffer + offset, buffer_len - offset,
360                                 "%s.rrd", ds->type);
361         if ((status < 1) || (status >= buffer_len - offset))
362                 return (-1);
363         offset += status;
364
365         return (0);
366 } /* int value_list_to_filename */
367
368 static int rrd_write (const data_set_t *ds, const value_list_t *vl)
369 {
370         struct stat statbuf;
371         char        filename[512];
372         char        values[512];
373         char       *argv[4] = { "update", filename, values, NULL };
374
375         if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
376                 return (-1);
377
378         if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
379                 return (-1);
380
381         if (stat (filename, &statbuf) == -1)
382         {
383                 if (errno == ENOENT)
384                 {
385                         if (rrd_create_file (filename, ds))
386                                 return (-1);
387                 }
388                 else
389                 {
390                         syslog (LOG_ERR, "stat(%s) failed: %s",
391                                         filename, strerror (errno));
392                         return (-1);
393                 }
394         }
395         else if (!S_ISREG (statbuf.st_mode))
396         {
397                 syslog (LOG_ERR, "stat(%s): Not a regular file!",
398                                 filename);
399                 return (-1);
400         }
401
402         optind = 0; /* bug in librrd? */
403         rrd_clear_error ();
404         if (rrd_update (3, argv) == -1)
405         {
406                 syslog (LOG_WARNING, "rrd_update failed: %s: %s",
407                                 filename, rrd_get_error ());
408                 return (-1);
409         }
410         return (0);
411 } /* int rrd_update_file */
412
413 void module_register (void)
414 {
415         plugin_register_write ("rrdtool", rrd_write);
416 }