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 **/
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <limits.h>
30 #include <errno.h>
31 #include <assert.h>
33 #include <rrd.h>
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"
43 #include <fcgiapp.h>
44 #include <fcgi_stdio.h>
46 struct dp_rrdtool_s
47 {
48 char *data_dir;
49 };
50 typedef struct dp_rrdtool_s dp_rrdtool_t;
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;
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;
69 file_len = strlen (file);
70 if (file_len < 5)
71 return (0);
73 /* Ignore files that don't end in ".rrd". */
74 if (strcasecmp (".rrd", file + (file_len - 4)) != 0)
75 return (0);
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;
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 }
94 ident_set_type (data->ident, type_copy);
95 ident_set_type_instance (data->ident, type_inst);
97 return (data->callback (data->ident, data->user_data));
98 } /* }}} int scan_type_cb */
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;
106 dp_get_idents_data_t *data = ud;
107 char abs_dir[PATH_MAX + 1];
109 strncpy (plugin_copy, sub_dir, sizeof (plugin_copy));
110 plugin_copy[sizeof (plugin_copy) - 1] = 0;
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 }
123 ident_set_plugin (data->ident, plugin_copy);
124 ident_set_plugin_instance (data->ident, plugin_inst);
126 snprintf (abs_dir, sizeof (abs_dir), "%s/%s", base_dir, sub_dir);
127 abs_dir[sizeof (abs_dir) - 1] = 0;
129 return (fs_foreach_file (abs_dir, scan_type_cb, data));
130 } /* }}} int scan_host_cb */
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];
138 ident_set_host (data->ident, sub_dir);
140 snprintf (abs_dir, sizeof (abs_dir), "%s/%s", base_dir, sub_dir);
141 abs_dir[sizeof (abs_dir) - 1] = 0;
143 return (fs_foreach_dir (abs_dir, scan_plugin_cb, data));
144 } /* }}} int scan_host_cb */
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;
153 plugin_instance = ident_get_plugin_instance (ident);
154 if ((plugin_instance != NULL) && (plugin_instance[0] == 0))
155 plugin_instance = NULL;
157 type_instance = ident_get_type_instance (ident);
158 if ((type_instance != NULL) && (type_instance[0] == 0))
159 type_instance = NULL;
161 buffer[0] = 0;
163 strlcat (buffer, config->data_dir, buffer_size);
164 strlcat (buffer, "/", buffer_size);
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 }
182 strlcat (buffer, ".rrd", buffer_size);
184 return (0);
185 } /* }}} int ident_to_rrdfile */
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;
197 data.ident = ident_create ("", "", "", "", "");
198 if (data.ident == NULL)
199 return (ENOMEM);
200 data.callback = cb;
201 data.user_data = ud;
203 status = fs_foreach_dir (config->data_dir, scan_host_cb, &data);
205 ident_destroy (data.ident);
206 return (status);
207 } /* }}} int get_idents */
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;
216 char *rrd_argv[] = { "info", file, NULL };
217 int rrd_argc = (sizeof (rrd_argv) / sizeof (rrd_argv[0])) - 1;
219 rrd_info_t *info;
220 rrd_info_t *ptr;
222 memset (file, 0, sizeof (file));
223 status = ident_to_rrdfile (ident, config, file, sizeof (file));
224 if (status != 0)
225 return (status);
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 }
235 for (ptr = info; ptr != NULL; ptr = ptr->next)
236 {
237 size_t keylen;
238 size_t dslen;
239 char *ds;
241 if (ptr->key[0] != 'd')
242 continue;
244 if (strncmp ("ds[", ptr->key, strlen ("ds[")) != 0)
245 continue;
247 keylen = strlen (ptr->key);
248 if (keylen < strlen ("ds[?].type"))
249 continue;
251 dslen = keylen - strlen ("ds[].type");
252 assert (dslen >= 1);
254 if (strcmp ("].type", ptr->key + (strlen ("ds[") + dslen)) != 0)
255 continue;
257 ds = malloc (dslen + 1);
258 if (ds == NULL)
259 continue;
261 memcpy (ds, ptr->key + strlen ("ds["), dslen);
262 ds[dslen] = 0;
264 status = (*cb) (ident, ds, ud);
266 free (ds);
268 if (status != 0)
269 break;
270 }
272 rrd_info_free (info);
274 return (status);
275 } /* }}} int get_ident_ds_names */
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;
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;
294 unsigned long ds_index;
295 unsigned long data_index;
296 unsigned long data_length;
298 dp_data_point_t *dp = NULL;
299 size_t dp_num = 0;
301 status = ident_to_rrdfile (ident, config, filename, sizeof (filename));
302 if (status != 0)
303 return (status);
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;
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);
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)
329 for (ds_index = 0; ds_index < ds_count; ds_index++)
330 if (strcmp (ds_name, ds_namv[ds_index]) == 0)
331 break;
333 if (ds_index >= ds_count)
334 BAIL_OUT (ENOENT);
336 /* Number of data points returned. */
337 data_length = (rrd_end - rrd_start) / step;
339 dp_num = (size_t) data_length;
340 dp = calloc (dp_num, sizeof (*dp));
341 if (dp == NULL)
342 BAIL_OUT (ENOMEM);
344 for (data_index = 0; data_index < data_length; data_index++)
345 {
346 unsigned long index = (ds_count * data_index) + ds_index;
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 }
353 status = (*cb) (ident, ds_name, dp, dp_num, ud);
354 if (status != 0)
355 BAIL_OUT (status);
357 BAIL_OUT (0);
358 #undef BAIL_OUT
359 } /* }}} int get_ident_data */
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;
368 return (-1);
369 } /* }}} int print_graph */
371 int dp_rrdtool_config (const oconfig_item_t *ci)
372 { /* {{{ */
373 dp_rrdtool_t *conf;
374 int i;
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 };
385 conf = malloc (sizeof (*conf));
386 if (conf == NULL)
387 return (ENOMEM);
388 memset (conf, 0, sizeof (*conf));
389 conf->data_dir = NULL;
391 for (i = 0; i < ci->children_num; i++)
392 {
393 oconfig_item_t *child = ci->children + i;
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 }
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 }
413 dp.private_data = conf;
415 data_provider_register ("rrdtool", &dp);
417 return (0);
418 } /* }}} int dp_rrdtool_config */
420 /* vim: set sw=2 sts=2 et fdm=marker : */