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