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 <sys/socket.h>
28 #include <netdb.h>
30 #define OLSRD_DEFAULT_NODE "localhost"
31 #define OLSRD_DEFAULT_SERVICE "2006"
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);
43 static char *config_node = NULL;
44 static char *config_service = NULL;
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;
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 */
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 */
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 */
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 */
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 */
106 /* Strip trailing newline characters. Returns length of string. */
107 static size_t strchomp (char *buffer) /* {{{ */
108 {
109 size_t buffer_len;
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 }
120 return (buffer_len);
121 } /* }}} size_t strchomp */
123 static size_t strtabsplit (char *string, char **fields, size_t size) /* {{{ */
124 {
125 size_t i;
126 char *ptr;
127 char *saveptr;
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++;
137 if (i >= size)
138 break;
139 }
141 return (i);
142 } /* }}} size_t strtabsplit */
144 static FILE *olsrd_connect (void) /* {{{ */
145 {
146 struct addrinfo ai_hints;
147 struct addrinfo *ai_list, *ai_ptr;
148 int ai_return;
150 FILE *fh;
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;
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 }
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];
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 }
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 }
196 fh = fdopen (fd, "r+");
197 if (fh == NULL)
198 {
199 ERROR ("olsrd plugin: fdopen failed.");
200 close (fd);
201 continue;
202 }
204 break;
205 } /* for (ai_ptr) */
207 freeaddrinfo (ai_list);
209 return (fh);
210 } /* }}} FILE *olsrd_connect */
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;
219 values[0].gauge = value;
221 vl.values = values;
222 vl.values_len = 1;
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));
234 plugin_dispatch_values (&vl);
235 } /* }}} void olsrd_submit */
237 static int olsrd_cb_ignore (int lineno, /* {{{ */
238 size_t fields_num, char **fields)
239 {
240 return (0);
241 } /* }}} int olsrd_cb_ignore */
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 */
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;
260 double lq; /* tx */
261 double nlq; /* rx */
263 char *endptr;
265 if (config_want_links == OLSRD_WANT_NOT)
266 return (0);
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;
277 return (0);
278 }
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);
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-tx", lq);
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-rx", nlq);
301 return (0);
302 }
304 if (fields_num != 6)
305 return (-1);
307 links_num++;
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 }
324 if (config_want_links == OLSRD_WANT_DETAIL)
325 {
326 char type_instance[DATA_MAX_NAME_LEN];
328 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-tx",
329 fields[0], fields[1]);
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 }
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 }
353 if (config_want_links == OLSRD_WANT_DETAIL)
354 {
355 char type_instance[DATA_MAX_NAME_LEN];
357 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
358 fields[0], fields[1]);
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 }
367 return (0);
368 } /* }}} int olsrd_cb_links */
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 */
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;
386 uint32_t metric;
387 double etx;
388 char *endptr;
390 if (config_want_routes == OLSRD_WANT_NOT)
391 return (0);
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;
402 return (0);
403 }
405 /* Special handling after the last line */
406 if (fields_num == 0)
407 {
408 double metric_avg;
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);
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);
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);
428 return (0);
429 }
431 if (fields_num != 5)
432 return (-1);
434 routes_num++;
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;
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 }
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 }
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 }
481 return (0);
482 } /* }}} int olsrd_cb_routes */
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 */
494 static double lq_sum;
495 static uint32_t lq_num;
497 static uint32_t links_num;
499 double lq;
500 char *endptr;
502 if (config_want_topology == OLSRD_WANT_NOT)
503 return (0);
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;
512 return (0);
513 }
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);
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);
529 return (0);
530 }
532 if (fields_num != 5)
533 return (-1);
535 links_num++;
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 }
552 if (config_want_topology == OLSRD_WANT_DETAIL)
553 {
554 char type_instance[DATA_MAX_NAME_LEN];
556 memset (type_instance, 0, sizeof (type_instance));
557 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
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 }
565 if (config_want_topology == OLSRD_WANT_DETAIL)
566 {
567 double nlq;
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];
580 memset (type_instance, 0, sizeof (type_instance));
581 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-tx",
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 }
589 return (0);
590 } /* }}} int olsrd_cb_topology */
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;
598 char *fields[32];
599 size_t fields_num;
601 int lineno;
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 }
614 fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
616 (*callback) (lineno, fields_num, fields);
617 lineno++;
618 } /* while (fgets) */
620 return (0);
621 } /* }}} int olsrd_read_table */
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 }
641 return (0);
642 } /* }}} int olsrd_config */
644 static int olsrd_read (void) /* {{{ */
645 {
646 FILE *fh;
647 char buffer[1024];
648 size_t buffer_len;
650 fh = olsrd_connect ();
651 if (fh == NULL)
652 return (-1);
654 while (fgets (buffer, sizeof (buffer), fh) != NULL)
655 {
656 buffer_len = strchomp (buffer);
657 if (buffer_len <= 0)
658 continue;
660 if (strcmp ("Table: Links", buffer) == 0)
661 olsrd_read_table (fh, olsrd_cb_links);
662 else if (strcmp ("Table: Neighbors", buffer) == 0)
663 olsrd_read_table (fh, olsrd_cb_ignore);
664 else if (strcmp ("Table: Topology", buffer) == 0)
665 olsrd_read_table (fh, olsrd_cb_topology);
666 else if (strcmp ("Table: HNA", buffer) == 0)
667 olsrd_read_table (fh, olsrd_cb_ignore);
668 else if (strcmp ("Table: MID", buffer) == 0)
669 olsrd_read_table (fh, olsrd_cb_ignore);
670 else if (strcmp ("Table: Routes", buffer) == 0)
671 olsrd_read_table (fh, olsrd_cb_routes);
672 else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0)
673 || (strcmp ("Content-type: text/plain", buffer) == 0))
674 {
675 /* ignore */
676 }
677 else
678 {
679 DEBUG ("olsrd plugin: Unable to handle line: %s", buffer);
680 }
681 } /* while (fgets) */
683 fclose (fh);
685 return (0);
686 } /* }}} int olsrd_read */
688 static int olsrd_shutdown (void) /* {{{ */
689 {
690 sfree (config_node);
691 sfree (config_service);
693 return (0);
694 } /* }}} int olsrd_shutdown */
696 void module_register (void)
697 {
698 plugin_register_config ("olsrd", olsrd_config,
699 config_keys, config_keys_num);
700 plugin_register_read ("olsrd", olsrd_read);
701 plugin_register_shutdown ("olsrd", olsrd_shutdown);
702 } /* void module_register */
704 /* vim: set sw=2 sts=2 et fdm=marker : */