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