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;
93 static lcc_identifier_t *selector;
95 static int *aggregation_types = NULL;
96 static size_t aggregation_types_num = 0;
98 static aggregation_group_t *aggregation_groups = NULL;
99 static size_t aggregation_groups_num = 0;
104 static int parse_aggr_type (const char *type) /* {{{ */
108 else if (strcasecmp ("count", type) == 0)
109 return (AGGR_TYPE_COUNT);
110 else if ((strcasecmp ("min", type) == 0)
111 || (strcasecmp ("minimum", type) == 0))
112 return (AGGR_TYPE_MIN);
113 else if ((strcasecmp ("max", type) == 0)
114 || (strcasecmp ("maximum", type) == 0))
115 return (AGGR_TYPE_MAX);
116 else if ((strcasecmp ("avg", type) == 0)
117 || (strcasecmp ("average", type) == 0))
118 return (AGGR_TYPE_AVG);
119 else if (strcasecmp ("sum", type) == 0)
120 return (AGGR_TYPE_SUM);
121 else if ((strcasecmp ("sdev", type) == 0)
122 || (strcasecmp ("stddev", type) == 0))
123 return (AGGR_TYPE_SDEV);
126 } /* }}} int parse_aggr_type */
128 static const char *aggr_type_to_string (int type) /* {{{ */
132 case AGGR_TYPE_COUNT: return ("Count");
133 case AGGR_TYPE_MIN: return ("Min");
134 case AGGR_TYPE_MAX: return ("Max");
135 case AGGR_TYPE_AVG: return ("Average");
136 case AGGR_TYPE_SUM: return ("Sum");
137 case AGGR_TYPE_SDEV: return ("Std. Dev.");
141 } /* }}} const char *aggr_type_to_string */
143 static int aggregation_type_add (const char *str_type) /* {{{ */
149 type = parse_aggr_type (str_type);
152 fprintf (stderr, "ERROR: \"%s\" is not a known aggregation function.\n",
157 /* Check for duplicate definitions */
158 for (i = 0; i < aggregation_types_num; i++)
160 if (aggregation_types[i] == type)
162 fprintf (stderr, "ERROR: Multiple aggregations with type \"%s\" "
163 "defined.\n", str_type);
168 tmp = realloc (aggregation_types,
169 (aggregation_types_num + 1) * sizeof (*aggregation_types));
172 aggregation_types = tmp;
173 aggregation_types[aggregation_types_num] = type;
174 aggregation_types_num++;
177 } /* }}} int aggregation_type_add */
179 static int group_name_from_ident (const lcc_identifier_t *selector, /* {{{ */
180 const lcc_identifier_t *identifier,
181 char *buffer, size_t buffer_size)
183 if ((selector == NULL)
184 || (identifier == NULL)
185 || (buffer == NULL) || (buffer_size < 2))
188 /* Check if there is no "grouping" wildcard. If there isn't, return "all" as
189 * the default value. */
190 if ((strcmp ("+", selector->host) != 0)
191 && (strcmp ("+", selector->plugin) != 0)
192 && (strcmp ("+", selector->plugin_instance) != 0)
193 && (strcmp ("+", selector->type) != 0)
194 && (strcmp ("+", selector->type_instance) != 0))
196 /* There is no wildcard at all => use the identifier. */
197 if ((strcmp ("*", selector->host) != 0)
198 && (strcmp ("*", selector->plugin) != 0)
199 && (strcmp ("*", selector->plugin_instance) != 0)
200 && (strcmp ("*", selector->type) != 0)
201 && (strcmp ("*", selector->type_instance) != 0))
202 lcc_identifier_to_string (/* connection = */ NULL,
203 buffer, buffer_size, identifier);
204 else /* there's wildcards but no grouping */
205 strncpy (buffer, "all", buffer_size);
206 buffer[buffer_size - 1] = 0;
210 memset (buffer, 0, buffer_size);
212 #define COPY_FIELD(field) do { \
213 if (strcmp ("+", selector->field) != 0) \
215 if (buffer[0] == 0) \
216 strncpy (buffer, identifier->field, buffer_size); \
219 char tmp[buffer_size]; \
220 snprintf (tmp, buffer_size, "%s/%s", buffer, identifier->field); \
221 memcpy (buffer, tmp, buffer_size); \
223 buffer[buffer_size - 1] = 0; \
228 COPY_FIELD (plugin_instance);
230 COPY_FIELD (type_instance);
235 } /* }}} int group_name_from_ident */
237 static _Bool ident_matches_selector (const lcc_identifier_t *selector, /* {{{ */
238 const lcc_identifier_t *identifier)
240 if ((selector == NULL) || (identifier == NULL))
243 if ((strcmp (identifier->host, selector->host) != 0)
244 && (strcmp ("*", selector->host) != 0)
245 && (strcmp ("+", selector->host) != 0))
248 if ((strcmp (identifier->plugin, selector->plugin) != 0)
249 && (strcmp ("*", selector->plugin) != 0)
250 && (strcmp ("+", selector->plugin) != 0))
253 if ((strcmp (identifier->plugin_instance, selector->plugin_instance) != 0)
254 && (strcmp ("*", selector->plugin_instance) != 0)
255 && (strcmp ("+", selector->plugin_instance) != 0))
258 if ((strcmp (identifier->type, selector->type) != 0)
259 && (strcmp ("*", selector->type) != 0)
260 && (strcmp ("+", selector->type) != 0))
263 if ((strcmp (identifier->type_instance, selector->type_instance) != 0)
264 && (strcmp ("*", selector->type_instance) != 0)
265 && (strcmp ("+", selector->type_instance) != 0))
269 } /* }}} _Bool ident_matches_selector */
271 static aggregation_group_t *aggregation_get_group ( const lcc_identifier_t *identifier) /* {{{ */
273 char group_name[LCC_NAME_LEN];
274 aggregation_group_t *g;
278 if (identifier == NULL)
281 status = group_name_from_ident (selector, identifier,
282 group_name, sizeof (group_name));
286 for (i = 0; i < aggregation_groups_num; i++)
287 if (strcmp (group_name, aggregation_groups[i].name) == 0)
288 return (aggregation_groups + i);
290 g = realloc (aggregation_groups,
291 (aggregation_groups_num + 1) * sizeof (*aggregation_groups));
294 aggregation_groups = g;
295 g = aggregation_groups + aggregation_groups_num;
297 memset (g, 0, sizeof (*g));
298 g->name = strdup (group_name);
305 g->sum_of_squares = NAN;
307 aggregation_groups_num++;
309 } /* }}} aggregation_group_t *aggregation_get_group */
311 static int aggregation_add_value (const lcc_identifier_t *identifier, /* {{{ */
314 aggregation_group_t *g;
316 if (identifier == NULL)
319 g = aggregation_get_group (identifier);
328 g->sum_of_squares = value * value;
336 if (isnan (g->min) || (g->min > value))
339 if (isnan (g->max) || (g->max < value))
347 if (isnan (g->sum_of_squares))
348 g->sum_of_squares = value * value;
350 g->sum_of_squares += value * value;
355 } /* }}} int aggregation_add_value */
357 static int read_data (lcc_connection_t *c) /* {{{ */
359 lcc_identifier_t *ret_ident = NULL;
360 size_t ret_ident_num = 0;
365 status = lcc_listval (c, &ret_ident, &ret_ident_num);
368 fprintf (stderr, "ERROR: lcc_listval: %s\n", lcc_strerror (c));
371 assert ((ret_ident != NULL) || (ret_ident_num == 0));
373 /* Iterate over all returned identifiers and figure out which ones are
374 * interesting, i.e. match a selector in an aggregation. */
375 for (i = 0; i < ret_ident_num; ++i)
377 size_t ret_values_num = 0;
378 gauge_t *ret_values = NULL;
380 if (!ident_matches_selector (selector, ret_ident + i))
383 status = lcc_getval (c, ret_ident + i,
384 &ret_values_num, &ret_values, /* values_names = */ NULL);
387 fprintf (stderr, "ERROR: lcc_getval: %s\n", lcc_strerror (c));
390 assert (ret_values != NULL);
392 /* FIXME: What to do with multiple data sources values? */
393 aggregation_add_value (ret_ident + i, ret_values[0]);
396 } /* for (ret_ident) */
401 } /* }}} int read_data */
403 static int print_horizontal_line (int name_len_max) /* {{{ */
410 for (i = 0; i < name_len_max; i++)
415 for (j = 0; j < aggregation_types_num; j++)
416 printf ("------------+");
421 } /* }}} int print_horizontal_line */
423 static int write_data (void) /* {{{ */
425 int name_len_max = 4;
428 for (i = 0; i < aggregation_groups_num; i++)
430 int name_len = (int) strlen (aggregation_groups[i].name);
431 if (name_len_max < name_len)
432 name_len_max = name_len;
435 print_horizontal_line (name_len_max);
436 printf ("! %-*s !", name_len_max, "Name");
437 for (i = 0; i < aggregation_types_num; i++)
438 printf (" %10s !", aggr_type_to_string (aggregation_types[i]));
440 print_horizontal_line (name_len_max);
442 for (i = 0; i < aggregation_groups_num; i++)
446 aggregation_group_t *g = aggregation_groups + i;
448 printf ("! %-*s !", name_len_max, g->name);
450 for (j = 0; j < aggregation_types_num; j++)
452 int type = aggregation_types[j];
455 if (type == AGGR_TYPE_COUNT)
456 value = (double) g->num;
457 else if (type == AGGR_TYPE_MIN)
459 else if (type == AGGR_TYPE_MAX)
461 else if (type == AGGR_TYPE_SUM)
463 else if ((type == AGGR_TYPE_AVG)
465 value = g->sum / ((double) g->num);
466 else if (type == AGGR_TYPE_SDEV)
474 - ((g->sum * g->sum) / ((double) g->num))
476 / ((double) (g->num - 1)));
479 printf (" %10g !", value);
485 print_horizontal_line (name_len_max);
488 } /* }}} int write_data */
490 __attribute__((noreturn))
491 static void exit_usage (int status) /* {{{ */
493 printf ("Usage: collectdctl show <selector> <aggregation> "
494 "[<aggregation> ...]\n"
497 " A selector is an identifier, where each part may be replaced "
508 } /* }}} void exit_usage */
510 int show (lcc_connection_t *c, int argc, char **argv) /* {{{ */
512 lcc_identifier_t tmp;
518 exit_usage (EXIT_FAILURE);
520 memset (&tmp, 0, sizeof (tmp));
521 status = lcc_string_to_identifier (c, &tmp, argv[1]);
526 for (i = 2; i < argc; i++)
527 aggregation_type_add (argv[i]);
529 status = read_data (c);
533 status = write_data ();
537 for (j = 0; j < aggregation_groups_num; j++)
538 free (aggregation_groups[j].name);
539 free (aggregation_groups);
540 free (aggregation_types);
545 /* vim: set sw=2 ts=2 tw=78 expandtab fdm=marker : */