b7c3ffaf267bd019baccd92fb622909af9a3d3a9
[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 char *hostname_g = NULL;
48
49 static range_t range_critical_g;
50 static range_t range_warning_g;
51 static int consolitation_g = CON_NONE;
52
53 static char **match_ds_g = NULL;
54 static int    match_ds_num_g = 0;
55
56 static int ignore_ds (const char *name)
57 {
58         int i;
59
60         if (match_ds_g == NULL)
61                 return (0);
62
63         for (i = 0; i < match_ds_num_g; i++)
64                 if (strcasecmp (match_ds_g[i], name) == 0)
65                         return (0);
66
67         return (1);
68 } /* int ignore_ds */
69
70 static void parse_range (char *string, range_t *range)
71 {
72         char *min_ptr;
73         char *max_ptr;
74
75         if (*string == '@')
76         {
77                 range->invert = 1;
78                 string++;
79         }
80
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         }
93
94         assert (max_ptr != NULL);
95
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);
104
105         if ((*max_ptr == '\0') || (*max_ptr == '~'))
106                 range->max = NAN;
107         else
108                 range->max = atof (max_ptr);
109 } /* void parse_range */
110
111 int match_range (range_t *range, double value)
112 {
113         int ret = 0;
114
115         if (!isnan (range->min) && (range->min > value))
116                 ret = 1;
117         if (!isnan (range->max) && (range->max < value))
118                 ret = 1;
119
120         return (((ret - range->invert) == 0) ? 0 : 1);
121 }
122
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];
131
132         int values_num;
133         double *values;
134         char **values_names;
135
136         int i;
137
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         }
145
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);
150
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         }
158
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         }
167
168         fprintf (fh, "GETVAL %s/%s\n", hostname_g, value_string_g);
169         fflush (fh);
170
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;
179
180         values_num = atoi (buffer);
181         if (values_num < 1)
182                 return (-1);
183
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         }
191
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         }
200
201         {
202                 char *ptr = strchr (buffer, ' ') + 1;
203                 char *key;
204                 char *value;
205
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++;
214
215                         if (ignore_ds (key) != 0)
216                                 continue;
217
218                         values_names[i] = strdup (key);
219                         values[i] = atof (value);
220
221                         i++;
222                         if (i >= values_num)
223                                 break;
224                 }
225                 values_num = i;
226         }
227
228         *ret_values_num = values_num;
229         *ret_values = values;
230         *ret_values_names = values_names;
231
232         return (0);
233 } /* int get_values */
234
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 */
260
261 int do_check_con_none (int values_num, double *values, char **values_names)
262 {
263         int i;
264
265         int num_critical = 0;
266         int num_warning  = 0;
267         int num_okay = 0;
268
269         for (i = 0; i < values_num; i++)
270         {
271                 if (isnan (values[i]))
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         }
280
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         }
298
299         return (RET_UNKNOWN);
300 } /* int do_check_con_none */
301
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;
307
308         total = 0.0;
309         total_num = 0;
310         for (i = 0; i < values_num; i++)
311         {
312                 if (!isnan (values[i]))
313                 {
314                         total += values[i];
315                         total_num++;
316                 }
317         }
318
319         if (total_num == 0)
320         {
321                 printf ("WARNING: No defined values found\n");
322                 return (RET_WARNING);
323         }
324
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         }
343
344         return (RET_UNKNOWN);
345 } /* int do_check_con_average */
346
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;
352
353         total = 0.0;
354         total_num = 0;
355         for (i = 0; i < values_num; i++)
356         {
357                 if (!isnan (values[i]))
358                 {
359                         total += values[i];
360                         total_num++;
361                 }
362         }
363
364         if (total_num == 0)
365         {
366                 printf ("WARNING: No defined values found\n");
367                 return (RET_WARNING);
368         }
369
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         }
385
386         return (RET_UNKNOWN);
387 } /* int do_check_con_sum */
388
389 int do_check (void)
390 {
391         double  *values;
392         char   **values_names;
393         int      values_num;
394
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         }
400
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));
407
408         free (values);
409         free (values_names);
410
411         return (RET_UNKNOWN);
412 }
413
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;
419
420         range_warning_g.min = NAN;
421         range_warning_g.max = NAN;
422         range_warning_g.invert = 0;
423
424         while (42)
425         {
426                 int c;
427
428                 c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
429                 if (c < 0)
430                         break;
431
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         }
486
487         if ((socket_file_g == NULL) || (value_string_g == NULL)
488                         || (hostname_g == NULL))
489                 usage (argv[0]);
490
491         return (do_check ());
492 } /* int main */