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