2 * collectd - src/collectdctl-show.c
3 * Copyright (C) 2011 Florian Forster
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Florian "octo" Forster <octo at collectd.org>
35 #if NAN_STATIC_DEFAULT
37 /* #endif NAN_STATIC_DEFAULT*/
40 # define DISABLE_ISOC99 1
41 # define __USE_ISOC99 1
42 # endif /* !defined(__USE_ISOC99) */
45 # undef DISABLE_ISOC99
47 # endif /* DISABLE_ISOC99 */
48 /* #endif NAN_STATIC_ISOC */
54 # define NAN (0.0 / 0.0)
56 # define isnan(f) ((f) != (f))
57 # endif /* !defined(isnan) */
59 # define isfinite(f) (((f) - (f)) == 0.0)
62 # define isinf(f) (!isfinite(f) && !isnan(f))
64 #endif /* NAN_ZERO_ZERO */
66 #include "libcollectdclient/collectd/client.h"
68 #define AGGR_TYPE_COUNT 0
69 #define AGGR_TYPE_MIN 1
70 #define AGGR_TYPE_MAX 2
71 #define AGGR_TYPE_AVG 3
72 #define AGGR_TYPE_SUM 4
73 #define AGGR_TYPE_SDEV 5
78 struct aggregation_group_s
86 double sum_of_squares;
88 typedef struct aggregation_group_s aggregation_group_t;
94 static const char *re_host = NULL;
95 static const char *re_plugin = NULL;
96 static const char *re_plugin_instance = NULL;
97 static const char *re_type = NULL;
98 static const char *re_type_instance = NULL;
101 static uint16_t grouping = 0;
104 static int *aggregation_types = NULL;
105 static size_t aggregation_types_num = 0;
107 static aggregation_group_t *aggregation_groups = NULL;
108 static size_t aggregation_groups_num = 0;
113 static int parse_aggr_type (const char *type) /* {{{ */
117 else if (strcasecmp ("count", type) == 0)
118 return (AGGR_TYPE_COUNT);
119 else if ((strcasecmp ("min", type) == 0)
120 || (strcasecmp ("minimum", type) == 0))
121 return (AGGR_TYPE_MIN);
122 else if ((strcasecmp ("max", type) == 0)
123 || (strcasecmp ("maximum", type) == 0))
124 return (AGGR_TYPE_MAX);
125 else if ((strcasecmp ("avg", type) == 0)
126 || (strcasecmp ("average", type) == 0))
127 return (AGGR_TYPE_AVG);
128 else if (strcasecmp ("sum", type) == 0)
129 return (AGGR_TYPE_SUM);
130 else if ((strcasecmp ("sdev", type) == 0)
131 || (strcasecmp ("stddev", type) == 0))
132 return (AGGR_TYPE_SDEV);
135 } /* }}} int parse_aggr_type */
137 static const char *aggr_type_to_string (int type) /* {{{ */
141 case AGGR_TYPE_COUNT: return ("Count");
142 case AGGR_TYPE_MIN: return ("Min");
143 case AGGR_TYPE_MAX: return ("Max");
144 case AGGR_TYPE_AVG: return ("Average");
145 case AGGR_TYPE_SUM: return ("Sum");
146 case AGGR_TYPE_SDEV: return ("Std. Dev.");
150 } /* }}} const char *aggr_type_to_string */
152 static int aggregation_type_add (const char *str_type) /* {{{ */
158 type = parse_aggr_type (str_type);
161 fprintf (stderr, "ERROR: \"%s\" is not a known aggregation function.\n",
166 /* Check for duplicate definitions */
167 for (i = 0; i < aggregation_types_num; i++)
169 if (aggregation_types[i] == type)
171 fprintf (stderr, "ERROR: Multiple aggregations with type \"%s\" "
172 "defined.\n", str_type);
177 tmp = realloc (aggregation_types,
178 (aggregation_types_num + 1) * sizeof (*aggregation_types));
181 aggregation_types = tmp;
182 aggregation_types[aggregation_types_num] = type;
183 aggregation_types_num++;
186 } /* }}} int aggregation_type_add */
188 static int group_name_from_ident (const lcc_identifier_t *identifier, /* {{{ */
189 char *buffer, size_t buffer_size)
191 if ((identifier == NULL)
192 || (buffer == NULL) || (buffer_size < 2))
197 lcc_identifier_to_string (/* connection = */ NULL,
198 buffer, buffer_size, identifier);
199 buffer[buffer_size - 1] = 0;
203 memset (buffer, 0, buffer_size);
205 #define COPY_FIELD(field,index) do { \
206 if ((grouping & (1 << index)) != 0) \
208 if (buffer[0] == 0) \
209 strncpy (buffer, identifier->field, buffer_size); \
212 char tmp[buffer_size]; \
213 snprintf (tmp, buffer_size, "%s/%s", buffer, identifier->field); \
214 memcpy (buffer, tmp, buffer_size); \
216 buffer[buffer_size - 1] = 0; \
220 COPY_FIELD (host, 0);
221 COPY_FIELD (plugin, 1);
222 COPY_FIELD (plugin_instance, 2);
223 COPY_FIELD (type, 3);
224 COPY_FIELD (type_instance, 4);
229 } /* }}} int group_name_from_ident */
231 static aggregation_group_t *aggregation_get_group ( const lcc_identifier_t *identifier) /* {{{ */
233 char group_name[LCC_NAME_LEN];
234 aggregation_group_t *g;
238 if (identifier == NULL)
241 status = group_name_from_ident (identifier,
242 group_name, sizeof (group_name));
246 for (i = 0; i < aggregation_groups_num; i++)
247 if (strcmp (group_name, aggregation_groups[i].name) == 0)
248 return (aggregation_groups + i);
250 g = realloc (aggregation_groups,
251 (aggregation_groups_num + 1) * sizeof (*aggregation_groups));
254 aggregation_groups = g;
255 g = aggregation_groups + aggregation_groups_num;
257 memset (g, 0, sizeof (*g));
258 g->name = strdup (group_name);
265 g->sum_of_squares = NAN;
267 aggregation_groups_num++;
269 } /* }}} aggregation_group_t *aggregation_get_group */
271 static int aggregation_add_value (const lcc_identifier_t *identifier, /* {{{ */
274 aggregation_group_t *g;
276 if (identifier == NULL)
279 g = aggregation_get_group (identifier);
288 g->sum_of_squares = value * value;
296 if (isnan (g->min) || (g->min > value))
299 if (isnan (g->max) || (g->max < value))
307 if (isnan (g->sum_of_squares))
308 g->sum_of_squares = value * value;
310 g->sum_of_squares += value * value;
315 } /* }}} int aggregation_add_value */
317 static int read_data (lcc_connection_t *c) /* {{{ */
319 lcc_identifier_t *ret_ident = NULL;
320 size_t ret_ident_num = 0;
325 status = lcc_listval_with_selection (c,
331 &ret_ident, &ret_ident_num);
334 fprintf (stderr, "ERROR: lcc_listval_with_selection: %s\n",
338 assert ((ret_ident != NULL) || (ret_ident_num == 0));
340 /* Iterate over all returned identifiers and figure out which ones are
341 * interesting, i.e. match a selector in an aggregation. */
342 for (i = 0; i < ret_ident_num; ++i)
344 size_t ret_values_num = 0;
345 gauge_t *ret_values = NULL;
347 status = lcc_getval (c, ret_ident + i,
348 &ret_values_num, &ret_values, /* values_names = */ NULL);
351 fprintf (stderr, "ERROR: lcc_getval: %s\n", lcc_strerror (c));
354 assert (ret_values != NULL);
356 /* FIXME: What to do with multiple data sources values? */
357 aggregation_add_value (ret_ident + i, ret_values[0]);
360 } /* for (ret_ident) */
365 } /* }}} int read_data */
367 static int print_horizontal_line (int name_len_max) /* {{{ */
374 for (i = 0; i < name_len_max; i++)
379 for (j = 0; j < aggregation_types_num; j++)
380 printf ("------------+");
381 if (aggregation_types_num == 0)
382 printf ("------------+");
387 } /* }}} int print_horizontal_line */
389 static int write_data (void) /* {{{ */
391 int name_len_max = 4;
394 for (i = 0; i < aggregation_groups_num; i++)
396 int name_len = (int) strlen (aggregation_groups[i].name);
397 if (name_len_max < name_len)
398 name_len_max = name_len;
401 print_horizontal_line (name_len_max);
402 printf ("! %-*s !", name_len_max, "Name");
403 for (i = 0; i < aggregation_types_num; i++)
404 printf (" %10s !", aggr_type_to_string (aggregation_types[i]));
405 if (aggregation_types_num == 0)
406 printf (" %10s !", "Value");
408 print_horizontal_line (name_len_max);
410 for (i = 0; i < aggregation_groups_num; i++)
414 aggregation_group_t *g = aggregation_groups + i;
416 printf ("! %-*s !", name_len_max, g->name);
418 for (j = 0; j < aggregation_types_num; j++)
420 int type = aggregation_types[j];
423 if (type == AGGR_TYPE_COUNT)
424 value = (double) g->num;
425 else if (type == AGGR_TYPE_MIN)
427 else if (type == AGGR_TYPE_MAX)
429 else if (type == AGGR_TYPE_SUM)
431 else if ((type == AGGR_TYPE_AVG)
433 value = g->sum / ((double) g->num);
434 else if (type == AGGR_TYPE_SDEV)
442 - ((g->sum * g->sum) / ((double) g->num))
444 / ((double) (g->num - 1)));
447 printf (" %10g !", value);
449 if (aggregation_types_num == 0)
451 /* g->num may be zero if the value is NAN. */
453 printf (" %10g !", g->min);
459 print_horizontal_line (name_len_max);
462 } /* }}} int write_data */
464 __attribute__((noreturn))
465 static void exit_usage (int status) /* {{{ */
467 printf ("Usage: collectdctl show [<Selection>] [<Aggregation> <Grouping>]\n"
471 " host=<regex> Regex for the host name.\n"
472 " plugin=<regex> Regex for the plugin.\n"
473 " plugin_instance=<regex> Regex for the plugin instance.\n"
474 " type=<regex> Regex for the type.\n"
475 " type_instance=<regex> Regex for the type instance.\n"
479 " aggregate=<aggr>[,<aggr>[...]] List of aggregations to use when\n"
480 " combining multiple values.\n"
481 " Valid aggregations are:\n"
482 " count, min, max, avg, sum, stddev\n"
486 " group=<field>[,<field>[...]] List of fields to group by.\n"
487 " Valid fields are:\n"
488 " host, plugin, plugin_instance,\n"
489 " type, type_instance\n"
492 } /* }}} void exit_usage */
494 static int parse_aggregate (const char *aggr) /* {{{ */
500 aggr_copy = strdup (aggr);
501 if (aggr_copy == NULL)
504 free (aggregation_types);
505 aggregation_types = NULL;
506 aggregation_types_num = 0;
509 while ((a = strtok (dummy, ",")) != NULL)
515 status = aggregation_type_add (a);
517 exit_usage (EXIT_FAILURE);
518 } /* while (strtok) */
523 } /* }}} int parse_group */
525 static int parse_group (const char *group) /* {{{ */
531 group_copy = strdup (group);
532 if (group_copy == NULL)
538 while ((g = strtok (dummy, ",")) != NULL)
544 if (strcasecmp ("host", g) == 0)
546 else if (strcasecmp ("plugin", g) == 0)
548 else if ((strcasecmp ("plugin_instance", g) == 0)
549 || (strcasecmp ("plugininstance", g) == 0)
550 || (strcasecmp ("pinst", g) == 0))
552 else if (strcasecmp ("type", g) == 0)
554 else if ((strcasecmp ("type_instance", g) == 0)
555 || (strcasecmp ("typeinstance", g) == 0)
556 || (strcasecmp ("tinst", g) == 0))
560 fprintf (stderr, "Unknown grouping field: \"%s\"\n", g);
561 exit_usage (EXIT_FAILURE);
564 grouping |= 1 << pos;
565 } /* while (strtok) */
570 } /* }}} int parse_group */
572 static int parse_arg (const char *arg) /* {{{ */
576 else if (strncasecmp ("host=", arg, strlen ("host=")) == 0)
577 re_host = arg + strlen ("host=");
578 else if (strncasecmp ("plugin=", arg, strlen ("plugin=")) == 0)
579 re_plugin = arg + strlen ("plugin=");
580 else if (strncasecmp ("plugin_instance=", arg, strlen ("plugin_instance=")) == 0)
581 re_plugin_instance = arg + strlen ("plugin_instance=");
582 else if (strncasecmp ("type=", arg, strlen ("type=")) == 0)
583 re_type = arg + strlen ("type=");
584 else if (strncasecmp ("type_instance=", arg, strlen ("type_instance=")) == 0)
585 re_type_instance = arg + strlen ("type_instance=");
588 else if (strncasecmp ("group=", arg, strlen ("group=")) == 0)
589 return (parse_group (arg + strlen ("group=")));
592 else if (strncasecmp ("aggregate=", arg, strlen ("aggregate=")) == 0)
593 return (parse_aggregate (arg + strlen ("aggregate=")));
595 /* Some alternative spellings to make it easier to guess a working argument
597 else if (strncasecmp ("hostname=", arg, strlen ("hostname=")) == 0)
598 re_host = arg + strlen ("hostname=");
599 else if (strncasecmp ("plugininstance=", arg, strlen ("plugininstance=")) == 0)
600 re_plugin_instance = arg + strlen ("plugininstance=");
601 else if (strncasecmp ("typeinstance=", arg, strlen ("typeinstance=")) == 0)
602 re_type_instance = arg + strlen ("typeinstance=");
603 else if (strncasecmp ("pinst=", arg, strlen ("pinst=")) == 0)
604 re_plugin_instance = arg + strlen ("pinst=");
605 else if (strncasecmp ("tinst=", arg, strlen ("tinst=")) == 0)
606 re_type_instance = arg + strlen ("tinst=");
607 else if (strncasecmp ("aggr=", arg, strlen ("aggr=")) == 0)
608 return (parse_aggregate (arg + strlen ("aggr=")));
610 /* Don't know what that is ... */
613 fprintf (stderr, "Unknown argument: \"%s\"\n", arg);
614 exit_usage (EXIT_FAILURE);
618 } /* }}} int parse_arg */
620 int show (lcc_connection_t *c, int argc, char **argv) /* {{{ */
626 for (i = 1; i < argc; i++)
628 status = parse_arg (argv[i]);
629 /* parse_arg calls exit_usage() on error. */
630 assert (status == 0);
633 if ((grouping == 0) && (aggregation_types_num > 0))
635 fprintf (stderr, "One or more aggregations were specified, but no fields "
636 "were selected for grouping values. Please use the ""\"group=...\" "
638 exit_usage (EXIT_FAILURE);
640 else if ((grouping != 0) && (aggregation_types_num == 0))
642 fprintf (stderr, "One or more fields were specified for grouping but no "
643 "aggregation was given. Please use the \"aggregate=...\" option.\n");
644 exit_usage (EXIT_FAILURE);
647 status = read_data (c);
651 status = write_data ();
655 for (j = 0; j < aggregation_groups_num; j++)
656 free (aggregation_groups[j].name);
657 free (aggregation_groups);
658 free (aggregation_types);
663 /* vim: set sw=2 ts=2 tw=78 expandtab fdm=marker : */