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 **/
22 #include "collectd.h"
23 #include "plugin.h"
24 #include "common.h"
25 #include "utils_debug.h"
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
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;
52 static char *rra_types[] =
53 {
54 "AVERAGE",
55 "MIN",
56 "MAX",
57 NULL
58 };
59 static int rra_types_num = 3;
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;
69 int rra_max = rra_timespans_num * rra_types_num;
71 int step;
72 int rows;
73 int span;
75 int cdp_num;
76 int cdp_len;
77 int i, j;
79 char buffer[64];
81 if ((rra_num != 0) && (rra_def != NULL))
82 {
83 *ret = rra_def;
84 return (rra_num);
85 }
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 *));
91 step = atoi (COLLECTD_STEP);
92 rows = atoi (COLLECTD_ROWS);
94 if ((step <= 0) || (rows <= 0))
95 {
96 *ret = NULL;
97 return (-1);
98 }
100 cdp_len = 0;
101 for (i = 0; i < rra_timespans_num; i++)
102 {
103 span = rra_timespans[i];
105 if ((span / step) < rows)
106 continue;
108 if (cdp_len == 0)
109 cdp_len = 1;
110 else
111 cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
113 cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
115 for (j = 0; j < rra_types_num; j++)
116 {
117 if (rra_num >= rra_max)
118 break;
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 }
128 rra_def[rra_num++] = sstrdup (buffer);
129 }
130 }
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
138 *ret = rra_def;
139 return (rra_num);
140 }
142 static void ds_free (int ds_num, char **ds_def)
143 {
144 int i;
146 for (i = 0; i < ds_num; i++)
147 if (ds_def[i] != NULL)
148 free (ds_def[i]);
149 free (ds_def);
150 }
152 static int ds_get (char ***ret, const data_set_t *ds)
153 {
154 char **ds_def;
155 int ds_num;
157 char min[32];
158 char max[32];
159 char buffer[128];
161 DBG ("ds->ds_num = %i", ds->ds_num);
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 *));
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;
178 ds_def[ds_num] = NULL;
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 }
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 }
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 }
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;
218 ds_def[ds_num] = sstrdup (buffer);
219 } /* for ds_num = 0 .. ds->ds_num */
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
230 if (ds_num != ds->ds_num)
231 {
232 ds_free (ds_num, ds_def);
233 return (-1);
234 }
236 *ret = ds_def;
237 return (ds_num);
238 }
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;
251 if (check_create_dir (filename))
252 return (-1);
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 }
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 }
266 argc = ds_num + rra_num + 4;
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 }
274 argv[0] = "create";
275 argv[1] = filename;
276 argv[2] = "-s";
277 argv[3] = COLLECTD_STEP;
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;
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 }
294 free (argv);
295 ds_free (ds_num, ds_def);
297 return (status);
298 }
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;
307 memset (buffer, '\0', sizeof (buffer_len));
308 buffer[0] = 'N';
309 offset = 1;
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);
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);
324 if ((status < 1) || (status >= (buffer_len - offset)))
325 return (-1);
327 offset += status;
328 } /* for ds->ds_num */
330 return (0);
331 } /* int value_list_to_string */
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;
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;
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;
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;
365 return (0);
366 } /* int value_list_to_filename */
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 };
375 if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
376 return (-1);
378 if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
379 return (-1);
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 }
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 */
413 void module_register (void)
414 {
415 plugin_register_write ("rrdtool", rrd_write);
416 }