86cd4e2f187c2a3c87413c463997780673868e4f
[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
309         status = snprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
310         if ((status < 1) || (status >= buffer_len))
311                 return (-1);
312         offset = status;
313
314         for (i = 0; i < ds->ds_num; i++)
315         {
316                 if ((ds->ds[i].type != DS_TYPE_COUNTER)
317                                 && (ds->ds[i].type != DS_TYPE_GAUGE))
318                         return (-1);
319
320                 if (ds->ds[i].type == DS_TYPE_COUNTER)
321                         status = snprintf (buffer + offset, buffer_len - offset,
322                                         ":%llu", vl->values[i].counter);
323                 else
324                         status = snprintf (buffer + offset, buffer_len - offset,
325                                         ":%lf", vl->values[i].gauge);
326
327                 if ((status < 1) || (status >= (buffer_len - offset)))
328                         return (-1);
329
330                 offset += status;
331         } /* for ds->ds_num */
332
333         return (0);
334 } /* int value_list_to_string */
335
336 static int value_list_to_filename (char *buffer, int buffer_len,
337                 const data_set_t *ds, const value_list_t *vl)
338 {
339         int offset = 0;
340         int status;
341
342         status = snprintf (buffer + offset, buffer_len - offset,
343                         "%s/", vl->host);
344         if ((status < 1) || (status >= buffer_len - offset))
345                 return (-1);
346         offset += status;
347
348         if (strlen (vl->plugin_instance) > 0)
349                 status = snprintf (buffer + offset, buffer_len - offset,
350                                 "%s-%s/", vl->plugin, vl->plugin_instance);
351         else
352                 status = snprintf (buffer + offset, buffer_len - offset,
353                                 "%s/", vl->plugin);
354         if ((status < 1) || (status >= buffer_len - offset))
355                 return (-1);
356         offset += status;
357
358         if (strlen (vl->type_instance) > 0)
359                 status = snprintf (buffer + offset, buffer_len - offset,
360                                 "%s-%s.rrd", ds->type, vl->type_instance);
361         else
362                 status = snprintf (buffer + offset, buffer_len - offset,
363                                 "%s.rrd", ds->type);
364         if ((status < 1) || (status >= buffer_len - offset))
365                 return (-1);
366         offset += status;
367
368         return (0);
369 } /* int value_list_to_filename */
370
371 static int rrd_write (const data_set_t *ds, const value_list_t *vl)
372 {
373         struct stat statbuf;
374         char        filename[512];
375         char        values[512];
376         char       *argv[4] = { "update", filename, values, NULL };
377
378         if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
379                 return (-1);
380
381         if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
382                 return (-1);
383
384         if (stat (filename, &statbuf) == -1)
385         {
386                 if (errno == ENOENT)
387                 {
388                         if (rrd_create_file (filename, ds))
389                                 return (-1);
390                 }
391                 else
392                 {
393                         syslog (LOG_ERR, "stat(%s) failed: %s",
394                                         filename, strerror (errno));
395                         return (-1);
396                 }
397         }
398         else if (!S_ISREG (statbuf.st_mode))
399         {
400                 syslog (LOG_ERR, "stat(%s): Not a regular file!",
401                                 filename);
402                 return (-1);
403         }
404
405         DBG ("rrd_update (%s, %s, %s)", argv[0], argv[1], argv[2]);
406
407         optind = 0; /* bug in librrd? */
408         rrd_clear_error ();
409         if (rrd_update (3, argv) == -1)
410         {
411                 syslog (LOG_WARNING, "rrd_update failed: %s: %s",
412                                 filename, rrd_get_error ());
413                 return (-1);
414         }
415         return (0);
416 } /* int rrd_update_file */
417
418 void module_register (void)
419 {
420         plugin_register_write ("rrdtool", rrd_write);
421 }