abf396de3ba4417518cb52a49a53a534433f5039
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 **/
24 #include "config.h"
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>
38 #include "graph_ident.h"
39 #include "common.h"
40 #include "data_provider.h"
41 #include "filesystem.h"
42 #include "utils_cgi.h"
44 #include <fcgiapp.h>
45 #include <fcgi_stdio.h>
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 */
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);
68 if ((flags & IDENT_FLAG_REPLACE_ANY) && IS_ANY (part))
69 return (NULL);
71 if ((flags & IDENT_FLAG_REPLACE_ALL) && IS_ALL (part))
72 return (NULL);
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 }
84 if (IS_ALL (selector))
85 {
86 if (flags & IDENT_FLAG_REPLACE_ALL)
87 return (strdup (part));
88 else
89 return (strdup (selector));
90 }
92 if (strcmp (selector, part) != 0)
93 return (NULL);
95 /* Otherwise (no replacement), return a copy of the selector. */
96 return (strdup (selector));
97 } /* }}} char *part_copy_with_selector */
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
107 if (selector == NULL) /* && (part != NULL) */
108 return (0);
110 if (IS_ANY(selector) || IS_ALL(selector))
111 return (1);
113 if (part == NULL) /* && (selector != NULL) */
114 return (0);
116 if (strcmp (selector, part) == 0)
117 return (1);
119 return (0);
120 } /* }}} _Bool part_matches */
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;
131 if ((host == NULL)
132 || (plugin == NULL) || (plugin_instance == NULL)
133 || (type == NULL) || (type_instance == NULL))
134 return (NULL);
136 ret = malloc (sizeof (*ret));
137 if (ret == NULL)
138 return (NULL);
139 memset (ret, 0, sizeof (*ret));
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;
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)
162 COPY_PART(host);
163 COPY_PART(plugin);
164 COPY_PART(plugin_instance);
165 COPY_PART(type);
166 COPY_PART(type_instance);
168 #undef COPY_PART
170 return (ret);
171 } /* }}} graph_ident_t *ident_create */
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 */
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;
185 if ((selector == NULL) || (ident == NULL))
186 return (NULL);
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;
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)
211 COPY_PART (host);
212 COPY_PART (plugin);
213 COPY_PART (plugin_instance);
214 COPY_PART (type);
215 COPY_PART (type_instance);
217 #undef COPY_PART
219 return (ret);
220 } /* }}} graph_ident_t *ident_copy_with_selector */
222 void ident_destroy (graph_ident_t *ident) /* {{{ */
223 {
224 if (ident == NULL)
225 return;
227 free (ident->host);
228 free (ident->plugin);
229 free (ident->plugin_instance);
230 free (ident->type);
231 free (ident->type_instance);
233 free (ident);
234 } /* }}} void ident_destroy */
236 /* ident_get_* methods {{{ */
237 const char *ident_get_host (const graph_ident_t *ident) /* {{{ */
238 {
239 if (ident == NULL)
240 return (NULL);
242 return (ident->host);
243 } /* }}} char *ident_get_host */
245 const char *ident_get_plugin (const graph_ident_t *ident) /* {{{ */
246 {
247 if (ident == NULL)
248 return (NULL);
250 return (ident->plugin);
251 } /* }}} char *ident_get_plugin */
253 const char *ident_get_plugin_instance (const graph_ident_t *ident) /* {{{ */
254 {
255 if (ident == NULL)
256 return (NULL);
258 return (ident->plugin_instance);
259 } /* }}} char *ident_get_plugin_instance */
261 const char *ident_get_type (const graph_ident_t *ident) /* {{{ */
262 {
263 if (ident == NULL)
264 return (NULL);
266 return (ident->type);
267 } /* }}} char *ident_get_type */
269 const char *ident_get_type_instance (const graph_ident_t *ident) /* {{{ */
270 {
271 if (ident == NULL)
272 return (NULL);
274 return (ident->type_instance);
275 } /* }}} char *ident_get_type_instance */
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);
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 */
298 /* ident_set_* methods {{{ */
299 int ident_set_host (graph_ident_t *ident, const char *host) /* {{{ */
300 {
301 char *tmp;
303 if ((ident == NULL) || (host == NULL))
304 return (EINVAL);
306 tmp = strdup (host);
307 if (tmp == NULL)
308 return (ENOMEM);
310 free (ident->host);
311 ident->host = tmp;
313 return (0);
314 } /* }}} int ident_set_host */
316 int ident_set_plugin (graph_ident_t *ident, const char *plugin) /* {{{ */
317 {
318 char *tmp;
320 if ((ident == NULL) || (plugin == NULL))
321 return (EINVAL);
323 tmp = strdup (plugin);
324 if (tmp == NULL)
325 return (ENOMEM);
327 free (ident->plugin);
328 ident->plugin = tmp;
330 return (0);
331 } /* }}} int ident_set_plugin */
333 int ident_set_plugin_instance (graph_ident_t *ident, const char *plugin_instance) /* {{{ */
334 {
335 char *tmp;
337 if ((ident == NULL) || (plugin_instance == NULL))
338 return (EINVAL);
340 tmp = strdup (plugin_instance);
341 if (tmp == NULL)
342 return (ENOMEM);
344 free (ident->plugin_instance);
345 ident->plugin_instance = tmp;
347 return (0);
348 } /* }}} int ident_set_plugin_instance */
350 int ident_set_type (graph_ident_t *ident, const char *type) /* {{{ */
351 {
352 char *tmp;
354 if ((ident == NULL) || (type == NULL))
355 return (EINVAL);
357 tmp = strdup (type);
358 if (tmp == NULL)
359 return (ENOMEM);
361 free (ident->type);
362 ident->type = tmp;
364 return (0);
365 } /* }}} int ident_set_type */
367 int ident_set_type_instance (graph_ident_t *ident, const char *type_instance) /* {{{ */
368 {
369 char *tmp;
371 if ((ident == NULL) || (type_instance == NULL))
372 return (EINVAL);
374 tmp = strdup (type_instance);
375 if (tmp == NULL)
376 return (ENOMEM);
378 free (ident->type_instance);
379 ident->type_instance = tmp;
381 return (0);
382 } /* }}} int ident_set_type_instance */
384 /* }}} ident_set_* methods */
386 int ident_compare (const graph_ident_t *i0, /* {{{ */
387 const graph_ident_t *i1)
388 {
389 int status;
391 #define COMPARE_PART(p) do { \
392 status = strcmp (i0->p, i1->p); \
393 if (status != 0) \
394 return (status); \
395 } while (0)
397 COMPARE_PART (host);
398 COMPARE_PART (plugin);
399 COMPARE_PART (plugin_instance);
400 COMPARE_PART (type);
401 COMPARE_PART (type_instance);
403 #undef COMPARE_PART
405 return (0);
406 } /* }}} int ident_compare */
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
416 if (!part_matches (selector->host, ident->host))
417 return (0);
419 if (!part_matches (selector->plugin, ident->plugin))
420 return (0);
422 if (!part_matches (selector->plugin_instance, ident->plugin_instance))
423 return (0);
425 if (!part_matches (selector->type, ident->type))
426 return (0);
428 if (!part_matches (selector->type_instance, ident->type_instance))
429 return (0);
431 return (1);
432 } /* }}} _Bool ident_matches */
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)
444 INTERSECT_PART (host);
445 INTERSECT_PART (plugin);
446 INTERSECT_PART (plugin_instance);
447 INTERSECT_PART (type);
448 INTERSECT_PART (type_instance);
450 #undef INTERSECT_PART
452 return (1);
453 } /* }}} _Bool ident_intersect */
455 char *ident_to_string (const graph_ident_t *ident) /* {{{ */
456 {
457 char buffer[PATH_MAX];
459 buffer[0] = 0;
461 strlcat (buffer, ident->host, sizeof (buffer));
462 strlcat (buffer, "/", sizeof (buffer));
463 strlcat (buffer, ident->plugin, sizeof (buffer));
464 if (ident->plugin_instance[0] != 0)
465 {
466 strlcat (buffer, "-", sizeof (buffer));
467 strlcat (buffer, ident->plugin_instance, sizeof (buffer));
468 }
469 strlcat (buffer, "/", sizeof (buffer));
470 strlcat (buffer, ident->type, sizeof (buffer));
471 if (ident->type_instance[0] != 0)
472 {
473 strlcat (buffer, "-", sizeof (buffer));
474 strlcat (buffer, ident->type_instance, sizeof (buffer));
475 }
477 return (strdup (buffer));
478 } /* }}} char *ident_to_string */
480 char *ident_to_file (const graph_ident_t *ident) /* {{{ */
481 {
482 char buffer[PATH_MAX];
484 buffer[0] = 0;
486 strlcat (buffer, DATA_DIR, sizeof (buffer));
487 strlcat (buffer, "/", sizeof (buffer));
489 strlcat (buffer, ident->host, sizeof (buffer));
490 strlcat (buffer, "/", sizeof (buffer));
491 strlcat (buffer, ident->plugin, sizeof (buffer));
492 if (ident->plugin_instance[0] != 0)
493 {
494 strlcat (buffer, "-", sizeof (buffer));
495 strlcat (buffer, ident->plugin_instance, sizeof (buffer));
496 }
497 strlcat (buffer, "/", sizeof (buffer));
498 strlcat (buffer, ident->type, sizeof (buffer));
499 if (ident->type_instance[0] != 0)
500 {
501 strlcat (buffer, "-", sizeof (buffer));
502 strlcat (buffer, ident->type_instance, sizeof (buffer));
503 }
505 strlcat (buffer, ".rrd", sizeof (buffer));
507 return (strdup (buffer));
508 } /* }}} char *ident_to_file */
510 int ident_to_json (const graph_ident_t *ident, /* {{{ */
511 yajl_gen handler)
512 {
513 yajl_gen_status status;
515 if ((ident == NULL) || (handler == NULL))
516 return (EINVAL);
518 #define ADD_STRING(str) do { \
519 status = yajl_gen_string (handler, \
520 (unsigned char *) (str), \
521 (unsigned int) strlen (str)); \
522 if (status != yajl_gen_status_ok) \
523 return ((int) status); \
524 } while (0)
526 yajl_gen_map_open (handler);
527 ADD_STRING ("host");
528 ADD_STRING (ident->host);
529 ADD_STRING ("plugin");
530 ADD_STRING (ident->plugin);
531 ADD_STRING ("plugin_instance");
532 ADD_STRING (ident->plugin_instance);
533 ADD_STRING ("type");
534 ADD_STRING (ident->type);
535 ADD_STRING ("type_instance");
536 ADD_STRING (ident->type_instance);
537 yajl_gen_map_close (handler);
539 #undef ADD_FIELD
541 return (0);
542 } /* }}} char *ident_to_json */
544 /* {{{ ident_data_to_json */
545 struct ident_data_to_json__data_s
546 {
547 dp_time_t begin;
548 dp_time_t end;
549 dp_time_t interval;
550 yajl_gen handler;
551 };
552 typedef struct ident_data_to_json__data_s ident_data_to_json__data_t;
554 #define yajl_gen_string_cast(h,s,l) \
555 yajl_gen_string (h, (unsigned char *) s, (unsigned int) l)
557 static int ident_data_to_json__get_ident_data (
558 __attribute__((unused)) graph_ident_t *ident, /* {{{ */
559 __attribute__((unused)) const char *ds_name,
560 dp_time_t first_value_time, dp_time_t interval,
561 size_t data_points_num, double *data_points,
562 void *user_data)
563 {
564 ident_data_to_json__data_t *data = user_data;
565 size_t i;
567 double first_value_time_double;
568 double interval_double;
569 double interval_requested;
570 size_t points_consolidate;
572 first_value_time_double = ((double) first_value_time.tv_sec)
573 + (((double) first_value_time.tv_nsec) / 1000000000.0);
574 interval_double = ((double) interval.tv_sec)
575 + (((double) interval.tv_nsec) / 1000000000.0);
576 interval_requested = ((double) data->interval.tv_sec)
577 + (((double) data->interval.tv_nsec) / 1000000000.0);
579 if (interval_requested < (2.0 * interval_double))
580 points_consolidate = 1;
581 else
582 points_consolidate = (size_t) (interval_requested / interval_double);
583 assert (points_consolidate >= 1);
585 if (points_consolidate > 1)
586 {
587 size_t offset = data_points_num % points_consolidate;
589 first_value_time_double += ((double) offset) * interval_double;
590 interval_double *= ((double) points_consolidate);
591 }
593 yajl_gen_map_open (data->handler);
595 yajl_gen_string_cast (data->handler, "first_value_time", strlen ("first_value_time"));
596 yajl_gen_double (data->handler, first_value_time_double);
598 yajl_gen_string_cast (data->handler, "interval", strlen ("interval"));
599 yajl_gen_double (data->handler, interval_double);
601 yajl_gen_string_cast (data->handler, "data", strlen ("data"));
602 yajl_gen_array_open (data->handler);
604 for (i = (data_points_num % points_consolidate);
605 i < data_points_num;
606 i += points_consolidate)
607 {
608 size_t j;
610 double sum = 0.0;
611 long num = 0;
613 for (j = 0; j < points_consolidate; j++)
614 {
615 if (isnan (data_points[i+j]))
616 continue;
618 sum += data_points[i+j];
619 num++;
620 }
622 if (num == 0)
623 yajl_gen_null (data->handler);
624 else
625 yajl_gen_double (data->handler, sum / ((double) num));
626 }
628 yajl_gen_array_close (data->handler);
630 return (0);
631 } /* }}} int ident_data_to_json__get_ident_data */
633 /* Called for each DS name */
634 static int ident_data_to_json__get_ds_name (graph_ident_t *ident, /* {{{ */
635 const char *ds_name, void *user_data)
636 {
637 ident_data_to_json__data_t *data = user_data;
638 int status;
640 yajl_gen_map_open (data->handler);
642 yajl_gen_string_cast (data->handler, "file", strlen ("file"));
643 ident_to_json (ident, data->handler);
645 yajl_gen_string_cast (data->handler, "data_source", strlen ("data_source"));
646 yajl_gen_string_cast (data->handler, ds_name, strlen (ds_name));
648 status = data_provider_get_ident_data (ident, ds_name,
649 data->begin, data->end,
650 ident_data_to_json__get_ident_data,
651 data);
653 yajl_gen_map_close (data->handler);
655 return (status);
656 } /* }}} int ident_data_to_json__get_ds_name */
658 int ident_data_to_json (graph_ident_t *ident, /* {{{ */
659 dp_time_t begin, dp_time_t end, dp_time_t res,
660 yajl_gen handler)
661 {
662 ident_data_to_json__data_t data;
663 int status;
665 data.begin = begin;
666 data.end = end;
667 data.interval = res;
668 data.handler = handler;
670 /* Iterate over all DS names */
671 status = data_provider_get_ident_ds_names (ident,
672 ident_data_to_json__get_ds_name, &data);
673 if (status != 0)
674 fprintf (stderr, "ident_data_to_json: data_provider_get_ident_ds_names "
675 "failed with status %i\n", status);
677 return (status);
678 } /* }}} int ident_data_to_json */
679 /* }}} ident_data_to_json */
681 int ident_describe (const graph_ident_t *ident, /* {{{ */
682 const graph_ident_t *selector,
683 char *buffer, size_t buffer_size)
684 {
685 if ((ident == NULL) || (selector == NULL)
686 || (buffer == NULL) || (buffer_size < 2))
687 return (EINVAL);
689 buffer[0] = 0;
691 #define CHECK_FIELD(field) do { \
692 if (strcasecmp (selector->field, ident->field) != 0) \
693 { \
694 if (buffer[0] != 0) \
695 strlcat (buffer, "/", buffer_size); \
696 strlcat (buffer, ident->field, buffer_size); \
697 } \
698 } while (0)
700 CHECK_FIELD (host);
701 CHECK_FIELD (plugin);
702 CHECK_FIELD (plugin_instance);
703 CHECK_FIELD (type);
704 CHECK_FIELD (type_instance);
706 #undef CHECK_FIELD
708 if (buffer[0] == 0)
709 strlcat (buffer, "default", buffer_size);
711 return (0);
712 } /* }}} int ident_describe */
714 time_t ident_get_mtime (const graph_ident_t *ident) /* {{{ */
715 {
716 char *file;
717 struct stat statbuf;
718 int status;
720 if (ident == NULL)
721 return (0);
723 file = ident_to_file (ident);
724 if (file == NULL)
725 return (0);
727 memset (&statbuf, 0, sizeof (statbuf));
728 status = stat (file, &statbuf);
729 if (status != 0)
730 {
731 fprintf (stderr, "ident_get_mtime: stat'ing file \"%s\" failed: %s\n",
732 file, strerror (errno));
733 return (0);
734 }
736 free (file);
737 return (statbuf.st_mtime);
738 } /* }}} time_t ident_get_mtime */
740 /* vim: set sw=2 sts=2 et fdm=marker : */