src/dp_rrdtool.c: Actually do handle the config option.
[collection4.git] / src / dp_rrdtool.c
1 /**
2  * collection4 - data_provider.h
3  * Copyright (C) 2010  Florian octo Forster
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  * 
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors:
21  *   Florian octo Forster <ff at octo.it>
22  **/
23
24 #include "config.h"
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <limits.h>
30 #include <errno.h>
31 #include <assert.h>
32
33 #include <rrd.h>
34
35 #include "graph_types.h"
36 #include "graph_config.h"
37 #include "graph_ident.h"
38 #include "data_provider.h"
39 #include "filesystem.h"
40 #include "oconfig.h"
41 #include "common.h"
42
43 #include <fcgiapp.h>
44 #include <fcgi_stdio.h>
45
46 struct dp_rrdtool_s
47 {
48   char *data_dir;
49 };
50 typedef struct dp_rrdtool_s dp_rrdtool_t;
51
52 struct dp_get_idents_data_s
53 { /* {{{ */
54   graph_ident_t *ident;
55   dp_get_idents_callback callback;
56   void *user_data;
57 }; /* }}} */
58 typedef struct dp_get_idents_data_s dp_get_idents_data_t;
59
60 static int scan_type_cb (__attribute__((unused)) const char *base_dir,
61     const char *file, void *ud)
62 { /* {{{ */
63   dp_get_idents_data_t *data = ud;
64   size_t file_len;
65   char type_copy[1024];
66   size_t type_copy_len;
67   char *type_inst;
68
69   file_len = strlen (file);
70   if (file_len < 5)
71     return (0);
72
73   /* Ignore files that don't end in ".rrd". */
74   if (strcasecmp (".rrd", file + (file_len - 4)) != 0)
75     return (0);
76
77   strncpy (type_copy, file, sizeof (type_copy));
78   type_copy_len = file_len - 4;
79   if (type_copy_len > (sizeof (type_copy) - 1))
80     type_copy_len = sizeof (type_copy) - 1;
81   type_copy[type_copy_len] = 0;
82
83   type_inst = strchr (type_copy, '-');
84   if (type_inst != NULL)
85   {
86     *type_inst = 0;
87     type_inst++;
88   }
89   else
90   {
91     type_inst = "";
92   }
93
94   ident_set_type (data->ident, type_copy);
95   ident_set_type_instance (data->ident, type_inst);
96
97   return (data->callback (data->ident, data->user_data));
98 } /* }}} int scan_type_cb */
99
100 static int scan_plugin_cb (const char *base_dir,
101     const char *sub_dir, void *ud)
102 { /* {{{ */
103   char plugin_copy[1024];
104   char *plugin_inst;
105
106   dp_get_idents_data_t *data = ud;
107   char abs_dir[PATH_MAX + 1];
108
109   strncpy (plugin_copy, sub_dir, sizeof (plugin_copy));
110   plugin_copy[sizeof (plugin_copy) - 1] = 0;
111
112   plugin_inst = strchr (plugin_copy, '-');
113   if (plugin_inst != NULL)
114   {
115     *plugin_inst = 0;
116     plugin_inst++;
117   }
118   else
119   {
120     plugin_inst = "";
121   }
122
123   ident_set_plugin (data->ident, plugin_copy);
124   ident_set_plugin_instance (data->ident, plugin_inst);
125
126   snprintf (abs_dir, sizeof (abs_dir), "%s/%s", base_dir, sub_dir);
127   abs_dir[sizeof (abs_dir) - 1] = 0;
128
129   return (fs_foreach_file (abs_dir, scan_type_cb, data));
130 } /* }}} int scan_host_cb */
131
132 static int scan_host_cb (const char *base_dir,
133     const char *sub_dir, void *ud)
134 { /* {{{ */
135   dp_get_idents_data_t *data = ud;
136   char abs_dir[PATH_MAX + 1];
137
138   ident_set_host (data->ident, sub_dir);
139
140   snprintf (abs_dir, sizeof (abs_dir), "%s/%s", base_dir, sub_dir);
141   abs_dir[sizeof (abs_dir) - 1] = 0;
142
143   return (fs_foreach_dir (abs_dir, scan_plugin_cb, data));
144 } /* }}} int scan_host_cb */
145
146 static int ident_to_rrdfile (const graph_ident_t *ident, /* {{{ */
147     dp_rrdtool_t *config,
148     char *buffer, size_t buffer_size)
149 {
150   const char *plugin_instance;
151   const char *type_instance;
152
153   plugin_instance = ident_get_plugin_instance (ident);
154   if ((plugin_instance != NULL) && (plugin_instance[0] == 0))
155     plugin_instance = NULL;
156
157   type_instance = ident_get_type_instance (ident);
158   if ((type_instance != NULL) && (type_instance[0] == 0))
159     type_instance = NULL;
160
161   buffer[0] = 0;
162
163   strlcat (buffer, config->data_dir, buffer_size);
164   strlcat (buffer, "/", buffer_size);
165
166   strlcat (buffer, ident_get_host (ident), buffer_size);
167   strlcat (buffer, "/", buffer_size);
168   strlcat (buffer, ident_get_plugin (ident), buffer_size);
169   if (plugin_instance != NULL)
170   {
171     strlcat (buffer, "-", buffer_size);
172     strlcat (buffer, plugin_instance, buffer_size);
173   }
174   strlcat (buffer, "/", buffer_size);
175   strlcat (buffer, ident_get_type (ident), buffer_size);
176   if (type_instance != NULL)
177   {
178     strlcat (buffer, "-", buffer_size);
179     strlcat (buffer, type_instance, buffer_size);
180   }
181
182   strlcat (buffer, ".rrd", buffer_size);
183
184   return (0);
185 } /* }}} int ident_to_rrdfile */
186
187 /*
188  * Callback functions
189  */
190 static int get_idents (void *priv,
191     dp_get_idents_callback cb, void *ud)
192 { /* {{{ */
193   dp_rrdtool_t *config = priv;
194   dp_get_idents_data_t data;
195   int status;
196
197   data.ident = ident_create ("", "", "", "", "");
198   if (data.ident == NULL)
199     return (ENOMEM);
200   data.callback = cb;
201   data.user_data = ud;
202
203   status = fs_foreach_dir (config->data_dir, scan_host_cb, &data);
204
205   ident_destroy (data.ident);
206   return (status);
207 } /* }}} int get_idents */
208
209 static int get_ident_ds_names (void *priv, graph_ident_t *ident,
210     dp_list_get_ident_ds_names_callback cb, void *ud)
211 { /* {{{ */
212   dp_rrdtool_t *config = priv;
213   char file[PATH_MAX + 1];
214   int status;
215
216   char *rrd_argv[] = { "info", file, NULL };
217   int rrd_argc = (sizeof (rrd_argv) / sizeof (rrd_argv[0])) - 1;
218
219   rrd_info_t *info;
220   rrd_info_t *ptr;
221
222   memset (file, 0, sizeof (file));
223   status = ident_to_rrdfile (ident, config, file, sizeof (file));
224   if (status != 0)
225     return (status);
226
227   info = rrd_info (rrd_argc, rrd_argv);
228   if (info == NULL)
229   {
230     fprintf (stderr, "%s: rrd_info (%s) failed.\n", __func__, file);
231     fflush (stderr);
232     return (-1);
233   }
234
235   for (ptr = info; ptr != NULL; ptr = ptr->next)
236   {
237     size_t keylen;
238     size_t dslen;
239     char *ds;
240
241     if (ptr->key[0] != 'd')
242       continue;
243
244     if (strncmp ("ds[", ptr->key, strlen ("ds[")) != 0)
245       continue;
246
247     keylen = strlen (ptr->key);
248     if (keylen < strlen ("ds[?].type"))
249       continue;
250
251     dslen = keylen - strlen ("ds[].type");
252     assert (dslen >= 1);
253
254     if (strcmp ("].type", ptr->key + (strlen ("ds[") + dslen)) != 0)
255       continue;
256
257     ds = malloc (dslen + 1);
258     if (ds == NULL)
259       continue;
260
261     memcpy (ds, ptr->key + strlen ("ds["), dslen);
262     ds[dslen] = 0;
263
264     status = (*cb) (ident, ds, ud);
265
266     free (ds);
267
268     if (status != 0)
269       break;
270   }
271
272   rrd_info_free (info);
273
274   return (status);
275 } /* }}} int get_ident_ds_names */
276
277 static int get_ident_data (void *priv,
278     graph_ident_t *ident, const char *ds_name,
279     dp_time_t begin, dp_time_t end,
280     dp_get_ident_data_callback cb, void *ud)
281 { /* {{{ */
282   dp_rrdtool_t *config = priv;
283
284   char filename[PATH_MAX + 1];
285   const char *cf = "AVERAGE"; /* FIXME */
286   time_t rrd_start;
287   time_t rrd_end;
288   unsigned long step;
289   unsigned long ds_count;
290   char **ds_namv;
291   rrd_value_t *data;
292   int status;
293
294   unsigned long ds_index;
295   unsigned long data_index;
296   unsigned long data_length;
297
298   dp_data_point_t *dp = NULL;
299   size_t dp_num = 0;
300
301   status = ident_to_rrdfile (ident, config, filename, sizeof (filename));
302   if (status != 0)
303     return (status);
304
305   rrd_start = (time_t) begin.tv_sec;
306   rrd_end = (time_t) end.tv_sec;
307   step = 0;
308   ds_count = 0;
309   ds_namv = NULL;
310   data = NULL;
311
312   status = rrd_fetch_r (filename, cf,
313       &rrd_start, &rrd_end,
314       &step, &ds_count, &ds_namv,
315       &data);
316   if (status != 0)
317     return (status);
318
319 #define BAIL_OUT(ret_status) do { \
320   unsigned long i;                \
321   for (i = 0; i < ds_count; i++)  \
322     free (ds_namv[i]);            \
323   free (ds_namv);                 \
324   free (data);                    \
325   free (dp);                      \
326   return (ret_status);            \
327 } while (0)
328
329   for (ds_index = 0; ds_index < ds_count; ds_index++)
330     if (strcmp (ds_name, ds_namv[ds_index]) == 0)
331       break;
332
333   if (ds_index >= ds_count)
334     BAIL_OUT (ENOENT);
335
336   /* Number of data points returned. */
337   data_length = (rrd_end - rrd_start) / step;
338
339   dp_num = (size_t) data_length;
340   dp = calloc (dp_num, sizeof (*dp));
341   if (dp == NULL)
342     BAIL_OUT (ENOMEM);
343
344   for (data_index = 0; data_index < data_length; data_index++)
345   {
346     unsigned long index = (ds_count * data_index) + ds_index;
347
348     dp[data_index].time.tv_sec = rrd_start + (data_index * step);
349     dp[data_index].time.tv_nsec = 0;
350     dp[data_index].value = (double) data[index];
351   }
352
353   status = (*cb) (ident, ds_name, dp, dp_num, ud);
354   if (status != 0)
355     BAIL_OUT (status);
356
357   BAIL_OUT (0);
358 #undef BAIL_OUT
359 } /* }}} int get_ident_data */
360
361 static int print_graph (void *priv,
362     graph_config_t *cfg, graph_instance_t *inst)
363 { /* {{{ */
364   priv = NULL;
365   cfg = NULL;
366   inst = NULL;
367
368   return (-1);
369 } /* }}} int print_graph */
370
371 int dp_rrdtool_config (const oconfig_item_t *ci)
372 { /* {{{ */
373   dp_rrdtool_t *conf;
374   int i;
375
376   data_provider_t dp =
377   {
378     get_idents,
379     get_ident_ds_names,
380     get_ident_data,
381     print_graph,
382     /* private_data = */ NULL
383   };
384
385   conf = malloc (sizeof (*conf));
386   if (conf == NULL)
387     return (ENOMEM);
388   memset (conf, 0, sizeof (*conf));
389   conf->data_dir = NULL;
390
391   for (i = 0; i < ci->children_num; i++)
392   {
393     oconfig_item_t *child = ci->children + i;
394
395     if (strcasecmp ("DataDir", child->key) == 0)
396       graph_config_get_string (child, &conf->data_dir);
397     else
398     {
399       fprintf (stderr, "dp_rrdtool_config: Ignoring unknown config option "
400           "\"%s\"\n", child->key);
401       fflush (stderr);
402     }
403   }
404
405   if (conf->data_dir == NULL)
406     conf->data_dir = strdup ("/var/lib/collectd/rrd");
407   if (conf->data_dir == NULL)
408   {
409     free (conf);
410     return (ENOMEM);
411   }
412
413   dp.private_data = conf;
414
415   data_provider_register ("rrdtool", &dp);
416
417   return (0);
418 } /* }}} int dp_rrdtool_config */
419
420 /* vim: set sw=2 sts=2 et fdm=marker : */