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;
146 char buffer[4096];
148 int values_num;
149 double *values;
150 char **values_names;
152 int i;
154 fd = socket (PF_UNIX, SOCK_STREAM, 0);
155 if (fd < 0)
156 {
157 fprintf (stderr, "socket failed: %s\n",
158 strerror (errno));
159 return (-1);
160 }
162 memset (&sa, '\0', sizeof (sa));
163 sa.sun_family = AF_UNIX;
164 strncpy (sa.sun_path, socket_file_g,
165 sizeof (sa.sun_path) - 1);
167 status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
168 if (status != 0)
169 {
170 fprintf (stderr, "connect failed: %s\n",
171 strerror (errno));
172 return (-1);
173 }
175 fh = fdopen (fd, "r+");
176 if (fh == NULL)
177 {
178 fprintf (stderr, "fdopen failed: %s\n",
179 strerror (errno));
180 close (fd);
181 return (-1);
182 }
184 fprintf (fh, "GETVAL %s/%s\n", hostname_g, value_string_g);
185 fflush (fh);
187 if (fgets (buffer, sizeof (buffer), fh) == NULL)
188 {
189 fprintf (stderr, "fgets failed: %s\n",
190 strerror (errno));
191 close (fd);
192 return (-1);
193 }
194 close (fd); fd = -1;
196 values_num = atoi (buffer);
197 if (values_num < 1)
198 return (-1);
200 values = (double *) malloc (values_num * sizeof (double));
201 if (values == NULL)
202 {
203 fprintf (stderr, "malloc failed: %s\n",
204 strerror (errno));
205 return (-1);
206 }
208 values_names = (char **) malloc (values_num * sizeof (char *));
209 if (values_names == NULL)
210 {
211 fprintf (stderr, "malloc failed: %s\n",
212 strerror (errno));
213 free (values);
214 return (-1);
215 }
217 {
218 char *ptr = strchr (buffer, ' ') + 1;
219 char *key;
220 char *value;
222 i = 0;
223 while ((key = strtok (ptr, " \t")) != NULL)
224 {
225 ptr = NULL;
226 value = strchr (key, '=');
227 if (value == NULL)
228 continue;
229 *value = '\0'; value++;
231 if (ignore_ds (key) != 0)
232 continue;
234 values_names[i] = strdup (key);
235 values[i] = atof (value);
237 i++;
238 if (i >= values_num)
239 break;
240 }
241 values_num = i;
242 }
244 *ret_values_num = values_num;
245 *ret_values = values;
246 *ret_values_names = values_names;
248 return (0);
249 } /* int get_values */
251 static void usage (const char *name)
252 {
253 fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> <-H hostname> [options]\n"
254 "\n"
255 "Valid options are:\n"
256 " -s <socket> Path to collectd's UNIX-socket.\n"
257 " -n <v_spec> Value specification to get from collectd.\n"
258 " Format: `plugin-instance/type-instance'\n"
259 " -d <ds> Select the DS to examine. May be repeated to examine multiple\n"
260 " DSes. By default all DSes are used.\n"
261 " -g <consol> Method to use to consolidate several DSes.\n"
262 " Valid arguments are `none', `average' and `sum'\n"
263 " -H <host> Hostname to query the values for.\n"
264 " -c <range> Critical range\n"
265 " -w <range> Warning range\n"
266 "\n"
267 "Consolidation functions:\n"
268 " none: Apply the warning- and critical-ranges to each data-source\n"
269 " individually.\n"
270 " average: Calculate the average of all matching DSes and apply the\n"
271 " warning- and critical-ranges to the calculated average.\n"
272 " sum: Apply the ranges to the sum of all DSes.\n"
273 "\n", name);
274 exit (1);
275 } /* void usage */
277 int do_check_con_none (int values_num, double *values, char **values_names)
278 {
279 int i;
281 int num_critical = 0;
282 int num_warning = 0;
283 int num_okay = 0;
285 for (i = 0; i < values_num; i++)
286 {
287 if (isnan (values[i]))
288 num_warning++;
289 else if (match_range (&range_critical_g, values[i]) != 0)
290 num_critical++;
291 else if (match_range (&range_warning_g, values[i]) != 0)
292 num_warning++;
293 else
294 num_okay++;
295 }
297 if ((num_critical != 0) || (values_num == 0))
298 {
299 printf ("CRITICAL: %i critical, %i warning, %i okay\n",
300 num_critical, num_warning, num_okay);
301 return (RET_CRITICAL);
302 }
303 else if (num_warning != 0)
304 {
305 printf ("WARNING: %i warning, %i okay\n",
306 num_warning, num_okay);
307 return (RET_WARNING);
308 }
309 else
310 {
311 printf ("OKAY: %i okay\n", num_okay);
312 return (RET_OKAY);
313 }
315 return (RET_UNKNOWN);
316 } /* int do_check_con_none */
318 int do_check_con_average (int values_num, double *values, char **values_names)
319 {
320 int i;
321 double total;
322 int total_num;
324 total = 0.0;
325 total_num = 0;
326 for (i = 0; i < values_num; i++)
327 {
328 if (!isnan (values[i]))
329 {
330 total += values[i];
331 total_num++;
332 }
333 }
335 if (total_num == 0)
336 {
337 printf ("WARNING: No defined values found\n");
338 return (RET_WARNING);
339 }
341 if (match_range (&range_critical_g, total / total_num) != 0)
342 {
343 printf ("CRITICAL: Average = %lf\n",
344 (double) (total / total_num));
345 return (RET_CRITICAL);
346 }
347 else if (match_range (&range_warning_g, total / total_num) != 0)
348 {
349 printf ("WARNING: Average = %lf\n",
350 (double) (total / total_num));
351 return (RET_WARNING);
352 }
353 else
354 {
355 printf ("OKAY: Average = %lf\n",
356 (double) (total / total_num));
357 return (RET_OKAY);
358 }
360 return (RET_UNKNOWN);
361 } /* int do_check_con_average */
363 int do_check_con_sum (int values_num, double *values, char **values_names)
364 {
365 int i;
366 double total;
367 int total_num;
369 total = 0.0;
370 total_num = 0;
371 for (i = 0; i < values_num; i++)
372 {
373 if (!isnan (values[i]))
374 {
375 total += values[i];
376 total_num++;
377 }
378 }
380 if (total_num == 0)
381 {
382 printf ("WARNING: No defined values found\n");
383 return (RET_WARNING);
384 }
386 if (match_range (&range_critical_g, total) != 0)
387 {
388 printf ("CRITICAL: Sum = %lf\n", total);
389 return (RET_CRITICAL);
390 }
391 else if (match_range (&range_warning_g, total) != 0)
392 {
393 printf ("WARNING: Sum = %lf\n", total);
394 return (RET_WARNING);
395 }
396 else
397 {
398 printf ("OKAY: Sum = %lf\n", total);
399 return (RET_OKAY);
400 }
402 return (RET_UNKNOWN);
403 } /* int do_check_con_sum */
405 int do_check (void)
406 {
407 double *values;
408 char **values_names;
409 int values_num;
411 if (get_values (&values_num, &values, &values_names) != 0)
412 {
413 fputs ("ERROR: Cannot get values from daemon\n", stdout);
414 return (RET_CRITICAL);
415 }
417 if (consolitation_g == CON_NONE)
418 return (do_check_con_none (values_num, values, values_names));
419 else if (consolitation_g == CON_AVERAGE)
420 return (do_check_con_average (values_num, values, values_names));
421 else if (consolitation_g == CON_SUM)
422 return (do_check_con_sum (values_num, values, values_names));
424 free (values);
425 free (values_names);
427 return (RET_UNKNOWN);
428 }
430 int main (int argc, char **argv)
431 {
432 range_critical_g.min = NAN;
433 range_critical_g.max = NAN;
434 range_critical_g.invert = 0;
436 range_warning_g.min = NAN;
437 range_warning_g.max = NAN;
438 range_warning_g.invert = 0;
440 while (42)
441 {
442 int c;
444 c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
445 if (c < 0)
446 break;
448 switch (c)
449 {
450 case 'c':
451 parse_range (optarg, &range_critical_g);
452 break;
453 case 'w':
454 parse_range (optarg, &range_warning_g);
455 break;
456 case 's':
457 socket_file_g = optarg;
458 break;
459 case 'n':
460 value_string_g = optarg;
461 break;
462 case 'H':
463 hostname_g = optarg;
464 break;
465 case 'g':
466 if (strcasecmp (optarg, "none") == 0)
467 consolitation_g = CON_NONE;
468 else if (strcasecmp (optarg, "average") == 0)
469 consolitation_g = CON_AVERAGE;
470 else if (strcasecmp (optarg, "sum") == 0)
471 consolitation_g = CON_SUM;
472 else
473 usage (argv[0]);
474 break;
475 case 'd':
476 {
477 char **tmp;
478 tmp = (char **) realloc (match_ds_g,
479 (match_ds_num_g + 1)
480 * sizeof (char *));
481 if (tmp == NULL)
482 {
483 fprintf (stderr, "realloc failed: %s\n",
484 strerror (errno));
485 return (RET_UNKNOWN);
486 }
487 match_ds_g = tmp;
488 match_ds_g[match_ds_num_g] = strdup (optarg);
489 if (match_ds_g[match_ds_num_g] == NULL)
490 {
491 fprintf (stderr, "strdup failed: %s\n",
492 strerror (errno));
493 return (RET_UNKNOWN);
494 }
495 match_ds_num_g++;
496 break;
497 }
498 default:
499 usage (argv[0]);
500 } /* switch (c) */
501 }
503 if ((socket_file_g == NULL) || (value_string_g == NULL)
504 || (hostname_g == NULL))
505 usage (argv[0]);
507 return (do_check ());
508 } /* int main */