Merge branch 'collectd-5.5' into collectd-5.6
[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_list;
154   int              ai_return;
155
156   FILE *fh;
157
158   struct addrinfo ai_hints = {
159     .ai_family   = AF_UNSPEC,
160     .ai_flags    = AI_ADDRCONFIG,
161     .ai_protocol = IPPROTO_TCP,
162     .ai_socktype = SOCK_STREAM
163   };
164
165   ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (),
166       &ai_hints, &ai_list);
167   if (ai_return != 0)
168   {
169     ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s",
170         olsrd_get_node (), olsrd_get_service (),
171         gai_strerror (ai_return));
172     return (NULL);
173   }
174
175   fh = NULL;
176   for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
177   {
178     int fd;
179     int status;
180     char errbuf[1024];
181
182     fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
183     if (fd < 0)
184     {
185       ERROR ("olsrd plugin: socket failed: %s",
186           sstrerror (errno, errbuf, sizeof (errbuf)));
187       continue;
188     }
189
190     status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
191     if (status != 0)
192     {
193       ERROR ("olsrd plugin: connect failed: %s",
194           sstrerror (errno, errbuf, sizeof (errbuf)));
195       close (fd);
196       continue;
197     }
198
199     fh = fdopen (fd, "r+");
200     if (fh == NULL)
201     {
202       ERROR ("olsrd plugin: fdopen failed.");
203       close (fd);
204       continue;
205     }
206
207     break;
208   } /* for (ai_ptr) */
209
210   freeaddrinfo (ai_list);
211
212   return (fh);
213 } /* }}} FILE *olsrd_connect */
214
215 __attribute__ ((nonnull(2)))
216 static void olsrd_submit (const char *plugin_instance, /* {{{ */
217     const char *type, const char *type_instance, gauge_t value)
218 {
219   value_t values[1];
220   value_list_t vl = VALUE_LIST_INIT;
221
222   values[0].gauge = value;
223
224   vl.values = values;
225   vl.values_len = 1;
226
227   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
228   sstrncpy (vl.plugin, "olsrd", sizeof (vl.plugin));
229   if (plugin_instance != NULL)
230     sstrncpy (vl.plugin_instance, plugin_instance,
231         sizeof (vl.plugin_instance));
232   sstrncpy (vl.type, type, sizeof (vl.type));
233   if (type_instance != NULL)
234     sstrncpy (vl.type_instance, type_instance,
235         sizeof (vl.type_instance));
236
237   plugin_dispatch_values (&vl);
238 } /* }}} void olsrd_submit */
239
240 static int olsrd_cb_ignore (int lineno, /* {{{ */
241     size_t fields_num, char **fields)
242 {
243   return (0);
244 } /* }}} int olsrd_cb_ignore */
245
246 static int olsrd_cb_links (int lineno, /* {{{ */
247     size_t fields_num, char **fields)
248 {
249   /* Fields:
250    *  0 = Local IP
251    *  1 = Remote IP
252    *  2 = Hyst.
253    *  3 = LQ
254    *  4 = NLQ
255    *  5 = Cost */
256
257   static uint32_t links_num;
258   static double    lq_sum;
259   static uint32_t  lq_num;
260   static double   nlq_sum;
261   static uint32_t nlq_num;
262
263   double lq;
264   double nlq;
265
266   char *endptr;
267
268   if (config_want_links == OLSRD_WANT_NOT)
269     return (0);
270
271   /* Special handling of the first line. */
272   if (lineno <= 0)
273   {
274     links_num = 0;
275     lq_sum = 0.0;
276     lq_num = 0;
277     nlq_sum = 0.0;
278     nlq_num = 0;
279
280     return (0);
281   }
282
283   /* Special handling of the last line. */
284   if (fields_num == 0)
285   {
286     DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num);
287     olsrd_submit (/* p.-inst = */ "links", /* type = */ "links",
288         /* t.-inst = */ NULL, (gauge_t) links_num);
289
290     lq = NAN;
291     if (lq_num > 0)
292       lq = lq_sum / ((double) lq_num);
293     DEBUG ("olsrd plugin: Average  LQ: %g", lq);
294     olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
295         "average-lq", lq);
296
297     nlq = NAN;
298     if (nlq_num > 0)
299       nlq = nlq_sum / ((double) nlq_num);
300     DEBUG ("olsrd plugin: Average NLQ: %g", nlq);
301     olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
302         "average-nlq", nlq);
303
304     return (0);
305   }
306
307   if (fields_num != 6)
308     return (-1);
309
310   links_num++;
311
312   errno = 0;
313   endptr = NULL;
314   lq = strtod (fields[3], &endptr);
315   if ((errno != 0) || (endptr == fields[3]))
316   {
317     ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]);
318   }
319   else
320   {
321     if (!isnan (lq))
322     {
323       lq_sum += lq;
324       lq_num++;
325     }
326
327     if (config_want_links == OLSRD_WANT_DETAIL)
328     {
329       char type_instance[DATA_MAX_NAME_LEN];
330
331       ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
332           fields[0], fields[1]);
333
334       DEBUG ("olsrd plugin: links: type_instance = %s;  lq = %g;",
335           type_instance, lq);
336       olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
337           type_instance, lq);
338     }
339   }
340
341   errno = 0;
342   endptr = NULL;
343   nlq = strtod (fields[4], &endptr);
344   if ((errno != 0) || (endptr == fields[4]))
345   {
346     ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
347   }
348   else
349   {
350     if (!isnan (nlq))
351     {
352       nlq_sum += nlq;
353       nlq_num++;
354     }
355
356     if (config_want_links == OLSRD_WANT_DETAIL)
357     {
358       char type_instance[DATA_MAX_NAME_LEN];
359
360       ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
361           fields[0], fields[1]);
362
363       DEBUG ("olsrd plugin: links: type_instance = %s; nlq = %g;",
364           type_instance, lq);
365       olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
366           type_instance, nlq);
367     }
368   }
369
370   return (0);
371 } /* }}} int olsrd_cb_links */
372
373 static int olsrd_cb_routes (int lineno, /* {{{ */
374     size_t fields_num, char **fields)
375 {
376   /* Fields:
377    *  0 = Destination
378    *  1 = Gateway IP
379    *  2 = Metric
380    *  3 = ETX
381    *  4 = Interface */
382
383   static uint32_t routes_num;
384   static uint32_t metric_sum;
385   static uint32_t metric_num;
386   static double   etx_sum;
387   static uint32_t etx_num;
388
389   uint32_t metric;
390   double etx;
391   char *endptr;
392
393   if (config_want_routes == OLSRD_WANT_NOT)
394     return (0);
395
396   /* Special handling of the first line */
397   if (lineno <= 0)
398   {
399     routes_num = 0;
400     metric_num = 0;
401     metric_sum = 0;
402     etx_sum = 0.0;
403     etx_num = 0;
404
405     return (0);
406   }
407
408   /* Special handling after the last line */
409   if (fields_num == 0)
410   {
411     double metric_avg;
412
413     DEBUG ("olsrd plugin: Number of routes: %"PRIu32, routes_num);
414     olsrd_submit (/* p.-inst = */ "routes", /* type = */ "routes",
415         /* t.-inst = */ NULL, (gauge_t) routes_num);
416
417     metric_avg = NAN;
418     if (metric_num > 0)
419       metric_avg = ((double) metric_sum) / ((double) metric_num);
420     DEBUG ("olsrd plugin: Average metric: %g", metric_avg);
421     olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
422         "average", metric_avg);
423
424     etx = NAN;
425     if (etx_num > 0)
426       etx = etx_sum / ((double) etx_sum);
427     DEBUG ("olsrd plugin: Average ETX: %g", etx);
428     olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
429         "average", etx);
430
431     return (0);
432   }
433
434   if (fields_num != 5)
435     return (-1);
436
437   routes_num++;
438
439   errno = 0;
440   endptr = NULL;
441   metric = (uint32_t) strtoul (fields[2], &endptr, 0);
442   if ((errno != 0) || (endptr == fields[2]))
443   {
444     ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]);
445   }
446   else
447   {
448     metric_num++;
449     metric_sum += metric;
450
451     if (config_want_routes == OLSRD_WANT_DETAIL)
452     {
453       DEBUG ("olsrd plugin: destination = %s; metric = %"PRIu32";",
454           fields[0], metric);
455       olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
456           /* t.-inst = */ fields[0], (gauge_t) metric);
457     }
458   }
459
460   errno = 0;
461   endptr = NULL;
462   etx = strtod (fields[3], &endptr);
463   if ((errno != 0) || (endptr == fields[3]))
464   {
465     ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]);
466   }
467   else
468   {
469     if (!isnan (etx))
470     {
471       etx_sum += etx;
472       etx_num++;
473     }
474
475     if (config_want_routes == OLSRD_WANT_DETAIL)
476     {
477       DEBUG ("olsrd plugin: destination = %s; etx = %g;",
478           fields[0], etx);
479       olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
480           /* t.-inst = */ fields[0], etx);
481     }
482   }
483
484   return (0);
485 } /* }}} int olsrd_cb_routes */
486
487 static int olsrd_cb_topology (int lineno, /* {{{ */
488     size_t fields_num, char **fields)
489 {
490   /* Fields:
491    *  0 = Dest. IP
492    *  1 = Last hop IP
493    *  2 = LQ
494    *  3 = NLQ
495    *  4 = Cost */
496
497   static double   lq_sum;
498   static uint32_t lq_num;
499
500   static uint32_t links_num;
501
502   double lq;
503   char *endptr;
504
505   if (config_want_topology == OLSRD_WANT_NOT)
506     return (0);
507
508   /* Special handling of the first line */
509   if (lineno <= 0)
510   {
511     lq_sum = 0.0;
512     lq_num = 0;
513     links_num = 0;
514
515     return (0);
516   }
517
518   /* Special handling after the last line */
519   if (fields_num == 0)
520   {
521     DEBUG ("olsrd plugin: topology: Number of links: %"PRIu32, links_num);
522     olsrd_submit (/* p.-inst = */ "topology", /* type = */ "links",
523         /* t.-inst = */ NULL, (gauge_t) links_num);
524
525     lq = NAN;
526     if (lq_num > 0)
527       lq = lq_sum / ((double) lq_sum);
528     DEBUG ("olsrd plugin: topology: Average link quality: %g", lq);
529     olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
530         /* t.-inst = */ "average", lq);
531
532     return (0);
533   }
534
535   if (fields_num != 5)
536     return (-1);
537
538   links_num++;
539
540   errno = 0;
541   endptr = NULL;
542   lq = strtod (fields[2], &endptr);
543   if ((errno != 0) || (endptr == fields[2]))
544   {
545     ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]);
546   }
547   else
548   {
549     if (!isnan (lq))
550     {
551       lq_sum += lq;
552       lq_num++;
553     }
554
555     if (config_want_topology == OLSRD_WANT_DETAIL)
556     {
557       char type_instance[DATA_MAX_NAME_LEN] = { 0 };
558
559       ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
560           fields[0], fields[1]);
561       DEBUG ("olsrd plugin: type_instance = %s; lq = %g;", type_instance, lq);
562       olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
563           type_instance, lq);
564     }
565   }
566
567   if (config_want_topology == OLSRD_WANT_DETAIL)
568   {
569     double nlq;
570
571     errno = 0;
572     endptr = NULL;
573     nlq = strtod (fields[3], &endptr);
574     if ((errno != 0) || (endptr == fields[3]))
575     {
576       ERROR ("olsrd plugin: Unable to parse NLQ: %s", fields[3]);
577     }
578     else
579     {
580       char type_instance[DATA_MAX_NAME_LEN] = { 0 };
581
582       ssnprintf (type_instance, sizeof (type_instance), "%s-%s-nlq",
583           fields[0], fields[1]);
584       DEBUG ("olsrd plugin: type_instance = %s; nlq = %g;", type_instance, nlq);
585       olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
586           type_instance, nlq);
587     }
588   }
589
590   return (0);
591 } /* }}} int olsrd_cb_topology */
592
593 static int olsrd_read_table (FILE *fh, /* {{{ */
594     int (*callback) (int lineno, size_t fields_num, char **fields))
595 {
596   char buffer[1024];
597   size_t buffer_len;
598
599   char *fields[32];
600   size_t fields_num;
601
602   int lineno;
603
604   lineno = 0;
605   while (fgets (buffer, sizeof (buffer), fh) != NULL)
606   {
607     /* An empty line ends the table. */
608     buffer_len = strchomp (buffer);
609     if (buffer_len == 0)
610     {
611       (*callback) (lineno, /* fields_num = */ 0, /* fields = */ NULL);
612       break;
613     }
614
615     fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
616
617     (*callback) (lineno, fields_num, fields);
618     lineno++;
619   } /* while (fgets) */
620
621   return (0);
622 } /* }}} int olsrd_read_table */
623
624 static int olsrd_config (const char *key, const char *value) /* {{{ */
625 {
626   if (strcasecmp ("Host", key) == 0)
627     olsrd_set_node (value);
628   else if (strcasecmp ("Port", key) == 0)
629     olsrd_set_service (value);
630   else if (strcasecmp ("CollectLinks", key) == 0)
631     olsrd_set_detail (&config_want_links, value, key);
632   else if (strcasecmp ("CollectRoutes", key) == 0)
633     olsrd_set_detail (&config_want_routes, value, key);
634   else if (strcasecmp ("CollectTopology", key) == 0)
635     olsrd_set_detail (&config_want_topology, value, key);
636   else
637   {
638     ERROR ("olsrd plugin: Unknown configuration option given: %s", key);
639     return (-1);
640   }
641
642   return (0);
643 } /* }}} int olsrd_config */
644
645 static int olsrd_read (void) /* {{{ */
646 {
647   FILE *fh;
648   char buffer[1024];
649   size_t buffer_len;
650
651   fh = olsrd_connect ();
652   if (fh == NULL)
653     return (-1);
654
655   fputs ("\r\n", fh);
656   fflush (fh);
657
658   while (fgets (buffer, sizeof (buffer), fh) != NULL)
659   {
660     buffer_len = strchomp (buffer);
661     if (buffer_len == 0)
662       continue;
663
664     if (strcmp ("Table: Links", buffer) == 0)
665       olsrd_read_table (fh, olsrd_cb_links);
666     else if (strcmp ("Table: Neighbors", buffer) == 0)
667       olsrd_read_table (fh, olsrd_cb_ignore);
668     else if (strcmp ("Table: Topology", buffer) == 0)
669       olsrd_read_table (fh, olsrd_cb_topology);
670     else if (strcmp ("Table: HNA", buffer) == 0)
671       olsrd_read_table (fh, olsrd_cb_ignore);
672     else if (strcmp ("Table: MID", buffer) == 0)
673       olsrd_read_table (fh, olsrd_cb_ignore);
674     else if (strcmp ("Table: Routes", buffer) == 0)
675       olsrd_read_table (fh, olsrd_cb_routes);
676     else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0)
677         || (strcmp ("Content-type: text/plain", buffer) == 0))
678     {
679       /* ignore */
680     }
681     else
682     {
683       DEBUG ("olsrd plugin: Unable to handle line: %s", buffer);
684     }
685   } /* while (fgets) */
686
687   fclose (fh);
688
689   return (0);
690 } /* }}} int olsrd_read */
691
692 static int olsrd_shutdown (void) /* {{{ */
693 {
694   sfree (config_node);
695   sfree (config_service);
696
697   return (0);
698 } /* }}} int olsrd_shutdown */
699
700 void module_register (void)
701 {
702   plugin_register_config ("olsrd", olsrd_config,
703       config_keys, config_keys_num);
704   plugin_register_read ("olsrd", olsrd_read);
705   plugin_register_shutdown ("olsrd", olsrd_shutdown);
706 } /* void module_register */
707
708 /* vim: set sw=2 sts=2 et fdm=marker : */