d3369b24a85ce012356a4709a602bee210c64838
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 range_t range_critical_g;
48 static range_t range_warning_g;
49 static int consolitation_g = CON_NONE;
51 static char **match_ds_g = NULL;
52 static int match_ds_num_g = 0;
54 static int ignore_ds (const char *name)
55 {
56 int i;
58 if (match_ds_g == NULL)
59 return (0);
61 for (i = 0; i < match_ds_num_g; i++)
62 if (strcasecmp (match_ds_g[i], name) == 0)
63 return (0);
65 return (1);
66 } /* int ignore_ds */
68 static void parse_range (char *string, range_t *range)
69 {
70 char *min_ptr;
71 char *max_ptr;
73 if (*string == '@')
74 {
75 range->invert = 1;
76 string++;
77 }
79 max_ptr = strchr (string, ':');
80 if (max_ptr == NULL)
81 {
82 min_ptr = NULL;
83 max_ptr = string;
84 }
85 else
86 {
87 min_ptr = string;
88 *max_ptr = '\0';
89 max_ptr++;
90 }
92 assert (max_ptr != NULL);
94 /* `10' == `0:10' */
95 if (min_ptr == NULL)
96 range->min = 0.0;
97 /* :10 == ~:10 == -inf:10 */
98 else if ((*min_ptr == '\0') || (*min_ptr == '~'))
99 range->min = NAN;
100 else
101 range->min = atof (min_ptr);
103 if ((*max_ptr == '\0') || (*max_ptr == '~'))
104 range->max = NAN;
105 else
106 range->max = atof (max_ptr);
107 } /* void parse_range */
109 int match_range (range_t *range, double value)
110 {
111 int ret = 0;
113 if ((range->min != NAN) && (range->min > value))
114 ret = 1;
115 if ((range->max != NAN) && (range->max < value))
116 ret = 1;
118 return (((ret - range->invert) == 0) ? 0 : 1);
119 }
121 static int get_values (int *ret_values_num, double **ret_values,
122 char ***ret_values_names)
123 {
124 struct sockaddr_un sa;
125 int status;
126 int fd;
127 FILE *fh;
128 char buffer[4096];
130 int values_num;
131 double *values;
132 char **values_names;
134 int i;
136 fd = socket (PF_UNIX, SOCK_STREAM, 0);
137 if (fd < 0)
138 {
139 fprintf (stderr, "socket failed: %s\n",
140 strerror (errno));
141 return (-1);
142 }
144 memset (&sa, '\0', sizeof (sa));
145 sa.sun_family = AF_UNIX;
146 strncpy (sa.sun_path, socket_file_g,
147 sizeof (sa.sun_path) - 1);
149 status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
150 if (status != 0)
151 {
152 fprintf (stderr, "connect failed: %s\n",
153 strerror (errno));
154 return (-1);
155 }
157 fh = fdopen (fd, "r+");
158 if (fh == NULL)
159 {
160 fprintf (stderr, "fdopen failed: %s\n",
161 strerror (errno));
162 close (fd);
163 return (-1);
164 }
166 fprintf (fh, "GETVAL %s\n", value_string_g);
167 fflush (fh);
169 if (fgets (buffer, sizeof (buffer), fh) == NULL)
170 {
171 fprintf (stderr, "fgets failed: %s\n",
172 strerror (errno));
173 close (fd);
174 return (-1);
175 }
176 close (fd); fd = -1;
178 values_num = atoi (buffer);
179 if (values_num < 1)
180 return (-1);
182 values = (double *) malloc (values_num * sizeof (double));
183 if (values == NULL)
184 {
185 fprintf (stderr, "malloc failed: %s\n",
186 strerror (errno));
187 return (-1);
188 }
190 values_names = (char **) malloc (values_num * sizeof (char *));
191 if (values_names == NULL)
192 {
193 fprintf (stderr, "malloc failed: %s\n",
194 strerror (errno));
195 free (values);
196 return (-1);
197 }
199 {
200 char *ptr = strchr (buffer, ' ') + 1;
201 char *key;
202 char *value;
204 i = 0;
205 while ((key = strtok (ptr, " \t")) != NULL)
206 {
207 ptr = NULL;
208 value = strchr (key, '=');
209 if (value == NULL)
210 continue;
211 *value = '\0'; value++;
213 if (ignore_ds (key) != 0)
214 continue;
216 values_names[i] = strdup (key);
217 values[i] = atof (value);
219 i++;
220 if (i >= values_num)
221 break;
222 }
223 values_num = i;
224 }
226 *ret_values_num = values_num;
227 *ret_values = values;
228 *ret_values_names = values_names;
230 return (0);
231 } /* int get_values */
233 static void usage (const char *name)
234 {
235 fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> [options]\n"
236 "\n"
237 "Valid options are:\n"
238 " -s <socket> Path to collectd's UNIX-socket.\n"
239 " -n <v_spec> Value specification to get from collectd.\n"
240 " Format: `plugin-instance/type-instance'\n"
241 " -d <ds> Select the DS to examine. May be repeated to examine multiple\n"
242 " DSes. By default all DSes are used.\n"
243 " -g <consol> Method to use to consolidate several DSes.\n"
244 " Valid arguments are `none', `average' and `sum'\n"
245 " -c <range> Critical range\n"
246 " -w <range> Warning range\n"
247 "\n"
248 "Consolidation functions:\n"
249 " none: Apply the warning- and critical-ranges to each data-source\n"
250 " individually.\n"
251 " average: Calculate the average of all matching DSes and apply the\n"
252 " warning- and critical-ranges to the calculated average.\n"
253 " sum: Apply the ranges to the sum of all DSes.\n"
254 "\n", name);
255 exit (1);
256 } /* void usage */
258 int do_check_con_none (int values_num, double *values, char **values_names)
259 {
260 int i;
262 int num_critical = 0;
263 int num_warning = 0;
264 int num_okay = 0;
266 for (i = 0; i < values_num; i++)
267 {
268 if (values[i] == NAN)
269 num_warning++;
270 else if (match_range (&range_critical_g, values[i]) != 0)
271 num_critical++;
272 else if (match_range (&range_warning_g, values[i]) != 0)
273 num_warning++;
274 else
275 num_okay++;
276 }
278 if ((num_critical != 0) || (values_num == 0))
279 {
280 printf ("CRITICAL: %i critical, %i warning, %i okay\n",
281 num_critical, num_warning, num_okay);
282 return (RET_CRITICAL);
283 }
284 else if (num_warning != 0)
285 {
286 printf ("WARNING: %i warning, %i okay\n",
287 num_warning, num_okay);
288 return (RET_WARNING);
289 }
290 else
291 {
292 printf ("OKAY: %i okay\n", num_okay);
293 return (RET_OKAY);
294 }
296 return (RET_UNKNOWN);
297 } /* int do_check_con_none */
299 int do_check_con_average (int values_num, double *values, char **values_names)
300 {
301 int i;
302 double total;
303 int total_num;
305 total = 0.0;
306 total_num = 0;
307 for (i = 0; i < values_num; i++)
308 {
309 if (values[i] != NAN)
310 {
311 total += values[i];
312 total_num++;
313 }
314 }
316 if (total_num == 0)
317 {
318 printf ("WARNING: No defined values found\n");
319 return (RET_WARNING);
320 }
322 if (match_range (&range_critical_g, total / total_num) != 0)
323 {
324 printf ("CRITICAL: Average = %lf\n",
325 (double) (total / total_num));
326 return (RET_CRITICAL);
327 }
328 else if (match_range (&range_warning_g, total / total_num) != 0)
329 {
330 printf ("WARNING: Average = %lf\n",
331 (double) (total / total_num));
332 return (RET_WARNING);
333 }
334 else
335 {
336 printf ("OKAY: Average = %lf\n",
337 (double) (total / total_num));
338 return (RET_OKAY);
339 }
341 return (RET_UNKNOWN);
342 } /* int do_check_con_average */
344 int do_check_con_sum (int values_num, double *values, char **values_names)
345 {
346 int i;
347 double total;
348 int total_num;
350 total = 0.0;
351 total_num = 0;
352 for (i = 0; i < values_num; i++)
353 {
354 if (values[i] != NAN)
355 {
356 total += values[i];
357 total_num++;
358 }
359 }
361 if (total_num == 0)
362 {
363 printf ("WARNING: No defined values found\n");
364 return (RET_WARNING);
365 }
367 if (match_range (&range_critical_g, total) != 0)
368 {
369 printf ("CRITICAL: Sum = %lf\n", total);
370 return (RET_CRITICAL);
371 }
372 else if (match_range (&range_warning_g, total) != 0)
373 {
374 printf ("WARNING: Sum = %lf\n", total);
375 return (RET_WARNING);
376 }
377 else
378 {
379 printf ("OKAY: Sum = %lf\n", total);
380 return (RET_OKAY);
381 }
383 return (RET_UNKNOWN);
384 } /* int do_check_con_sum */
386 int do_check (void)
387 {
388 double *values;
389 char **values_names;
390 int values_num;
392 if (get_values (&values_num, &values, &values_names) != 0)
393 {
394 fputs ("ERROR: Cannot get values from daemon\n", stdout);
395 return (RET_CRITICAL);
396 }
398 if (consolitation_g == CON_NONE)
399 return (do_check_con_none (values_num, values, values_names));
400 else if (consolitation_g == CON_AVERAGE)
401 return (do_check_con_average (values_num, values, values_names));
402 else if (consolitation_g == CON_SUM)
403 return (do_check_con_sum (values_num, values, values_names));
405 free (values);
406 free (values_names);
408 return (RET_UNKNOWN);
409 }
411 int main (int argc, char **argv)
412 {
413 range_critical_g.min = NAN;
414 range_critical_g.max = NAN;
415 range_critical_g.invert = 0;
417 range_warning_g.min = NAN;
418 range_warning_g.max = NAN;
419 range_warning_g.invert = 0;
421 while (42)
422 {
423 int c;
425 c = getopt (argc, argv, "w:c:s:n:g:d:h");
426 if (c < 0)
427 break;
429 switch (c)
430 {
431 case 'c':
432 parse_range (optarg, &range_critical_g);
433 break;
434 case 'w':
435 parse_range (optarg, &range_warning_g);
436 break;
437 case 's':
438 socket_file_g = optarg;
439 break;
440 case 'n':
441 value_string_g = optarg;
442 break;
443 case 'g':
444 if (strcasecmp (optarg, "none") == 0)
445 consolitation_g = CON_NONE;
446 else if (strcasecmp (optarg, "average") == 0)
447 consolitation_g = CON_AVERAGE;
448 else if (strcasecmp (optarg, "sum") == 0)
449 consolitation_g = CON_SUM;
450 else
451 usage (argv[0]);
452 break;
453 case 'd':
454 {
455 char **tmp;
456 tmp = (char **) realloc (match_ds_g,
457 (match_ds_num_g + 1)
458 * sizeof (char *));
459 if (tmp == NULL)
460 {
461 fprintf (stderr, "realloc failed: %s\n",
462 strerror (errno));
463 return (RET_UNKNOWN);
464 }
465 match_ds_g = tmp;
466 match_ds_g[match_ds_num_g] = strdup (optarg);
467 if (match_ds_g[match_ds_num_g] == NULL)
468 {
469 fprintf (stderr, "strdup failed: %s\n",
470 strerror (errno));
471 return (RET_UNKNOWN);
472 }
473 match_ds_num_g++;
474 break;
475 }
476 default:
477 usage (argv[0]);
478 } /* switch (c) */
479 }
481 if ((socket_file_g == NULL) || (value_string_g == NULL))
482 usage (argv[0]);
484 return (do_check ());
485 } /* int main */