src/dp_rrdtool.c: Cleanups.
[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_ident.h"
37 #include "data_provider.h"
38 #include "filesystem.h"
39 #include "oconfig.h"
40 #include "common.h"
41
42 #include <fcgiapp.h>
43 #include <fcgi_stdio.h>
44
45 struct dp_rrdtool_s
46 {
47   char *data_dir;
48 };
49 typedef struct dp_rrdtool_s dp_rrdtool_t;
50
51 struct dp_get_idents_data_s
52 { /* {{{ */
53   graph_ident_t *ident;
54   dp_get_idents_callback callback;
55   void *user_data;
56 }; /* }}} */
57 typedef struct dp_get_idents_data_s dp_get_idents_data_t;
58
59 static int scan_type_cb (__attribute__((unused)) const char *base_dir,
60     const char *file, void *ud)
61 { /* {{{ */
62   dp_get_idents_data_t *data = ud;
63   size_t file_len;
64   char type_copy[1024];
65   size_t type_copy_len;
66   char *type_inst;
67
68   file_len = strlen (file);
69   if (file_len < 5)
70     return (0);
71
72   /* Ignore files that don't end in ".rrd". */
73   if (strcasecmp (".rrd", file + (file_len - 4)) != 0)
74     return (0);
75
76   strncpy (type_copy, file, sizeof (type_copy));
77   type_copy_len = file_len - 4;
78   if (type_copy_len > (sizeof (type_copy) - 1))
79     type_copy_len = sizeof (type_copy) - 1;
80   type_copy[type_copy_len] = 0;
81
82   type_inst = strchr (type_copy, '-');
83   if (type_inst != NULL)
84   {
85     *type_inst = 0;
86     type_inst++;
87   }
88   else
89   {
90     type_inst = "";
91   }
92
93   ident_set_type (data->ident, type_copy);
94   ident_set_type_instance (data->ident, type_inst);
95
96   return (data->callback (data->ident, data->user_data));
97 } /* }}} int scan_type_cb */
98
99 static int scan_plugin_cb (const char *base_dir,
100     const char *sub_dir, void *ud)
101 { /* {{{ */
102   char plugin_copy[1024];
103   char *plugin_inst;
104
105   dp_get_idents_data_t *data = ud;
106   char abs_dir[PATH_MAX + 1];
107
108   strncpy (plugin_copy, sub_dir, sizeof (plugin_copy));
109   plugin_copy[sizeof (plugin_copy) - 1] = 0;
110
111   plugin_inst = strchr (plugin_copy, '-');
112   if (plugin_inst != NULL)
113   {
114     *plugin_inst = 0;
115     plugin_inst++;
116   }
117   else
118   {
119     plugin_inst = "";
120   }
121
122   ident_set_plugin (data->ident, plugin_copy);
123   ident_set_plugin_instance (data->ident, plugin_inst);
124
125   snprintf (abs_dir, sizeof (abs_dir), "%s/%s", base_dir, sub_dir);
126   abs_dir[sizeof (abs_dir) - 1] = 0;
127
128   return (fs_foreach_file (abs_dir, scan_type_cb, data));
129 } /* }}} int scan_host_cb */
130
131 static int scan_host_cb (const char *base_dir,
132     const char *sub_dir, void *ud)
133 { /* {{{ */
134   dp_get_idents_data_t *data = ud;
135   char abs_dir[PATH_MAX + 1];
136
137   ident_set_host (data->ident, sub_dir);
138
139   snprintf (abs_dir, sizeof (abs_dir), "%s/%s", base_dir, sub_dir);
140   abs_dir[sizeof (abs_dir) - 1] = 0;
141
142   return (fs_foreach_dir (abs_dir, scan_plugin_cb, data));
143 } /* }}} int scan_host_cb */
144
145 static int ident_to_rrdfile (const graph_ident_t *ident, /* {{{ */
146     dp_rrdtool_t *config,
147     char *buffer, size_t buffer_size)
148 {
149   const char *plugin_instance;
150   const char *type_instance;
151
152   plugin_instance = ident_get_plugin_instance (ident);
153   if ((plugin_instance != NULL) && (plugin_instance[0] == 0))
154     plugin_instance = NULL;
155
156   type_instance = ident_get_type_instance (ident);
157   if ((type_instance != NULL) && (type_instance[0] == 0))
158     type_instance = NULL;
159
160   buffer[0] = 0;
161
162   strlcat (buffer, config->data_dir, buffer_size);
163   strlcat (buffer, "/", buffer_size);
164
165   strlcat (buffer, ident_get_host (ident), buffer_size);
166   strlcat (buffer, "/", buffer_size);
167   strlcat (buffer, ident_get_plugin (ident), buffer_size);
168   if (plugin_instance != NULL)
169   {
170     strlcat (buffer, "-", buffer_size);
171     strlcat (buffer, plugin_instance, buffer_size);
172   }
173   strlcat (buffer, "/", buffer_size);
174   strlcat (buffer, ident_get_type (ident), buffer_size);
175   if (type_instance != NULL)
176   {
177     strlcat (buffer, "-", buffer_size);
178     strlcat (buffer, type_instance, buffer_size);
179   }
180
181   strlcat (buffer, ".rrd", buffer_size);
182
183   return (0);
184 } /* }}} int ident_to_rrdfile */
185
186 /*
187  * Callback functions
188  */
189 static int get_idents (void *priv,
190     dp_get_idents_callback cb, void *ud)
191 { /* {{{ */
192   dp_rrdtool_t *config = priv;
193   dp_get_idents_data_t data;
194   int status;
195
196   data.ident = ident_create ("", "", "", "", "");
197   if (data.ident == NULL)
198     return (ENOMEM);
199   data.callback = cb;
200   data.user_data = ud;
201
202   status = fs_foreach_dir (config->data_dir, scan_host_cb, &data);
203
204   ident_destroy (data.ident);
205   return (status);
206 } /* }}} int get_idents */
207
208 static int get_ident_ds_names (void *priv, graph_ident_t *ident,
209     dp_list_get_ident_ds_names_callback cb, void *ud)
210 { /* {{{ */
211   dp_rrdtool_t *config = priv;
212   char file[PATH_MAX + 1];
213   int status;
214
215   char *rrd_argv[] = { "info", file, NULL };
216   int rrd_argc = (sizeof (rrd_argv) / sizeof (rrd_argv[0])) - 1;
217
218   rrd_info_t *info;
219   rrd_info_t *ptr;
220
221   memset (file, 0, sizeof (file));
222   status = ident_to_rrdfile (ident, config, file, sizeof (file));
223   if (status != 0)
224     return (status);
225
226   info = rrd_info (rrd_argc, rrd_argv);
227   if (info == NULL)
228   {
229     fprintf (stderr, "%s: rrd_info (%s) failed.\n", __func__, file);
230     fflush (stderr);
231     return (-1);
232   }
233
234   for (ptr = info; ptr != NULL; ptr = ptr->next)
235   {
236     size_t keylen;
237     size_t dslen;
238     char *ds;
239
240     if (ptr->key[0] != 'd')
241       continue;
242
243     if (strncmp ("ds[", ptr->key, strlen ("ds[")) != 0)
244       continue;
245
246     keylen = strlen (ptr->key);
247     if (keylen < strlen ("ds[?].index"))
248       continue;
249
250     dslen = keylen - strlen ("ds[].index");
251     assert (dslen >= 1);
252
253     if (strcmp ("].index", ptr->key + (strlen ("ds[") + dslen)) != 0)
254       continue;
255
256     ds = malloc (dslen + 1);
257     if (ds == NULL)
258       continue;
259
260     memcpy (ds, ptr->key + strlen ("ds["), dslen);
261     ds[dslen] = 0;
262
263     status = (*cb) (ident, ds, ud);
264
265     free (ds);
266
267     if (status != 0)
268       break;
269   }
270
271   rrd_info_free (info);
272
273   return (status);
274 } /* }}} int get_ident_ds_names */
275
276 static int get_ident_data (void *priv,
277     graph_ident_t *ident, const char *ds_name,
278     dp_time_t begin, dp_time_t end,
279     dp_get_ident_data_callback cb, void *ud)
280 { /* {{{ */
281   dp_rrdtool_t *config = priv;
282
283   char filename[PATH_MAX + 1];
284   const char *cf = "AVERAGE"; /* FIXME */
285   time_t rrd_start;
286   time_t rrd_end;
287   unsigned long step;
288   unsigned long ds_count;
289   char **ds_namv;
290   rrd_value_t *data;
291   int status;
292
293   unsigned long ds_index;
294   unsigned long data_index;
295   unsigned long data_length;
296
297   dp_data_point_t *dp = NULL;
298   size_t dp_num = 0;
299
300   status = ident_to_rrdfile (ident, config, filename, sizeof (filename));
301   if (status != 0)
302     return (status);
303
304   rrd_start = (time_t) begin.tv_sec;
305   rrd_end = (time_t) end.tv_sec;
306   step = 0;
307   ds_count = 0;
308   ds_namv = NULL;
309   data = NULL;
310
311   status = rrd_fetch_r (filename, cf,
312       &rrd_start, &rrd_end,
313       &step, &ds_count, &ds_namv,
314       &data);
315   if (status != 0)
316     return (status);
317
318 #define BAIL_OUT(ret_status) do { \
319   unsigned long i;                \
320   for (i = 0; i < ds_count; i++)  \
321     free (ds_namv[i]);            \
322   free (ds_namv);                 \
323   free (data);                    \
324   free (dp);                      \
325   return (ret_status);            \
326 } while (0)
327
328   for (ds_index = 0; ds_index < ds_count; ds_index++)
329     if (strcmp (ds_name, ds_namv[ds_index]) == 0)
330       break;
331
332   if (ds_index >= ds_count)
333     BAIL_OUT (ENOENT);
334
335   /* Number of data points returned. */
336   data_length = (rrd_end - rrd_start) / step;
337
338   dp_num = (size_t) data_length;
339   dp = calloc (dp_num, sizeof (*dp));
340   if (dp == NULL)
341     BAIL_OUT (ENOMEM);
342
343   for (data_index = 0; data_index < data_length; data_index++)
344   {
345     unsigned long index = (ds_count * data_index) + ds_index;
346
347     dp[data_index].time.tv_sec = rrd_start + (data_index * step);
348     dp[data_index].time.tv_nsec = 0;
349     dp[data_index].value = (double) data[index];
350   }
351
352   status = (*cb) (ident, ds_name, dp, dp_num, ud);
353   if (status != 0)
354     BAIL_OUT (status);
355
356   BAIL_OUT (0);
357 #undef BAIL_OUT
358 } /* }}} int get_ident_data */
359
360 static int print_graph (void *priv,
361     graph_config_t *cfg, graph_instance_t *inst)
362 { /* {{{ */
363   priv = NULL;
364   cfg = NULL;
365   inst = NULL;
366
367   return (-1);
368 } /* }}} int print_graph */
369
370 int dp_rrdtool_config (const oconfig_item_t *ci)
371 { /* {{{ */
372   dp_rrdtool_t *conf;
373
374   data_provider_t dp =
375   {
376     get_idents,
377     get_ident_ds_names,
378     get_ident_data,
379     print_graph,
380     /* private_data = */ NULL
381   };
382
383   /* FIXME: Actuelly do config parsing here. */
384   ci = NULL; /* FIXME */
385   conf = malloc (sizeof (dp_rrdtool_t));
386   conf->data_dir = strdup ("/var/lib/collectd/rrd");
387
388   dp.private_data = conf;
389
390   data_provider_register ("rrdtool", &dp);
391
392   return (0);
393 } /* }}} int dp_rrdtool_config */
394
395 /* vim: set sw=2 sts=2 et fdm=marker : */