Include `config.h' from all files.
[collectd.git] / src / collectd-nagios.c
1 #include "config.h"
2
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <stdio.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <assert.h>
9
10 #include <sys/socket.h>
11 #include <sys/un.h>
12
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
26
27 #define RET_OKAY     0
28 #define RET_WARNING  1
29 #define RET_CRITICAL 2
30 #define RET_UNKNOWN  3
31
32 #define CON_NONE     0
33 #define CON_AVERAGE  1
34 #define CON_SUM      2
35
36 struct range_s
37 {
38         double min;
39         double max;
40         int    invert;
41 };
42 typedef struct range_s range_t;
43
44 extern char *optarg;
45 extern int optind, opterr, optopt;
46
47 static char *socket_file_g = NULL;
48 static char *value_string_g = NULL;
49 static char *hostname_g = NULL;
50
51 static range_t range_critical_g;
52 static range_t range_warning_g;
53 static int consolitation_g = CON_NONE;
54
55 static char **match_ds_g = NULL;
56 static int    match_ds_num_g = 0;
57
58 static int ignore_ds (const char *name)
59 {
60         int i;
61
62         if (match_ds_g == NULL)
63                 return (0);
64
65         for (i = 0; i < match_ds_num_g; i++)
66                 if (strcasecmp (match_ds_g[i], name) == 0)
67                         return (0);
68
69         return (1);
70 } /* int ignore_ds */
71
72 static void parse_range (char *string, range_t *range)
73 {
74         char *min_ptr;
75         char *max_ptr;
76
77         if (*string == '@')
78         {
79                 range->invert = 1;
80                 string++;
81         }
82
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         }
95
96         assert (max_ptr != NULL);
97
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);
106
107         if ((*max_ptr == '\0') || (*max_ptr == '~'))
108                 range->max = NAN;
109         else
110                 range->max = atof (max_ptr);
111 } /* void parse_range */
112
113 int match_range (range_t *range, double value)
114 {
115         int ret = 0;
116
117         if (!isnan (range->min) && (range->min > value))
118                 ret = 1;
119         if (!isnan (range->max) && (range->max < value))
120                 ret = 1;
121
122         return (((ret - range->invert) == 0) ? 0 : 1);
123 }
124
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];
133
134         int values_num;
135         double *values;
136         char **values_names;
137
138         int i;
139
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         }
147
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);
152
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         }
160
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         }
169
170         fprintf (fh, "GETVAL %s/%s\n", hostname_g, value_string_g);
171         fflush (fh);
172
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;
181
182         values_num = atoi (buffer);
183         if (values_num < 1)
184                 return (-1);
185
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         }
193
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         }
202
203         {
204                 char *ptr = strchr (buffer, ' ') + 1;
205                 char *key;
206                 char *value;
207
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++;
216
217                         if (ignore_ds (key) != 0)
218                                 continue;
219
220                         values_names[i] = strdup (key);
221                         values[i] = atof (value);
222
223                         i++;
224                         if (i >= values_num)
225                                 break;
226                 }
227                 values_num = i;
228         }
229
230         *ret_values_num = values_num;
231         *ret_values = values;
232         *ret_values_names = values_names;
233
234         return (0);
235 } /* int get_values */
236
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 */
262
263 int do_check_con_none (int values_num, double *values, char **values_names)
264 {
265         int i;
266
267         int num_critical = 0;
268         int num_warning  = 0;
269         int num_okay = 0;
270
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         }
282
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         }
300
301         return (RET_UNKNOWN);
302 } /* int do_check_con_none */
303
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;
309
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         }
320
321         if (total_num == 0)
322         {
323                 printf ("WARNING: No defined values found\n");
324                 return (RET_WARNING);
325         }
326
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         }
345
346         return (RET_UNKNOWN);
347 } /* int do_check_con_average */
348
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;
354
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         }
365
366         if (total_num == 0)
367         {
368                 printf ("WARNING: No defined values found\n");
369                 return (RET_WARNING);
370         }
371
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         }
387
388         return (RET_UNKNOWN);
389 } /* int do_check_con_sum */
390
391 int do_check (void)
392 {
393         double  *values;
394         char   **values_names;
395         int      values_num;
396
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         }
402
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));
409
410         free (values);
411         free (values_names);
412
413         return (RET_UNKNOWN);
414 }
415
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;
421
422         range_warning_g.min = NAN;
423         range_warning_g.max = NAN;
424         range_warning_g.invert = 0;
425
426         while (42)
427         {
428                 int c;
429
430                 c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
431                 if (c < 0)
432                         break;
433
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         }
488
489         if ((socket_file_g == NULL) || (value_string_g == NULL)
490                         || (hostname_g == NULL))
491                 usage (argv[0]);
492
493         return (do_check ());
494 } /* int main */