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