treewide: add blank line below collectd.h
[collectd.git] / src / olsrd.c
1 /**
2  * collectd - src/olsrd.c
3  * Copyright (C) 2009       Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28
29 #include "common.h"
30 #include "plugin.h"
31
32 #include <sys/types.h>
33 #include <netdb.h>
34 #include <netinet/in.h>
35 #include <netinet/tcp.h>
36
37 #define OLSRD_DEFAULT_NODE "localhost"
38 #define OLSRD_DEFAULT_SERVICE "2006"
39
40 static const char *config_keys[] =
41 {
42   "Host",
43   "Port",
44   "CollectLinks",
45   "CollectRoutes",
46   "CollectTopology"
47 };
48 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
49
50 static char *config_node = NULL;
51 static char *config_service = NULL;
52
53 #define OLSRD_WANT_NOT     0
54 #define OLSRD_WANT_SUMMARY 1
55 #define OLSRD_WANT_DETAIL  2
56 static int config_want_links    = OLSRD_WANT_DETAIL;
57 static int config_want_routes   = OLSRD_WANT_SUMMARY;
58 static int config_want_topology = OLSRD_WANT_SUMMARY;
59
60 static const char *olsrd_get_node (void) /* {{{ */
61 {
62   if (config_node != NULL)
63     return (config_node);
64   return (OLSRD_DEFAULT_NODE);
65 } /* }}} const char *olsrd_get_node */
66
67 static const char *olsrd_get_service (void) /* {{{ */
68 {
69   if (config_service != NULL)
70     return (config_service);
71   return (OLSRD_DEFAULT_SERVICE);
72 } /* }}} const char *olsrd_get_service */
73
74 static void olsrd_set_node (const char *node) /* {{{ */
75 {
76   char *tmp;
77   if (node == NULL)
78     return;
79   tmp = strdup (node);
80   if (tmp == NULL)
81     return;
82   config_node = tmp;
83 } /* }}} void olsrd_set_node */
84
85 static void olsrd_set_service (const char *service) /* {{{ */
86 {
87   char *tmp;
88   if (service == NULL)
89     return;
90   tmp = strdup (service);
91   if (tmp == NULL)
92     return;
93   config_service = tmp;
94 } /* }}} void olsrd_set_service */
95
96 static void olsrd_set_detail (int *varptr, const char *detail, /* {{{ */
97     const char *key)
98 {
99   if (strcasecmp ("No", detail) == 0)
100     *varptr = OLSRD_WANT_NOT;
101   else if (strcasecmp ("Summary", detail) == 0)
102     *varptr = OLSRD_WANT_SUMMARY;
103   else if (strcasecmp ("Detail", detail) == 0)
104     *varptr = OLSRD_WANT_DETAIL;
105   else
106   {
107     ERROR ("olsrd plugin: Invalid argument given to the `%s' configuration "
108         "option: `%s'. Expected: `No', `Summary', or `Detail'.",
109         key, detail);
110   }
111 } /* }}} void olsrd_set_detail */
112
113 /* Strip trailing newline characters. Returns length of string. */
114 static size_t strchomp (char *buffer) /* {{{ */
115 {
116   size_t buffer_len;
117
118   buffer_len = strlen (buffer);
119   while ((buffer_len > 0)
120       && ((buffer[buffer_len - 1] == '\r')
121         || (buffer[buffer_len - 1] == '\n')))
122   {
123     buffer_len--;
124     buffer[buffer_len] = 0;
125   }
126
127   return (buffer_len);
128 } /* }}} size_t strchomp */
129
130 static size_t strtabsplit (char *string, char **fields, size_t size) /* {{{ */
131 {
132   size_t i;
133   char *ptr;
134   char *saveptr;
135
136   i = 0;
137   ptr = string;
138   saveptr = NULL;
139   while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
140   {
141     ptr = NULL;
142     i++;
143
144     if (i >= size)
145       break;
146   }
147
148   return (i);
149 } /* }}} size_t strtabsplit */
150
151 static FILE *olsrd_connect (void) /* {{{ */
152 {
153   struct addrinfo  ai_hints = { 0 };
154   struct addrinfo *ai_list, *ai_ptr;
155   int              ai_return;
156
157   FILE *fh;
158
159 #ifdef AI_ADDRCONFIG
160   ai_hints.ai_flags   |= AI_ADDRCONFIG;
161 #endif
162   ai_hints.ai_family   = PF_UNSPEC;
163   ai_hints.ai_socktype = SOCK_STREAM;
164   ai_hints.ai_protocol = IPPROTO_TCP;
165
166   ai_list = NULL;
167   ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (),
168       &ai_hints, &ai_list);
169   if (ai_return != 0)
170   {
171     ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s",
172         olsrd_get_node (), olsrd_get_service (),
173         gai_strerror (ai_return));
174     return (NULL);
175   }
176
177   fh = NULL;
178   for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
179   {
180     int fd;
181     int status;
182     char errbuf[1024];
183
184     fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
185     if (fd < 0)
186     {
187       ERROR ("olsrd plugin: socket failed: %s",
188           sstrerror (errno, errbuf, sizeof (errbuf)));
189       continue;
190     }
191
192     status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
193     if (status != 0)
194     {
195       ERROR ("olsrd plugin: connect failed: %s",
196           sstrerror (errno, errbuf, sizeof (errbuf)));
197       close (fd);
198       continue;
199     }
200
201     fh = fdopen (fd, "r+");
202     if (fh == NULL)
203     {
204       ERROR ("olsrd plugin: fdopen failed.");
205       close (fd);
206       continue;
207     }
208
209     break;
210   } /* for (ai_ptr) */
211
212   freeaddrinfo (ai_list);
213
214   return (fh);
215 } /* }}} FILE *olsrd_connect */
216
217 __attribute__ ((nonnull(2)))
218 static void olsrd_submit (const char *plugin_instance, /* {{{ */
219     const char *type, const char *type_instance, gauge_t value)
220 {
221   value_t values[1];
222   value_list_t vl = VALUE_LIST_INIT;
223
224   values[0].gauge = value;
225
226   vl.values = values;
227   vl.values_len = 1;
228
229   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
230   sstrncpy (vl.plugin, "olsrd", sizeof (vl.plugin));
231   if (plugin_instance != NULL)
232     sstrncpy (vl.plugin_instance, plugin_instance,
233         sizeof (vl.plugin_instance));
234   sstrncpy (vl.type, type, sizeof (vl.type));
235   if (type_instance != NULL)
236     sstrncpy (vl.type_instance, type_instance,
237         sizeof (vl.type_instance));
238
239   plugin_dispatch_values (&vl);
240 } /* }}} void olsrd_submit */
241
242 static int olsrd_cb_ignore (int lineno, /* {{{ */
243     size_t fields_num, char **fields)
244 {
245   return (0);
246 } /* }}} int olsrd_cb_ignore */
247
248 static int olsrd_cb_links (int lineno, /* {{{ */
249     size_t fields_num, char **fields)
250 {
251   /* Fields:
252    *  0 = Local IP
253    *  1 = Remote IP
254    *  2 = Hyst.
255    *  3 = LQ
256    *  4 = NLQ
257    *  5 = Cost */
258
259   static uint32_t links_num;
260   static double    lq_sum;
261   static uint32_t  lq_num;
262   static double   nlq_sum;
263   static uint32_t nlq_num;
264
265   double lq;
266   double nlq;
267
268   char *endptr;
269
270   if (config_want_links == OLSRD_WANT_NOT)
271     return (0);
272
273   /* Special handling of the first line. */
274   if (lineno <= 0)
275   {
276     links_num = 0;
277     lq_sum = 0.0;
278     lq_num = 0;
279     nlq_sum = 0.0;
280     nlq_num = 0;
281
282     return (0);
283   }
284
285   /* Special handling of the last line. */
286   if (fields_num == 0)
287   {
288     DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num);
289     olsrd_submit (/* p.-inst = */ "links", /* type = */ "links",
290         /* t.-inst = */ NULL, (gauge_t) links_num);
291
292     lq = NAN;
293     if (lq_num > 0)
294       lq = lq_sum / ((double) lq_num);
295     DEBUG ("olsrd plugin: Average  LQ: %g", lq);
296     olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
297         "average-lq", lq);
298
299     nlq = NAN;
300     if (nlq_num > 0)
301       nlq = nlq_sum / ((double) nlq_num);
302     DEBUG ("olsrd plugin: Average NLQ: %g", nlq);
303     olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
304         "average-nlq", nlq);
305
306     return (0);
307   }
308
309   if (fields_num != 6)
310     return (-1);
311
312   links_num++;
313
314   errno = 0;
315   endptr = NULL;
316   lq = strtod (fields[3], &endptr);
317   if ((errno != 0) || (endptr == fields[3]))
318   {
319     ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]);
320   }
321   else
322   {
323     if (!isnan (lq))
324     {
325       lq_sum += lq;
326       lq_num++;
327     }
328
329     if (config_want_links == OLSRD_WANT_DETAIL)
330     {
331       char type_instance[DATA_MAX_NAME_LEN];
332
333       ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
334           fields[0], fields[1]);
335
336       DEBUG ("olsrd plugin: links: type_instance = %s;  lq = %g;",
337           type_instance, lq);
338       olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
339           type_instance, lq);
340     }
341   }
342
343   errno = 0;
344   endptr = NULL;
345   nlq = strtod (fields[4], &endptr);
346   if ((errno != 0) || (endptr == fields[4]))
347   {
348     ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
349   }
350   else
351   {
352     if (!isnan (nlq))
353     {
354       nlq_sum += nlq;
355       nlq_num++;
356     }
357
358     if (config_want_links == OLSRD_WANT_DETAIL)
359     {
360       char type_instance[DATA_MAX_NAME_LEN];
361
362       ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
363           fields[0], fields[1]);
364
365       DEBUG ("olsrd plugin: links: type_instance = %s; nlq = %g;",
366           type_instance, lq);
367       olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
368           type_instance, nlq);
369     }
370   }
371
372   return (0);
373 } /* }}} int olsrd_cb_links */
374
375 static int olsrd_cb_routes (int lineno, /* {{{ */
376     size_t fields_num, char **fields)
377 {
378   /* Fields:
379    *  0 = Destination
380    *  1 = Gateway IP
381    *  2 = Metric
382    *  3 = ETX
383    *  4 = Interface */
384
385   static uint32_t routes_num;
386   static uint32_t metric_sum;
387   static uint32_t metric_num;
388   static double   etx_sum;
389   static uint32_t etx_num;
390
391   uint32_t metric;
392   double etx;
393   char *endptr;
394
395   if (config_want_routes == OLSRD_WANT_NOT)
396     return (0);
397
398   /* Special handling of the first line */
399   if (lineno <= 0)
400   {
401     routes_num = 0;
402     metric_num = 0;
403     metric_sum = 0;
404     etx_sum = 0.0;
405     etx_num = 0;
406
407     return (0);
408   }
409
410   /* Special handling after the last line */
411   if (fields_num == 0)
412   {
413     double metric_avg;
414
415     DEBUG ("olsrd plugin: Number of routes: %"PRIu32, routes_num);
416     olsrd_submit (/* p.-inst = */ "routes", /* type = */ "routes",
417         /* t.-inst = */ NULL, (gauge_t) routes_num);
418
419     metric_avg = NAN;
420     if (metric_num > 0)
421       metric_avg = ((double) metric_sum) / ((double) metric_num);
422     DEBUG ("olsrd plugin: Average metric: %g", metric_avg);
423     olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
424         "average", metric_avg);
425
426     etx = NAN;
427     if (etx_num > 0)
428       etx = etx_sum / ((double) etx_sum);
429     DEBUG ("olsrd plugin: Average ETX: %g", etx);
430     olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
431         "average", etx);
432
433     return (0);
434   }
435
436   if (fields_num != 5)
437     return (-1);
438
439   routes_num++;
440
441   errno = 0;
442   endptr = NULL;
443   metric = (uint32_t) strtoul (fields[2], &endptr, 0);
444   if ((errno != 0) || (endptr == fields[2]))
445   {
446     ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]);
447   }
448   else
449   {
450     metric_num++;
451     metric_sum += metric;
452
453     if (config_want_routes == OLSRD_WANT_DETAIL)
454     {
455       DEBUG ("olsrd plugin: destination = %s; metric = %"PRIu32";",
456           fields[0], metric);
457       olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
458           /* t.-inst = */ fields[0], (gauge_t) metric);
459     }
460   }
461
462   errno = 0;
463   endptr = NULL;
464   etx = strtod (fields[3], &endptr);
465   if ((errno != 0) || (endptr == fields[3]))
466   {
467     ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]);
468   }
469   else
470   {
471     if (!isnan (etx))
472     {
473       etx_sum += etx;
474       etx_num++;
475     }
476
477     if (config_want_routes == OLSRD_WANT_DETAIL)
478     {
479       DEBUG ("olsrd plugin: destination = %s; etx = %g;",
480           fields[0], etx);
481       olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
482           /* t.-inst = */ fields[0], etx);
483     }
484   }
485
486   return (0);
487 } /* }}} int olsrd_cb_routes */
488
489 static int olsrd_cb_topology (int lineno, /* {{{ */
490     size_t fields_num, char **fields)
491 {
492   /* Fields:
493    *  0 = Dest. IP
494    *  1 = Last hop IP
495    *  2 = LQ
496    *  3 = NLQ
497    *  4 = Cost */
498
499   static double   lq_sum;
500   static uint32_t lq_num;
501
502   static uint32_t links_num;
503
504   double lq;
505   char *endptr;
506
507   if (config_want_topology == OLSRD_WANT_NOT)
508     return (0);
509
510   /* Special handling of the first line */
511   if (lineno <= 0)
512   {
513     lq_sum = 0.0;
514     lq_num = 0;
515     links_num = 0;
516
517     return (0);
518   }
519
520   /* Special handling after the last line */
521   if (fields_num == 0)
522   {
523     DEBUG ("olsrd plugin: topology: Number of links: %"PRIu32, links_num);
524     olsrd_submit (/* p.-inst = */ "topology", /* type = */ "links",
525         /* t.-inst = */ NULL, (gauge_t) links_num);
526
527     lq = NAN;
528     if (lq_num > 0)
529       lq = lq_sum / ((double) lq_sum);
530     DEBUG ("olsrd plugin: topology: Average link quality: %g", lq);
531     olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
532         /* t.-inst = */ "average", lq);
533
534     return (0);
535   }
536
537   if (fields_num != 5)
538     return (-1);
539
540   links_num++;
541
542   errno = 0;
543   endptr = NULL;
544   lq = strtod (fields[2], &endptr);
545   if ((errno != 0) || (endptr == fields[2]))
546   {
547     ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]);
548   }
549   else
550   {
551     if (!isnan (lq))
552     {
553       lq_sum += lq;
554       lq_num++;
555     }
556
557     if (config_want_topology == OLSRD_WANT_DETAIL)
558     {
559       char type_instance[DATA_MAX_NAME_LEN] = { 0 };
560
561       ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
562           fields[0], fields[1]);
563       DEBUG ("olsrd plugin: type_instance = %s; lq = %g;", type_instance, lq);
564       olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
565           type_instance, lq);
566     }
567   }
568
569   if (config_want_topology == OLSRD_WANT_DETAIL)
570   {
571     double nlq;
572
573     errno = 0;
574     endptr = NULL;
575     nlq = strtod (fields[3], &endptr);
576     if ((errno != 0) || (endptr == fields[3]))
577     {
578       ERROR ("olsrd plugin: Unable to parse NLQ: %s", fields[3]);
579     }
580     else
581     {
582       char type_instance[DATA_MAX_NAME_LEN] = { 0 };
583
584       ssnprintf (type_instance, sizeof (type_instance), "%s-%s-nlq",
585           fields[0], fields[1]);
586       DEBUG ("olsrd plugin: type_instance = %s; nlq = %g;", type_instance, nlq);
587       olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
588           type_instance, nlq);
589     }
590   }
591
592   return (0);
593 } /* }}} int olsrd_cb_topology */
594
595 static int olsrd_read_table (FILE *fh, /* {{{ */
596     int (*callback) (int lineno, size_t fields_num, char **fields))
597 {
598   char buffer[1024];
599   size_t buffer_len;
600
601   char *fields[32];
602   size_t fields_num;
603
604   int lineno;
605
606   lineno = 0;
607   while (fgets (buffer, sizeof (buffer), fh) != NULL)
608   {
609     /* An empty line ends the table. */
610     buffer_len = strchomp (buffer);
611     if (buffer_len == 0)
612     {
613       (*callback) (lineno, /* fields_num = */ 0, /* fields = */ NULL);
614       break;
615     }
616
617     fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
618
619     (*callback) (lineno, fields_num, fields);
620     lineno++;
621   } /* while (fgets) */
622
623   return (0);
624 } /* }}} int olsrd_read_table */
625
626 static int olsrd_config (const char *key, const char *value) /* {{{ */
627 {
628   if (strcasecmp ("Host", key) == 0)
629     olsrd_set_node (value);
630   else if (strcasecmp ("Port", key) == 0)
631     olsrd_set_service (value);
632   else if (strcasecmp ("CollectLinks", key) == 0)
633     olsrd_set_detail (&config_want_links, value, key);
634   else if (strcasecmp ("CollectRoutes", key) == 0)
635     olsrd_set_detail (&config_want_routes, value, key);
636   else if (strcasecmp ("CollectTopology", key) == 0)
637     olsrd_set_detail (&config_want_topology, value, key);
638   else
639   {
640     ERROR ("olsrd plugin: Unknown configuration option given: %s", key);
641     return (-1);
642   }
643
644   return (0);
645 } /* }}} int olsrd_config */
646
647 static int olsrd_read (void) /* {{{ */
648 {
649   FILE *fh;
650   char buffer[1024];
651   size_t buffer_len;
652
653   fh = olsrd_connect ();
654   if (fh == NULL)
655     return (-1);
656
657   fputs ("\r\n", fh);
658   fflush (fh);
659
660   while (fgets (buffer, sizeof (buffer), fh) != NULL)
661   {
662     buffer_len = strchomp (buffer);
663     if (buffer_len == 0)
664       continue;
665
666     if (strcmp ("Table: Links", buffer) == 0)
667       olsrd_read_table (fh, olsrd_cb_links);
668     else if (strcmp ("Table: Neighbors", buffer) == 0)
669       olsrd_read_table (fh, olsrd_cb_ignore);
670     else if (strcmp ("Table: Topology", buffer) == 0)
671       olsrd_read_table (fh, olsrd_cb_topology);
672     else if (strcmp ("Table: HNA", buffer) == 0)
673       olsrd_read_table (fh, olsrd_cb_ignore);
674     else if (strcmp ("Table: MID", buffer) == 0)
675       olsrd_read_table (fh, olsrd_cb_ignore);
676     else if (strcmp ("Table: Routes", buffer) == 0)
677       olsrd_read_table (fh, olsrd_cb_routes);
678     else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0)
679         || (strcmp ("Content-type: text/plain", buffer) == 0))
680     {
681       /* ignore */
682     }
683     else
684     {
685       DEBUG ("olsrd plugin: Unable to handle line: %s", buffer);
686     }
687   } /* while (fgets) */
688
689   fclose (fh);
690
691   return (0);
692 } /* }}} int olsrd_read */
693
694 static int olsrd_shutdown (void) /* {{{ */
695 {
696   sfree (config_node);
697   sfree (config_service);
698
699   return (0);
700 } /* }}} int olsrd_shutdown */
701
702 void module_register (void)
703 {
704   plugin_register_config ("olsrd", olsrd_config,
705       config_keys, config_keys_num);
706   plugin_register_read ("olsrd", olsrd_read);
707   plugin_register_shutdown ("olsrd", olsrd_shutdown);
708 } /* void module_register */
709
710 /* vim: set sw=2 sts=2 et fdm=marker : */