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 **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
26 #include <sys/types.h>
27 #include <netdb.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <netinet/tcp.h>
32 #define OLSRD_DEFAULT_NODE "localhost"
33 #define OLSRD_DEFAULT_SERVICE "2006"
35 static const char *config_keys[] =
36 {
37 "Host",
38 "Port",
39 "CollectLinks",
40 "CollectRoutes",
41 "CollectTopology"
42 };
43 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
45 static char *config_node = NULL;
46 static char *config_service = NULL;
48 #define OLSRD_WANT_NOT 0
49 #define OLSRD_WANT_SUMMARY 1
50 #define OLSRD_WANT_DETAIL 2
51 static int config_want_links = OLSRD_WANT_DETAIL;
52 static int config_want_routes = OLSRD_WANT_SUMMARY;
53 static int config_want_topology = OLSRD_WANT_SUMMARY;
55 static const char *olsrd_get_node (void) /* {{{ */
56 {
57 if (config_node != NULL)
58 return (config_node);
59 return (OLSRD_DEFAULT_NODE);
60 } /* }}} const char *olsrd_get_node */
62 static const char *olsrd_get_service (void) /* {{{ */
63 {
64 if (config_service != NULL)
65 return (config_service);
66 return (OLSRD_DEFAULT_SERVICE);
67 } /* }}} const char *olsrd_get_service */
69 static void olsrd_set_node (const char *node) /* {{{ */
70 {
71 char *tmp;
72 if (node == NULL)
73 return;
74 tmp = strdup (node);
75 if (tmp == NULL)
76 return;
77 config_node = tmp;
78 } /* }}} void olsrd_set_node */
80 static void olsrd_set_service (const char *service) /* {{{ */
81 {
82 char *tmp;
83 if (service == NULL)
84 return;
85 tmp = strdup (service);
86 if (tmp == NULL)
87 return;
88 config_service = tmp;
89 } /* }}} void olsrd_set_service */
91 static void olsrd_set_detail (int *varptr, const char *detail, /* {{{ */
92 const char *key)
93 {
94 if (strcasecmp ("No", detail) == 0)
95 *varptr = OLSRD_WANT_NOT;
96 else if (strcasecmp ("Summary", detail) == 0)
97 *varptr = OLSRD_WANT_SUMMARY;
98 else if (strcasecmp ("Detail", detail) == 0)
99 *varptr = OLSRD_WANT_DETAIL;
100 else
101 {
102 ERROR ("olsrd plugin: Invalid argument given to the `%s' configuration "
103 "option: `%s'. Expected: `No', `Summary', or `Detail'.",
104 key, detail);
105 }
106 } /* }}} void olsrd_set_detail */
108 /* Strip trailing newline characters. Returns length of string. */
109 static size_t strchomp (char *buffer) /* {{{ */
110 {
111 size_t buffer_len;
113 buffer_len = strlen (buffer);
114 while ((buffer_len > 0)
115 && ((buffer[buffer_len - 1] == '\r')
116 || (buffer[buffer_len - 1] == '\n')))
117 {
118 buffer_len--;
119 buffer[buffer_len] = 0;
120 }
122 return (buffer_len);
123 } /* }}} size_t strchomp */
125 static size_t strtabsplit (char *string, char **fields, size_t size) /* {{{ */
126 {
127 size_t i;
128 char *ptr;
129 char *saveptr;
131 i = 0;
132 ptr = string;
133 saveptr = NULL;
134 while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
135 {
136 ptr = NULL;
137 i++;
139 if (i >= size)
140 break;
141 }
143 return (i);
144 } /* }}} size_t strtabsplit */
146 static FILE *olsrd_connect (void) /* {{{ */
147 {
148 struct addrinfo ai_hints;
149 struct addrinfo *ai_list, *ai_ptr;
150 int ai_return;
152 FILE *fh;
154 memset (&ai_hints, 0, sizeof (ai_hints));
155 ai_hints.ai_flags = 0;
156 #ifdef AI_ADDRCONFIG
157 ai_hints.ai_flags |= AI_ADDRCONFIG;
158 #endif
159 ai_hints.ai_family = PF_UNSPEC;
160 ai_hints.ai_socktype = SOCK_STREAM;
161 ai_hints.ai_protocol = IPPROTO_TCP;
163 ai_list = NULL;
164 ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (),
165 &ai_hints, &ai_list);
166 if (ai_return != 0)
167 {
168 ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s",
169 olsrd_get_node (), olsrd_get_service (),
170 gai_strerror (ai_return));
171 return (NULL);
172 }
174 fh = NULL;
175 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
176 {
177 int fd;
178 int status;
179 char errbuf[1024];
181 fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
182 if (fd < 0)
183 {
184 ERROR ("olsrd plugin: socket failed: %s",
185 sstrerror (errno, errbuf, sizeof (errbuf)));
186 continue;
187 }
189 status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
190 if (status != 0)
191 {
192 ERROR ("olsrd plugin: connect failed: %s",
193 sstrerror (errno, errbuf, sizeof (errbuf)));
194 close (fd);
195 continue;
196 }
198 fh = fdopen (fd, "r+");
199 if (fh == NULL)
200 {
201 ERROR ("olsrd plugin: fdopen failed.");
202 close (fd);
203 continue;
204 }
206 break;
207 } /* for (ai_ptr) */
209 freeaddrinfo (ai_list);
211 return (fh);
212 } /* }}} FILE *olsrd_connect */
214 __attribute__ ((nonnull(2)))
215 static void olsrd_submit (const char *plugin_instance, /* {{{ */
216 const char *type, const char *type_instance, gauge_t value)
217 {
218 value_t values[1];
219 value_list_t vl = VALUE_LIST_INIT;
221 values[0].gauge = value;
223 vl.values = values;
224 vl.values_len = 1;
226 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
227 sstrncpy (vl.plugin, "olsrd", sizeof (vl.plugin));
228 if (plugin_instance != NULL)
229 sstrncpy (vl.plugin_instance, plugin_instance,
230 sizeof (vl.plugin_instance));
231 sstrncpy (vl.type, type, sizeof (vl.type));
232 if (type_instance != NULL)
233 sstrncpy (vl.type_instance, type_instance,
234 sizeof (vl.type_instance));
236 plugin_dispatch_values (&vl);
237 } /* }}} void olsrd_submit */
239 static int olsrd_cb_ignore (int lineno, /* {{{ */
240 size_t fields_num, char **fields)
241 {
242 return (0);
243 } /* }}} int olsrd_cb_ignore */
245 static int olsrd_cb_links (int lineno, /* {{{ */
246 size_t fields_num, char **fields)
247 {
248 /* Fields:
249 * 0 = Local IP
250 * 1 = Remote IP
251 * 2 = Hyst.
252 * 3 = LQ
253 * 4 = NLQ
254 * 5 = Cost */
256 static uint32_t links_num;
257 static double lq_sum;
258 static uint32_t lq_num;
259 static double nlq_sum;
260 static uint32_t nlq_num;
262 double lq;
263 double nlq;
265 char *endptr;
267 if (config_want_links == OLSRD_WANT_NOT)
268 return (0);
270 /* Special handling of the first line. */
271 if (lineno <= 0)
272 {
273 links_num = 0;
274 lq_sum = 0.0;
275 lq_num = 0;
276 nlq_sum = 0.0;
277 nlq_num = 0;
279 return (0);
280 }
282 /* Special handling of the last line. */
283 if (fields_num == 0)
284 {
285 DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num);
286 olsrd_submit (/* p.-inst = */ "links", /* type = */ "links",
287 /* t.-inst = */ NULL, (gauge_t) links_num);
289 lq = NAN;
290 if (lq_num > 0)
291 lq = lq_sum / ((double) lq_num);
292 DEBUG ("olsrd plugin: Average LQ: %g", lq);
293 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
294 "average-lq", lq);
296 nlq = NAN;
297 if (nlq_num > 0)
298 nlq = nlq_sum / ((double) nlq_num);
299 DEBUG ("olsrd plugin: Average NLQ: %g", nlq);
300 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
301 "average-nlq", nlq);
303 return (0);
304 }
306 if (fields_num != 6)
307 return (-1);
309 links_num++;
311 errno = 0;
312 endptr = NULL;
313 lq = strtod (fields[3], &endptr);
314 if ((errno != 0) || (endptr == fields[3]))
315 {
316 ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]);
317 }
318 else
319 {
320 if (!isnan (lq))
321 {
322 lq_sum += lq;
323 lq_num++;
324 }
326 if (config_want_links == OLSRD_WANT_DETAIL)
327 {
328 char type_instance[DATA_MAX_NAME_LEN];
330 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
331 fields[0], fields[1]);
333 DEBUG ("olsrd plugin: links: type_instance = %s; lq = %g;",
334 type_instance, lq);
335 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
336 type_instance, lq);
337 }
338 }
340 errno = 0;
341 endptr = NULL;
342 nlq = strtod (fields[4], &endptr);
343 if ((errno != 0) || (endptr == fields[4]))
344 {
345 ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
346 }
347 else
348 {
349 if (!isnan (nlq))
350 {
351 nlq_sum += nlq;
352 nlq_num++;
353 }
355 if (config_want_links == OLSRD_WANT_DETAIL)
356 {
357 char type_instance[DATA_MAX_NAME_LEN];
359 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
360 fields[0], fields[1]);
362 DEBUG ("olsrd plugin: links: type_instance = %s; nlq = %g;",
363 type_instance, lq);
364 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
365 type_instance, nlq);
366 }
367 }
369 return (0);
370 } /* }}} int olsrd_cb_links */
372 static int olsrd_cb_routes (int lineno, /* {{{ */
373 size_t fields_num, char **fields)
374 {
375 /* Fields:
376 * 0 = Destination
377 * 1 = Gateway IP
378 * 2 = Metric
379 * 3 = ETX
380 * 4 = Interface */
382 static uint32_t routes_num;
383 static uint32_t metric_sum;
384 static uint32_t metric_num;
385 static double etx_sum;
386 static uint32_t etx_num;
388 uint32_t metric;
389 double etx;
390 char *endptr;
392 if (config_want_routes == OLSRD_WANT_NOT)
393 return (0);
395 /* Special handling of the first line */
396 if (lineno <= 0)
397 {
398 routes_num = 0;
399 metric_num = 0;
400 metric_sum = 0;
401 etx_sum = 0.0;
402 etx_num = 0;
404 return (0);
405 }
407 /* Special handling after the last line */
408 if (fields_num == 0)
409 {
410 double metric_avg;
412 DEBUG ("olsrd plugin: Number of routes: %"PRIu32, routes_num);
413 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "routes",
414 /* t.-inst = */ NULL, (gauge_t) routes_num);
416 metric_avg = NAN;
417 if (metric_num > 0)
418 metric_avg = ((double) metric_sum) / ((double) metric_num);
419 DEBUG ("olsrd plugin: Average metric: %g", metric_avg);
420 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
421 "average", metric_avg);
423 etx = NAN;
424 if (etx_num > 0)
425 etx = etx_sum / ((double) etx_sum);
426 DEBUG ("olsrd plugin: Average ETX: %g", etx);
427 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
428 "average", etx);
430 return (0);
431 }
433 if (fields_num != 5)
434 return (-1);
436 routes_num++;
438 errno = 0;
439 endptr = NULL;
440 metric = (uint32_t) strtoul (fields[2], &endptr, 0);
441 if ((errno != 0) || (endptr == fields[2]))
442 {
443 ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]);
444 }
445 else
446 {
447 metric_num++;
448 metric_sum += metric;
450 if (config_want_routes == OLSRD_WANT_DETAIL)
451 {
452 DEBUG ("olsrd plugin: destination = %s; metric = %"PRIu32";",
453 fields[0], metric);
454 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
455 /* t.-inst = */ fields[0], (gauge_t) metric);
456 }
457 }
459 errno = 0;
460 endptr = NULL;
461 etx = strtod (fields[3], &endptr);
462 if ((errno != 0) || (endptr == fields[3]))
463 {
464 ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]);
465 }
466 else
467 {
468 if (!isnan (etx))
469 {
470 etx_sum += etx;
471 etx_num++;
472 }
474 if (config_want_routes == OLSRD_WANT_DETAIL)
475 {
476 DEBUG ("olsrd plugin: destination = %s; etx = %g;",
477 fields[0], etx);
478 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
479 /* t.-inst = */ fields[0], etx);
480 }
481 }
483 return (0);
484 } /* }}} int olsrd_cb_routes */
486 static int olsrd_cb_topology (int lineno, /* {{{ */
487 size_t fields_num, char **fields)
488 {
489 /* Fields:
490 * 0 = Dest. IP
491 * 1 = Last hop IP
492 * 2 = LQ
493 * 3 = NLQ
494 * 4 = Cost */
496 static double lq_sum;
497 static uint32_t lq_num;
499 static uint32_t links_num;
501 double lq;
502 char *endptr;
504 if (config_want_topology == OLSRD_WANT_NOT)
505 return (0);
507 /* Special handling of the first line */
508 if (lineno <= 0)
509 {
510 lq_sum = 0.0;
511 lq_num = 0;
512 links_num = 0;
514 return (0);
515 }
517 /* Special handling after the last line */
518 if (fields_num == 0)
519 {
520 DEBUG ("olsrd plugin: topology: Number of links: %"PRIu32, links_num);
521 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "links",
522 /* t.-inst = */ NULL, (gauge_t) links_num);
524 lq = NAN;
525 if (lq_num > 0)
526 lq = lq_sum / ((double) lq_sum);
527 DEBUG ("olsrd plugin: topology: Average link quality: %g", lq);
528 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
529 /* t.-inst = */ "average", lq);
531 return (0);
532 }
534 if (fields_num != 5)
535 return (-1);
537 links_num++;
539 errno = 0;
540 endptr = NULL;
541 lq = strtod (fields[2], &endptr);
542 if ((errno != 0) || (endptr == fields[2]))
543 {
544 ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]);
545 }
546 else
547 {
548 if (!isnan (lq))
549 {
550 lq_sum += lq;
551 lq_num++;
552 }
554 if (config_want_topology == OLSRD_WANT_DETAIL)
555 {
556 char type_instance[DATA_MAX_NAME_LEN];
558 memset (type_instance, 0, sizeof (type_instance));
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 }
567 if (config_want_topology == OLSRD_WANT_DETAIL)
568 {
569 double nlq;
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];
582 memset (type_instance, 0, sizeof (type_instance));
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 }
591 return (0);
592 } /* }}} int olsrd_cb_topology */
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;
600 char *fields[32];
601 size_t fields_num;
603 int lineno;
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 }
616 fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
618 (*callback) (lineno, fields_num, fields);
619 lineno++;
620 } /* while (fgets) */
622 return (0);
623 } /* }}} int olsrd_read_table */
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 }
643 return (0);
644 } /* }}} int olsrd_config */
646 static int olsrd_read (void) /* {{{ */
647 {
648 FILE *fh;
649 char buffer[1024];
650 size_t buffer_len;
652 fh = olsrd_connect ();
653 if (fh == NULL)
654 return (-1);
656 fputs ("\r\n", fh);
657 fflush (fh);
659 while (fgets (buffer, sizeof (buffer), fh) != NULL)
660 {
661 buffer_len = strchomp (buffer);
662 if (buffer_len <= 0)
663 continue;
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) */
688 fclose (fh);
690 return (0);
691 } /* }}} int olsrd_read */
693 static int olsrd_shutdown (void) /* {{{ */
694 {
695 sfree (config_node);
696 sfree (config_service);
698 return (0);
699 } /* }}} int olsrd_shutdown */
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 */
709 /* vim: set sw=2 sts=2 et fdm=marker : */