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