collectd-nagios: Added an option to select certain DSes and updated the usage.
[collectd.git] / src / collectd-nagios.c
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <assert.h>
7
8 #include <sys/socket.h>
9 #include <sys/un.h>
10
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
24
25 #define RET_OKAY     0
26 #define RET_WARNING  1
27 #define RET_CRITICAL 2
28 #define RET_UNKNOWN  3
29
30 #define CON_NONE     0
31 #define CON_AVERAGE  1
32 #define CON_SUM      2
33
34 struct range_s
35 {
36         double min;
37         double max;
38         int    invert;
39 };
40 typedef struct range_s range_t;
41
42 extern char *optarg;
43 extern int optind, opterr, optopt;
44
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;
50
51 static char **match_ds_g = NULL;
52 static int    match_ds_num_g = 0;
53
54 static int ignore_ds (const char *name)
55 {
56         int i;
57
58         if (match_ds_g == NULL)
59                 return (0);
60
61         for (i = 0; i < match_ds_num_g; i++)
62                 if (strcasecmp (match_ds_g[i], name) == 0)
63                         return (0);
64
65         return (1);
66 } /* int ignore_ds */
67
68 static void parse_range (char *string, range_t *range)
69 {
70         char *min_ptr;
71         char *max_ptr;
72
73         if (*string == '@')
74         {
75                 range->invert = 1;
76                 string++;
77         }
78
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         }
91
92         assert (max_ptr != NULL);
93
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);
102
103         if ((*max_ptr == '\0') || (*max_ptr == '~'))
104                 range->max = NAN;
105         else
106                 range->max = atof (max_ptr);
107 } /* void parse_range */
108
109 int match_range (range_t *range, double value)
110 {
111         int ret = 0;
112
113         if ((range->min != NAN) && (range->min > value))
114                 ret = 1;
115         if ((range->max != NAN) && (range->max < value))
116                 ret = 1;
117
118         return (((ret - range->invert) == 0) ? 0 : 1);
119 }
120
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];
129
130         int values_num;
131         double *values;
132         char **values_names;
133
134         int i;
135
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         }
143
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);
148
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         }
156
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         }
165
166         fprintf (fh, "GETVAL %s\n", value_string_g);
167         fflush (fh);
168
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;
177
178         values_num = atoi (buffer);
179         if (values_num < 1)
180                 return (-1);
181
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         }
189
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         }
198
199         {
200                 char *ptr = strchr (buffer, ' ') + 1;
201                 char *key;
202                 char *value;
203
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++;
212
213                         if (ignore_ds (key) != 0)
214                                 continue;
215
216                         values_names[i] = strdup (key);
217                         values[i] = atof (value);
218
219                         i++;
220                         if (i >= values_num)
221                                 break;
222                 }
223                 values_num = i;
224         }
225
226         *ret_values_num = values_num;
227         *ret_values = values;
228         *ret_values_names = values_names;
229
230         return (0);
231 } /* int get_values */
232
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 */
257
258 int do_check_con_none (int values_num, double *values, char **values_names)
259 {
260         int i;
261
262         int num_critical = 0;
263         int num_warning  = 0;
264         int num_okay = 0;
265
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         }
277
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         }
295
296         return (RET_UNKNOWN);
297 } /* int do_check_con_none */
298
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;
304
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         }
315
316         if (total_num == 0)
317         {
318                 printf ("WARNING: No defined values found\n");
319                 return (RET_WARNING);
320         }
321
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         }
340
341         return (RET_UNKNOWN);
342 } /* int do_check_con_average */
343
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;
349
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         }
360
361         if (total_num == 0)
362         {
363                 printf ("WARNING: No defined values found\n");
364                 return (RET_WARNING);
365         }
366
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         }
382
383         return (RET_UNKNOWN);
384 } /* int do_check_con_sum */
385
386 int do_check (void)
387 {
388         double  *values;
389         char   **values_names;
390         int      values_num;
391
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         }
397
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));
404
405         free (values);
406         free (values_names);
407
408         return (RET_UNKNOWN);
409 }
410
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;
416
417         range_warning_g.min = NAN;
418         range_warning_g.max = NAN;
419         range_warning_g.invert = 0;
420
421         while (42)
422         {
423                 int c;
424
425                 c = getopt (argc, argv, "w:c:s:n:g:d:h");
426                 if (c < 0)
427                         break;
428
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         }
480
481         if ((socket_file_g == NULL) || (value_string_g == NULL))
482                 usage (argv[0]);
483
484         return (do_check ());
485 } /* int main */