src/data_provider.c: Call lcc_flush() before querying the data.
[collection4.git] / src / graph_ident.c
1 /**
2  * collection4 - graph_ident.c
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 <unistd.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <errno.h>
32 #include <limits.h> /* PATH_MAX */
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <math.h>
36 #include <assert.h>
37
38 #include "graph_ident.h"
39 #include "common.h"
40 #include "data_provider.h"
41 #include "filesystem.h"
42 #include "utils_cgi.h"
43
44 #include <fcgiapp.h>
45 #include <fcgi_stdio.h>
46
47 /*
48  * Data types
49  */
50 struct graph_ident_s /* {{{ */
51 {
52   char *host;
53   char *plugin;
54   char *plugin_instance;
55   char *type;
56   char *type_instance;
57 }; /* }}} struct graph_ident_s */
58
59 /*
60  * Private functions
61  */
62 static char *part_copy_with_selector (const char *selector, /* {{{ */
63     const char *part, unsigned int flags)
64 {
65   if ((selector == NULL) || (part == NULL))
66     return (NULL);
67
68   if ((flags & IDENT_FLAG_REPLACE_ANY) && IS_ANY (part))
69     return (NULL);
70
71   if ((flags & IDENT_FLAG_REPLACE_ALL) && IS_ALL (part))
72     return (NULL);
73
74   /* Replace the ANY and ALL flags if requested and if the selecter actually
75    * *is* that flag. */
76   if (IS_ANY (selector))
77   {
78     if (flags & IDENT_FLAG_REPLACE_ANY)
79       return (strdup (part));
80     else
81       return (strdup (selector));
82   }
83
84   if (IS_ALL (selector))
85   {
86     if (flags & IDENT_FLAG_REPLACE_ALL)
87       return (strdup (part));
88     else
89       return (strdup (selector));
90   }
91
92   if (strcmp (selector, part) != 0)
93     return (NULL);
94
95   /* Otherwise (no replacement), return a copy of the selector. */
96   return (strdup (selector));
97 } /* }}} char *part_copy_with_selector */
98
99 static _Bool part_matches (const char *selector, /* {{{ */
100     const char *part)
101 {
102 #if C4_DEBUG
103   if ((selector == NULL) && (part == NULL))
104     return (1);
105 #endif
106
107   if (selector == NULL) /* && (part != NULL) */
108     return (0);
109
110   if (IS_ANY(selector) || IS_ALL(selector))
111     return (1);
112
113   if (part == NULL) /* && (selector != NULL) */
114     return (0);
115
116   if (strcmp (selector, part) == 0)
117     return (1);
118
119   return (0);
120 } /* }}} _Bool part_matches */
121
122 /*
123  * Public functions
124  */
125 graph_ident_t *ident_create (const char *host, /* {{{ */
126     const char *plugin, const char *plugin_instance,
127     const char *type, const char *type_instance)
128 {
129   graph_ident_t *ret;
130
131   if ((host == NULL)
132       || (plugin == NULL) || (plugin_instance == NULL)
133       || (type == NULL) || (type_instance == NULL))
134     return (NULL);
135
136   ret = malloc (sizeof (*ret));
137   if (ret == NULL)
138     return (NULL);
139   memset (ret, 0, sizeof (*ret));
140
141   ret->host = NULL;
142   ret->host = NULL;
143   ret->plugin = NULL;
144   ret->plugin_instance = NULL;
145   ret->type = NULL;
146   ret->type_instance = NULL;
147
148 #define COPY_PART(p) do {        \
149   ret->p = strdup (p);           \
150   if (ret->p == NULL)            \
151   {                              \
152     free (ret->host);            \
153     free (ret->plugin);          \
154     free (ret->plugin_instance); \
155     free (ret->type);            \
156     free (ret->type_instance);   \
157     free (ret);                  \
158     return (NULL);               \
159   }                              \
160 } while (0)
161
162   COPY_PART(host);
163   COPY_PART(plugin);
164   COPY_PART(plugin_instance);
165   COPY_PART(type);
166   COPY_PART(type_instance);
167
168 #undef COPY_PART
169
170   return (ret);
171 } /* }}} graph_ident_t *ident_create */
172
173 graph_ident_t *ident_clone (const graph_ident_t *ident) /* {{{ */
174 {
175   return (ident_create (ident->host,
176         ident->plugin, ident->plugin_instance,
177         ident->type, ident->type_instance));
178 } /* }}} graph_ident_t *ident_clone */
179
180 graph_ident_t *ident_copy_with_selector (const graph_ident_t *selector, /* {{{ */
181     const graph_ident_t *ident, unsigned int flags)
182 {
183   graph_ident_t *ret;
184
185   if ((selector == NULL) || (ident == NULL))
186     return (NULL);
187
188   ret = malloc (sizeof (*ret));
189   if (ret == NULL)
190     return (NULL);
191   memset (ret, 0, sizeof (*ret));
192   ret->host = NULL;
193   ret->plugin = NULL;
194   ret->plugin_instance = NULL;
195   ret->type = NULL;
196   ret->type_instance = NULL;
197
198 #define COPY_PART(p) do {                                  \
199   ret->p = part_copy_with_selector (selector->p, ident->p, flags); \
200   if (ret->p == NULL)                                      \
201   {                                                        \
202     free (ret->host);                                      \
203     free (ret->plugin);                                    \
204     free (ret->plugin_instance);                           \
205     free (ret->type);                                      \
206     free (ret->type_instance);                             \
207     return (NULL);                                         \
208   }                                                        \
209 } while (0)
210
211   COPY_PART (host);
212   COPY_PART (plugin);
213   COPY_PART (plugin_instance);
214   COPY_PART (type);
215   COPY_PART (type_instance);
216
217 #undef COPY_PART
218
219   return (ret);
220 } /* }}} graph_ident_t *ident_copy_with_selector */
221
222 void ident_destroy (graph_ident_t *ident) /* {{{ */
223 {
224   if (ident == NULL)
225     return;
226
227   free (ident->host);
228   free (ident->plugin);
229   free (ident->plugin_instance);
230   free (ident->type);
231   free (ident->type_instance);
232
233   free (ident);
234 } /* }}} void ident_destroy */
235
236 /* ident_get_* methods {{{ */
237 const char *ident_get_host (const graph_ident_t *ident) /* {{{ */
238 {
239   if (ident == NULL)
240     return (NULL);
241
242   return (ident->host);
243 } /* }}} char *ident_get_host */
244
245 const char *ident_get_plugin (const graph_ident_t *ident) /* {{{ */
246 {
247   if (ident == NULL)
248     return (NULL);
249
250   return (ident->plugin);
251 } /* }}} char *ident_get_plugin */
252
253 const char *ident_get_plugin_instance (const graph_ident_t *ident) /* {{{ */
254 {
255   if (ident == NULL)
256     return (NULL);
257
258   return (ident->plugin_instance);
259 } /* }}} char *ident_get_plugin_instance */
260
261 const char *ident_get_type (const graph_ident_t *ident) /* {{{ */
262 {
263   if (ident == NULL)
264     return (NULL);
265
266   return (ident->type);
267 } /* }}} char *ident_get_type */
268
269 const char *ident_get_type_instance (const graph_ident_t *ident) /* {{{ */
270 {
271   if (ident == NULL)
272     return (NULL);
273
274   return (ident->type_instance);
275 } /* }}} char *ident_get_type_instance */
276
277 const char *ident_get_field (const graph_ident_t *ident, /* {{{ */
278     graph_ident_field_t field)
279 {
280   if ((ident == NULL) || (field >= _GIF_LAST))
281     return (NULL);
282
283   if (field == GIF_HOST)
284     return (ident->host);
285   else if (field == GIF_PLUGIN)
286     return (ident->plugin);
287   else if (field == GIF_PLUGIN_INSTANCE)
288     return (ident->plugin_instance);
289   else if (field == GIF_TYPE)
290     return (ident->type);
291   else if (field == GIF_TYPE_INSTANCE)
292     return (ident->type_instance);
293   else
294     return (NULL); /* never reached */
295 } /* }}} const char *ident_get_field */
296 /* }}} ident_get_* methods */
297
298 /* ident_set_* methods {{{ */
299 int ident_set_host (graph_ident_t *ident, const char *host) /* {{{ */
300 {
301   char *tmp;
302
303   if ((ident == NULL) || (host == NULL))
304     return (EINVAL);
305
306   tmp = strdup (host);
307   if (tmp == NULL)
308     return (ENOMEM);
309
310   free (ident->host);
311   ident->host = tmp;
312
313   return (0);
314 } /* }}} int ident_set_host */
315
316 int ident_set_plugin (graph_ident_t *ident, const char *plugin) /* {{{ */
317 {
318   char *tmp;
319
320   if ((ident == NULL) || (plugin == NULL))
321     return (EINVAL);
322
323   tmp = strdup (plugin);
324   if (tmp == NULL)
325     return (ENOMEM);
326
327   free (ident->plugin);
328   ident->plugin = tmp;
329
330   return (0);
331 } /* }}} int ident_set_plugin */
332
333 int ident_set_plugin_instance (graph_ident_t *ident, const char *plugin_instance) /* {{{ */
334 {
335   char *tmp;
336
337   if ((ident == NULL) || (plugin_instance == NULL))
338     return (EINVAL);
339
340   tmp = strdup (plugin_instance);
341   if (tmp == NULL)
342     return (ENOMEM);
343
344   free (ident->plugin_instance);
345   ident->plugin_instance = tmp;
346
347   return (0);
348 } /* }}} int ident_set_plugin_instance */
349
350 int ident_set_type (graph_ident_t *ident, const char *type) /* {{{ */
351 {
352   char *tmp;
353
354   if ((ident == NULL) || (type == NULL))
355     return (EINVAL);
356
357   tmp = strdup (type);
358   if (tmp == NULL)
359     return (ENOMEM);
360
361   free (ident->type);
362   ident->type = tmp;
363
364   return (0);
365 } /* }}} int ident_set_type */
366
367 int ident_set_type_instance (graph_ident_t *ident, const char *type_instance) /* {{{ */
368 {
369   char *tmp;
370
371   if ((ident == NULL) || (type_instance == NULL))
372     return (EINVAL);
373
374   tmp = strdup (type_instance);
375   if (tmp == NULL)
376     return (ENOMEM);
377
378   free (ident->type_instance);
379   ident->type_instance = tmp;
380
381   return (0);
382 } /* }}} int ident_set_type_instance */
383
384 /* }}} ident_set_* methods */
385
386 int ident_compare (const graph_ident_t *i0, /* {{{ */
387     const graph_ident_t *i1)
388 {
389   int status;
390
391 #define COMPARE_PART(p) do {       \
392   status = strcmp (i0->p, i1->p);  \
393   if (status != 0)                 \
394     return (status);               \
395 } while (0)
396
397   COMPARE_PART (host);
398   COMPARE_PART (plugin);
399   COMPARE_PART (plugin_instance);
400   COMPARE_PART (type);
401   COMPARE_PART (type_instance);
402
403 #undef COMPARE_PART
404
405   return (0);
406 } /* }}} int ident_compare */
407
408 _Bool ident_matches (const graph_ident_t *selector, /* {{{ */
409     const graph_ident_t *ident)
410 {
411 #if C4_DEBUG
412   if ((selector == NULL) || (ident == NULL))
413     return (0);
414 #endif
415
416   if (!part_matches (selector->host, ident->host))
417     return (0);
418
419   if (!part_matches (selector->plugin, ident->plugin))
420     return (0);
421
422   if (!part_matches (selector->plugin_instance, ident->plugin_instance))
423     return (0);
424
425   if (!part_matches (selector->type, ident->type))
426     return (0);
427
428   if (!part_matches (selector->type_instance, ident->type_instance))
429     return (0);
430
431   return (1);
432 } /* }}} _Bool ident_matches */
433
434 _Bool ident_intersect (const graph_ident_t *s0, /* {{{ */
435     const graph_ident_t *s1)
436 {
437 #define INTERSECT_PART(p) do {                                               \
438   if (!IS_ANY (s0->p) && !IS_ALL (s0->p)                                     \
439       && !IS_ANY (s1->p) && !IS_ALL (s1->p)                                  \
440       && (strcmp (s0->p, s1->p) != 0))                                       \
441     return (0);                                                              \
442 } while (0)
443
444   INTERSECT_PART (host);
445   INTERSECT_PART (plugin);
446   INTERSECT_PART (plugin_instance);
447   INTERSECT_PART (type);
448   INTERSECT_PART (type_instance);
449
450 #undef INTERSECT_PART
451
452   return (1);
453 } /* }}} _Bool ident_intersect */
454
455 char *ident_to_string (const graph_ident_t *ident) /* {{{ */
456 {
457   char buffer[PATH_MAX];
458
459   if (ident == NULL)
460     return (NULL);
461
462   buffer[0] = 0;
463
464   strlcat (buffer, ident->host, sizeof (buffer));
465   strlcat (buffer, "/", sizeof (buffer));
466   strlcat (buffer, ident->plugin, sizeof (buffer));
467   if (ident->plugin_instance[0] != 0)
468   {
469     strlcat (buffer, "-", sizeof (buffer));
470     strlcat (buffer, ident->plugin_instance, sizeof (buffer));
471   }
472   strlcat (buffer, "/", sizeof (buffer));
473   strlcat (buffer, ident->type, sizeof (buffer));
474   if (ident->type_instance[0] != 0)
475   {
476     strlcat (buffer, "-", sizeof (buffer));
477     strlcat (buffer, ident->type_instance, sizeof (buffer));
478   }
479
480   return (strdup (buffer));
481 } /* }}} char *ident_to_string */
482
483 char *ident_to_file (const graph_ident_t *ident) /* {{{ */
484 {
485   char buffer[PATH_MAX];
486
487   buffer[0] = 0;
488
489   strlcat (buffer, DATA_DIR, sizeof (buffer));
490   strlcat (buffer, "/", sizeof (buffer));
491
492   strlcat (buffer, ident->host, sizeof (buffer));
493   strlcat (buffer, "/", sizeof (buffer));
494   strlcat (buffer, ident->plugin, sizeof (buffer));
495   if (ident->plugin_instance[0] != 0)
496   {
497     strlcat (buffer, "-", sizeof (buffer));
498     strlcat (buffer, ident->plugin_instance, sizeof (buffer));
499   }
500   strlcat (buffer, "/", sizeof (buffer));
501   strlcat (buffer, ident->type, sizeof (buffer));
502   if (ident->type_instance[0] != 0)
503   {
504     strlcat (buffer, "-", sizeof (buffer));
505     strlcat (buffer, ident->type_instance, sizeof (buffer));
506   }
507
508   strlcat (buffer, ".rrd", sizeof (buffer));
509
510   return (strdup (buffer));
511 } /* }}} char *ident_to_file */
512
513 int ident_to_json (const graph_ident_t *ident, /* {{{ */
514     yajl_gen handler)
515 {
516   yajl_gen_status status;
517
518   if ((ident == NULL) || (handler == NULL))
519     return (EINVAL);
520
521 #define ADD_STRING(str) do {                              \
522   status = yajl_gen_string (handler,                      \
523       (unsigned char *) (str),                            \
524       (unsigned int) strlen (str));                       \
525   if (status != yajl_gen_status_ok)                       \
526     return ((int) status);                                \
527 } while (0)
528
529   yajl_gen_map_open (handler);
530   ADD_STRING ("host");
531   ADD_STRING (ident->host);
532   ADD_STRING ("plugin");
533   ADD_STRING (ident->plugin);
534   ADD_STRING ("plugin_instance");
535   ADD_STRING (ident->plugin_instance);
536   ADD_STRING ("type");
537   ADD_STRING (ident->type);
538   ADD_STRING ("type_instance");
539   ADD_STRING (ident->type_instance);
540   yajl_gen_map_close (handler);
541
542 #undef ADD_FIELD
543
544   return (0);
545 } /* }}} char *ident_to_json */
546
547 /* {{{ ident_data_to_json */
548 struct ident_data_to_json__data_s
549 {
550   dp_time_t begin;
551   dp_time_t end;
552   dp_time_t interval;
553   yajl_gen handler;
554 };
555 typedef struct ident_data_to_json__data_s ident_data_to_json__data_t;
556
557 #define yajl_gen_string_cast(h,s,l) \
558   yajl_gen_string (h, (unsigned char *) s, (unsigned int) l)
559
560 static int ident_data_to_json__get_ident_data (
561     __attribute__((unused)) graph_ident_t *ident, /* {{{ */
562     __attribute__((unused)) const char *ds_name,
563     dp_time_t first_value_time, dp_time_t interval,
564     size_t data_points_num, double *data_points,
565     void *user_data)
566 {
567   ident_data_to_json__data_t *data = user_data;
568   size_t i;
569
570   double first_value_time_double;
571   double interval_double;
572   double interval_requested;
573   size_t points_consolidate;
574
575   first_value_time_double = ((double) first_value_time.tv_sec)
576     + (((double) first_value_time.tv_nsec) / 1000000000.0);
577   interval_double = ((double) interval.tv_sec)
578     + (((double) interval.tv_nsec) / 1000000000.0);
579   interval_requested = ((double) data->interval.tv_sec)
580     + (((double) data->interval.tv_nsec) / 1000000000.0);
581
582   if (interval_requested < (2.0 * interval_double))
583     points_consolidate = 1;
584   else
585     points_consolidate = (size_t) (interval_requested / interval_double);
586   assert (points_consolidate >= 1);
587
588   if (points_consolidate > 1)
589   {
590     size_t offset = data_points_num % points_consolidate;
591
592     first_value_time_double += ((double) offset) * interval_double;
593     interval_double *= ((double) points_consolidate);
594   }
595
596   yajl_gen_map_open (data->handler);
597
598   yajl_gen_string_cast (data->handler, "first_value_time", strlen ("first_value_time"));
599   yajl_gen_double (data->handler, first_value_time_double);
600
601   yajl_gen_string_cast (data->handler, "interval", strlen ("interval"));
602   yajl_gen_double (data->handler, interval_double);
603
604   yajl_gen_string_cast (data->handler, "data", strlen ("data"));
605   yajl_gen_array_open (data->handler);
606
607   for (i = (data_points_num % points_consolidate);
608       i < data_points_num;
609       i += points_consolidate)
610   {
611     size_t j;
612
613     double sum = 0.0;
614     long num = 0;
615
616     for (j = 0; j < points_consolidate; j++)
617     {
618       if (isnan (data_points[i+j]))
619         continue;
620
621       sum += data_points[i+j];
622       num++;
623     }
624
625     if (num == 0)
626       yajl_gen_null (data->handler);
627     else
628       yajl_gen_double (data->handler, sum / ((double) num));
629   }
630
631   yajl_gen_array_close (data->handler);
632
633   return (0);
634 } /* }}} int ident_data_to_json__get_ident_data */
635
636 /* Called for each DS name */
637 static int ident_data_to_json__get_ds_name (graph_ident_t *ident, /* {{{ */
638     const char *ds_name, void *user_data)
639 {
640   ident_data_to_json__data_t *data = user_data;
641   int status;
642
643   yajl_gen_map_open (data->handler);
644
645   yajl_gen_string_cast (data->handler, "file", strlen ("file"));
646   ident_to_json (ident, data->handler);
647
648   yajl_gen_string_cast (data->handler, "data_source", strlen ("data_source"));
649   yajl_gen_string_cast (data->handler, ds_name, strlen (ds_name));
650
651   status = data_provider_get_ident_data (ident, ds_name,
652       data->begin, data->end,
653       ident_data_to_json__get_ident_data,
654       data);
655
656   yajl_gen_map_close (data->handler);
657
658   return (status);
659 } /* }}} int ident_data_to_json__get_ds_name */
660
661 int ident_data_to_json (graph_ident_t *ident, /* {{{ */
662     dp_time_t begin, dp_time_t end, dp_time_t res,
663     yajl_gen handler)
664 {
665   ident_data_to_json__data_t data;
666   int status;
667
668   data.begin = begin;
669   data.end = end;
670   data.interval = res;
671   data.handler = handler;
672
673   /* Iterate over all DS names */
674   status = data_provider_get_ident_ds_names (ident,
675       ident_data_to_json__get_ds_name, &data);
676   if (status != 0)
677     fprintf (stderr, "ident_data_to_json: data_provider_get_ident_ds_names "
678         "failed with status %i\n", status);
679
680   return (status);
681 } /* }}} int ident_data_to_json */
682 /* }}} ident_data_to_json */
683
684 int ident_describe (const graph_ident_t *ident, /* {{{ */
685     const graph_ident_t *selector,
686     char *buffer, size_t buffer_size)
687 {
688   if ((ident == NULL) || (selector == NULL)
689       || (buffer == NULL) || (buffer_size < 2))
690     return (EINVAL);
691
692   buffer[0] = 0;
693
694 #define CHECK_FIELD(field) do {                                              \
695   if (strcasecmp (selector->field, ident->field) != 0)                       \
696   {                                                                          \
697     if (buffer[0] != 0)                                                      \
698       strlcat (buffer, "/", buffer_size);                                    \
699     strlcat (buffer, ident->field, buffer_size);                             \
700   }                                                                          \
701 } while (0)
702
703   CHECK_FIELD (host);
704   CHECK_FIELD (plugin);
705   CHECK_FIELD (plugin_instance);
706   CHECK_FIELD (type);
707   CHECK_FIELD (type_instance);
708
709 #undef CHECK_FIELD
710
711   if (buffer[0] == 0)
712     strlcat (buffer, "default", buffer_size);
713
714   return (0);
715 } /* }}} int ident_describe */
716
717 time_t ident_get_mtime (const graph_ident_t *ident) /* {{{ */
718 {
719   char *file;
720   struct stat statbuf;
721   int status;
722
723   if (ident == NULL)
724     return (0);
725
726   file = ident_to_file (ident);
727   if (file == NULL)
728     return (0);
729
730   memset (&statbuf, 0, sizeof (statbuf));
731   status = stat (file, &statbuf);
732   if (status != 0)
733   {
734     fprintf (stderr, "ident_get_mtime: stat'ing file \"%s\" failed: %s\n",
735         file, strerror (errno));
736     return (0);
737   }
738
739   free (file);
740   return (statbuf.st_mtime);
741 } /* }}} time_t ident_get_mtime */
742
743 /* vim: set sw=2 sts=2 et fdm=marker : */
744