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 weird macro cascade forces the glibc to define `NAN'. I don't know
15 * another way to solve this, so more intelligent solutions are welcome. -octo
16 */
17 #ifndef __USE_ISOC99
18 # define DISABLE__USE_ISOC99 1
19 # define __USE_ISOC99 1
20 #endif
21 #include <math.h>
22 #ifdef DISABLE__USE_ISOC99
23 # undef DISABLE__USE_ISOC99
24 # undef __USE_ISOC99
25 #endif
27 #define RET_OKAY 0
28 #define RET_WARNING 1
29 #define RET_CRITICAL 2
30 #define RET_UNKNOWN 3
32 #define CON_NONE 0
33 #define CON_AVERAGE 1
34 #define CON_SUM 2
36 struct range_s
37 {
38 double min;
39 double max;
40 int invert;
41 };
42 typedef struct range_s range_t;
44 extern char *optarg;
45 extern int optind, opterr, optopt;
47 static char *socket_file_g = NULL;
48 static char *value_string_g = NULL;
49 static char *hostname_g = NULL;
51 static range_t range_critical_g;
52 static range_t range_warning_g;
53 static int consolitation_g = CON_NONE;
55 static char **match_ds_g = NULL;
56 static int match_ds_num_g = 0;
58 static int ignore_ds (const char *name)
59 {
60 int i;
62 if (match_ds_g == NULL)
63 return (0);
65 for (i = 0; i < match_ds_num_g; i++)
66 if (strcasecmp (match_ds_g[i], name) == 0)
67 return (0);
69 return (1);
70 } /* int ignore_ds */
72 static void parse_range (char *string, range_t *range)
73 {
74 char *min_ptr;
75 char *max_ptr;
77 if (*string == '@')
78 {
79 range->invert = 1;
80 string++;
81 }
83 max_ptr = strchr (string, ':');
84 if (max_ptr == NULL)
85 {
86 min_ptr = NULL;
87 max_ptr = string;
88 }
89 else
90 {
91 min_ptr = string;
92 *max_ptr = '\0';
93 max_ptr++;
94 }
96 assert (max_ptr != NULL);
98 /* `10' == `0:10' */
99 if (min_ptr == NULL)
100 range->min = 0.0;
101 /* :10 == ~:10 == -inf:10 */
102 else if ((*min_ptr == '\0') || (*min_ptr == '~'))
103 range->min = NAN;
104 else
105 range->min = atof (min_ptr);
107 if ((*max_ptr == '\0') || (*max_ptr == '~'))
108 range->max = NAN;
109 else
110 range->max = atof (max_ptr);
111 } /* void parse_range */
113 int match_range (range_t *range, double value)
114 {
115 int ret = 0;
117 if (!isnan (range->min) && (range->min > value))
118 ret = 1;
119 if (!isnan (range->max) && (range->max < value))
120 ret = 1;
122 return (((ret - range->invert) == 0) ? 0 : 1);
123 }
125 static int get_values (int *ret_values_num, double **ret_values,
126 char ***ret_values_names)
127 {
128 struct sockaddr_un sa;
129 int status;
130 int fd;
131 FILE *fh;
132 char buffer[4096];
134 int values_num;
135 double *values;
136 char **values_names;
138 int i;
140 fd = socket (PF_UNIX, SOCK_STREAM, 0);
141 if (fd < 0)
142 {
143 fprintf (stderr, "socket failed: %s\n",
144 strerror (errno));
145 return (-1);
146 }
148 memset (&sa, '\0', sizeof (sa));
149 sa.sun_family = AF_UNIX;
150 strncpy (sa.sun_path, socket_file_g,
151 sizeof (sa.sun_path) - 1);
153 status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
154 if (status != 0)
155 {
156 fprintf (stderr, "connect failed: %s\n",
157 strerror (errno));
158 return (-1);
159 }
161 fh = fdopen (fd, "r+");
162 if (fh == NULL)
163 {
164 fprintf (stderr, "fdopen failed: %s\n",
165 strerror (errno));
166 close (fd);
167 return (-1);
168 }
170 fprintf (fh, "GETVAL %s/%s\n", hostname_g, value_string_g);
171 fflush (fh);
173 if (fgets (buffer, sizeof (buffer), fh) == NULL)
174 {
175 fprintf (stderr, "fgets failed: %s\n",
176 strerror (errno));
177 close (fd);
178 return (-1);
179 }
180 close (fd); fd = -1;
182 values_num = atoi (buffer);
183 if (values_num < 1)
184 return (-1);
186 values = (double *) malloc (values_num * sizeof (double));
187 if (values == NULL)
188 {
189 fprintf (stderr, "malloc failed: %s\n",
190 strerror (errno));
191 return (-1);
192 }
194 values_names = (char **) malloc (values_num * sizeof (char *));
195 if (values_names == NULL)
196 {
197 fprintf (stderr, "malloc failed: %s\n",
198 strerror (errno));
199 free (values);
200 return (-1);
201 }
203 {
204 char *ptr = strchr (buffer, ' ') + 1;
205 char *key;
206 char *value;
208 i = 0;
209 while ((key = strtok (ptr, " \t")) != NULL)
210 {
211 ptr = NULL;
212 value = strchr (key, '=');
213 if (value == NULL)
214 continue;
215 *value = '\0'; value++;
217 if (ignore_ds (key) != 0)
218 continue;
220 values_names[i] = strdup (key);
221 values[i] = atof (value);
223 i++;
224 if (i >= values_num)
225 break;
226 }
227 values_num = i;
228 }
230 *ret_values_num = values_num;
231 *ret_values = values;
232 *ret_values_names = values_names;
234 return (0);
235 } /* int get_values */
237 static void usage (const char *name)
238 {
239 fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> <-H hostname> [options]\n"
240 "\n"
241 "Valid options are:\n"
242 " -s <socket> Path to collectd's UNIX-socket.\n"
243 " -n <v_spec> Value specification to get from collectd.\n"
244 " Format: `plugin-instance/type-instance'\n"
245 " -d <ds> Select the DS to examine. May be repeated to examine multiple\n"
246 " DSes. By default all DSes are used.\n"
247 " -g <consol> Method to use to consolidate several DSes.\n"
248 " Valid arguments are `none', `average' and `sum'\n"
249 " -H <host> Hostname to query the values for.\n"
250 " -c <range> Critical range\n"
251 " -w <range> Warning range\n"
252 "\n"
253 "Consolidation functions:\n"
254 " none: Apply the warning- and critical-ranges to each data-source\n"
255 " individually.\n"
256 " average: Calculate the average of all matching DSes and apply the\n"
257 " warning- and critical-ranges to the calculated average.\n"
258 " sum: Apply the ranges to the sum of all DSes.\n"
259 "\n", name);
260 exit (1);
261 } /* void usage */
263 int do_check_con_none (int values_num, double *values, char **values_names)
264 {
265 int i;
267 int num_critical = 0;
268 int num_warning = 0;
269 int num_okay = 0;
271 for (i = 0; i < values_num; i++)
272 {
273 if (isnan (values[i]))
274 num_warning++;
275 else if (match_range (&range_critical_g, values[i]) != 0)
276 num_critical++;
277 else if (match_range (&range_warning_g, values[i]) != 0)
278 num_warning++;
279 else
280 num_okay++;
281 }
283 if ((num_critical != 0) || (values_num == 0))
284 {
285 printf ("CRITICAL: %i critical, %i warning, %i okay\n",
286 num_critical, num_warning, num_okay);
287 return (RET_CRITICAL);
288 }
289 else if (num_warning != 0)
290 {
291 printf ("WARNING: %i warning, %i okay\n",
292 num_warning, num_okay);
293 return (RET_WARNING);
294 }
295 else
296 {
297 printf ("OKAY: %i okay\n", num_okay);
298 return (RET_OKAY);
299 }
301 return (RET_UNKNOWN);
302 } /* int do_check_con_none */
304 int do_check_con_average (int values_num, double *values, char **values_names)
305 {
306 int i;
307 double total;
308 int total_num;
310 total = 0.0;
311 total_num = 0;
312 for (i = 0; i < values_num; i++)
313 {
314 if (!isnan (values[i]))
315 {
316 total += values[i];
317 total_num++;
318 }
319 }
321 if (total_num == 0)
322 {
323 printf ("WARNING: No defined values found\n");
324 return (RET_WARNING);
325 }
327 if (match_range (&range_critical_g, total / total_num) != 0)
328 {
329 printf ("CRITICAL: Average = %lf\n",
330 (double) (total / total_num));
331 return (RET_CRITICAL);
332 }
333 else if (match_range (&range_warning_g, total / total_num) != 0)
334 {
335 printf ("WARNING: Average = %lf\n",
336 (double) (total / total_num));
337 return (RET_WARNING);
338 }
339 else
340 {
341 printf ("OKAY: Average = %lf\n",
342 (double) (total / total_num));
343 return (RET_OKAY);
344 }
346 return (RET_UNKNOWN);
347 } /* int do_check_con_average */
349 int do_check_con_sum (int values_num, double *values, char **values_names)
350 {
351 int i;
352 double total;
353 int total_num;
355 total = 0.0;
356 total_num = 0;
357 for (i = 0; i < values_num; i++)
358 {
359 if (!isnan (values[i]))
360 {
361 total += values[i];
362 total_num++;
363 }
364 }
366 if (total_num == 0)
367 {
368 printf ("WARNING: No defined values found\n");
369 return (RET_WARNING);
370 }
372 if (match_range (&range_critical_g, total) != 0)
373 {
374 printf ("CRITICAL: Sum = %lf\n", total);
375 return (RET_CRITICAL);
376 }
377 else if (match_range (&range_warning_g, total) != 0)
378 {
379 printf ("WARNING: Sum = %lf\n", total);
380 return (RET_WARNING);
381 }
382 else
383 {
384 printf ("OKAY: Sum = %lf\n", total);
385 return (RET_OKAY);
386 }
388 return (RET_UNKNOWN);
389 } /* int do_check_con_sum */
391 int do_check (void)
392 {
393 double *values;
394 char **values_names;
395 int values_num;
397 if (get_values (&values_num, &values, &values_names) != 0)
398 {
399 fputs ("ERROR: Cannot get values from daemon\n", stdout);
400 return (RET_CRITICAL);
401 }
403 if (consolitation_g == CON_NONE)
404 return (do_check_con_none (values_num, values, values_names));
405 else if (consolitation_g == CON_AVERAGE)
406 return (do_check_con_average (values_num, values, values_names));
407 else if (consolitation_g == CON_SUM)
408 return (do_check_con_sum (values_num, values, values_names));
410 free (values);
411 free (values_names);
413 return (RET_UNKNOWN);
414 }
416 int main (int argc, char **argv)
417 {
418 range_critical_g.min = NAN;
419 range_critical_g.max = NAN;
420 range_critical_g.invert = 0;
422 range_warning_g.min = NAN;
423 range_warning_g.max = NAN;
424 range_warning_g.invert = 0;
426 while (42)
427 {
428 int c;
430 c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
431 if (c < 0)
432 break;
434 switch (c)
435 {
436 case 'c':
437 parse_range (optarg, &range_critical_g);
438 break;
439 case 'w':
440 parse_range (optarg, &range_warning_g);
441 break;
442 case 's':
443 socket_file_g = optarg;
444 break;
445 case 'n':
446 value_string_g = optarg;
447 break;
448 case 'H':
449 hostname_g = optarg;
450 break;
451 case 'g':
452 if (strcasecmp (optarg, "none") == 0)
453 consolitation_g = CON_NONE;
454 else if (strcasecmp (optarg, "average") == 0)
455 consolitation_g = CON_AVERAGE;
456 else if (strcasecmp (optarg, "sum") == 0)
457 consolitation_g = CON_SUM;
458 else
459 usage (argv[0]);
460 break;
461 case 'd':
462 {
463 char **tmp;
464 tmp = (char **) realloc (match_ds_g,
465 (match_ds_num_g + 1)
466 * sizeof (char *));
467 if (tmp == NULL)
468 {
469 fprintf (stderr, "realloc failed: %s\n",
470 strerror (errno));
471 return (RET_UNKNOWN);
472 }
473 match_ds_g = tmp;
474 match_ds_g[match_ds_num_g] = strdup (optarg);
475 if (match_ds_g[match_ds_num_g] == NULL)
476 {
477 fprintf (stderr, "strdup failed: %s\n",
478 strerror (errno));
479 return (RET_UNKNOWN);
480 }
481 match_ds_num_g++;
482 break;
483 }
484 default:
485 usage (argv[0]);
486 } /* switch (c) */
487 }
489 if ((socket_file_g == NULL) || (value_string_g == NULL)
490 || (hostname_g == NULL))
491 usage (argv[0]);
493 return (do_check ());
494 } /* int main */