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>
37 #include "graph_ident.h"
38 #include "common.h"
39 #include "data_provider.h"
40 #include "filesystem.h"
41 #include "utils_cgi.h"
43 #include <fcgiapp.h>
44 #include <fcgi_stdio.h>
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 */
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);
67 if ((flags & IDENT_FLAG_REPLACE_ANY) && IS_ANY (part))
68 return (NULL);
70 if ((flags & IDENT_FLAG_REPLACE_ALL) && IS_ALL (part))
71 return (NULL);
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 }
83 if (IS_ALL (selector))
84 {
85 if (flags & IDENT_FLAG_REPLACE_ALL)
86 return (strdup (part));
87 else
88 return (strdup (selector));
89 }
91 if (strcmp (selector, part) != 0)
92 return (NULL);
94 /* Otherwise (no replacement), return a copy of the selector. */
95 return (strdup (selector));
96 } /* }}} char *part_copy_with_selector */
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
106 if (selector == NULL) /* && (part != NULL) */
107 return (0);
109 if (IS_ANY(selector) || IS_ALL(selector))
110 return (1);
112 if (part == NULL) /* && (selector != NULL) */
113 return (0);
115 if (strcmp (selector, part) == 0)
116 return (1);
118 return (0);
119 } /* }}} _Bool part_matches */
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;
130 if ((host == NULL)
131 || (plugin == NULL) || (plugin_instance == NULL)
132 || (type == NULL) || (type_instance == NULL))
133 return (NULL);
135 ret = malloc (sizeof (*ret));
136 if (ret == NULL)
137 return (NULL);
138 memset (ret, 0, sizeof (*ret));
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;
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)
161 COPY_PART(host);
162 COPY_PART(plugin);
163 COPY_PART(plugin_instance);
164 COPY_PART(type);
165 COPY_PART(type_instance);
167 #undef COPY_PART
169 return (ret);
170 } /* }}} graph_ident_t *ident_create */
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 */
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;
184 if ((selector == NULL) || (ident == NULL))
185 return (NULL);
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;
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)
210 COPY_PART (host);
211 COPY_PART (plugin);
212 COPY_PART (plugin_instance);
213 COPY_PART (type);
214 COPY_PART (type_instance);
216 #undef COPY_PART
218 return (ret);
219 } /* }}} graph_ident_t *ident_copy_with_selector */
221 void ident_destroy (graph_ident_t *ident) /* {{{ */
222 {
223 if (ident == NULL)
224 return;
226 free (ident->host);
227 free (ident->plugin);
228 free (ident->plugin_instance);
229 free (ident->type);
230 free (ident->type_instance);
232 free (ident);
233 } /* }}} void ident_destroy */
235 /* ident_get_* methods {{{ */
236 const char *ident_get_host (const graph_ident_t *ident) /* {{{ */
237 {
238 if (ident == NULL)
239 return (NULL);
241 return (ident->host);
242 } /* }}} char *ident_get_host */
244 const char *ident_get_plugin (const graph_ident_t *ident) /* {{{ */
245 {
246 if (ident == NULL)
247 return (NULL);
249 return (ident->plugin);
250 } /* }}} char *ident_get_plugin */
252 const char *ident_get_plugin_instance (const graph_ident_t *ident) /* {{{ */
253 {
254 if (ident == NULL)
255 return (NULL);
257 return (ident->plugin_instance);
258 } /* }}} char *ident_get_plugin_instance */
260 const char *ident_get_type (const graph_ident_t *ident) /* {{{ */
261 {
262 if (ident == NULL)
263 return (NULL);
265 return (ident->type);
266 } /* }}} char *ident_get_type */
268 const char *ident_get_type_instance (const graph_ident_t *ident) /* {{{ */
269 {
270 if (ident == NULL)
271 return (NULL);
273 return (ident->type_instance);
274 } /* }}} char *ident_get_type_instance */
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);
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 */
297 /* ident_set_* methods {{{ */
298 int ident_set_host (graph_ident_t *ident, const char *host) /* {{{ */
299 {
300 char *tmp;
302 if ((ident == NULL) || (host == NULL))
303 return (EINVAL);
305 tmp = strdup (host);
306 if (tmp == NULL)
307 return (ENOMEM);
309 free (ident->host);
310 ident->host = tmp;
312 return (0);
313 } /* }}} int ident_set_host */
315 int ident_set_plugin (graph_ident_t *ident, const char *plugin) /* {{{ */
316 {
317 char *tmp;
319 if ((ident == NULL) || (plugin == NULL))
320 return (EINVAL);
322 tmp = strdup (plugin);
323 if (tmp == NULL)
324 return (ENOMEM);
326 free (ident->plugin);
327 ident->plugin = tmp;
329 return (0);
330 } /* }}} int ident_set_plugin */
332 int ident_set_plugin_instance (graph_ident_t *ident, const char *plugin_instance) /* {{{ */
333 {
334 char *tmp;
336 if ((ident == NULL) || (plugin_instance == NULL))
337 return (EINVAL);
339 tmp = strdup (plugin_instance);
340 if (tmp == NULL)
341 return (ENOMEM);
343 free (ident->plugin_instance);
344 ident->plugin_instance = tmp;
346 return (0);
347 } /* }}} int ident_set_plugin_instance */
349 int ident_set_type (graph_ident_t *ident, const char *type) /* {{{ */
350 {
351 char *tmp;
353 if ((ident == NULL) || (type == NULL))
354 return (EINVAL);
356 tmp = strdup (type);
357 if (tmp == NULL)
358 return (ENOMEM);
360 free (ident->type);
361 ident->type = tmp;
363 return (0);
364 } /* }}} int ident_set_type */
366 int ident_set_type_instance (graph_ident_t *ident, const char *type_instance) /* {{{ */
367 {
368 char *tmp;
370 if ((ident == NULL) || (type_instance == NULL))
371 return (EINVAL);
373 tmp = strdup (type_instance);
374 if (tmp == NULL)
375 return (ENOMEM);
377 free (ident->type_instance);
378 ident->type_instance = tmp;
380 return (0);
381 } /* }}} int ident_set_type_instance */
383 /* }}} ident_set_* methods */
385 int ident_compare (const graph_ident_t *i0, /* {{{ */
386 const graph_ident_t *i1)
387 {
388 int status;
390 #define COMPARE_PART(p) do { \
391 status = strcmp (i0->p, i1->p); \
392 if (status != 0) \
393 return (status); \
394 } while (0)
396 COMPARE_PART (host);
397 COMPARE_PART (plugin);
398 COMPARE_PART (plugin_instance);
399 COMPARE_PART (type);
400 COMPARE_PART (type_instance);
402 #undef COMPARE_PART
404 return (0);
405 } /* }}} int ident_compare */
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
415 if (!part_matches (selector->host, ident->host))
416 return (0);
418 if (!part_matches (selector->plugin, ident->plugin))
419 return (0);
421 if (!part_matches (selector->plugin_instance, ident->plugin_instance))
422 return (0);
424 if (!part_matches (selector->type, ident->type))
425 return (0);
427 if (!part_matches (selector->type_instance, ident->type_instance))
428 return (0);
430 return (1);
431 } /* }}} _Bool ident_matches */
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)
443 INTERSECT_PART (host);
444 INTERSECT_PART (plugin);
445 INTERSECT_PART (plugin_instance);
446 INTERSECT_PART (type);
447 INTERSECT_PART (type_instance);
449 #undef INTERSECT_PART
451 return (1);
452 } /* }}} _Bool ident_intersect */
454 char *ident_to_string (const graph_ident_t *ident) /* {{{ */
455 {
456 char buffer[PATH_MAX];
458 buffer[0] = 0;
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 }
476 return (strdup (buffer));
477 } /* }}} char *ident_to_string */
479 char *ident_to_file (const graph_ident_t *ident) /* {{{ */
480 {
481 char buffer[PATH_MAX];
483 buffer[0] = 0;
485 strlcat (buffer, DATA_DIR, sizeof (buffer));
486 strlcat (buffer, "/", sizeof (buffer));
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 }
504 strlcat (buffer, ".rrd", sizeof (buffer));
506 return (strdup (buffer));
507 } /* }}} char *ident_to_file */
509 int ident_to_json (const graph_ident_t *ident, /* {{{ */
510 yajl_gen handler)
511 {
512 yajl_gen_status status;
514 if ((ident == NULL) || (handler == NULL))
515 return (EINVAL);
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)
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);
538 #undef ADD_FIELD
540 return (0);
541 } /* }}} char *ident_to_json */
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;
552 #define yajl_gen_string_cast(h,s,l) \
553 yajl_gen_string (h, (unsigned char *) s, (unsigned int) l)
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;
564 /* TODO: Make points_num_limit configurable. */
565 /* points_num_limit: The number of data-points to send at least. */
566 size_t points_num_limit = 400;
567 size_t points_consolidate;
569 if (dp_num <= points_num_limit)
570 points_consolidate = 1;
571 else
572 points_consolidate = dp_num / points_num_limit;
574 yajl_gen_array_open (data->handler);
576 for (i = (dp_num % points_consolidate); i < dp_num; i += points_consolidate)
577 {
578 size_t j;
580 double sum = 0.0;
581 long num = 0;
583 yajl_gen_array_open (data->handler);
584 yajl_gen_integer (data->handler, (long) dp[i].time.tv_sec);
586 for (j = 0; j < points_consolidate; j++)
587 {
588 if (isnan (dp[i+j].value))
589 continue;
591 sum += dp[i+j].value;
592 num++;
593 }
595 if (num == 0)
596 yajl_gen_null (data->handler);
597 else
598 yajl_gen_double (data->handler, sum / ((double) num));
600 yajl_gen_array_close (data->handler);
601 }
603 yajl_gen_array_close (data->handler);
605 return (0);
606 } /* }}} int ident_data_to_json__get_ident_data */
608 /* Called for each DS name */
609 static int ident_data_to_json__get_ds_name (graph_ident_t *ident, /* {{{ */
610 const char *ds_name, void *user_data)
611 {
612 ident_data_to_json__data_t *data = user_data;
613 int status;
615 yajl_gen_map_open (data->handler);
617 yajl_gen_string_cast (data->handler, "file", strlen ("file"));
618 ident_to_json (ident, data->handler);
620 yajl_gen_string_cast (data->handler, "data_source", strlen ("data_source"));
621 yajl_gen_string_cast (data->handler, ds_name, strlen (ds_name));
623 yajl_gen_string_cast (data->handler, "data", strlen ("data"));
624 status = data_provider_get_ident_data (ident, ds_name,
625 data->begin, data->end,
626 ident_data_to_json__get_ident_data,
627 data);
629 yajl_gen_map_close (data->handler);
631 return (status);
632 } /* }}} int ident_data_to_json__get_ds_name */
634 int ident_data_to_json (graph_ident_t *ident, /* {{{ */
635 dp_time_t begin, dp_time_t end,
636 yajl_gen handler)
637 {
638 ident_data_to_json__data_t data;
639 int status;
641 data.begin = begin;
642 data.end = end;
643 data.handler = handler;
645 /* Iterate over all DS names */
646 status = data_provider_get_ident_ds_names (ident,
647 ident_data_to_json__get_ds_name, &data);
648 if (status != 0)
649 fprintf (stderr, "ident_data_to_json: data_provider_get_ident_ds_names "
650 "failed with status %i\n", status);
652 return (status);
653 } /* }}} int ident_data_to_json */
654 /* }}} ident_data_to_json */
656 int ident_describe (const graph_ident_t *ident, /* {{{ */
657 const graph_ident_t *selector,
658 char *buffer, size_t buffer_size)
659 {
660 if ((ident == NULL) || (selector == NULL)
661 || (buffer == NULL) || (buffer_size < 2))
662 return (EINVAL);
664 buffer[0] = 0;
666 #define CHECK_FIELD(field) do { \
667 if (strcasecmp (selector->field, ident->field) != 0) \
668 { \
669 if (buffer[0] != 0) \
670 strlcat (buffer, "/", buffer_size); \
671 strlcat (buffer, ident->field, buffer_size); \
672 } \
673 } while (0)
675 CHECK_FIELD (host);
676 CHECK_FIELD (plugin);
677 CHECK_FIELD (plugin_instance);
678 CHECK_FIELD (type);
679 CHECK_FIELD (type_instance);
681 #undef CHECK_FIELD
683 if (buffer[0] == 0)
684 strlcat (buffer, "default", buffer_size);
686 return (0);
687 } /* }}} int ident_describe */
689 time_t ident_get_mtime (const graph_ident_t *ident) /* {{{ */
690 {
691 char *file;
692 struct stat statbuf;
693 int status;
695 if (ident == NULL)
696 return (0);
698 file = ident_to_file (ident);
699 if (file == NULL)
700 return (0);
702 memset (&statbuf, 0, sizeof (statbuf));
703 status = stat (file, &statbuf);
704 if (status != 0)
705 {
706 fprintf (stderr, "ident_get_mtime: stat'ing file \"%s\" failed: %s\n",
707 file, strerror (errno));
708 return (0);
709 }
711 free (file);
712 return (statbuf.st_mtime);
713 } /* }}} time_t ident_get_mtime */
715 /* vim: set sw=2 sts=2 et fdm=marker : */