src/graph.c: graph_add_def: Add graphs in reverse order.
[collection4.git] / src / graph.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <stdint.h>
4 #include <inttypes.h>
5 #include <string.h>
6 #include <time.h>
7 #include <errno.h>
8 #include <assert.h>
9
10 #include "graph.h"
11 #include "graph_ident.h"
12 #include "graph_instance.h"
13 #include "graph_list.h"
14 #include "graph_def.h"
15 #include "graph_config.h"
16 #include "common.h"
17 #include "filesystem.h"
18 #include "utils_cgi.h"
19
20 #include <fcgiapp.h>
21 #include <fcgi_stdio.h>
22
23 /*
24  * Data types
25  */
26 struct graph_config_s /* {{{ */
27 {
28   graph_ident_t *select;
29
30   char *title;
31   char *vertical_label;
32   _Bool show_zero;
33
34   graph_def_t *defs;
35
36   graph_instance_t **instances;
37   size_t instances_num;
38 }; /* }}} struct graph_config_s */
39
40 /*
41  * Private functions
42  */
43
44 /*
45  * Config functions
46  */
47 static graph_ident_t *graph_config_get_selector (const oconfig_item_t *ci) /* {{{ */
48 {
49   char *host = NULL;
50   char *plugin = NULL;
51   char *plugin_instance = NULL;
52   char *type = NULL;
53   char *type_instance = NULL;
54   graph_ident_t *ret;
55   int i;
56
57   for (i = 0; i < ci->children_num; i++)
58   {
59     oconfig_item_t *child;
60
61     child = ci->children + i;
62
63     if (strcasecmp ("Host", child->key) == 0)
64       graph_config_get_string (child, &host);
65     else if (strcasecmp ("Plugin", child->key) == 0)
66       graph_config_get_string (child, &plugin);
67     else if (strcasecmp ("PluginInstance", child->key) == 0)
68       graph_config_get_string (child, &plugin_instance);
69     else if (strcasecmp ("Type", child->key) == 0)
70       graph_config_get_string (child, &type);
71     else if (strcasecmp ("TypeInstance", child->key) == 0)
72       graph_config_get_string (child, &type_instance);
73     /* else: ignore all other directives here. */
74   } /* for */
75
76   ret = ident_create (host, plugin, plugin_instance, type, type_instance);
77
78   free (host);
79   free (plugin);
80   free (plugin_instance);
81   free (type);
82   free (type_instance);
83
84   return (ret);
85 } /* }}} int graph_config_get_selector */
86
87 /*
88  * Global functions
89  */
90 graph_config_t *graph_create (const graph_ident_t *selector) /* {{{ */
91 {
92   graph_config_t *cfg;
93
94   cfg = malloc (sizeof (*cfg));
95   if (cfg == NULL)
96     return (NULL);
97   memset (cfg, 0, sizeof (*cfg));
98
99   if (selector != NULL)
100     cfg->select = ident_clone (selector);
101   else
102     cfg->select = NULL;
103
104   cfg->title = NULL;
105   cfg->vertical_label = NULL;
106   cfg->defs = NULL;
107   cfg->instances = NULL;
108
109   return (cfg);
110 } /* }}} int graph_create */
111
112 void graph_destroy (graph_config_t *cfg) /* {{{ */
113 {
114   size_t i;
115
116   if (cfg == NULL)
117     return;
118
119   ident_destroy (cfg->select);
120
121   free (cfg->title);
122   free (cfg->vertical_label);
123
124   def_destroy (cfg->defs);
125
126   for (i = 0; i < cfg->instances_num; i++)
127     inst_destroy (cfg->instances[i]);
128   free (cfg->instances);
129 } /* }}} void graph_destroy */
130
131 int graph_config_add (const oconfig_item_t *ci) /* {{{ */
132 {
133   graph_ident_t *select;
134   graph_config_t *cfg = NULL;
135   int i;
136
137   select = graph_config_get_selector (ci);
138   if (select == NULL)
139     return (EINVAL);
140
141   cfg = graph_create (/* selector = */ NULL);
142   if (cfg == NULL)
143     return (ENOMEM);
144
145   cfg->select = select;
146
147   for (i = 0; i < ci->children_num; i++)
148   {
149     oconfig_item_t *child;
150
151     child = ci->children + i;
152
153     if (strcasecmp ("Title", child->key) == 0)
154       graph_config_get_string (child, &cfg->title);
155     else if (strcasecmp ("VerticalLabel", child->key) == 0)
156       graph_config_get_string (child, &cfg->vertical_label);
157     else if (strcasecmp ("ShowZero", child->key) == 0)
158       graph_config_get_bool (child, &cfg->show_zero);
159     else if (strcasecmp ("DEF", child->key) == 0)
160       def_config (cfg, child);
161   } /* for */
162
163   gl_add_graph (cfg);
164
165   return (0);
166 } /* }}} graph_config_add */
167
168 int graph_add_file (graph_config_t *cfg, const graph_ident_t *file) /* {{{ */
169 {
170   graph_instance_t *inst;
171
172   inst = graph_inst_find_matching (cfg, file);
173   if (inst == NULL)
174   {
175     graph_instance_t **tmp;
176
177     tmp = realloc (cfg->instances,
178         sizeof (*cfg->instances) * (cfg->instances_num + 1));
179     if (tmp == NULL)
180       return (ENOMEM);
181     cfg->instances = tmp;
182
183     inst = inst_create (cfg, file);
184     if (inst == NULL)
185       return (ENOMEM);
186
187     cfg->instances[cfg->instances_num] = inst;
188     cfg->instances_num++;
189   }
190
191   return (inst_add_file (inst, file));
192 } /* }}} int graph_add_file */
193
194 int graph_get_title (graph_config_t *cfg, /* {{{ */
195     char *buffer, size_t buffer_size)
196 {
197   if ((cfg == NULL) || (buffer == NULL) || (buffer_size < 1))
198     return (EINVAL);
199
200   if (cfg->title == NULL)
201     cfg->title = ident_to_string (cfg->select);
202
203   if (cfg->title == NULL)
204     return (ENOMEM);
205
206   strncpy (buffer, cfg->title, buffer_size);
207   buffer[buffer_size - 1] = 0;
208
209   return (0);
210 } /* }}} int graph_get_title */
211
212 int graph_get_params (graph_config_t *cfg, /* {{{ */
213     char *buffer, size_t buffer_size)
214 {
215   buffer[0] = 0;
216
217 #define COPY_FIELD(field) do {                                       \
218   const char *str = ident_get_##field (cfg->select);                 \
219   char uri_str[1024];                                                \
220   uri_escape_copy (uri_str, str, sizeof (uri_str));                  \
221   strlcat (buffer, #field, buffer_size);                             \
222   strlcat (buffer, "=", buffer_size);                                \
223   strlcat (buffer, uri_str, buffer_size);                            \
224 } while (0)
225
226   COPY_FIELD(host);
227   strlcat (buffer, ";", buffer_size);
228   COPY_FIELD(plugin);
229   strlcat (buffer, ";", buffer_size);
230   COPY_FIELD(plugin_instance);
231   strlcat (buffer, ";", buffer_size);
232   COPY_FIELD(type);
233   strlcat (buffer, ";", buffer_size);
234   COPY_FIELD(type_instance);
235
236 #undef COPY_FIELD
237
238   return (0);
239 } /* }}} int graph_get_params */
240
241 graph_ident_t *graph_get_selector (graph_config_t *cfg) /* {{{ */
242 {
243   if (cfg == NULL)
244     return (NULL);
245
246   return (ident_clone (cfg->select));
247 } /* }}} graph_ident_t *graph_get_selector */
248
249 graph_def_t *graph_get_defs (graph_config_t *cfg) /* {{{ */
250 {
251   if (cfg == NULL)
252     return (NULL);
253
254   return (cfg->defs);
255 } /* }}} graph_def_t *graph_get_defs */
256
257 int graph_add_def (graph_config_t *cfg, graph_def_t *def) /* {{{ */
258 {
259   graph_def_t *tmp;
260
261   if ((cfg == NULL) || (def == NULL))
262     return (EINVAL);
263
264   if (cfg->defs == NULL)
265   {
266     cfg->defs = def;
267     return (0);
268   }
269
270   /* Insert in reverse order. This makes the order in the config file and the
271    * order of the DEFs in the graph more natural. Really. */
272   tmp = cfg->defs;
273   cfg->defs = def;
274   return (def_append (cfg->defs, tmp));
275 } /* }}} int graph_add_def */
276
277 _Bool graph_matches_ident (graph_config_t *cfg, const graph_ident_t *ident) /* {{{ */
278 {
279   if ((cfg == NULL) || (ident == NULL))
280     return (0);
281
282   return (ident_matches (cfg->select, ident));
283 } /* }}} _Bool graph_matches_ident */
284
285 _Bool graph_matches_field (graph_config_t *cfg, /* {{{ */
286     graph_ident_field_t field, const char *field_value)
287 {
288   const char *selector_value;
289
290   if ((cfg == NULL) || (field_value == NULL))
291     return (0);
292
293   selector_value = ident_get_field (cfg->select, field);
294   if (selector_value == NULL)
295     return (0);
296
297   if (IS_ALL (selector_value) || IS_ANY (selector_value))
298     return (1);
299   else if (strcasecmp (selector_value, field_value) == 0)
300     return (1);
301
302   return (0);
303 } /* }}} _Bool graph_matches_field */
304
305 int graph_inst_foreach (graph_config_t *cfg, /* {{{ */
306                 inst_callback_t cb, void *user_data)
307 {
308   size_t i;
309   int status;
310
311   for (i = 0; i < cfg->instances_num; i++)
312   {
313     status = (*cb) (cfg->instances[i], user_data);
314     if (status != 0)
315       return (status);
316   }
317
318   return (0);
319 } /* }}} int graph_inst_foreach */
320
321 graph_instance_t *graph_inst_find_exact (graph_config_t *cfg, /* {{{ */
322     graph_ident_t *ident)
323 {
324   size_t i;
325
326   if ((cfg == NULL) || (ident == NULL))
327     return (NULL);
328
329   for (i = 0; i < cfg->instances_num; i++)
330     if (inst_compare_ident (cfg->instances[i], ident) == 0)
331       return (cfg->instances[i]);
332
333   return (NULL);
334 } /* }}} graph_instance_t *graph_inst_find_exact */
335
336 graph_instance_t *graph_inst_find_matching (graph_config_t *cfg, /* {{{ */
337     const graph_ident_t *ident)
338 {
339   size_t i;
340
341   if ((cfg == NULL) || (ident == NULL))
342     return (NULL);
343
344   for (i = 0; i < cfg->instances_num; i++)
345     if (inst_ident_matches (cfg->instances[i], ident))
346       return (cfg->instances[i]);
347
348   return (NULL);
349 } /* }}} graph_instance_t *graph_inst_find_matching */
350
351 int graph_inst_find_all_matching (graph_config_t *cfg, /* {{{ */
352     const graph_ident_t *ident,
353     graph_inst_callback_t callback, void *user_data)
354 {
355   size_t i;
356
357   if ((cfg == NULL) || (ident == NULL) || (callback == NULL))
358     return (EINVAL);
359
360   for (i = 0; i < cfg->instances_num; i++)
361   {
362     int status;
363
364     if (!inst_matches_ident (cfg->instances[i], ident))
365       continue;
366
367     status = (*callback) (cfg, cfg->instances[i], user_data);
368     if (status != 0)
369       return (status);
370   }
371
372   return (0);
373 } /* }}} int graph_inst_find_all_matching */
374
375 int graph_inst_search (graph_config_t *cfg, const char *term, /* {{{ */
376     graph_inst_callback_t cb,
377     void *user_data)
378 {
379   char buffer[1024];
380   int status;
381   size_t i;
382
383   status = graph_get_title (cfg, buffer, sizeof (buffer));
384   if (status != 0)
385   {
386     fprintf (stderr, "graph_inst_search: graph_get_title failed\n");
387     return (status);
388   }
389
390   strtolower (buffer);
391
392   if (strstr (buffer, term) != NULL)
393   {
394     for (i = 0; i < cfg->instances_num; i++)
395     {
396       status = (*cb) (cfg, cfg->instances[i], user_data);
397       if (status != 0)
398         return (status);
399     }
400   }
401   else
402   {
403     for (i = 0; i < cfg->instances_num; i++)
404     {
405       if (inst_matches_string (cfg, cfg->instances[i], term))
406       {
407         status = (*cb) (cfg, cfg->instances[i], user_data);
408         if (status != 0)
409           return (status);
410       }
411     }
412   }
413
414   return (0);
415 } /* }}} int graph_inst_search */
416
417 int graph_inst_search_field (graph_config_t *cfg, /* {{{ */
418     graph_ident_field_t field, const char *field_value,
419     graph_inst_callback_t callback, void *user_data)
420 {
421   size_t i;
422   const char *selector_field;
423   _Bool need_check_instances = 0;
424
425   if ((cfg == NULL) || (field_value == NULL) || (callback == NULL))
426     return (EINVAL);
427
428   if (!graph_matches_field (cfg, field, field_value))
429     return (0);
430
431   selector_field = ident_get_field (cfg->select, field);
432   if (selector_field == NULL)
433     return (-1);
434
435   if (IS_ALL (selector_field) || IS_ANY (selector_field))
436     need_check_instances = 1;
437
438   for (i = 0; i < cfg->instances_num; i++)
439   {
440     int status;
441
442     if (need_check_instances
443         && !inst_matches_field (cfg->instances[i], field, field_value))
444       continue;
445
446     status = (*callback) (cfg, cfg->instances[i], user_data);
447     if (status != 0)
448       return (status);
449   }
450
451   return (0);
452 } /* }}} int graph_inst_search_field */
453
454 int graph_compare (graph_config_t *cfg, const graph_ident_t *ident) /* {{{ */
455 {
456   if ((cfg == NULL) || (ident == NULL))
457     return (0);
458
459   return (ident_compare (cfg->select, ident));
460 } /* }}} int graph_compare */
461
462 int graph_clear_instances (graph_config_t *cfg) /* {{{ */
463 {
464   size_t i;
465
466   if (cfg == NULL)
467     return (EINVAL);
468
469   for (i = 0; i < cfg->instances_num; i++)
470     inst_destroy (cfg->instances[i]);
471   free (cfg->instances);
472   cfg->instances = NULL;
473   cfg->instances_num = 0;
474
475   return (0);
476 } /* }}} int graph_clear_instances */
477
478 int graph_get_rrdargs (graph_config_t *cfg, graph_instance_t *inst, /* {{{ */
479     rrd_args_t *args)
480 {
481   if ((cfg == NULL) || (inst == NULL) || (args == NULL))
482     return (EINVAL);
483
484   if (cfg->title != NULL)
485   {
486     array_append (args->options, "-t");
487     array_append (args->options, cfg->title);
488   }
489
490   if (cfg->vertical_label != NULL)
491   {
492     array_append (args->options, "-v");
493     array_append (args->options, cfg->vertical_label);
494   }
495
496   if (cfg->show_zero)
497   {
498     array_append (args->options, "-l");
499     array_append (args->options, "0");
500   }
501
502   return (0);
503 } /* }}} int graph_get_rrdargs */
504
505 /* vim: set sw=2 sts=2 et fdm=marker : */