collectd-nagios: Added a small program to perform Nagios-compatible checks on values.
[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 void parse_range (char *string, range_t *range)
52 {
53         char *min_ptr;
54         char *max_ptr;
55
56         if (*string == '@')
57         {
58                 range->invert = 1;
59                 string++;
60         }
61
62         max_ptr = strchr (string, ':');
63         if (max_ptr == NULL)
64         {
65                 min_ptr = NULL;
66                 max_ptr = string;
67         }
68         else
69         {
70                 min_ptr = string;
71                 *max_ptr = '\0';
72                 max_ptr++;
73         }
74
75         assert (max_ptr != NULL);
76
77         /* `10' == `0:10' */
78         if (min_ptr == NULL)
79                 range->min = 0.0;
80         /* :10 == ~:10 == -inf:10 */
81         else if ((*min_ptr == '\0') || (*min_ptr == '~'))
82                 range->min = NAN;
83         else
84                 range->min = atof (min_ptr);
85
86         if ((*max_ptr == '\0') || (*max_ptr == '~'))
87                 range->max = NAN;
88         else
89                 range->max = atof (max_ptr);
90 } /* void parse_range */
91
92 int match_range (range_t *range, double value)
93 {
94         int ret = 0;
95
96         if ((range->min != NAN) && (range->min > value))
97                 ret = 1;
98         if ((range->max != NAN) && (range->max < value))
99                 ret = 1;
100
101         return (((ret - range->invert) == 0) ? 0 : 1);
102 }
103
104 static int get_values (int *ret_values_num, double **ret_values,
105                 char ***ret_values_names)
106 {
107         struct sockaddr_un sa;
108         int status;
109         int fd;
110         FILE *fh;
111         char buffer[4096];
112
113         int values_num;
114         double *values;
115         char **values_names;
116
117         int i;
118
119         fd = socket (PF_UNIX, SOCK_STREAM, 0);
120         if (fd < 0)
121         {
122                 fprintf (stderr, "socket failed: %s\n",
123                                 strerror (errno));
124                 return (-1);
125         }
126
127         memset (&sa, '\0', sizeof (sa));
128         sa.sun_family = AF_UNIX;
129         strncpy (sa.sun_path, socket_file_g,
130                         sizeof (sa.sun_path) - 1);
131
132         status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
133         if (status != 0)
134         {
135                 fprintf (stderr, "connect failed: %s\n",
136                                 strerror (errno));
137                 return (-1);
138         }
139
140         fh = fdopen (fd, "r+");
141         if (fh == NULL)
142         {
143                 fprintf (stderr, "fdopen failed: %s\n",
144                                 strerror (errno));
145                 close (fd);
146                 return (-1);
147         }
148
149         fprintf (fh, "GETVAL %s\n", value_string_g);
150         fflush (fh);
151
152         if (fgets (buffer, sizeof (buffer), fh) == NULL)
153         {
154                 fprintf (stderr, "fgets failed: %s\n",
155                                 strerror (errno));
156                 close (fd);
157                 return (-1);
158         }
159         close (fd); fd = -1;
160
161         values_num = atoi (buffer);
162         if (values_num < 1)
163                 return (-1);
164
165         values = (double *) malloc (values_num * sizeof (double));
166         if (values == NULL)
167         {
168                 fprintf (stderr, "malloc failed: %s\n",
169                                 strerror (errno));
170                 return (-1);
171         }
172
173         values_names = (char **) malloc (values_num * sizeof (char *));
174         if (values_names == NULL)
175         {
176                 fprintf (stderr, "malloc failed: %s\n",
177                                 strerror (errno));
178                 free (values);
179                 return (-1);
180         }
181
182         {
183                 char *ptr = strchr (buffer, ' ') + 1;
184                 char *key;
185                 char *value;
186
187                 i = 0;
188                 while ((key = strtok (ptr, " \t")) != NULL)
189                 {
190                         ptr = NULL;
191                         value = strchr (key, '=');
192                         if (value == NULL)
193                                 continue;
194                         *value = '\0'; value++;
195
196                         values_names[i] = strdup (key);
197                         values[i] = atof (value);
198
199                         i++;
200                         if (i >= values_num)
201                                 break;
202                 }
203                 values_num = i;
204         }
205
206         *ret_values_num = values_num;
207         *ret_values = values;
208         *ret_values_names = values_names;
209
210         return (0);
211 } /* int get_values */
212
213 static void usage (const char *name)
214 {
215         fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> [options]\n"
216                         "\n"
217                         "Valid options are:\n"
218                         " -s <socket>   Path to collectd's UNIX-socket\n"
219                         " -n <v_spec>   Value specification to get from collectd\n"
220                         " -c <range>    Critical range\n"
221                         " -w <range>    Range for critical values\n",
222                         name);
223         exit (1);
224 } /* void usage */
225
226 int do_check_con_none (int values_num, double *values, char **values_names)
227 {
228         int i;
229
230         int num_critical = 0;
231         int num_warning  = 0;
232         int num_okay = 0;
233
234         for (i = 0; i < values_num; i++)
235         {
236                 if (values[i] == NAN)
237                         num_warning++;
238                 else if (match_range (&range_critical_g, values[i]) != 0)
239                         num_critical++;
240                 else if (match_range (&range_warning_g, values[i]) != 0)
241                         num_warning++;
242                 else
243                         num_okay++;
244         }
245
246         if ((num_critical != 0) || (values_num == 0))
247         {
248                 printf ("CRITICAL: %i critical, %i warning, %i okay\n",
249                                 num_critical, num_warning, num_okay);
250                 return (RET_CRITICAL);
251         }
252         else if (num_warning != 0)
253         {
254                 printf ("WARNING: %i warning, %i okay\n",
255                                 num_warning, num_okay);
256                 return (RET_WARNING);
257         }
258         else
259         {
260                 printf ("OKAY: %i okay\n", num_okay);
261                 return (RET_OKAY);
262         }
263
264         return (RET_UNKNOWN);
265 } /* int do_check_con_none */
266
267 int do_check_con_average (int values_num, double *values, char **values_names)
268 {
269         int i;
270         double total;
271         int total_num;
272
273         total = 0.0;
274         total_num = 0;
275         for (i = 0; i < values_num; i++)
276         {
277                 if (values[i] != NAN)
278                 {
279                         total += values[i];
280                         total_num++;
281                 }
282         }
283
284         if (total_num == 0)
285         {
286                 printf ("WARNING: No defined values found\n");
287                 return (RET_WARNING);
288         }
289
290         if (match_range (&range_critical_g, total / total_num) != 0)
291         {
292                 printf ("CRITICAL: Average = %lf\n",
293                                 (double) (total / total_num));
294                 return (RET_CRITICAL);
295         }
296         else if (match_range (&range_warning_g, total / total_num) != 0)
297         {
298                 printf ("WARNING: Average = %lf\n",
299                                 (double) (total / total_num));
300                 return (RET_WARNING);
301         }
302         else
303         {
304                 printf ("OKAY: Average = %lf\n",
305                                 (double) (total / total_num));
306                 return (RET_OKAY);
307         }
308
309         return (RET_UNKNOWN);
310 } /* int do_check_con_average */
311
312 int do_check_con_sum (int values_num, double *values, char **values_names)
313 {
314         int i;
315         double total;
316         int total_num;
317
318         total = 0.0;
319         total_num = 0;
320         for (i = 0; i < values_num; i++)
321         {
322                 if (values[i] != NAN)
323                 {
324                         total += values[i];
325                         total_num++;
326                 }
327         }
328
329         if (total_num == 0)
330         {
331                 printf ("WARNING: No defined values found\n");
332                 return (RET_WARNING);
333         }
334
335         if (match_range (&range_critical_g, total) != 0)
336         {
337                 printf ("CRITICAL: Sum = %lf\n", total);
338                 return (RET_CRITICAL);
339         }
340         else if (match_range (&range_warning_g, total) != 0)
341         {
342                 printf ("WARNING: Sum = %lf\n", total);
343                 return (RET_WARNING);
344         }
345         else
346         {
347                 printf ("OKAY: Sum = %lf\n", total);
348                 return (RET_OKAY);
349         }
350
351         return (RET_UNKNOWN);
352 } /* int do_check_con_sum */
353
354 int do_check (void)
355 {
356         double  *values;
357         char   **values_names;
358         int      values_num;
359         int i;
360
361         if (get_values (&values_num, &values, &values_names) != 0)
362         {
363                 fputs ("ERROR: Cannot get values from daemon\n", stdout);
364                 return (RET_CRITICAL);
365         }
366
367         for (i = 0; i < values_num; i++)
368                 printf ("%s=%lf\n", values_names[i], values[i]);
369
370         if (consolitation_g == CON_NONE)
371                 return (do_check_con_none (values_num, values, values_names));
372         else if (consolitation_g == CON_AVERAGE)
373                 return (do_check_con_average (values_num, values, values_names));
374         else if (consolitation_g == CON_SUM)
375                 return (do_check_con_sum (values_num, values, values_names));
376
377         return (RET_UNKNOWN);
378 }
379
380 int main (int argc, char **argv)
381 {
382         range_critical_g.min = NAN;
383         range_critical_g.max = NAN;
384         range_critical_g.invert = 0;
385
386         range_warning_g.min = NAN;
387         range_warning_g.max = NAN;
388         range_warning_g.invert = 0;
389
390         while (42)
391         {
392                 int c;
393
394                 c = getopt (argc, argv, "w:c:s:n:g:h");
395                 if (c < 0)
396                         break;
397
398                 switch (c)
399                 {
400                         case 'c':
401                                 parse_range (optarg, &range_critical_g);
402                                 break;
403                         case 'w':
404                                 parse_range (optarg, &range_warning_g);
405                                 break;
406                         case 's':
407                                 socket_file_g = optarg;
408                                 break;
409                         case 'n':
410                                 value_string_g = optarg;
411                                 break;
412                         case 'g':
413                                 if (strcasecmp (optarg, "none") == 0)
414                                         consolitation_g = CON_NONE;
415                                 else if (strcasecmp (optarg, "average") == 0)
416                                         consolitation_g = CON_AVERAGE;
417                                 else if (strcasecmp (optarg, "sum") == 0)
418                                         consolitation_g = CON_SUM;
419                                 else
420                                         usage (argv[0]);
421                                 break;
422                         default:
423                                 usage (argv[0]);
424                 } /* switch (c) */
425         }
426
427         if ((socket_file_g == NULL) || (value_string_g == NULL))
428                 usage (argv[0]);
429
430         return (do_check ());
431 } /* int main */