1 #include "config.h"
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <stdio.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <assert.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
13 /*
14 * This is copied directly from collectd.h. Make changes there!
15 */
16 #if NAN_STATIC_DEFAULT
17 # include <math.h>
18 /* #endif NAN_STATIC_DEFAULT*/
19 #elif NAN_STATIC_ISOC
20 # ifndef __USE_ISOC99
21 # define DISABLE_ISOC99 1
22 # define __USE_ISOC99 1
23 # endif /* !defined(__USE_ISOC99) */
24 # include <math.h>
25 # if DISABLE_ISOC99
26 # undef DISABLE_ISOC99
27 # undef __USE_ISOC99
28 # endif /* DISABLE_ISOC99 */
29 /* #endif NAN_STATIC_ISOC */
30 #elif NAN_ZERO_ZERO
31 # include <math.h>
32 # ifdef NAN
33 # undef NAN
34 # endif
35 # define NAN (0.0 / 0.0)
36 # ifndef isnan
37 # define isnan(f) ((f) != (f))
38 # endif /* !defined(isnan) */
39 #endif /* NAN_ZERO_ZERO */
41 #define RET_OKAY 0
42 #define RET_WARNING 1
43 #define RET_CRITICAL 2
44 #define RET_UNKNOWN 3
46 #define CON_NONE 0
47 #define CON_AVERAGE 1
48 #define CON_SUM 2
50 struct range_s
51 {
52 double min;
53 double max;
54 int invert;
55 };
56 typedef struct range_s range_t;
58 extern char *optarg;
59 extern int optind, opterr, optopt;
61 static char *socket_file_g = NULL;
62 static char *value_string_g = NULL;
63 static char *hostname_g = NULL;
65 static range_t range_critical_g;
66 static range_t range_warning_g;
67 static int consolitation_g = CON_NONE;
69 static char **match_ds_g = NULL;
70 static int match_ds_num_g = 0;
72 static int ignore_ds (const char *name)
73 {
74 int i;
76 if (match_ds_g == NULL)
77 return (0);
79 for (i = 0; i < match_ds_num_g; i++)
80 if (strcasecmp (match_ds_g[i], name) == 0)
81 return (0);
83 return (1);
84 } /* int ignore_ds */
86 static void parse_range (char *string, range_t *range)
87 {
88 char *min_ptr;
89 char *max_ptr;
91 if (*string == '@')
92 {
93 range->invert = 1;
94 string++;
95 }
97 max_ptr = strchr (string, ':');
98 if (max_ptr == NULL)
99 {
100 min_ptr = NULL;
101 max_ptr = string;
102 }
103 else
104 {
105 min_ptr = string;
106 *max_ptr = '\0';
107 max_ptr++;
108 }
110 assert (max_ptr != NULL);
112 /* `10' == `0:10' */
113 if (min_ptr == NULL)
114 range->min = 0.0;
115 /* :10 == ~:10 == -inf:10 */
116 else if ((*min_ptr == '\0') || (*min_ptr == '~'))
117 range->min = NAN;
118 else
119 range->min = atof (min_ptr);
121 if ((*max_ptr == '\0') || (*max_ptr == '~'))
122 range->max = NAN;
123 else
124 range->max = atof (max_ptr);
125 } /* void parse_range */
127 int match_range (range_t *range, double value)
128 {
129 int ret = 0;
131 if (!isnan (range->min) && (range->min > value))
132 ret = 1;
133 if (!isnan (range->max) && (range->max < value))
134 ret = 1;
136 return (((ret - range->invert) == 0) ? 0 : 1);
137 }
139 static int get_values (int *ret_values_num, double **ret_values,
140 char ***ret_values_names)
141 {
142 struct sockaddr_un sa;
143 int status;
144 int fd;
145 FILE *fh_in, *fh_out;
146 char buffer[4096];
148 int values_num;
149 double *values;
150 char **values_names;
152 int i;
153 int j;
155 fd = socket (PF_UNIX, SOCK_STREAM, 0);
156 if (fd < 0)
157 {
158 fprintf (stderr, "socket failed: %s\n",
159 strerror (errno));
160 return (-1);
161 }
163 memset (&sa, '\0', sizeof (sa));
164 sa.sun_family = AF_UNIX;
165 strncpy (sa.sun_path, socket_file_g,
166 sizeof (sa.sun_path) - 1);
168 status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
169 if (status != 0)
170 {
171 fprintf (stderr, "connect failed: %s\n",
172 strerror (errno));
173 return (-1);
174 }
176 fh_in = fdopen (fd, "r");
177 if (fh_in == NULL)
178 {
179 fprintf (stderr, "fdopen failed: %s\n",
180 strerror (errno));
181 close (fd);
182 return (-1);
183 }
185 fh_out = fdopen (fd, "w");
186 if (fh_out == NULL)
187 {
188 fprintf (stderr, "fdopen failed: %s\n",
189 strerror (errno));
190 fclose (fh_in);
191 return (-1);
192 }
194 fprintf (fh_out, "GETVAL %s/%s\n", hostname_g, value_string_g);
195 fflush (fh_out);
197 if (fgets (buffer, sizeof (buffer), fh_in) == NULL)
198 {
199 fprintf (stderr, "fgets failed: %s\n",
200 strerror (errno));
201 fclose (fh_in);
202 fclose (fh_out);
203 return (-1);
204 }
206 {
207 char *ptr = strchr (buffer, ' ');
209 if (ptr != NULL)
210 *ptr = '\0';
212 values_num = atoi (buffer);
213 if (values_num < 1)
214 return (-1);
215 }
217 values = (double *) malloc (values_num * sizeof (double));
218 if (values == NULL)
219 {
220 fprintf (stderr, "malloc failed: %s\n",
221 strerror (errno));
222 return (-1);
223 }
225 values_names = (char **) malloc (values_num * sizeof (char *));
226 if (values_names == NULL)
227 {
228 fprintf (stderr, "malloc failed: %s\n",
229 strerror (errno));
230 free (values);
231 return (-1);
232 }
233 memset (values_names, 0, values_num * sizeof (char *));
235 i = 0; /* index of the values returned by the server */
236 j = 0; /* number of values in `values_names' and `values' */
237 while (fgets (buffer, sizeof (buffer), fh_in) != NULL)
238 {
239 do /* while (0) */
240 {
241 char *key;
242 char *value;
243 char *endptr;
245 key = buffer;
247 value = strchr (key, '=');
248 if (value == NULL)
249 {
250 fprintf (stderr, "Cannot parse line: %s\n", buffer);
251 break;
252 }
253 *value = 0;
254 value++;
256 if (ignore_ds (key) != 0)
257 break;
259 endptr = NULL;
260 errno = 0;
261 values[j] = strtod (value, &endptr);
262 if ((endptr == value) || (errno != 0))
263 {
264 fprintf (stderr, "Could not parse buffer "
265 "as number: %s\n", value);
266 break;
267 }
269 values_names[j] = strdup (key);
270 if (values_names[j] == NULL)
271 {
272 fprintf (stderr, "strdup failed.\n");
273 break;
274 }
275 j++;
276 } while (0);
278 i++;
279 if (i >= values_num)
280 break;
281 }
282 /* Set `values_num' to the number of values actually stored in the
283 * array. */
284 values_num = j;
286 fclose (fh_in); fh_in = NULL; fd = -1;
287 fclose (fh_out); fh_out = NULL;
289 *ret_values_num = values_num;
290 *ret_values = values;
291 *ret_values_names = values_names;
293 return (0);
294 } /* int get_values */
296 static void usage (const char *name)
297 {
298 fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> <-H hostname> [options]\n"
299 "\n"
300 "Valid options are:\n"
301 " -s <socket> Path to collectd's UNIX-socket.\n"
302 " -n <v_spec> Value specification to get from collectd.\n"
303 " Format: `plugin-instance/type-instance'\n"
304 " -d <ds> Select the DS to examine. May be repeated to examine multiple\n"
305 " DSes. By default all DSes are used.\n"
306 " -g <consol> Method to use to consolidate several DSes.\n"
307 " Valid arguments are `none', `average' and `sum'\n"
308 " -H <host> Hostname to query the values for.\n"
309 " -c <range> Critical range\n"
310 " -w <range> Warning range\n"
311 "\n"
312 "Consolidation functions:\n"
313 " none: Apply the warning- and critical-ranges to each data-source\n"
314 " individually.\n"
315 " average: Calculate the average of all matching DSes and apply the\n"
316 " warning- and critical-ranges to the calculated average.\n"
317 " sum: Apply the ranges to the sum of all DSes.\n"
318 "\n", name);
319 exit (1);
320 } /* void usage */
322 int do_check_con_none (int values_num, double *values, char **values_names)
323 {
324 int i;
326 int num_critical = 0;
327 int num_warning = 0;
328 int num_okay = 0;
330 for (i = 0; i < values_num; i++)
331 {
332 if (isnan (values[i]))
333 num_warning++;
334 else if (match_range (&range_critical_g, values[i]) != 0)
335 num_critical++;
336 else if (match_range (&range_warning_g, values[i]) != 0)
337 num_warning++;
338 else
339 num_okay++;
340 }
342 printf ("%i critical, %i warning, %i okay",
343 num_critical, num_warning, num_okay);
344 if (values_num > 0)
345 {
346 printf (" |");
347 for (i = 0; i < values_num; i++)
348 printf (" %s=%lf;;;;", values_names[i], values[i]);
349 }
350 printf ("\n");
352 if ((num_critical != 0) || (values_num == 0))
353 return (RET_CRITICAL);
354 else if (num_warning != 0)
355 return (RET_WARNING);
357 return (RET_OKAY);
358 } /* int do_check_con_none */
360 int do_check_con_average (int values_num, double *values, char **values_names)
361 {
362 int i;
363 double total;
364 int total_num;
365 double average;
367 total = 0.0;
368 total_num = 0;
369 for (i = 0; i < values_num; i++)
370 {
371 if (!isnan (values[i]))
372 {
373 total += values[i];
374 total_num++;
375 }
376 }
378 if (total_num == 0)
379 average = NAN;
380 else
381 average = total / total_num;
382 printf ("%lf average |", average);
383 for (i = 0; i < values_num; i++)
384 printf (" %s=%lf;;;;", values_names[i], values[i]);
386 if (total_num == 0)
387 return (RET_WARNING);
389 if (isnan (average)
390 || match_range (&range_critical_g, average))
391 return (RET_CRITICAL);
392 else if (match_range (&range_warning_g, average) != 0)
393 return (RET_WARNING);
395 return (RET_OKAY);
396 } /* int do_check_con_average */
398 int do_check_con_sum (int values_num, double *values, char **values_names)
399 {
400 int i;
401 double total;
402 int total_num;
404 total = 0.0;
405 total_num = 0;
406 for (i = 0; i < values_num; i++)
407 {
408 if (!isnan (values[i]))
409 {
410 total += values[i];
411 total_num++;
412 }
413 }
415 if (total_num == 0)
416 {
417 printf ("WARNING: No defined values found\n");
418 return (RET_WARNING);
419 }
421 if (match_range (&range_critical_g, total) != 0)
422 {
423 printf ("CRITICAL: Sum = %lf\n", total);
424 return (RET_CRITICAL);
425 }
426 else if (match_range (&range_warning_g, total) != 0)
427 {
428 printf ("WARNING: Sum = %lf\n", total);
429 return (RET_WARNING);
430 }
431 else
432 {
433 printf ("OKAY: Sum = %lf\n", total);
434 return (RET_OKAY);
435 }
437 return (RET_UNKNOWN);
438 } /* int do_check_con_sum */
440 int do_check (void)
441 {
442 double *values;
443 char **values_names;
444 int values_num;
446 if (get_values (&values_num, &values, &values_names) != 0)
447 {
448 fputs ("ERROR: Cannot get values from daemon\n", stdout);
449 return (RET_CRITICAL);
450 }
452 if (consolitation_g == CON_NONE)
453 return (do_check_con_none (values_num, values, values_names));
454 else if (consolitation_g == CON_AVERAGE)
455 return (do_check_con_average (values_num, values, values_names));
456 else if (consolitation_g == CON_SUM)
457 return (do_check_con_sum (values_num, values, values_names));
459 free (values);
460 free (values_names); /* FIXME? */
462 return (RET_UNKNOWN);
463 }
465 int main (int argc, char **argv)
466 {
467 range_critical_g.min = NAN;
468 range_critical_g.max = NAN;
469 range_critical_g.invert = 0;
471 range_warning_g.min = NAN;
472 range_warning_g.max = NAN;
473 range_warning_g.invert = 0;
475 while (42)
476 {
477 int c;
479 c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
480 if (c < 0)
481 break;
483 switch (c)
484 {
485 case 'c':
486 parse_range (optarg, &range_critical_g);
487 break;
488 case 'w':
489 parse_range (optarg, &range_warning_g);
490 break;
491 case 's':
492 socket_file_g = optarg;
493 break;
494 case 'n':
495 value_string_g = optarg;
496 break;
497 case 'H':
498 hostname_g = optarg;
499 break;
500 case 'g':
501 if (strcasecmp (optarg, "none") == 0)
502 consolitation_g = CON_NONE;
503 else if (strcasecmp (optarg, "average") == 0)
504 consolitation_g = CON_AVERAGE;
505 else if (strcasecmp (optarg, "sum") == 0)
506 consolitation_g = CON_SUM;
507 else
508 usage (argv[0]);
509 break;
510 case 'd':
511 {
512 char **tmp;
513 tmp = (char **) realloc (match_ds_g,
514 (match_ds_num_g + 1)
515 * sizeof (char *));
516 if (tmp == NULL)
517 {
518 fprintf (stderr, "realloc failed: %s\n",
519 strerror (errno));
520 return (RET_UNKNOWN);
521 }
522 match_ds_g = tmp;
523 match_ds_g[match_ds_num_g] = strdup (optarg);
524 if (match_ds_g[match_ds_num_g] == NULL)
525 {
526 fprintf (stderr, "strdup failed: %s\n",
527 strerror (errno));
528 return (RET_UNKNOWN);
529 }
530 match_ds_num_g++;
531 break;
532 }
533 default:
534 usage (argv[0]);
535 } /* switch (c) */
536 }
538 if ((socket_file_g == NULL) || (value_string_g == NULL)
539 || (hostname_g == NULL))
540 usage (argv[0]);
542 return (do_check ());
543 } /* int main */